跳到主要内容

Pairing web and standalone

Pairing is the act of pointing the web CLI tab at a running standalone CLI so commands typed in the browser execute inside the standalone process. Under the hood it's the listener WS transport plus the trusted-clients allowlist — this page is the end-to-end walkthrough that ties them together.


When you'd pair

GoalWhy pairing helps
Keep account WebSockets streaming on a dedicated machineStandalone is always-on; browser tabs can suspend, refresh, or close without dropping exchange connections.
Leave hooks alive across browser sessionsHooks live inside the standalone listener (see Hooks & webhooks). The web app doesn't host them.
Survive browser backgrounding / tab suspensionBackgrounded tabs throttle timers and may close sockets. A paired standalone keeps running regardless of what the tab does.
Quick-fire trades from a phone but execute from a real shellThe phone web tab gets the touch UI; the desktop standalone gets the real terminal, full keyboard, and proper logs.
Run the recorder, watch modes, or chart panelsAll of those are standalone-only (see Web CLI vs standalone).

End-to-end setup

From the standalone side

  1. Launch the CLI. tealstreet (or tealstreet -a <account>). The listener auto-binds on 127.0.0.1:53219 on every start.
  2. Stay at the REPL. The first connection from a new web client pauses here for your approval — you have to be looking at the REPL to grant it.

That's it from the standalone side. After the first approval, the web client's clientId is in ~/.tealstreet/trusted-clients.json and future reconnects pass through silently.

From the web side

  1. Open the web CLI tab.
  2. Set the target to listener in the tab's settings.
  3. The web app opens ws://127.0.0.1:53219/ws and sends its hello frame.
  4. Switch to the standalone, approve the prompt.
  5. Web CLI is now wired through the standalone — every command you type in the browser ships as a cmd:exec frame and runs inside the standalone process.

For the per-tab settings UI and reconnect behaviour, see Web CLI vs standalone. For the recipe-style walkthrough see Listener from web.


What flows where

Once welcomed, the WS carries five conversation types. The full frame catalogue is in Listener → wire frames; the flow at runtime looks like:

You do…Frames on the wire
Type a command in the web CLIWeb sends cmd:exec { id, command, account?, symbol? }. Standalone dispatches as if typed at its own REPL.
Standalone prints output for that runEach line streams back as cmd:output { id, kind, text } (kind ∈ print | success | warn | error | info).
Standalone finishes the runcmd:done { id, success, error? }. Late frames from ; / & chains may still arrive — match by id.
Web edits an accountWeb sends mutate:account { id, op, account?, name? }; standalone replies mutate:ack or mutate:err. Account snapshot reflows via state:accounts.
Web creates / rotates / revokes a hookWeb sends mutate:hook { id, op, name?, command?, account?, hookId? }; standalone replies mutate:ack { data } with the new hook (secret included on create/rotate).
A webhook fires while you're pairedStandalone publishes cmd:output + cmd:done on the hook-output topic. Same frame shape — paired clients see the run in real time.

state:accounts, state:tasks, and state:hooks are pushed to subscribed clients on connect and after every mutation; state:hooks is scrubbed of secrets server-side, so the web never has to handle them.


Failure modes

Symptom from the web sideLikely causeFix
Socket closes with code 4001clientId was revoked, or you denied the approval prompt.At the standalone REPL: clients to inspect, re-approve on next attempt. See Trusted clients.
cmd:exec returns an error framelistener.remoteExec is false.Edit ~/.tealstreet/config.jsonlistener.remoteExec = true. Read fresh on every call, no restart needed.
Webhook fires return HTTP 423Same kill switch — listener.remoteExec gates HTTP too.Same fix. See Hooks & webhooks for the status table.
WS upgrade refused / HTTP 503listener.enabled is false, or the standalone isn't running.Start the CLI; if it's already running, flip listener.enabled back to true in config.json.
Web prints "different CLI" warningThe standalone's listenerId changed — different machine or wiped ~/.tealstreet/listener.json.Approve the new pairing, or restore the old listener.json if you wiped it by accident.
Web stops reconnecting after a long disconnectWeb caps backoff at 60 s. Standalone prints a refresh hint after 75 s of no clients.Refresh the web tab.

For the listener server itself (HTTP routes, kill switches, file paths), see Listener. For revoking trust after pairing, see Trusted clients. For the HTTP webhook variant of cmd:exec, see Hooks & webhooks.