Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 36 additions & 3 deletions sdk/typescript/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ const CREDENTIAL_LOGOUT_MARKER = ".codex-security-logged-out";
const CREDENTIAL_LOCK_POLL_MILLISECONDS = 25;
const INCOMPLETE_CREDENTIAL_LOCK_MILLISECONDS = 30_000;
const MAX_WINDOWS_CREDENTIAL_ACL_STDERR = 64 * 1024;
const WINDOWS_APPS_PATH = /(^|[\\/])windowsapps([\\/]|$)/iu;
const WINDOWS_DIRECT_EXECUTABLE = /^\.(?:exe|com)$/iu;

export interface PluginInstall {
pluginRoot: string;
Expand Down Expand Up @@ -2289,15 +2291,46 @@ export async function resolvePluginPython(
export function pluginExecutionEnvironment(
python: string,
environment: ProcessEnvironment = process.env,
platform: NodeJS.Platform = process.platform,
): ProcessEnvironment {
const configuredCodexPath = configuredCodexCliPath(environment, platform);
const codexPath =
configuredCodexPath !== undefined &&
(platform !== "win32" ||
(WINDOWS_DIRECT_EXECUTABLE.test(extname(configuredCodexPath)) &&
!WINDOWS_APPS_PATH.test(configuredCodexPath)))
? configuredCodexPath
: resolveCodexCommand().command;
const normalizedEnvironment =
platform === "win32"
? Object.fromEntries(
Object.entries(environment).filter(
([name]) => name.toUpperCase() !== "CODEX_CLI_PATH",
),
)
: environment;
return {
...environment,
...normalizedEnvironment,
PYTHON: python,
CODEX_CLI_PATH:
environment["CODEX_CLI_PATH"]?.trim() || resolveCodexCommand().command,
CODEX_CLI_PATH: codexPath,
};
}

function configuredCodexCliPath(
environment: ProcessEnvironment,
platform: NodeJS.Platform,
): string | undefined {
const exact = environment["CODEX_CLI_PATH"]?.trim();
if (exact) return exact;
if (platform !== "win32") return undefined;
return Object.entries(environment)
.find(
([name, value]) =>
name.toUpperCase() === "CODEX_CLI_PATH" && value?.trim(),
)?.[1]
?.trim();
}

export async function cleanupSdkDirectory(path: string): Promise<void> {
await rm(path, { recursive: true, force: true });
}
Expand Down
30 changes: 30 additions & 0 deletions sdk/typescript/tests-ts/runtime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1715,6 +1715,36 @@ describe("plugin runtime preparation", () => {
).toBe(resolveCodexCommand().command);
});

test("uses only directly spawnable Windows Codex overrides for nested workers", () => {
const configured = String.raw`C:\Users\example\AppData\Local\OpenAI\Codex\bin\0.146.0\codex.exe`;
const bundled = resolveCodexCommand().command;

expect(
pluginExecutionEnvironment(
String.raw`C:\Python\python.exe`,
{ Codex_Cli_Path: ` ${configured} ` },
"win32",
),
).toEqual({
CODEX_CLI_PATH: configured,
PYTHON: String.raw`C:\Python\python.exe`,
});

for (const rejected of [
String.raw`C:\Users\example\AppData\Roaming\npm\codex`,
String.raw`C:\Users\example\AppData\Roaming\npm\codex.cmd`,
String.raw`C:\Program Files\WindowsApps\OpenAI.Codex_26.727.6591.0_x64__2p2nqsd0c76g0\app\resources\codex.exe`,
]) {
expect(
pluginExecutionEnvironment(
String.raw`C:\Python\python.exe`,
{ CODEX_CLI_PATH: rejected },
"win32",
)["CODEX_CLI_PATH"],
).toBe(bundled);
}
});

test("selects the native Windows Codex executable package", () => {
expect(codexPlatformPackage("win32", "x64")).toEqual({
packageName: "@openai/codex-win32-x64",
Expand Down
Loading