diff --git a/browse/src/bun-polyfill.cjs b/browse/src/bun-polyfill.cjs index e0ada11b3..ecfd26efe 100644 --- a/browse/src/bun-polyfill.cjs +++ b/browse/src/bun-polyfill.cjs @@ -75,6 +75,9 @@ globalThis.Bun = { timeout: options.timeout, env: options.env, cwd: options.cwd, + // The browse server runs console-less on Windows; without this every + // subprocess (tasklist, git, icacls) pops a visible console window. + windowsHide: true, }); return { @@ -91,6 +94,10 @@ globalThis.Bun = { stdio, env: options.env, cwd: options.cwd, + detached: options.detached, + // Same as spawnSync: detached daemons (terminal-agent respawns) must + // not flash a console window on Windows. + windowsHide: true, }); return { diff --git a/browse/test/bun-polyfill.test.ts b/browse/test/bun-polyfill.test.ts index 7ca25dfab..f9c3dafe6 100644 --- a/browse/test/bun-polyfill.test.ts +++ b/browse/test/bun-polyfill.test.ts @@ -1,4 +1,5 @@ import { describe, test, expect, afterAll } from 'bun:test'; +import * as fs from 'fs'; import * as path from 'path'; // Load the polyfill into a fresh object (don't clobber globalThis.Bun) @@ -48,6 +49,23 @@ describe('bun-polyfill', () => { expect(lines[2]).toBe('HAS_UNREF'); }); + test('spawn and spawnSync pass windowsHide (no console flash from console-less daemons)', () => { + // Static tripwire (#2151): on Windows the browse server always runs + // under Node via this polyfill, and node's child_process defaults to + // windowsHide: false — so every subprocess the console-less daemon + // spawns (terminal-agent respawns, git, icacls) pops a visible console + // window. Both spawn paths must pass windowsHide: true, and spawn must + // forward `detached` for daemon children. Behavioral assertion isn't + // possible cross-platform (window creation is invisible to the child), + // hence source-level pinning. + const src = fs.readFileSync(polyfillPath, 'utf-8'); + const spawnSyncBlock = src.slice(src.indexOf('spawnSync(cmd'), src.indexOf('spawn(cmd')); + const spawnBlock = src.slice(src.indexOf('spawn(cmd')); + expect(spawnSyncBlock).toContain('windowsHide: true'); + expect(spawnBlock).toContain('windowsHide: true'); + expect(spawnBlock).toContain('detached: options.detached'); + }); + test('Bun.serve creates an HTTP server that responds', async () => { const result = Bun.spawnSync(['node', '-e', ` require('${polyfillPath}');