diff --git a/src/targets/codex-detector.ts b/src/targets/codex-detector.ts index e7070617..eedc5ed8 100644 --- a/src/targets/codex-detector.ts +++ b/src/targets/codex-detector.ts @@ -53,12 +53,14 @@ function runCodexProbe(codexPath: string, args: string[]): string | undefined { if (needsShell) { const cmdString = [codexPath, ...args].map(escapeShellArg).join(' '); - return childProcess.execFileSync('cmd.exe', ['/d', '/s', '/c', cmdString], { + const result = childProcess.spawnSync(cmdString, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 5000, windowsHide: true, + shell: 'cmd.exe', }); + return result.status === 0 ? result.stdout : undefined; } return childProcess.execFileSync(codexPath, args, { diff --git a/tests/unit/targets/codex-detector.test.ts b/tests/unit/targets/codex-detector.test.ts index b0bb1e0d..b0fc8e52 100644 --- a/tests/unit/targets/codex-detector.test.ts +++ b/tests/unit/targets/codex-detector.test.ts @@ -58,19 +58,35 @@ describe('codex-detector', () => { process.env.CCS_CODEX_PATH = fakeCodex; Object.defineProperty(process, 'platform', { value: 'win32' }); - const execFileSyncSpy = spyOn(childProcess, 'execFileSync').mockImplementation((command, args) => { - return String(command).includes('cmd.exe') && Array.isArray(args) && args.join(' ').includes('--help') - ? 'Codex CLI\n -c, --config \n' - : 'codex-cli 0.118.0-alpha.3'; + const spawnSyncSpy = spyOn(childProcess, 'spawnSync').mockImplementation((command) => { + const commandString = String(command); + return { + pid: 123, + output: ['', '', ''], + stdout: commandString.includes('--help') + ? 'Codex CLI\n -c, --config \n' + : 'codex-cli 0.118.0-alpha.3', + stderr: '', + status: 0, + signal: null, + } as unknown as ReturnType; }); const info = getCodexBinaryInfo(); + const calls = spawnSyncSpy.mock.calls; + const cmdWrapperProbeCall = calls.find(([command]) => { + return String(command).includes(fakeCodex); + }); - expect(execFileSyncSpy).toHaveBeenCalled(); + expect(spawnSyncSpy).toHaveBeenCalled(); + expect(cmdWrapperProbeCall).toBeDefined(); + expect((cmdWrapperProbeCall?.[1] as Record | undefined)?.shell).toBe( + 'cmd.exe' + ); expect(info?.needsShell).toBe(true); expect(info?.features).toContain('config-overrides'); - execFileSyncSpy.mockRestore(); + spawnSyncSpy.mockRestore(); }); it('keeps the cmd wrapper when Windows PATH exposes codex.cmd and a sibling ps1 also exists', () => {