Просмотр исходного кода

fix(ui): launch a CODEGRAPH_BROWSER override through cmd on Windows

CreateProcess — which node's spawn uses without a shell — only launches a real
.exe, so a `.cmd`/`.bat` browser shim (how most Windows wrappers are written)
silently launched nothing. Routing the override through `cmd /c`, the way the
default `start` opener already goes, makes .exe, .cmd and .bat all work and
keeps node's per-argument quoting so a path with spaces survives.

Caught on the Windows VM.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Colby McHenry 1 неделя назад
Родитель
Сommit
bcfe52efa5
2 измененных файлов с 14 добавлено и 0 удалено
  1. 6 0
      __tests__/ui-server.test.ts
  2. 8 0
      src/ui-server/open-browser.ts

+ 6 - 0
__tests__/ui-server.test.ts

@@ -523,6 +523,12 @@ describe('browserOpenCommand', () => {
       command: 'firefox',
       args: ['http://x'],
     });
+    // Windows routes the override through cmd so a `.cmd`/`.bat` shim — which
+    // CreateProcess cannot launch directly — still works.
+    expect(browserOpenCommand('http://x', 'win32', 'C:\\tools\\open.cmd')).toEqual({
+      command: 'cmd',
+      args: ['/c', 'C:\\tools\\open.cmd', 'http://x'],
+    });
     for (const off of ['none', 'NONE', '0', 'false', 'off', '', '  ']) {
       expect(browserOpenCommand('http://x', 'darwin', off), off).toBeNull();
     }

+ 8 - 0
src/ui-server/open-browser.ts

@@ -32,6 +32,14 @@ export function browserOpenCommand(
   if (override !== undefined) {
     const trimmed = override.trim();
     if (SUPPRESS_VALUES.has(trimmed.toLowerCase())) return null;
+    // Windows: go through `cmd /c` rather than spawning the override directly.
+    // `spawn` there is CreateProcess, which only ever launches a real .exe — a
+    // `.cmd`/`.bat` browser shim (how most Windows wrappers are written) fails
+    // outright, and an extension-less name only resolves because CreateProcess
+    // appends `.exe`. Routing through cmd makes .exe, .cmd and .bat all work,
+    // and node quotes each argument, so a path with spaces survives. Caught on
+    // the Windows VM, where the direct spawn silently launched nothing.
+    if (platform === 'win32') return { command: 'cmd', args: ['/c', trimmed, url] };
     return { command: trimmed, args: [url] };
   }
   if (platform === 'darwin') return { command: 'open', args: [url] };