Opening a URL in the user's browser, from the proxy.
Under WSL the browser is Windows': rundll32.exe url.dll,FileProtocolHandler <url> through interop hands the URL to Windows' default browser whole.
Measured 2026-09-26 on nyx: it opened Chrome with every query parameter
intact, and Chrome's request to http://127.0.0.1:<port>/ reached a listener
bound to 127.0.0.1 in the guest (WSL's localhost forwarding). explorer.exe <url> exited 1 and opened nothing; cmd.exe /c start would split the URL
at its first &. windows-exec (SSH) cannot show a window at all: its
logon is session 0.
Elsewhere, xdg-open or macOS's open. Whatever happens, the caller also
hands the URL to the user, so a launch that fails costs one click.
JEVSTRUDEL_MCP_OPEN_BROWSER=0 opens nothing: for tests, whose own headless
browser takes the URL instead.
19const RUNDLL32 = '/mnt/c/Windows/System32/rundll32.exe'; 20 21function isWsl() { 22 try { 23 return /microsoft/i.test(readFileSync('/proc/sys/kernel/osrelease', 'utf8')); 24 } catch { 25 return false; 26 } 27} 28 29function launcher(url) { 30 if (isWsl() && existsSync(RUNDLL32)) return [RUNDLL32, ['url.dll,FileProtocolHandler', url]]; 31 if (process.platform === 'darwin') return ['open', [url]]; 32 return ['xdg-open', [url]]; 33}
Resolves to how it was opened, or rejects with why not.
36export function openBrowser(url) { 37 if (process.env.JEVSTRUDEL_MCP_OPEN_BROWSER === '0') return Promise.reject(new Error('JEVSTRUDEL_MCP_OPEN_BROWSER=0')); 38 const [command, args] = launcher(url); 39 return new Promise((resolve, reject) => { 40 const child = spawn(command, args, { stdio: 'ignore', detached: true }); 41 child.on('error', reject); 42 child.on('exit', (code) => (code === 0 ? resolve(command) : reject(new Error(`${command} exited ${code}`)))); 43 child.unref(); 44 }); 45}