Skip to content

fix(cli): hide console window for detached child processes on Windows - #3851

Open
dajiaohuang wants to merge 1 commit into
heygen-com:mainfrom
dajiaohuang:fix/windowshide-detached-spawns
Open

fix(cli): hide console window for detached child processes on Windows#3851
dajiaohuang wants to merge 1 commit into
heygen-com:mainfrom
dajiaohuang:fix/windowshide-detached-spawns

Conversation

@dajiaohuang

Copy link
Copy Markdown

What

Add windowsHide: true to the three detached: true spawns in packages/cli that were missing it, so they stop opening a console window on Windows.

File Spawn
src/telemetry/transport.ts (flushSync) exit-time telemetry flush → PostHog
src/utils/openBrowser.ts (openBrowser) --browser-path browser launch
src/commands/previewLifecycle.ts (spawnDetachedPreview) background preview server

Why

Closes #3476. child_process.spawn() defaults windowsHide to false, and Node's docs state that on Windows a detached: true child has its own console window. So all three sites flash a console window at the user on every invocation.

flushSync() is the worst of the three: it is the process-exit flush path, so it fires on nearly every short-lived command (check, lint, keyframes, capture, snapshot, preview) that exits with queued events — not just renders.

src/utils/autoUpdate.ts already passes windowsHide: true, so the intent was clearly there and the other three were oversights rather than a deliberate difference.

How

Three one-line option additions. windowsHide is a documented no-op on macOS and Linux, so this is inert off Windows — same rationale as the #3379 ffmpeg fix.

One supporting type change: spawnDetachedPreview builds its options object as an object literal and passes it to the SpawnPreview-typed dependencies.spawn. TypeScript's excess-property check rejects a key that is not on that type, so SpawnPreview's options parameter gains windowsHide: boolean.

Not in scope, deliberately:

Tests: the two existing spawn-asserting tests are extended (telemetry/client.test.ts, commands/previewLifecycle.test.ts), and utils/openBrowser.windowsHide.test.ts is added. That last one goes in its own file rather than into openBrowser.test.ts to follow the repo's existing *.windowsHide.test.ts convention (7 such files, incl. orphanCleanup.windowsHide.test.ts next door) — openBrowser.test.ts otherwise holds only pure-function tests and never mocks spawn.

Test plan

  • Unit tests added/updated
  • Manual testing performed
  • Documentation updated (if applicable)

The three touched test files pass in full (36/36):

bunx vitest run src/telemetry/client.test.ts \
  src/commands/previewLifecycle.test.ts \
  src/utils/openBrowser.windowsHide.test.ts

Reverting the three one-line changes makes exactly the three new assertions fail and nothing else, so the tests genuinely pin the fix rather than passing vacuously.

Also run on the changed files: oxlint (0 warnings, 0 errors), oxfmt --check (clean), git diff --check (clean), and the repo's own pre-commit suite (tracked-artifacts, largefiles, lint, format, fallow, typecheck) via git commit.

Two caveats, stated plainly:

  • No manual Windows verification. I have no Windows desktop session to watch for the flashing window, so the fix rests on the documented windowsHide/detached semantics quoted in the issue — which is also how the issue itself was diagnosed. Someone on Windows confirming the window is gone would be worth more than my static reasoning.
  • bun run --filter @hyperframes/cli test is not green, but not because of this change: 28 tests in 8 unrelated files (commands/init*, commands/cloud/render, registry/publication, tts/synthesize.cache, utils/projectConfig.create, utils/skillsMirror, capture/scaffolding) fail identically with and without this patch — I checked out the base commit and reproduced the same 28 failures. They look environment-related rather than code-related.

One note on the branch: it is based on e5d89f77 rather than the current tip of main. The patch is byte-identical either way (none of these six files changed in between) and it merges cleanly, but I am happy to rebase if you would rather review it against the tip.

🤖 Generated with Claude Code

Three `detached: true` spawns left `windowsHide` at its default of
`false`. On Windows a detached child gets its own console window, so each
of these flashed one at the user:

- `telemetry/transport.ts` `flushSync()` — the exit-time telemetry flush,
  which fires on nearly every CLI invocation that exits with queued events
- `utils/openBrowser.ts` — the `--browser-path` browser launch
- `commands/previewLifecycle.ts` — the detached background preview server

`utils/autoUpdate.ts` already passes `windowsHide: true`, so the intent was
there; the other three were oversights rather than a deliberate difference.

`windowsHide` is a documented no-op on macOS and Linux, so this changes
nothing off Windows. Same rationale as the heygen-com#3379 ffmpeg fix.

The `SpawnPreview` options type gains the field because the object literal
is passed to a `SpawnPreview`-typed parameter, where excess-property
checking would otherwise reject it.

Closes heygen-com#3476
@miguel-heygen

Copy link
Copy Markdown
Collaborator

@dajiaohuang sign the commits pls

@dajiaohuang
dajiaohuang force-pushed the fix/windowshide-detached-spawns branch from 011745b to df122a6 Compare September 11, 2026 10:51
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.

[Windows] Three detached: true spawns missing windowsHide — telemetry flushSync opens a console window on nearly every CLI exit

2 participants