feat(cli): support fleet-node enrollment in `fleet serve`

Merged
KH
4mo ago
+913-47

What

Cloud's Enroll node UI mints:

agent-relay fleet serve --enrollment-token '<ocl_node_enr_...>' \
  --enrollment-url 'https://agentrelay.com/api/v1/fleet/register' --name '<node>'

…but agent-relay fleet serve only accepted a <file> node-def plus --name/--workspace/--max-agents/--base-url, so the minted command errored on the unknown options. The Cloud endpoints (enrollment-tokens mint + register redeem + /fleet/nodes) already exist — this adds the missing CLI consumer.

Changes

  • fleet serve gains --enrollment-token and --enrollment-url. When a token is provided, the CLI exchanges the one-time token at the enrollment URL (cloud POST /api/v1/fleet/register) for durable node credentials before the broker boots.
  • New enrollFleetNode in @agent-relay/cloud (packages/cloud/src/fleet.ts), modeled on the worker enrollment exchange (registerCloudWorker / cloud worker register --token): a single POST that redeems the token and returns { nodeId, nodeName, nodeToken, relayWorkspaceId, relaycastUrl, websocketUrl }. Clear, actionable errors on expired/invalid/consumed tokens (401), rate-limit (429), and wrong-URL HTML 404s.
  • Credential wiring: the exchange sets RELAY_NODE_TOKEN (the relaycast node token used over the fleet WS) and RELAY_BASE_URL (the relaycast origin), which is what the broker reads to bind itself to the Cloud workspace's fleet roster.
  • <file> is now optional in enrollment mode. Identity/name/max-agents come from the enrollment record (falling back to an implicit local node via createImplicitLocalFleetNode); a <file>, when present, overrides/augments. Outside enrollment mode a <file> is still required.

Register endpoint contract (matched)

Request: { enrollmentToken, name?, capabilities?, maxAgents?, tags?, version? }
Response: { nodeId, nodeName, nodeToken, relayWorkspaceId, relaycastUrl, websocketUrl }

Tests

  • packages/cloud/src/fleet.test.ts — token exchange (trim/dedupe inputs), websocket-url derivation, 401/429/missing-creds/empty-token, and HTML-404 no-markup-dump (mocked fetch).
  • packages/cli/src/cli/commands/fleet.test.ts — flag parsing + exchange wiring (env set), optional-<file> serving, and the two validation errors.
  • Full packages/cloud + packages/cli suites green (508 passed, 5 skipped); lint clean; tsc clean.

Verify

node packages/cli/dist/cli/index.js fleet serve --help   # lists both flags; [file] optional

🤖 Generated with Claude Code