Skip to content

Commit f5705cd

Browse files
backnotpropclaude
andauthored
fix: non-blocking browser launch in Pi extension (Linux) (#292)
* fix: use non-blocking spawn for browser launch in Pi extension execSync(xdg-open ...) blocks the Node.js event loop, preventing the HTTP server from responding to the browser's initial request on Linux desktops where xdg-open doesn't immediately exit. Closes #288 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: handle async spawn errors in Pi openBrowser spawn() reports missing-command failures asynchronously via an 'error' event. Without a listener, a bad PLANNOTATOR_BROWSER or missing xdg-open crashes the Pi process. Add child.once("error", () => {}) to swallow it silently, matching the original execSync try/catch behavior. --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d2238ac commit f5705cd

1 file changed

Lines changed: 20 additions & 7 deletions

File tree

apps/pi-extension/server.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import { createServer, type IncomingMessage, type Server } from "node:http";
10-
import { execSync } from "node:child_process";
10+
import { execSync, spawn } from "node:child_process";
1111
import os from "node:os";
1212
import { mkdirSync, writeFileSync, readFileSync, readdirSync, statSync } from "node:fs";
1313
import { join, basename } from "node:path";
@@ -54,21 +54,34 @@ export function openBrowser(url: string): void {
5454
const platform = process.platform;
5555
const wsl = platform === "linux" && os.release().toLowerCase().includes("microsoft");
5656

57+
let cmd: string;
58+
let args: string[];
59+
5760
if (browser) {
5861
if (process.env.PLANNOTATOR_BROWSER && platform === "darwin") {
59-
execSync(`open -a ${JSON.stringify(browser)} ${JSON.stringify(url)}`, { stdio: "ignore" });
62+
cmd = "open";
63+
args = ["-a", browser, url];
6064
} else if (platform === "win32" || wsl) {
61-
execSync(`cmd.exe /c start "" ${JSON.stringify(browser)} ${JSON.stringify(url)}`, { stdio: "ignore" });
65+
cmd = "cmd.exe";
66+
args = ["/c", "start", "", browser, url];
6267
} else {
63-
execSync(`${JSON.stringify(browser)} ${JSON.stringify(url)}`, { stdio: "ignore" });
68+
cmd = browser;
69+
args = [url];
6470
}
6571
} else if (platform === "win32" || wsl) {
66-
execSync(`cmd.exe /c start "" ${JSON.stringify(url)}`, { stdio: "ignore" });
72+
cmd = "cmd.exe";
73+
args = ["/c", "start", "", url];
6774
} else if (platform === "darwin") {
68-
execSync(`open ${JSON.stringify(url)}`, { stdio: "ignore" });
75+
cmd = "open";
76+
args = [url];
6977
} else {
70-
execSync(`xdg-open ${JSON.stringify(url)}`, { stdio: "ignore" });
78+
cmd = "xdg-open";
79+
args = [url];
7180
}
81+
82+
const child = spawn(cmd, args, { detached: true, stdio: "ignore" });
83+
child.once("error", () => {});
84+
child.unref();
7285
} catch {
7386
// Silently fail
7487
}

0 commit comments

Comments
 (0)