Cross-harness, cross-OS: boot-time tool detection and native-first image gen

context.mjs now probes cwebp/sips/magick/ffmpeg once (which/where per
OS) and prints IMAGE_TOOLS, replacing macOS-specific prose; the
IMAGE_GEN_AVAILABLE directive leads with the harness-native tool so a
present OpenAI key stops reading as an instruction to bill it; and the
sandboxed board-start guidance sheds codex vocabulary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Paul Bakaus
2026-07-31 17:54:55 -07:00
co-authored by Claude Fable 5
parent af56fae571
commit b1c5707fde
5 changed files with 47 additions and 16 deletions
+20 -3
View File
@@ -27,6 +27,7 @@
* shape rather than the markdown block.
*/
import fs from 'node:fs';
import { spawnSync } from 'node:child_process';
import os from 'node:os';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
@@ -1146,6 +1147,7 @@ async function cli() {
if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) {
parts.push(buildMissingTargetDirective());
}
appendImageToolsDirective(parts);
appendStalenessDirective(parts, ctx, cliOptions);
if (updateDirective) parts.push(updateDirective);
process.stdout.write(parts.join('\n\n---\n\n') + '\n');
@@ -1180,6 +1182,7 @@ async function cli() {
`# NATIVE PLATFORM REFERENCE: ${reference.name.toUpperCase()} (reference/${reference.name}.md)\n\n${reference.content.trim()}`,
);
}
appendImageToolsDirective(parts);
appendStalenessDirective(parts, ctx, cliOptions);
if (!ctx.platform) {
// A `## Platform` section that names something we don't recognize (a
@@ -1275,9 +1278,10 @@ function appendImageGenDirective(parts) {
if (!process.env.OPENAI_API_KEY) return;
const scriptsPath = path.dirname(fileURLToPath(import.meta.url));
parts.push([
'IMAGE_GEN_AVAILABLE: An OpenAI key is present, so image generation works even without a harness-native image tool:',
`\`node ${scriptsPath}/generate-image.mjs --prompt "..." --out <file>\` (gpt-image-2, billed to the user's key; say so before the first render).`,
'Prefer the harness-native image tool when one exists. Visualizing a direction before building it measurably strengthens the result.',
'IMAGE_GEN_AVAILABLE: your harness-native image tool is always the first choice for generation; use it whenever one exists.',
'This environment also carries an OpenAI key as the fallback for harnesses with no native tool:',
`\`node ${scriptsPath}/generate-image.mjs --prompt "..." --out <file>\` (gpt-image-2, billed to the user's key; say so before the first render, and never reach for it when a native tool exists).`,
'Visualizing a direction before building it measurably strengthens the result.',
].join(' '));
}
@@ -1332,6 +1336,19 @@ function appendDetectorFallback(parts, ctx) {
// markdown already in memory, a bounded set of stats, or one of the small JSON
// files the boot reads regardless. The deep pass (git drift, token divergence,
// cross-workspace sweep) belongs to the doctor command, not to every session.
// One boot-time probe replaces every session re-deriving its image toolchain:
// harnesses and OSes differ (cwebp, sips on macOS, magick, ffmpeg), and the
// agent should read this line instead of running command -v per image.
function appendImageToolsDirective(parts) {
const probe = process.platform === 'win32' ? 'where' : 'which';
const found = ['cwebp', 'sips', 'magick', 'ffmpeg'].filter((tool) => {
try { return spawnSync(probe, [tool], { stdio: 'ignore' }).status === 0; } catch { return false; }
});
parts.push(found.length
? `IMAGE_TOOLS: available image converters on this machine: ${found.join(', ')}. Use the first suitable one; never probe again this session.`
: 'IMAGE_TOOLS: no image converter found (cwebp, sips, magick, ffmpeg). Ship PNG output unconverted rather than probing per image.');
}
function appendStalenessDirective(parts, ctx, options) {
const projectRoot = ctx.projectRoot || process.cwd();
if (stalenessCheckDisabled([projectRoot, ctx.repoRoot])) return;
+8 -2
View File
@@ -3766,7 +3766,10 @@
const container = copyEditContainerContext(contextElement);
if (container) for (const op of ops) op.container = container;
try {
const res = await fetch('http://localhost:' + PORT + '/manual-edit-stash', {
// Token in the query string as well as the body: the URL token is what
// authorizes the CORS preflight when the page runs on a non-loopback
// dev host (ddev, Valet), since the preflight carries no request body.
const res = await fetch('http://localhost:' + PORT + '/manual-edit-stash?token=' + encodeURIComponent(TOKEN), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -7150,7 +7153,10 @@
console.debug('[impeccable] Dropped optional live event:', err);
return null;
}
const doSend = () => fetch('http://localhost:' + PORT + '/events', {
// Token in the query string as well as the body: the URL token is what
// authorizes the CORS preflight when the page runs on a non-loopback
// dev host (ddev, Valet), since the preflight carries no request body.
const doSend = () => fetch('http://localhost:' + PORT + '/events?token=' + encodeURIComponent(TOKEN), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(msg),
+17 -9
View File
@@ -689,16 +689,24 @@ function isLoopbackOrigin(origin) {
function createRequestHandler({ detectScript, liveScriptParts }) {
return (req, res) => {
const url = new URL(req.url, `http://localhost:${state.port}`);
// Loopback-restricted CORS. Reflect the caller's Origin only when it is a
// loopback origin, always paired with `Vary: Origin` so an intermediary
// cache never serves a response authorized for one origin to another. A
// remote page (e.g. https://evil.example probing the port from a tab open
// on the same machine) gets no Access-Control-Allow-Origin, so its
// JS-initiated fetch cannot read any response. Requests with no Origin
// header (script tags, curl, the agent's own fetches) are not subject to
// CORS and keep working; no ACAO header is needed for them.
// Token-or-loopback CORS. Reflect the caller's Origin when it is a
// loopback origin OR the request carries the valid session token, always
// paired with `Vary: Origin` so an intermediary cache never serves a
// response authorized for one origin to another. A remote page (e.g.
// https://evil.example probing the port from a tab open on the same
// machine) has no token and gets no Access-Control-Allow-Origin, so its
// JS-initiated fetch cannot read any response. The token branch exists for
// dev servers on non-localhost loopback aliases (ddev's *.ddev.site,
// Valet's *.test, hosts-file entries): the injected classic <script src>
// delivers the token to the page regardless of origin, every overlay
// request carries it in the query string (preflights included, since
// OPTIONS hits the same URL), and a token bearer is already fully
// authorized on every route — the token is the security boundary, not the
// origin. Requests with no Origin header (script tags, curl, the agent's
// own fetches) are not subject to CORS and keep working; no ACAO header
// is needed for them.
const origin = req.headers.origin;
if (origin && isLoopbackOrigin(origin)) {
if (origin && (isLoopbackOrigin(origin) || url.searchParams.get('token') === state.token)) {
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Vary', 'Origin');
}