fix: resolve Codex spawn failures on Windows (#562)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jinwoo Hong 2026-04-13 18:49:44 -04:00 committed by GitHub
parent 114c0d1b69
commit 3793125319
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 81 additions and 4 deletions

View file

@ -134,7 +134,8 @@
"node-pty"
],
"patchedDependencies": {
"@xterm/addon-fit@0.11.0": "config/patches/@xterm__addon-fit@0.11.0.patch"
"@xterm/addon-fit@0.11.0": "config/patches/@xterm__addon-fit@0.11.0.patch",
"node-pty@1.1.0": "patches/node-pty@1.1.0.patch"
}
}
}

View file

@ -0,0 +1,56 @@
diff --git a/binding.gyp b/binding.gyp
index 5f63978b07ab50aaf7523219a2170ec737a6b5db..b3309a07ef99dea7967d7bdd04b9fc3500acacae 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -5,9 +5,6 @@
],
'conditions': [
['OS=="win"', {
- 'msvs_configuration_attributes': {
- 'SpectreMitigation': 'Spectre'
- },
'msvs_settings': {
'VCCLCompilerTool': {
'AdditionalOptions': [
diff --git a/deps/winpty/src/winpty.gyp b/deps/winpty/src/winpty.gyp
index 1ac5758bedd8cf54f32280dea4e4aeb5afdee30d..e619813759c6f14694838bdfbd0ea5f8360130ef 100644
--- a/deps/winpty/src/winpty.gyp
+++ b/deps/winpty/src/winpty.gyp
@@ -10,7 +10,7 @@
# make -j4 CXX=i686-w64-mingw32-g++ LDFLAGS="-static -static-libgcc -static-libstdc++"
'variables': {
- 'WINPTY_COMMIT_HASH%': '<!(cmd /c "cd shared && GetCommitHash.bat")',
+ 'WINPTY_COMMIT_HASH%': '<!(cmd /c "cd shared && .\\GetCommitHash.bat")',
},
'target_defaults' : {
'defines' : [
@@ -22,7 +22,7 @@
'include_dirs': [
# Add the 'src/gen' directory to the include path and force gyp to
# run the script (re)generating the version header.
- '<!(cmd /c "cd shared && UpdateGenVersion.bat <(WINPTY_COMMIT_HASH)")',
+ '<!(cmd /c "cd shared && .\\UpdateGenVersion.bat <(WINPTY_COMMIT_HASH)")',
]
},
'targets' : [
@@ -40,9 +40,6 @@
'-lshell32',
'-luser32',
],
- 'msvs_configuration_attributes': {
- 'SpectreMitigation': 'Spectre'
- },
'msvs_settings': {
# Specify this setting here to override a setting from somewhere
# else, such as node's common.gypi.
@@ -142,9 +139,6 @@
'-ladvapi32',
'-luser32',
],
- 'msvs_configuration_attributes': {
- 'SpectreMitigation': 'Spectre'
- },
'msvs_settings': {
# Specify this setting here to override a setting from somewhere
# else, such as node's common.gypi.

View file

@ -8,6 +8,9 @@ patchedDependencies:
'@xterm/addon-fit@0.11.0':
hash: 12b1dc2811a859b6703ebb63be654d1682ff1bb6045713a54e7d47e28deb7776
path: config/patches/@xterm__addon-fit@0.11.0.patch
node-pty@1.1.0:
hash: d35190e5f762b8d3289f13df0ff40c7ad19dfb64ca488494cce9bea79ab5c0ad
path: patches/node-pty@1.1.0.patch
importers:
@ -132,7 +135,7 @@ importers:
version: 0.55.1
node-pty:
specifier: ^1.1.0
version: 1.1.0
version: 1.1.0(patch_hash=d35190e5f762b8d3289f13df0ff40c7ad19dfb64ca488494cce9bea79ab5c0ad)
radix-ui:
specifier: ^1.4.3
version: 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@ -10756,7 +10759,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
node-pty@1.1.0:
node-pty@1.1.0(patch_hash=d35190e5f762b8d3289f13df0ff40c7ad19dfb64ca488494cce9bea79ab5c0ad):
dependencies:
node-addon-api: 7.1.1

View file

@ -284,6 +284,11 @@ export class CodexAccountService {
await new Promise<void>((resolvePromise, rejectPromise) => {
const child = spawn(resolveCodexCommand(), ['login'], {
stdio: ['ignore', 'pipe', 'pipe'],
// Why: on Windows, resolveCodexCommand() may return a .cmd/.bat file
// (e.g. codex.cmd from npm). Node's child_process.spawn cannot execute
// batch scripts directly — it needs cmd.exe as an intermediary. Setting
// shell: true on win32 avoids the EINVAL error this would otherwise cause.
shell: process.platform === 'win32',
env: {
...process.env,
CODEX_HOME: managedHomePath

View file

@ -90,6 +90,11 @@ async function fetchViaRpc(options?: FetchCodexRateLimitsOptions): Promise<Provi
['-s', 'read-only', '-a', 'untrusted', 'app-server'],
{
stdio: ['pipe', 'pipe', 'pipe'],
// Why: on Windows, resolveCodexCommand() may return a .cmd/.bat file
// (e.g. codex.cmd from npm). Node's child_process.spawn cannot execute
// batch scripts directly — it needs cmd.exe as an intermediary. Setting
// shell: true on win32 avoids the EINVAL error this would otherwise cause.
shell: process.platform === 'win32',
// Why: the selected Codex rate-limit account must only affect this fetch
// subprocess. Never mutate process.env globally or other Codex features
// would inherit the managed account unintentionally.
@ -285,12 +290,19 @@ async function fetchViaPty(options?: FetchCodexRateLimitsOptions): Promise<Provi
const pty = await import('node-pty')
const codexCommand = resolveCodexCommand()
// Why: node-pty cannot spawn .cmd/.bat batch scripts directly on Windows —
// those need cmd.exe as an interpreter. Route through `cmd.exe /c <command>`
// so the PTY fallback works when Codex is installed via npm (codex.cmd).
const isWindowsBatchScript = process.platform === 'win32' && /\.(cmd|bat)$/i.test(codexCommand)
const spawnFile = isWindowsBatchScript ? 'cmd.exe' : codexCommand
const spawnArgs = isWindowsBatchScript ? ['/c', codexCommand] : []
return new Promise<ProviderRateLimits>((resolve) => {
let output = ''
let resolved = false
let sentStatus = false
const term = pty.spawn(codexCommand, [], {
const term = pty.spawn(spawnFile, spawnArgs, {
name: 'xterm-256color',
cols: 120,
rows: 40,