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/validator.ts
1import { stat, readdir } from "node:fs/promises";2import { homedir } from "node:os";3import path from "node:path";4import { GenError } from "./types.ts";5import type { ToolCall } from "./types.ts";6import { hasImageGenInvocation } from "./parser.ts";78const PNG_MAGIC = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);910export function codexHome(): string {11return process.env.CODEX_HOME ?? path.join(homedir(), ".codex");12}1314export async function verifyImageGenWasInvoked(threadId: string | null): Promise<{ ok: boolean; reason?: string }> {15if (!threadId) return { ok: false, reason: "no thread id" };16const dir = path.join(codexHome(), "generated_images", threadId);17try {18const entries = await readdir(dir);19const pngs = entries.filter((e) => e.toLowerCase().endsWith(".png"));20if (pngs.length === 0) return { ok: false, reason: `no PNG in ${dir}` };21return { ok: true };22} catch (e: any) {23return { ok: false, reason: `cannot read ${dir}: ${e?.code ?? e?.message}` };24}25}2627// Real evidence that image_gen ran in THIS thread. Codex's image_gen tool does28// not surface as a stream item, so a successful run shows only reasoning/shell/29// agent_message — `dirHasImage` (a PNG in this thread's generated_images dir) is30// what proves it. The stream check is kept as a forward-compatible signal in31// case a future Codex version emits the item. The #185 shortcut (copying an32// unrelated history image, which lives under a different thread id) yields33// neither, so it is correctly rejected.34export function hasImageGenEvidence(toolCalls: ToolCall[], dirHasImage: boolean): boolean {35return dirHasImage || hasImageGenInvocation(toolCalls);36}3738export async function verifyOutput(outputPath: string): Promise<{ bytes: number }> {39let s;40try {41s = await stat(outputPath);42} catch {43throw new GenError("output_missing", `Output file not created: ${outputPath}`);44}45if (s.size < 1000) {46throw new GenError("invalid_png", `Output file too small (${s.size} bytes)`);47}48const file = Bun.file(outputPath);49const head = new Uint8Array(await file.slice(0, 8).arrayBuffer());50for (let i = 0; i < PNG_MAGIC.length; i++) {51if (head[i] !== PNG_MAGIC[i]) {52throw new GenError("invalid_png", `Output is not a valid PNG (magic mismatch)`);53}54}55return { bytes: s.size };56}57