Files
pbakaus_impeccable/skill/scripts/generate-image.mjs
T
Paul BakausandClaude Fable 5 5612cdf45b Visual decisions and default visualization
Paul's design, three pieces:

serve-question.mjs: the world decision presented as a themed page instead
of a text prompt. The script serves an impeccable-styled option board
(assigned direction leading with THE ROLL badge, dealt challengers as
alternates carrying their QUALITY BAR cards, re-roll and steer built in),
prints the URL, opens the browser, and blocks until the user chooses;
the answer lands on stdout as ANSWER JSON, so the shell call itself is
the wait and no harness machinery is needed. Local images are served by
the ephemeral server; nothing leaves the machine.

generate-image.mjs + context.mjs IMAGE_GEN_AVAILABLE: when an OpenAI key
is in the environment, context reports that image generation works even
without a harness-native tool (gpt-image-2, billed to the user's key,
stated before first use; Google skipped by decision). Harness-native
tools always win when present.

new-work.md: visualize-before-build is now the default whenever any
image generation exists, not a codex.md special case; the attended
presentation prefers the visual decision page and falls back to the
structured question tool. Evals keep the unattended path untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 16:43:21 -07:00

55 lines
2.1 KiB
JavaScript

#!/usr/bin/env node
/**
* API image generation fallback: renders a mock or world board with the
* user's own OpenAI key when the harness has no native image generation.
*
* context.mjs reports availability (it checks OPENAI_API_KEY); harness-native
* generation always wins when present. This uses gpt-image-2 and spends the
* user's API credit (roughly $0.05-0.25 per image at default quality), so the
* skill states that before the first call in a session.
*
* node generate-image.mjs --prompt "..." --out mock.png [--size 1536x1024] [--quality medium]
* node generate-image.mjs --prompt-file prompt.txt --out mock.png
*/
import fs from 'node:fs';
function arg(name, fallback = null) {
const i = process.argv.indexOf(`--${name}`);
if (i === -1) return fallback;
const v = process.argv[i + 1];
return v && !v.startsWith('--') ? v : fallback;
}
const key = process.env.OPENAI_API_KEY;
if (!key) {
console.error('generate-image: OPENAI_API_KEY is not set; use the harness-native image tool instead.');
process.exit(1);
}
const promptFile = arg('prompt-file');
const prompt = promptFile ? fs.readFileSync(promptFile, 'utf8') : arg('prompt');
const out = arg('out');
if (!prompt || !out) {
console.error('generate-image: --prompt (or --prompt-file) and --out are required.');
process.exit(1);
}
const size = arg('size', '1536x1024');
const quality = arg('quality', 'medium');
const response = await fetch('https://api.openai.com/v1/images/generations', {
method: 'POST',
headers: { Authorization: `Bearer ${key}`, 'content-type': 'application/json' },
body: JSON.stringify({ model: 'gpt-image-2', prompt, size, quality, n: 1 }),
});
if (!response.ok) {
console.error(`generate-image: API error ${response.status}: ${(await response.text()).slice(0, 300)}`);
process.exit(1);
}
const json = await response.json();
const b64 = json?.data?.[0]?.b64_json;
if (!b64) {
console.error('generate-image: no image in response');
process.exit(1);
}
fs.writeFileSync(out, Buffer.from(b64, 'base64'));
console.log(`IMAGE: ${out} (${size}, ${quality}, gpt-image-2, billed to your OpenAI key)`);