fix(coccontainer): build coc in serve loop and fail fast when dashboard template is missing#91
Open
kah279 wants to merge 1 commit into
Conversation
…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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Running
./scripts/coccontainer-serve-loop.ps1on a freshly cloned repo says the dashboard is athttp://localhost:5000, but every browser request hangs forever — the process binds the port but noGET /response is ever sent.
Root cause
The
coccontainerGET /handler renders the dashboard HTML byrequire()-ingcoc's compileddist/server/spa/html-template.jsat runtime. That file is produced bycoc's build step — but the serve-loopscripts only built
forgeandcoccontainer, nevercoc. So on a fresh checkout the template was missing andthe
requirethrew.The throw happened after the handler called
res.writeHead(200, ...), so the outertry/catchsawheadersSent === trueand 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
cocin the loopscripts/coccontainer-serve-loop.ps1andscripts/coccontainer-serve-loop.shnow build the full chainforge → coc-client → coc → coccontainerbefore serving (matching whatcoc:linkdoes for thecocserve loop). Thisguarantees
dist/server/spa/html-template.jsexists before the container ever tries to render it.2. Server hardening — fail fast & observable
packages/coccontainer/src/server/index.ts:ContainerServerOptionswith injectablehtmlRendererandskipHtmlPrecheckseams so the failuremodes are testable.
createContainerServerinvokes the renderer once before binding the port. If thetemplate can't be loaded, the process exits with an actionable error ("
Build coc first: \npm run build` inpackages/coc`") instead of binding :5000 and hanging requests.
GET /handler: dashboard HTML is now computed beforeres.writeHead(200). Any futurerender failure lands in the existing
!headersSentcatch and produces a clean500JSON instead of a hungsocket — preserving observability for the next time something breaks.
3. Drive-by fix to keep
cocbuildingpackages/coc/src/server/spa/client/react/contexts/ContainerAgentContext.tsxwas importing/callingsetServerAuthAgents, a symbol deleted by the earlier devtunnel-token-auth revert (#…). That brokenpm run build -w packages/cocwithNo matching export ... for import "setServerAuthAgents", which would haveprevented 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:GET /returns the dashboard HTML.createContainerServerto throw at startup with theactionable message (no port is bound).
500 application/json, not a hung socket.npm run buildinpackages/coc.All 48 coccontainer tests pass on Windows.
npm run lintis clean (0 errors).Verification
./scripts/coccontainer-serve-loop.ps1→http://localhost:5000renders the dashboardimmediately.
packages/coc/distand re-ran the script → coc gets rebuilt before serve.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(-)