Skip to content

fix(coccontainer): build coc in serve loop and fail fast when dashboard template is missing#91

Open
kah279 wants to merge 1 commit into
plusplusoneplusplus:mainfrom
kah279:fix/coccontainer-serve-loop-builds-coc
Open

fix(coccontainer): build coc in serve loop and fail fast when dashboard template is missing#91
kah279 wants to merge 1 commit into
plusplusoneplusplus:mainfrom
kah279:fix/coccontainer-serve-loop-builds-coc

Conversation

@kah279
Copy link
Copy Markdown

@kah279 kah279 commented May 15, 2026

Problem

Running ./scripts/coccontainer-serve-loop.ps1 on a freshly cloned repo says the dashboard is at
http://localhost:5000, but every browser request hangs forever — the process binds the port but no GET /
response is ever sent.

Root cause

The coccontainer GET / handler renders the dashboard HTML by require()-ing coc's compiled
dist/server/spa/html-template.js at runtime. That file is produced by coc's build step — but the serve-loop
scripts only built forge and coccontainer, never coc. So on a fresh checkout the template was missing and
the require threw.

The throw happened after the handler called res.writeHead(200, ...), so the outer try/catch saw
headersSent === true and silently dropped the response, leaving the socket open until the client timed out.

Fix

Two layered changes, each useful on its own:

1. Scripts — build coc in the loop

scripts/coccontainer-serve-loop.ps1 and scripts/coccontainer-serve-loop.sh now build the full chain forge → coc-client → coc → coccontainer before serving (matching what coc:link does for the coc serve loop). This
guarantees dist/server/spa/html-template.js exists before the container ever tries to render it.

2. Server hardening — fail fast & observable

packages/coccontainer/src/server/index.ts:

  • Adds ContainerServerOptions with injectable htmlRenderer and skipHtmlPrecheck seams so the failure
    modes are testable.
  • Eager probe at startup: createContainerServer invokes the renderer once before binding the port. If the
    template can't be loaded, the process exits with an actionable error ("Build coc first: \npm run build` in
    packages/coc`") instead of binding :5000 and hanging requests.
  • Reordered GET / handler: dashboard HTML is now computed before res.writeHead(200). Any future
    render failure lands in the existing !headersSent catch and produces a clean 500 JSON instead of a hung
    socket — preserving observability for the next time something breaks.

3. Drive-by fix to keep coc building

packages/coc/src/server/spa/client/react/contexts/ContainerAgentContext.tsx was importing/calling
setServerAuthAgents, a symbol deleted by the earlier devtunnel-token-auth revert (#…). That broke npm run build -w packages/coc with No matching export ... for import "setServerAuthAgents", which would have
prevented the script fix above from unblocking anything. Removed the dead reference.

Tests

New file: packages/coccontainer/test/server/dashboard-html.test.ts — 4 Vitest cases:

  1. Happy path: server starts and GET / returns the dashboard HTML.
  2. Eager probe failure: a broken renderer causes createContainerServer to throw at startup with the
    actionable message (no port is bound).
  3. Request-time render failure: a renderer that succeeds at probe but throws on a later request returns
    500 application/json, not a hung socket.
  4. Actionable startup message: the thrown error mentions running npm run build in packages/coc.

All 48 coccontainer tests pass on Windows. npm run lint is clean (0 errors).

Verification

  • Fresh clone → ./scripts/coccontainer-serve-loop.ps1http://localhost:5000 renders the dashboard
    immediately.
  • Force-deleted packages/coc/dist and re-ran the script → coc gets rebuilt before serve.
  • Manually broke the html-template import → server now exits at startup with the actionable error rather than
    binding :5000 and hanging.

Files changed

.../react/contexts/ContainerAgentContext.tsx | 4 +- packages/coccontainer/src/server/index.ts | 74 ++++++++---
.../test/server/dashboard-html.test.ts | 150 +++++++++++++++++++++ scripts/coccontainer-serve-loop.ps1 | 15 ++-
scripts/coccontainer-serve-loop.sh | 10 +- 5 files changed, 231 insertions(+), 22 deletions(-)

…rd template is missing

The coccontainer dashboard requires coc's compiled
`dist/server/spa/html-template.js` at runtime to render `GET /`. The
serve-loop scripts only built forge and coccontainer, never coc, so a fresh
clone produced a process that bound :5000 but hung every browser request:
the `GET /` handler called `res.writeHead(200)` before invoking the
template renderer, and when the require threw the catch block silently
dropped the response (`headersSent` was already true).

Changes:

- `scripts/coccontainer-serve-loop.{ps1,sh}`: build forge -> coc-client
  -> coc (full `npm run build`) -> coccontainer so the html template
  always exists before serve.
- `packages/coccontainer/src/server/index.ts`:
  - Add `ContainerServerOptions` with injectable `htmlRenderer` and
    `skipHtmlPrecheck` seams (testing-friendly).
  - Eagerly probe the renderer in `createContainerServer` so a missing
    template throws a clear, actionable error before binding the port.
  - Render the dashboard HTML in the `GET /` branch *before* writing
    headers so future failures produce a 500 JSON instead of a hung socket.
- `packages/coccontainer/test/server/dashboard-html.test.ts`: new Vitest
  cases covering happy path, eager probe failure, request-time render
  failure (returns 500, never hangs), and the actionable startup message.
- `packages/coc/src/server/spa/client/react/contexts/ContainerAgentContext.tsx`:
  drop the dangling `setServerAuthAgents` import/call left over from the
  reverted devtunnel-token-auth feature -- without this coc itself fails to
  build, so the script fix can't unblock anything.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant