Background watchers

Poll a resource field until it changes or settles with revenexx watch — block a script on a long-running import, or leave a watcher running in the REPL and the TUI while you keep working.

A long-running operation — an import, a deployment, an order moving through fulfilment — usually ends the same way: re-running the same get by hand until the status finally moves. A watcher does that for you. It polls one get-by-id command in the background and tells you when a field reaches the condition you named.

Shell
# Block until the order stops moving, then carry on. The exit code says how it ended.
revenexx watch add --until 'status terminal' -- orders get --id <ORDER_ID> \
  && revenexx products list

Inside the interactive shell or the full-screen app the same command registers a watcher and hands the prompt straight back, so you keep working while it polls. The watcher prints its own notice when it settles, and rings the terminal bell:

Shell
revenexx repl
# revenexx> watch add --until 'status terminal' --every 5s -- orders get --id <ORDER_ID>

Watchers need an interactive terminal. In a script without one, write the loop yourself around <get command> --json — see Scripting & output.

Sub-commands

CommandDoes
watch add --until <expr> -- <get command>Start a watcher. Outside a REPL/TUI session it blocks until the watcher settles.
watch listShow this session's watchers with live state. Add --json for the full records.
watch rm <id>Cancel one watcher.
watch rm --allCancel every active watcher.

Everything after -- is the command to poll, exactly as you would type it yourself.

Conditions

--until takes an optional field path followed by one condition:

ConditionFires when the field…
terminalstops moving — done, ready, succeeded, failed, cancelled, expired, …
changeddiffers from its value on the first poll
equals <value>matches exactly, case-insensitively. equals 200 matches the number 200, equals null matches JSON null
matches <regex>matches a regular expression. /pattern/flags is accepted too
truthybecomes non-empty. [], {}, "", "false" and "0" all count as empty

terminal deliberately includes the failure sinks as well as the successes. A watcher's job is to tell you the operation stopped, not to wait for it to stop happily — a failed import ends the watcher immediately instead of running out the clock.

Narrow the set per watcher with terminal(ready,failed), or change it for every watcher with the REVENEXX_WATCH_TERMINAL_STATES environment variable (comma-separated).

Shell
revenexx watch add --until 'status terminal(ready,failed)' \
  -- apps get-deployment --function-id <APP_ID> --deployment-id <DEPLOYMENT_ID>
revenexx watch add --until 'page.total equals 0' -- orders list --status pending
revenexx watch add --until 'on_hold truthy'      -- orders get --id <ORDER_ID>

The full-screen app offers the same five, with the field you picked in the header:

Field paths

A field path is a dot path into the response body:

PathReads
statusa top-level field of a single record
items.0.statethe first row of a list response
page.totalthe total from the pagination envelope
items.lengthhow many rows came back

There is no implicit descent into a list. A bare status against a list response is treated as "no such field", not as "row 0's status" — a page of 50 records has 50 statuses, and which one sorted first is not something the CLI controls or shows you. Say items.0.status when that is what you mean.

A field that is not there yet is not an error. The watcher keeps polling and shows field \status` not present yet, which is what you want when a job's error.message` only materialises on the ninth poll. If it never appears, the watcher ends as a timeout carrying that note.

You never have to guess the paths in the full-screen app: it fetches the resource once and offers what that payload actually holds, each with the value it has right now.

Cadence and limits

FlagDefaultNotes
--every <duration>5sInterval between polls. Floored at 2s.
--for <duration>5mGive up after this long.
--field <path>Keep the field path out of --until if you prefer.
--wait-for-createoffTreat 404 as "not yet" instead of fatal — for a resource that does not exist yet.

Durations accept 500ms, 5s, 2m, 1h; a bare number means seconds. The app asks for the same two values, and shows the floor and the defaults next to them:

Polling goes through the same transport as every other command, so the request timeout, retry/backoff and 429 Retry-After handling all apply — a watcher will not hammer a rate-limited gateway. On top of that, the interval carries ±15% jitter so watchers started together do not stay in lockstep, and at most 8 watchers run at once.

A poll that is already in flight cannot be cut short, so a watcher can overrun --for by up to one poll.

Exit codes

watch add in its blocking form turns the outcome into an exit code, extending the CLI's exit codes:

CodeMeaning
0Condition satisfied
2Bad usage — unknown command, unparseable condition or duration
7Timed out before the condition was met
130Cancelled with Ctrl-C
otherThe failing poll's own code — 4 auth, 5 not found, 8 rate limited
Shell
revenexx watch add --until 'status terminal' --for 10m -- orders get --id <ORDER_ID>
case $? in
  0) echo "finished" ;;
  7) echo "still running after 10 minutes" ;;
  *) echo "failed" ;;
esac

In the full-screen app

The TUI has the same watchers behind two commands you type into the command filter:

  • /watch walks you through creating one: pick a get-by-id command, fill its parameters, and the CLI fetches the resource once so it can offer you the real field names — then pick the condition and the cadence. That first fetch also becomes the baseline for changed, so it means "different from what you just saw".
  • /watchlist opens the watchlist pane: every watcher with live state, its last value and poll count. / select, x cancels one, X cancels all, Esc closes.

The flow opens on the resources it can poll — every get-by-id command in the tree, searchable:

A badge in the status bar counts the active watchers from anywhere in the app, and a completing watcher raises a toast and rings the terminal bell.

Scope

Watchers are session-scoped and in-memory. They live for the length of the repl/tui session, or of the blocking watch add, and are torn down when it exits. Nothing survives the process, and nothing is written to disk.

Polling is the only mechanism today — the gateway has no server push, so there is no websocket or SSE path to wait on instead.

Was this page helpful?