Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Generate images via OpenAI, Google, OpenRouter, DashScope, Jimeng, Seedream, and Replicate APIs with batch support.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
scripts/codex-imagegen/spawn.ts
1import { spawn } from "node:child_process";2import { writeFile, mkdtemp } from "node:fs/promises";3import { tmpdir } from "node:os";4import path from "node:path";5import { GenError, type CodexRunResult } from "./types.ts";6import { parseEventStream } from "./parser.ts";78export interface SpawnInput {9instruction: string;10timeoutMs: number;11refImages?: string[];12}1314export async function runCodexExec(input: SpawnInput): Promise<CodexRunResult> {15const start = Date.now();16const logDir = await mkdtemp(path.join(tmpdir(), "codex-imggen-"));17const rawLogPath = path.join(logDir, "stream.jsonl");1819// --skip-git-repo-check: lets the wrapper run from non-git cwds20// (e.g. /tmp, or a skill installed under ~/.claude/plugins/...).21// Without it, codex refuses with "Not inside a trusted directory".22const args = [23"exec",24"--json",25"--sandbox",26"danger-full-access",27"--skip-git-repo-check",28];29for (const img of input.refImages ?? []) {30args.push("--image", img);31}32args.push("-");3334let timedOut = false;35const child = spawn("codex", args, { stdio: ["pipe", "pipe", "pipe"] });3637let stdout = "";38let stderr = "";39child.stdout.on("data", (chunk) => {40stdout += chunk.toString();41});42child.stderr.on("data", (chunk) => {43stderr += chunk.toString();44});4546child.stdin.write(input.instruction);47child.stdin.end();4849const timer = setTimeout(() => {50timedOut = true;51child.kill("SIGTERM");52setTimeout(() => child.kill("SIGKILL"), 2000);53}, input.timeoutMs);5455const exit = await new Promise<{ code: number | null; signal: NodeJS.Signals | null }>((resolve) => {56child.on("close", (code, signal) => resolve({ code, signal }));57});58clearTimeout(timer);5960await writeFile(rawLogPath, stdout + (stderr ? `\n--- stderr ---\n${stderr}` : ""));6162if (timedOut) {63throw new GenError("timeout", `codex exec exceeded ${input.timeoutMs}ms (log: ${rawLogPath})`);64}65if (exit.code !== 0) {66if (stderr.includes("command not found") || stderr.includes("not found: codex")) {67throw new GenError("codex_not_installed", "codex CLI not installed", false);68}69throw new GenError(70"spawn_failed",71`codex exec exited ${exit.code} signal=${exit.signal} (log: ${rawLogPath})`,72);73}7475const parsed = parseEventStream(stdout);76return {77...parsed,78rawLogPath,79durationMs: Date.now() - start,80};81}82