Skip to main content
A finder that runs only while your terminal is open is a hobby. Install the dashboard as a service and it comes back after a restart or a crash. The dashboard server runs the scheduler itself, so an enabled trigger is polled while the dashboard is up. On a headless machine run find watch instead. Run one or the other for a workspace, never both: each polls the same triggers. The scheduler polls finders and runs the learning jobs. It does not send. A batch goes out when you press Batch send… or run find drain.

The dashboard as a service

ui --install-service generates a service file that starts the dashboard at login and restarts it after a crash: a launchd agent on macOS, a systemd user unit on Linux. It prints the file; --write puts it in place. The workspace’s name, home and port are read from the workspace the command runs in, so run it once per workspace.
macOS (launchd). The label is com.oneshot-gtm.workspace.<name> (default without named workspaces). Logs go to <home>/dashboard.log.
Linux (systemd user unit). The unit is oneshot-gtm-workspace-<name>.service. Logs go to the user journal: journalctl --user -u oneshot-gtm-workspace-acme.
  • The service needs the web build (bun run --cwd apps/web build) and will not generate without it.
  • A stop you ask for is respected. Only a crash restarts it.
  • It starts at login, not at power-on. On Linux, loginctl enable-linger $USER changes that.
  • After moving bun or the checkout, or changing the port, run --write again and reload. A restart alone keeps the old values.
  • It needs a clone. bunx oneshot-gtm-server has no service file.

The watch daemon as a service

find watch --install-service does the same for the headless daemon. Paths are written absolute, including the active workspace home, so --workspace acme find watch --install-service pins the service to that workspace. macOS (launchd). Logs go to <home>/find-watch.log.
Linux (systemd user unit). Logs go to the user journal: journalctl --user -u oneshot-gtm-find-watch.
On a headless box, run loginctl enable-linger $USER once so the unit starts at boot. Windows (Task Scheduler). There is no service template. Schedule find watch --once, which runs every due trigger and exits:
Cron works on POSIX hosts: */15 * * * * ONESHOT_GTM_HOME=$HOME/.oneshot-gtm /path/to/bun /path/to/apps/cli/src/main.ts find watch --once --quiet.

Exit codes for scheduled runs

A cron wrapper needs to tell idle from broken. --fail-on-empty gives it the third answer. An error beats emptiness: a run that errored and produced nothing exits 1. “Queued nothing” counts candidates that reached the queue, so a poll whose every hit was a duplicate reads as empty. The flag needs --once; the daemon rejects it.

Spend ceiling

Per-run caps bound one run. They do not stop several finders from overspending across a day. config spend-ceiling <amount>, or the Wallet card on Setup, sets a daily limit in USD for the whole workspace.
  • Checked before every finder run and every batch send. Spend is reserved first, so two runs at once cannot both slip under it.
  • Once reached, finders and batches stop with a named reason, daily spend ceiling reached, shown on the trigger cards and in doctor. Approved rows stay approved.
  • A single send you make by hand is never blocked. That is a decision, and the ceiling is for what runs without one.
  • It resets at local midnight. Unset means no limit.
The learning jobs reserve against the same ceiling.