Repository navigation
Improved deployment visibility #526
Description
Problem
deploy_component is a synchronous HTTP request that returns a single JSON result only after the full pipeline completes: upload → decompress → install dependencies (npm/pnpm/yarn/custom) → component load validation → replication to peers → worker restart. With non-trivial packages this routinely takes minutes. The CLI shows nothing during that time, and when something fails — network drop, npm error, lock conflict, peer rejection — the user gets one terse error string with no indication of which phase broke, on which node, or how far the cluster got.
We need step-level progress and clear error attribution across every phase, on the origin and each replica.
Plan
Transport: text/event-stream response on deploy_component
When the CLI sends Accept: text/event-stream (and once the request matches an operation that supports it), the operations API returns SSE instead of a single JSON response. The SSE serializer already exists at server/serverHelpers/contentTypes.ts:104 and is selected by Accept in REST handling — this work wires deploy_component (and the relayed peer deploys) to that path.
Event shape (each is an SSE event: line plus a JSON data: payload):
event: phase
data: { phase: "upload"|"extract"|"install"|"load"|"replicate"|"restart",
status: "start"|"progress"|"done"|"error", node?, ... }
event: upload
data: { bytes, total? }
event: install
data: { manager: "npm"|"pnpm"|"yarn"|"custom", stream: "stdout"|"stderr", line: "..." }
event: replicate
data: { node, phase, status, attempt?, ... } // relayed from each peer's SSE
event: error
data: { phase, node?, message, code? }
event: done
data: { result, peers: [{ node, status, error? }, ...] }
CLI rendering:
- A byte-based progress bar for
upload(origin only; peer uploads happen server-side and surface asreplicatephase events). - A spinner + the latest log line for
installper node, with full lines streamed through verbose mode. - A per-node status grid for
replicate: each peer shows its current phase and any retry attempt count. - A clear, attributed error block (phase + node + message) for any failure, distinguishing application errors (npm install failed) from transport errors that were retried (transient HTTPS errors per Support for deployments larger than 2GB #524).
Why SSE instead of a richer job record
We considered the "create a job and poll get_job for state" pattern. It doesn't fit well here:
- The most useful signal during install is the live
npm installstdout/stderr. Persisting every chunk into a job record so it can be polled is wasteful, and polling cadence trades latency against load. - The CLI is already connected; SSE reuses that single connection. Polling adds round-trips and forces every progress event to be persisted somewhere queryable just so it can be retrieved seconds later.
We still emit a job record so post-hoc inspection works (get_job <id>), but live progress comes through SSE. Phase transitions and final status are mirrored into the job record; per-chunk install output is not (too noisy to persist).
Emitting progress in deployComponent
The deployComponent handler in components/operations.js receives a progress emitter from the SSE response wrapper. Emit points (see implementation map in #524 discussion):
- Upload: byte count tracked on the server-side multipart parser as the file part is consumed.
- Extract:
components/Application.ts:217— emitstart/donearound thegunzip-maybe+tar-fspipeline. - Install:
components/Application.ts:255–410—installApplication()already spawns the package manager (Application.ts:622–645handles stdout/stderr chunks for debug logging); we forward those chunks asinstallevents instead of (or in addition to) the existing debug logs. - Load:
components/operations.js:405–418— emit aroundcomponentLoader.loadComponent(). - Replicate: emit per-peer events around each direct-HTTPS deploy started by the new relay (see Support for deployments larger than 2GB #524). Each peer's SSE stream is re-emitted on the origin's SSE response, tagged with the peer node id.
- Restart: emit around
manageThreads.restartWorkers('http')/ rolling-restart job dispatch.
Per-peer relay visibility (with #524)
Origin opens a streaming HTTPS deploy to each peer with Accept: text/event-stream. Each peer's SSE events are re-emitted on the origin's SSE response with the peer node id attached. Transient transport failures that trigger a retry surface as replicate events with an attempt counter so the user sees the retry happening rather than the operation appearing stalled. Final success/failure per peer is included in the terminating done event.
Backward compatibility
If the client doesn't send Accept: text/event-stream, behavior is unchanged: single JSON response after completion. External operations API callers that don't know about SSE continue to work.
Files touched (approximate)
bin/cliOperations.js— requesttext/event-stream, render progress/grid (likely a small renderer module underbin/).server/operationsServer.ts/server/serverHelpers/serverHandlers.js— wire SSE response whenAccept: text/event-streamand operation isdeploy_component; thread a progress emitter into the operation handler.components/operations.js,components/Application.ts— emit phase + chunk events from each step; forward spawn stdout/stderr asinstallevents.- Replication relay (see Support for deployments larger than 2GB #524) — re-emit peer SSE events on the origin stream, with retry attempt counts.
- Job records — mirror phase transitions and final per-peer status into the existing job record so
get_jobis still useful after the fact.
Related
- Support for deployments larger than 2GB #524 — the multipart/HTTPS-relay transport this depends on for both origin upload progress and per-peer replication visibility.
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Activity
feat(deploy): live SSE progress for deploy_component
Merge main into record-caching; wire PrimaryRocksDatabase + fix VT ca…
Merge main into record-caching; bump rocksdb-js to ^2.2.0
kriszyp commentedon Jul 7, 2026
Appears to be a duplicate of / consolidate under #641 — deployment progress visibility — folded into the deployment-tracking redesign.
— Claude (Opus 4.8), issue-backlog triage
kriszyp commentedon Aug 7, 2026
Closing as superseded.
The multipart-transport prerequisite this depends on shipped via merged PR #530, and the SSE-progress half (PR #531) was closed unmerged. Whatever remains is covered by the deployment-tracking epic #641, and this issue already carries the
duplicatelabel.— KrAIs (Claude Opus 5)