Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions bin/ocx.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,10 @@ const launchContext = JSON.stringify({
});
const child = spawn(bun, [cliPath, `${NODE_LAUNCH_PROOF_PREFIX}${launchProof}`, ...process.argv.slice(2)], {
stdio: "inherit",
// A headless Windows parent (Task Scheduler, dashboard restart, shortcut) has no
// console to inherit. Without this flag Windows allocates a visible console for
// the long-running Bun child, and closing that window kills the proxy (#1236).
windowsHide: true,
env: {
...process.env,
[NODE_LAUNCH_CONTEXT_ENV]: launchContext,
Expand Down
11 changes: 11 additions & 0 deletions tests/ocx-launcher-source.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,17 @@ describe("ocx.mjs npm launcher (source invariants)", () => {
expect(runtimeSource).toContain('export const BUN_RUNTIME_SOURCE_ENV = "OCX_BUN_RUNTIME_SOURCE";');
});

test("the long-running Bun child stays hidden under a headless Windows launcher (#1236)", () => {
const spawnStart = source.indexOf("const child = spawn(bun, [cliPath");
expect(spawnStart).toBeGreaterThanOrEqual(0);
const spawnCall = source.slice(spawnStart, source.indexOf("});", spawnStart));

// Scope this to the final Node-to-Bun launch. Other helper spawns already hide
// their windows, but they do not cover the child that owns the proxy lifetime.
expect(spawnCall).toContain('stdio: "inherit"');
expect(spawnCall).toContain("windowsHide: true");
});

test("Windows npm spawns use the trusted absolute invocation without shell lookup", () => {
expect(source).toContain("const latestInvocation = npmInvocation(");
expect(source).toContain("const installInvocation = npmInvocation(");
Expand Down
Loading