Skip to content

Commit 3595cd3

Browse files
committed
fix(codex): handle windows cli resume shims
1 parent fcc27c3 commit 3595cd3

2 files changed

Lines changed: 71 additions & 1 deletion

File tree

extensions/codex/src/node-cli-sessions.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest";
66
import {
77
CODEX_CLI_SESSIONS_LIST_COMMAND,
88
createCodexCliSessionNodeHostCommands,
9+
resolveCodexCliResumeSpawnInvocation,
910
} from "./node-cli-sessions.js";
1011

1112
let tempDir: string;
@@ -121,4 +122,30 @@ describe("codex cli node sessions", () => {
121122
},
122123
]);
123124
});
125+
126+
it("resolves Windows npm .cmd Codex shims through Node for resume", async () => {
127+
const binDir = path.join(tempDir, "bin");
128+
const entryPath = path.join(binDir, "node_modules", "@openai", "codex", "bin", "codex.js");
129+
const shimPath = path.join(binDir, "codex.cmd");
130+
await fs.mkdir(path.dirname(entryPath), { recursive: true });
131+
await fs.writeFile(entryPath, "console.log('codex')\n", "utf8");
132+
await fs.writeFile(
133+
shimPath,
134+
'@ECHO off\r\n"%~dp0\\node_modules\\@openai\\codex\\bin\\codex.js" %*\r\n',
135+
"utf8",
136+
);
137+
138+
const resolved = resolveCodexCliResumeSpawnInvocation(["exec", "resume", "session-id"], {
139+
platform: "win32",
140+
env: { PATH: binDir, PATHEXT: ".CMD;.EXE;.BAT" },
141+
execPath: "C:\\node\\node.exe",
142+
});
143+
144+
expect(resolved).toEqual({
145+
command: "C:\\node\\node.exe",
146+
args: [entryPath, "exec", "resume", "session-id"],
147+
shell: undefined,
148+
windowsHide: true,
149+
});
150+
});
124151
});

extensions/codex/src/node-cli-sessions.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ import type {
99
} from "openclaw/plugin-sdk/plugin-entry";
1010
import type { PluginRuntime } from "openclaw/plugin-sdk/plugin-runtime";
1111
import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
12+
import {
13+
materializeWindowsSpawnProgram,
14+
resolveWindowsSpawnProgram,
15+
} from "openclaw/plugin-sdk/windows-spawn";
1216
import { formatCodexDisplayText } from "./command-formatters.js";
1317

1418
export const CODEX_CLI_SESSIONS_LIST_COMMAND = "codex.cli.sessions.list";
@@ -48,6 +52,18 @@ type CodexCliSessionNodeInfo = {
4852
commands?: string[];
4953
};
5054

55+
type CodexCliResumeSpawnRuntime = {
56+
platform: NodeJS.Platform;
57+
env: NodeJS.ProcessEnv;
58+
execPath: string;
59+
};
60+
61+
const DEFAULT_RESUME_SPAWN_RUNTIME: CodexCliResumeSpawnRuntime = {
62+
platform: process.platform,
63+
env: process.env,
64+
execPath: process.execPath,
65+
};
66+
5167
export function createCodexCliSessionNodeHostCommands(): OpenClawPluginNodeHostCommand[] {
5268
return [
5369
{
@@ -249,10 +265,17 @@ async function runCodexExecResume(params: {
249265
params.sessionId,
250266
"-",
251267
];
252-
const child = spawn("codex", args, {
268+
const invocation = resolveCodexCliResumeSpawnInvocation(args, {
269+
platform: process.platform,
270+
env: process.env,
271+
execPath: process.execPath,
272+
});
273+
const child = spawn(invocation.command, invocation.args, {
253274
cwd: params.cwd || process.cwd(),
254275
stdio: ["pipe", "pipe", "pipe"],
255276
env: process.env,
277+
shell: invocation.shell,
278+
windowsHide: invocation.windowsHide,
256279
});
257280
const stdout: Buffer[] = [];
258281
const stderr: Buffer[] = [];
@@ -292,6 +315,26 @@ async function runCodexExecResume(params: {
292315
}
293316
}
294317

318+
export function resolveCodexCliResumeSpawnInvocation(
319+
args: string[],
320+
runtime: CodexCliResumeSpawnRuntime = DEFAULT_RESUME_SPAWN_RUNTIME,
321+
): { command: string; args: string[]; shell?: boolean; windowsHide?: boolean } {
322+
const program = resolveWindowsSpawnProgram({
323+
command: "codex",
324+
platform: runtime.platform,
325+
env: runtime.env,
326+
execPath: runtime.execPath,
327+
packageName: "@openai/codex",
328+
});
329+
const resolved = materializeWindowsSpawnProgram(program, args);
330+
return {
331+
command: resolved.command,
332+
args: resolved.argv,
333+
shell: resolved.shell,
334+
windowsHide: resolved.windowsHide,
335+
};
336+
}
337+
295338
async function readHistorySessions(
296339
codexHome: string,
297340
): Promise<Map<string, CodexCliSessionSummary>> {

0 commit comments

Comments
 (0)