Saltar para o conteúdo

Run a fleet worker

Este conteúdo ainda não está disponível no seu idioma.

Run cc_worker on a spare machine to add execution capacity to your fleet. It is a pure-Dart binary — no Flutter engine — that pairs with a cc_server, declares the host’s capabilities, heartbeats, pulls leased jobs, executes them and streams process events back. It holds no durable state: a supervisor restarts it and it re-registers.

You need a running cc_server first — see Run a headless server.

A paired worker registers, heartbeats and waits for leased jobs. Submit work with fleet.submitJob. Agent dispatch on this server does not route through the fleet; the success signal for this guide is the worker appearing in the fleet panel with a fresh heartbeat. The in-process worker registered at boot has an empty runner map, so a job placed on it returns No local runner for job kind ….

  • The Dart SDK. This repo pins its SDK with fvm, so prefix commands with fvm.
  • This repository checked out on the worker machine.
  • A reachable cc_server (loopback for dev, LAN or remote for production).
  • git on the PATH — the worker materializes repos and runs git probes.
Terminal window
cd apps/cc_worker
fvm dart build cli

This produces a self-contained bundle at ./build/cli/<os_arch>/bundle/bin/cc_worker.

A cc_server only accepts a worker that presents a paired-device id and pre-shared key — including a loopback server. Mint them on the server machine (pair can run while the server is up; the new device is picked up without a restart):

Terminal window
cc_server pair --data-dir <dir> --device my-worker --label "Build box"

It prints a Device id and a Pairing key — those are what --device-id and --psk / --psk-file / CC_WORKER_PSK take.

Against a paired server:

Terminal window
./build/cli/<os_arch>/bundle/bin/cc_worker \
--server wss://host:9030 --device-id my-worker --psk-file /path/to/worker.psk

Against a loopback server (still paired — cc_server always runs the PSK handshake):

Terminal window
fvm dart run cc_worker --server ws://localhost:9030 \
--device-id my-worker --psk-file /path/to/worker.psk
Flag Description Default
--server <url> cc_server URL (ws:///wss://; http(s) and a missing /rpc path are coerced). Required. —
--name <name> Operator-facing worker name. host name
--device-id <id> Stable paired-device id, also used as the worker id. cc-worker
--psk-file <path> File whose first non-empty line is the pairing key. Preferred over --psk. —
--psk <key> Pairing key on the command line. Visible in ps and shell history — the binary warns. —
--max-jobs <n> Hard ceiling on jobs this process executes concurrently (positive integer). 4

Resolution order for the key is --psk-file → CC_WORKER_PSK → --psk. Omitting every source skips the worker’s auth handshake; a real cc_server then denies the session (auth_denied) rather than accepting an unauthenticated worker. Unknown flags are a usage error (exit 64) — unlike cc_server, this binary uses package:args and does not ignore typos.

CC_WORKER_CACHE overrides the worktree materialization cache directory (default: a cc_worker_cache directory under the system temp dir).

The worker registers with fleet.registerWorker, then heartbeats every 20s and polls for leases every 2s. It runs until SIGINT/SIGTERM; put it under systemd or launchd so it restarts and re-registers after a crash or reboot.

Open Observability in the desktop or web app, switch to the Live tab and scroll to the bottom. Workers lists every registered worker with its platform, cores, capability keys and last heartbeat; Jobs lists the jobs distributed across them.

Your worker appearing there with a recent heartbeat is the confirmation that pairing and registration worked.

From a worker’s tile you can drain it (finish current jobs, take no new leases), resume a draining worker, revoke its credential, or remove the row.

Worker and job rows live in the server’s global.db — the worker itself writes nothing to disk beyond the materialization cache.

On registration the worker auto-detects its capabilities and the scheduler matches jobs against them. You do not configure these by hand:

  • OS and arch (macos/linux/windows, arm64/x64) — exact probes, with fallbacks to uname -m / PROCESSOR_ARCHITECTURE
  • Cores and RAM — exact core count; RAM degrades to 0 when unprobeable
  • flutter — set when a Flutter SDK is reachable (bare flutter, then fvm flutter); golden-render jobs require it
  • sandbox — native-macos on macOS, native-linux on Linux. This is a placement hint, not a promise: the worker advertises the backend its platform has, but it does not wrap the jobs it runs in one. Unlike cc_server, cc_worker spawns a leased job directly
  • always-on and parallel — a dedicated cc_worker always advertises both, so it is eligible for eval batches and other throwaway parallel capacity

Detection is best-effort: anything it cannot probe degrades to a safe default rather than failing startup.

The lease protocol carries six job kinds:

Kind What it is
agentRun A single agent run (dispatch)
pipelineStep A pipeline step execution
codeIndex A repo code-index build
goldenRender A UI visual golden render (requires flutter)
benchmark A performance benchmark run
evalBatch An eval batch (prefers parallel capacity)

Execution is a real subprocess-streaming implementation, not a full embedded agent runtime. When a lease carries repoRemote, the worker does git clone --depth 1 into a remote-and-SHA-keyed cache directory, then git fetch and git checkout. For agentRun it runs the command in the lease env’s CC_JOB_COMMAND in the work directory, streaming stdout and stderr back as events; with no CC_JOB_COMMAND it echoes the prompt so the transport is still exercised end to end. The other kinds run small real commands (git rev-parse HEAD, git --version) as honest probes. Job-scoped credentials in the lease env are injected into every subprocess and never written to a log.

Events are batched and flushed every 250ms or every 32 events; each job ends with a done event and a completion report. The model is heartbeats plus short-TTL leases: if a worker loses contact its lease expires and the server reaps the job for retry. There is no durable state on the worker and no consensus or work-stealing between workers.