Skip to main content

Fleet Commands

Fleet is what makes peering useful to an agent, not just to a human clicking around Settings: an agent driving one TermFlow instance can route a command to a paired machine and have it run there — "build this on the Mac" from the Windows box — and watch it happen.

Targeting precedence

A Fleet request can specify up to three targeting fields. TermFlow resolves them in a fixed order, and the first one present wins:

terminalId  →  machineId  →  targetOS  →  (none) local
FieldResolves toRequirement
terminalIdThat exact terminal, wherever it livesRequires a per-terminal Control grant on that terminal — see Grants & consent
machineIdA specific paired machineRequires that peer's Allow fleet commands consent to spawn a new terminal there
targetOSwindows | macos | linux — the unique online peer running that OSSame consent requirement; fails if the OS isn't uniquely matched (see below)
(none of the above)Runs locally, exactly like a non-fleet command
terminalId skips the spawn question entirely

Naming an existing terminalId never creates anything new — it runs in a terminal that already exists, gated by the Control grant on that terminal. machineId and targetOS are the ones that can spawn a fresh terminal, which is why they're gated by the separate fleet-exec consent instead.

When targetOS doesn't resolve to exactly one machine

targetOS only works when there's a single unambiguous online peer running that OS:

SituationResult
More than one online peer matches the OSHTTP 409 — ambiguous. Response includes the candidate machines plus the full roster.
No online peer matches the OSHTTP 404 — no match. Response includes the full roster so the caller can pick something else.
Exactly one online peer matchesRequest routes to that machine.

The remote terminal Fleet spawns

When a machineId- or targetOS-targeted request needs a new terminal on the responder, that terminal is:

  • Persistent — it isn't a throwaway subprocess; it behaves like any other terminal on that machine.
  • Labeled Fleet: <initiator name>, so the person at the responding machine can see exactly which peer spawned it and why.
  • Visible — it's a real tab in the responder's UI, not hidden or headless.
  • Never auto-killed. It stays open until someone explicitly closes it: the responder's human, the initiator (via the API/MCP close call), or a peer revocation.
  • Auto-grants the initiator Control on that specific terminal for the duration it's open — so follow-up commands can target it directly by terminalId without needing a fresh spawn. That Control grant is revoked when the terminal closes.

The long-poll execution model

execute on a Fleet request behaves differently from a plain local one: the responder holds the request open and only responds once the command finishes, or once the caller's timeoutMs elapses — whichever comes first.

  • Command finishes first: the response comes back with done: true and the real exitCode.
  • Timeout elapses first: the response comes back with a live handledone: false, exitCode: null — plus the terminal's current screen. The command is still running on the responder; nothing was cancelled.

Completion itself is detected by a done-marker the responder injects around the command in the shell; the marker's own exit code is what's reported back as exitCode.

ParameterTypeClampDefault
timeoutMsnumber[1000, 3,600,000] ms (1s – 1h)60000 (60s)
A timeout is not a failure

Getting done: false back doesn't mean anything went wrong — it means the command outlived the wait window you gave it. Long builds, installs, or watch processes will routinely hit this. Don't treat it as an error; poll instead (see below).

Watching progress: get_terminal_screen

To check on a command after a timeout (or just to watch it live), call get_terminal_screen with the same terminalId (and machineId if remote). It returns the terminal's current live screen — the same authoritative, styled snapshot the UI itself renders — not the lossy, capped output history. This is the preferred way to observe an in-flight Fleet command; polling the plain output history can miss or garble full-screen output.

Worked example

An agent on the Windows machine wants to build a project on whichever paired Mac is online, without knowing its machine id in advance:

// 1. Discover the fleet
{ "name": "list_machines", "arguments": {} }
{
"machines": [
{ "machineId": "self", "deviceName": "win-dev-box", "os": "windows", "online": true, "self": true },
{ "machineId": "mac-mini", "deviceName": "mac-mini-studio", "os": "macos", "online": true, "self": false }
]
}
// 2. Route the build to the unique online macOS peer
{
"name": "execute_command",
"arguments": {
"targetOS": "macos",
"command": "cd ~/repo && bun run build",
"timeoutMs": 120000
}
}
// Timed out while the build was still running:
{
"machineId": "mac-mini",
"terminalId": "term_9f2a",
"deviceName": "mac-mini-studio",
"done": false,
"exitCode": null,
"screen": "…compiling module 214/300…"
}
// 3. Check on it later
{
"name": "get_terminal_screen",
"arguments": { "machineId": "mac-mini", "terminalId": "term_9f2a" }
}
{
"machineId": "mac-mini",
"terminalId": "term_9f2a",
"title": "Fleet: win-dev-box",
"running": true,
"screen": "…compiling module 289/300…"
}

Security notes

Fleet doesn't introduce a new trust model on top of peering — it composes the existing controls:

  • The existing peering mTLS identity authenticates every request between machines.
  • Per-terminal Control grants gate terminalId-targeted execution.
  • The per-peer, default-deny "Allow fleet commands" consent gates spawning new terminals, and is re-checked live on every request.
  • The initiator is auto-granted Control on any terminal Fleet spawns for it, revoked when that terminal closes.
  • The command string is written to the responder's audit log.
  • The spawned terminal is labeled and visible in the responder's own UI — never hidden.

There is deliberately no blocking confirmation dialog on the responder for an individual command — that would stall an unattended agent workflow. The grants, consent, labeling, and audit log above are the compensating controls instead.

Next steps

  • Fleet API & MCP — the exact REST endpoints and MCP tool schemas behind everything on this page.
  • Grants & consent — set up the grants and consent this page assumes are already in place.