fix: prepare writable codex home for runner jobs

This commit is contained in:
Codex
2026-05-29 13:27:11 +08:00
parent 25e017ba67
commit da50a34eef
9 changed files with 96 additions and 16 deletions
+35
View File
@@ -1,6 +1,7 @@
import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process";
import { createHash } from "node:crypto";
import { accessSync, constants as fsConstants } from "node:fs";
import { chmod, copyFile, mkdir } from "node:fs/promises";
import path from "node:path";
import * as readline from "node:readline";
import type { BackendEvent, BackendTurnResult, FailureKind, JsonRecord, JsonValue, TerminalStatus } from "../common/types.js";
@@ -256,6 +257,8 @@ export class CodexStdioClient {
export async function runCodexStdioTurn(options: CodexStdioTurnOptions): Promise<BackendTurnResult> {
const codexHome = resolveCodexHome(options);
const projectionFailure = await prepareProjectedCodexHome(codexHome, options.env?.AGENTRUN_CODEX_SECRET_HOME ?? process.env.AGENTRUN_CODEX_SECRET_HOME);
if (projectionFailure) return projectionFailure;
const secretFailure = codexHomeReadiness(codexHome);
if (secretFailure) return secretFailure;
const env = childEnv(options, codexHome);
@@ -347,6 +350,38 @@ export async function runCodexStdioTurn(options: CodexStdioTurnOptions): Promise
return { terminalStatus: terminal.status, failureKind: terminal.failureKind, failureMessage: terminal.message, events: events.map((event) => ({ ...event, payload: redactJson(event.payload) })), ...(threadId ? { threadId } : {}), ...(turnId ? { turnId } : {}) };
}
async function prepareProjectedCodexHome(codexHome: string, projectedHome: string | undefined): Promise<BackendTurnResult | null> {
if (!projectedHome || projectedHome.trim().length === 0) return null;
if (path.resolve(projectedHome) === path.resolve(codexHome)) return null;
try {
await mkdir(codexHome, { recursive: true, mode: 0o700 });
for (const fileName of ["auth.json", "config.toml"]) {
await copyFile(path.join(projectedHome, fileName), path.join(codexHome, fileName));
await chmod(path.join(codexHome, fileName), 0o600);
}
return null;
} catch (error) {
const payload = {
failureKind: "secret-unavailable",
projection: {
source: pathSummary(projectedHome),
destination: pathSummary(codexHome),
valuesPrinted: false,
},
message: error instanceof Error ? redactText(error.message) : "failed to prepare writable Codex home",
} satisfies JsonRecord;
return {
terminalStatus: "blocked",
failureKind: "secret-unavailable",
failureMessage: "Codex Secret projection could not be copied to writable CODEX_HOME",
events: [
{ type: "error", payload },
{ type: "terminal_status", payload: { terminalStatus: "blocked", failureKind: "secret-unavailable" } },
],
};
}
}
function codexHomeReadiness(codexHome: string): BackendTurnResult | null {
const auth = fileReadable(`${codexHome}/auth.json`);
const config = fileReadable(`${codexHome}/config.toml`);