mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 16:50:43 +00:00
fix(codex): resolve Windows app-server shims
This commit is contained in:
@@ -24,6 +24,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Fixes
|
||||
|
||||
- Codex harness: route native `request_user_input` prompts back to the originating chat, preserve queued follow-up answers, and honor newer app-server command approval amendment decisions.
|
||||
- Codex harness/Windows: resolve npm-installed `codex.cmd` shims through PATHEXT before starting the native app-server, so `codex/*` models work without a manual `.exe` shim. Fixes #70913.
|
||||
- Slack/groups: classify MPIM group DMs as group chat context and suppress verbose tool/plan progress on Slack non-DM surfaces, so internal "Working…" traces no longer leak into rooms. Fixes #70912.
|
||||
- Agents/replay: stop OpenAI/Codex transcript replay from synthesizing missing tool results while still preserving synthetic repair on Anthropic, Gemini, and Bedrock transport-owned sessions. (#61556) Thanks @VictorJeon and @vincentkoc.
|
||||
- Telegram/media replies: parse remote markdown image syntax into outbound media payloads on the final reply path, so Telegram group chats stop falling back to plain-text image URLs when the model or a tool emits `` instead of a `MEDIA:` token. (#66191) Thanks @apezam and @vincentkoc.
|
||||
|
||||
72
extensions/codex/src/app-server/transport-stdio.test.ts
Normal file
72
extensions/codex/src/app-server/transport-stdio.test.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import type { CodexAppServerStartOptions } from "./config.js";
|
||||
import { resolveCodexAppServerSpawnInvocation } from "./transport-stdio.js";
|
||||
|
||||
const tempDirs: string[] = [];
|
||||
|
||||
async function createTempDir(): Promise<string> {
|
||||
const dir = await mkdtemp(path.join(os.tmpdir(), "openclaw-codex-spawn-"));
|
||||
tempDirs.push(dir);
|
||||
return dir;
|
||||
}
|
||||
|
||||
afterEach(async () => {
|
||||
for (const dir of tempDirs.splice(0)) {
|
||||
await rm(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
function startOptions(command: string): CodexAppServerStartOptions {
|
||||
return {
|
||||
transport: "stdio",
|
||||
command,
|
||||
args: ["app-server", "--listen", "stdio://"],
|
||||
headers: {},
|
||||
};
|
||||
}
|
||||
|
||||
describe("resolveCodexAppServerSpawnInvocation", () => {
|
||||
it("keeps non-Windows Codex app-server invocation unchanged", () => {
|
||||
const resolved = resolveCodexAppServerSpawnInvocation(startOptions("codex"), {
|
||||
platform: "darwin",
|
||||
env: {},
|
||||
execPath: "/usr/local/bin/node",
|
||||
});
|
||||
|
||||
expect(resolved).toEqual({
|
||||
command: "codex",
|
||||
args: ["app-server", "--listen", "stdio://"],
|
||||
shell: undefined,
|
||||
windowsHide: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it("resolves Windows npm .cmd Codex shims through Node instead of raw spawn", async () => {
|
||||
const binDir = await createTempDir();
|
||||
const entryPath = path.join(binDir, "node_modules", "@openai", "codex", "bin", "codex.js");
|
||||
const shimPath = path.join(binDir, "codex.cmd");
|
||||
await mkdir(path.dirname(entryPath), { recursive: true });
|
||||
await writeFile(entryPath, "console.log('codex')\n", "utf8");
|
||||
await writeFile(
|
||||
shimPath,
|
||||
'@ECHO off\r\n"%~dp0\\node_modules\\@openai\\codex\\bin\\codex.js" %*\r\n',
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const resolved = resolveCodexAppServerSpawnInvocation(startOptions("codex"), {
|
||||
platform: "win32",
|
||||
env: { PATH: binDir, PATHEXT: ".CMD;.EXE;.BAT" },
|
||||
execPath: "C:\\node\\node.exe",
|
||||
});
|
||||
|
||||
expect(resolved).toEqual({
|
||||
command: "C:\\node\\node.exe",
|
||||
args: [entryPath, "app-server", "--listen", "stdio://"],
|
||||
shell: undefined,
|
||||
windowsHide: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,43 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import {
|
||||
materializeWindowsSpawnProgram,
|
||||
resolveWindowsSpawnProgram,
|
||||
} from "openclaw/plugin-sdk/windows-spawn";
|
||||
import type { CodexAppServerStartOptions } from "./config.js";
|
||||
import type { CodexAppServerTransport } from "./transport.js";
|
||||
|
||||
type CodexAppServerSpawnRuntime = {
|
||||
platform: NodeJS.Platform;
|
||||
env: NodeJS.ProcessEnv;
|
||||
execPath: string;
|
||||
};
|
||||
|
||||
const DEFAULT_SPAWN_RUNTIME: CodexAppServerSpawnRuntime = {
|
||||
platform: process.platform,
|
||||
env: process.env,
|
||||
execPath: process.execPath,
|
||||
};
|
||||
|
||||
export function resolveCodexAppServerSpawnInvocation(
|
||||
options: CodexAppServerStartOptions,
|
||||
runtime: CodexAppServerSpawnRuntime = DEFAULT_SPAWN_RUNTIME,
|
||||
): { command: string; args: string[]; shell?: boolean; windowsHide?: boolean } {
|
||||
const program = resolveWindowsSpawnProgram({
|
||||
command: options.command,
|
||||
platform: runtime.platform,
|
||||
env: runtime.env,
|
||||
execPath: runtime.execPath,
|
||||
packageName: "@openai/codex",
|
||||
});
|
||||
const resolved = materializeWindowsSpawnProgram(program, options.args);
|
||||
return {
|
||||
command: resolved.command,
|
||||
args: resolved.argv,
|
||||
shell: resolved.shell,
|
||||
windowsHide: resolved.windowsHide,
|
||||
};
|
||||
}
|
||||
|
||||
export function createStdioTransport(options: CodexAppServerStartOptions): CodexAppServerTransport {
|
||||
const env = {
|
||||
...process.env,
|
||||
@@ -10,9 +46,16 @@ export function createStdioTransport(options: CodexAppServerStartOptions): Codex
|
||||
for (const key of options.clearEnv ?? []) {
|
||||
delete env[key];
|
||||
}
|
||||
return spawn(options.command, options.args, {
|
||||
const invocation = resolveCodexAppServerSpawnInvocation(options, {
|
||||
platform: process.platform,
|
||||
env,
|
||||
execPath: process.execPath,
|
||||
});
|
||||
return spawn(invocation.command, invocation.args, {
|
||||
env,
|
||||
detached: process.platform !== "win32",
|
||||
shell: invocation.shell,
|
||||
stdio: ["pipe", "pipe", "pipe"],
|
||||
windowsHide: invocation.windowsHide,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user