Skip to content

0.2.1 - Windows dev-server hang fix; server-guard ships with the kit

Choose a tag to compare

@chadixearth chadixearth released this 16 Aug 02:45
· 31 commits to main since this release

Fixed: npm run dev no longer hangs a Windows agent session forever

An agent shell tool reads a command's stdout until EOF, and EOF only arrives once every handle to the pipe's write end is closed — not when the direct child exits. Detaching a dev server with Start-Process -RedirectStandardOutput <out> -RedirectStandardError <err> makes PowerShell call CreateProcess with bInheritHandles=TRUE, which duplicates every inheritable handle of the launching shell into the child. The dev server inherited the tool's stdout pipe and held it open for its entire life.

The symptom: the turn printed PID and SERVER_UP, then never returned. The user had to cancel a task that had already finished.

Measured with a self-exiting fixture server:

Launcher exits Caller's stdout EOF Lag
Before 2.3 s 21.8 s (when the server died) 19,498 ms
After 2.5 s 2.5 s 8 ms

With a real dev server, "21.8 s" is "never".

The launcher now writes the redirection into a generated per-port .cmd wrapper and starts it with no -Redirect* parameter, so PowerShell goes through ShellExecuteEx, which creates the process with bInheritHandles=FALSE. The server is still answering 200 after the call returns.

Also in this release

  • server-guard and start-server.ps1 now ship with the kit. The workflow rules told agents to use a launcher the kit never installed — flagged inline as "[OPTIONAL — community scripts; create or skip]". plugin/server-guard/ installs to <config>/plugins/server-guard/ with the launcher beside it, and the plugin entry is merged into opencode.json.
  • The workflow rules had recommended the broken pattern, calling Start-Process with both redirects "the ONLY safe detach on Windows … zero pipe inheritance". The redirect parameters do give the child its own std handles, but they do not turn inheritance off. That section now explains the EOF/handle mechanism.
  • server-guard whitelisted the leak. isSafelyDetached() treated any Start-Process with both redirects as already-safe and let it through un-rewritten. It is now blocked with the measured explanation (-Wait stays allowed — a foreground run is meant to be waited on).
  • -Stop orphaned servers. npm run dev reaches the listener through intermediate cmd.exe shims that are not children of the launched process, so killing the saved PID left the server alive. Stop now kills process trees (taskkill /T) plus any listener on the port, and pid files are per-port with the resolved LISTENER_PID recorded.
  • The installer copies every plugin/<name>/ directory instead of only plugin/graphyloop, and refreshes a legacy <config>/scripts/start-server.ps1 when one exists (backup kept) so older setups stop hanging without a manual edit. It never creates that path — the plugin-local copy is canonical.
  • 28 new tests (143 total), including a regression that fails if Start-Process in the launcher ever regains a stdio redirect.

Windows-only by design: the launcher is PowerShell and the handle-inheritance trap is a Win32 behaviour, so the plugin installs no hooks elsewhere.

Full changelog: v0.2.0...v0.2.1