From 3bde235a85b9a42f9efd1968da8fb852761e6dbd Mon Sep 17 00:00:00 2001 From: Oleksii Nikiforov Date: Fri, 5 Jun 2026 18:52:33 +0300 Subject: [PATCH] fix(server): launch Glimpse via node on Windows instead of unspawnable npm shim (#861) On Windows, Bun.which("glimpseui") resolves to an npm script shim (.cmd / extensionless shebang), which spawn() cannot launch without a shell: it throws EINVAL on .cmd and ENOENT on the shim, so openGlimpse() always returned false and plannotator fell back to the browser. shell:true is not viable because it severs the stdin HTML pipe (glimpse.mjs then exits 1). Resolve the package entry and run it with node directly instead. --- packages/server/browser.ts | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/server/browser.ts b/packages/server/browser.ts index b602e96d..14c40977 100644 --- a/packages/server/browser.ts +++ b/packages/server/browser.ts @@ -139,6 +139,26 @@ async function openGlimpse(url: string): Promise { ]; const html = buildGlimpseHtml(url); + // On Windows, `glimpseui` resolves to an npm script shim, not an exe, which + // spawn() can't launch without a shell. `shell: true` would break the stdin + // HTML pipe below, so run the package entry with node directly instead. + let command = glimpseCli; + let spawnArgs = args; + if (process.platform === "win32" && !/\.exe$/i.test(glimpseCli)) { + const node = Bun.which("node"); + const entry = path.join( + path.dirname(glimpseCli), + "node_modules", + "glimpseui", + "bin", + "glimpse.mjs" + ); + if (node && fs.existsSync(entry)) { + command = node; + spawnArgs = [entry, ...args]; + } + } + return await new Promise((resolve) => { let settled = false; let successTimer: ReturnType | undefined; @@ -149,7 +169,7 @@ async function openGlimpse(url: string): Promise { resolve(opened); }; - const child = spawn(glimpseCli, args, { + const child = spawn(command, spawnArgs, { detached: true, stdio: ["pipe", "ignore", "ignore"], });