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
18 changes: 17 additions & 1 deletion dist/main.js

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions src/writeProxyConfig.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import * as fs from "node:fs/promises";
import * as path from "node:path";
import * as os from "node:os";
import { execFileSync } from "node:child_process";
import { SafetyStrategy } from "./runCodexExec";
import { checkOutput } from "./checkOutput";

const MODEL_PROVIDER = "codex-action-responses-proxy";
const CODEX_VERSION_PATTERN =
/^codex-cli[ \t]+(\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?)$/;

export async function writeProxyConfig(
codexHome: string,
port: number,
safetyStrategy: SafetyStrategy
): Promise<void> {
const configPath = path.join(codexHome, "config.toml");
const codexVersion = readInstalledCodexVersion();
const versionHeader =
codexVersion == null
? ""
: `http_headers = { version = "${codexVersion}" }\n`;

let existing = "";
try {
Expand All @@ -32,6 +40,7 @@ model_provider = "${MODEL_PROVIDER}"
name = "Codex Action Responses Proxy"
base_url = "http://127.0.0.1:${port}/v1"
wire_api = "responses"
${versionHeader}
`;

// Prepend model_provider at the very top.
Expand All @@ -53,3 +62,16 @@ wire_api = "responses"
await fs.writeFile(configPath, output, "utf8");
}
}

function readInstalledCodexVersion(): string | null {
try {
const output = execFileSync("codex", ["--version"], {
encoding: "utf8",
}).trim();
return CODEX_VERSION_PATTERN.exec(output)?.[1] ?? null;
} catch {
// Missing version metadata must keep this caller on the legacy Responses
// error code, not make an otherwise valid action run fail.
return null;
}
}
128 changes: 128 additions & 0 deletions test/writeProxyConfig.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import assert from "node:assert/strict";
import {
chmodSync,
mkdtempSync,
readFileSync,
rmSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { spawnSync } from "node:child_process";
import { test } from "node:test";
import { fileURLToPath } from "node:url";

const mainPath = fileURLToPath(new URL("../dist/main.js", import.meta.url));

test("forwards the installed Codex runtime version through the proxy provider", () => {
const tempDir = mkdtempSync(path.join(tmpdir(), "codex-action-proxy-config-"));
const codexHome = path.join(tempDir, "codex-home");
const fakeCodexPath = path.join(tempDir, "codex");
writeFileSync(fakeCodexPath, "#!/bin/sh\necho 'codex-cli 0.144.0-alpha.1'\n", "utf8");
chmodSync(fakeCodexPath, 0o755);

try {
const result = spawnSync(
process.execPath,
[
mainPath,
"write-proxy-config",
"--codex-home",
codexHome,
"--port",
"9876",
"--safety-strategy",
"drop-sudo",
],
{
encoding: "utf8",
env: {
...process.env,
PATH: `${tempDir}${path.delimiter}${process.env.PATH ?? ""}`,
},
}
);

assert.equal(result.status, 0, result.stderr);
const config = readFileSync(path.join(codexHome, "config.toml"), "utf8");
assert.match(config, /http_headers = \{ version = "0\.144\.0-alpha\.1" \}/);
} finally {
rmSync(tempDir, { recursive: true, force: true });
}
});

test("keeps writing config when the installed Codex version cannot be parsed", () => {
const tempDir = mkdtempSync(path.join(tmpdir(), "codex-action-proxy-config-"));
const codexHome = path.join(tempDir, "codex-home");
const fakeCodexPath = path.join(tempDir, "codex");
writeFileSync(fakeCodexPath, "#!/bin/sh\necho 'codex-cli dev-build'\n", "utf8");
chmodSync(fakeCodexPath, 0o755);

try {
const result = spawnSync(
process.execPath,
[
mainPath,
"write-proxy-config",
"--codex-home",
codexHome,
"--port",
"9876",
"--safety-strategy",
"drop-sudo",
],
{
encoding: "utf8",
env: {
...process.env,
PATH: `${tempDir}${path.delimiter}${process.env.PATH ?? ""}`,
},
}
);

assert.equal(result.status, 0, result.stderr);
const config = readFileSync(path.join(codexHome, "config.toml"), "utf8");
assert.doesNotMatch(config, /http_headers =/);
} finally {
rmSync(tempDir, { recursive: true, force: true });
}
});

test("omits the version header for malformed Codex build metadata", () => {
for (const version of ["0.144.0+.", "0.144.0+build."]) {
const tempDir = mkdtempSync(path.join(tmpdir(), "codex-action-proxy-config-"));
const codexHome = path.join(tempDir, "codex-home");
const fakeCodexPath = path.join(tempDir, "codex");
writeFileSync(fakeCodexPath, `#!/bin/sh\necho 'codex-cli ${version}'\n`, "utf8");
chmodSync(fakeCodexPath, 0o755);

try {
const result = spawnSync(
process.execPath,
[
mainPath,
"write-proxy-config",
"--codex-home",
codexHome,
"--port",
"9876",
"--safety-strategy",
"drop-sudo",
],
{
encoding: "utf8",
env: {
...process.env,
PATH: `${tempDir}${path.delimiter}${process.env.PATH ?? ""}`,
},
}
);

assert.equal(result.status, 0, result.stderr);
const config = readFileSync(path.join(codexHome, "config.toml"), "utf8");
assert.doesNotMatch(config, /http_headers =/, version);
} finally {
rmSync(tempDir, { recursive: true, force: true });
}
}
});
Loading