Add a codex-cli image provider: no-API-key generation via the signed-in Codex CLI#4129
Add a codex-cli image provider: no-API-key generation via the signed-in Codex CLI#4129fancy-agent wants to merge 5 commits into
codex-cli image provider: no-API-key generation via the signed-in Codex CLI#4129Conversation
Drives the operator's signed-in Codex CLI (its built-in image_gen tool) so any coding agent can generate images through `od media generate` with no OPENAI_API_KEY — the bytes bill against the user's ChatGPT subscription. Today the only no-key image path is PR nexu-io#622's prompt override, which only fires when the chat agent is Codex itself. This adds a real media provider (credentialsRequired/settingsVisible false, like hyperframes) so the no-key path works for Claude Code, Gemini, and every other agent. The daemon spawns a headless `codex exec --json` turn, has it copy the generated PNG to a temp workspace, and confirms success from ground truth (exit 0 + the file exists + a PNG signature); on any failure it throws rather than falling back to a stub. Registered in both media-model registries and added to the New Project image picker's supported-provider allowlist.
Siri-Ray
left a comment
There was a problem hiding this comment.
@fancy-agent thanks for putting this together. I reviewed the daemon provider wiring, the model registries, and the picker visibility test path. I found one launch-environment issue worth tightening before this ships broadly, but I’m leaving it as non-blocking because it depends on how the daemon was launched and how Codex is installed.
🔁 Powered by Looper · runner=reviewer · agent=codex · An autonomous AI dev team for your GitHub repos.…efault The codex-cli provider is credentialsRequired:false, so the model picker treated it as always-ready and auto-selected codex-image-gen whenever no API-key image provider was configured. That broke two existing contracts: the picker must show "Pick a model" (submit no imageModel) when nothing is eligible, and must switch to a configured provider's model — not codex-cli. It also violated the feature's own rule: codex-cli spends the operator's ChatGPT subscription, so it must be chosen on purpose, never a silent default. Mark the provider optInOnly and skip such providers when picking the auto-default; they stay fully pickable in the list. Adds a regression test that codex-image-gen is offered but never auto-selected.
Siri-Ray
left a comment
There was a problem hiding this comment.
@fancy-agent thanks for the follow-up here. I reviewed the current daemon provider wiring, the model registries, and the New Project picker change that keeps codex-image-gen explicit opt-in. The picker/default behavior looks covered; I still see one launch-environment issue in the daemon path that is worth tightening, but I’m keeping it non-blocking because it depends on desktop/packaged launch conditions and Codex install shape.
…-launch PATH) The provider resolved Codex with resolveAgentExecutable and spawned it with the daemon's raw process.env. The repo's normal Codex launch path instead uses resolveAgentLaunch + applyAgentLaunchEnv: the former swaps an npm wrapper for the native binary, the latter prepends the running Node dir and Codex toolchain dirs to the child PATH. Without that symmetry a GUI-launched daemon (the desktop/packaged app, where PATH is minimal) could resolve a shell-installed Codex but still fail at spawn — its '#!/usr/bin/env node' shim can't find an interpreter. Mirror the launcher: resolve via resolveAgentLaunch, spawn launch.launchPath with env: applyAgentLaunchEnv(process.env, launch). Adds a regression test that strips PATH of any node dir and points CODEX_BIN at the node-shebang fake Codex, so the spawn only succeeds via the augmented PATH the launcher injects. Addresses Looper review on media.ts.
Siri-Ray
left a comment
There was a problem hiding this comment.
@fancy-agent thanks for the quick follow-up here. I verified the launcher-path fix on the current head, reviewed the daemon provider and picker changes again, and ran the focused daemon/web suites plus the media registry verifier. I found one remaining config-parity issue worth tightening, but I’m keeping it non-blocking because it only affects deployments that explicitly disable Codex plugins.
🔁 Powered by Looper · runner=reviewer · agent=codex · An autonomous AI dev team for your GitHub repos.…s for codex-image-gen The codex-cli image provider only emitted passive errors AFTER running a full (multi-minute) codex turn, so an API-key / programmatic / third-party login wasted a turn before failing with a vague message. - Add codex-image-auth.ts: read $CODEX_HOME/auth.json + config.toml and classify the login BEFORE spawning. Mirror codex's resolved_mode (OAuth tokens win, then OPENAI_API_KEY); fail fast on api-key / programmatic / third-party model_provider / not-signed-in with precise switch-to- ChatGPT-plan guidance. Schema matches openai/codex codex-rs/login (AuthDotJson + AuthMode lowercase serde). - Split a non-zero codex exit into auth / quota / transient, each with an actionable message (quota = ChatGPT usage limit, ~5h reset). Transient keeps the original 'codex image_gen exited' wording. - Tests: pure classifier + two auth.json fixtures (subscription vs api-key) + integration (fail-fast under api-key/not-signed-in, quota classified). Verified: daemon tsc clean, vitest 29/29, pnpm guard 54/54, and the live ~/.codex (real ChatGPT subscription) classifies ok (no false-block).
|
Follow-up in f5422f3: the codex-cli image provider now fails fast on a non-subscription login before spawning a turn, and splits runtime failures into actionable classes. Previously auth problems only surfaced after a full (multi-minute) A non-zero codex exit is also split into auth / quota / transient — a ChatGPT usage-limit (the Verified: daemon |
Siri-Ray
left a comment
There was a problem hiding this comment.
@fancy-agent thanks for the quick follow-up on the auth classification path. I reviewed the current daemon provider/auth gate, model registry sync, and New Project picker behavior. The new fail-fast auth handling and opt-in picker behavior look on track; I’m leaving one non-blocking config-parity issue because this media-specific Codex spawn still bypasses the existing global plugin-disable knob.
🔁 Powered by Looper · runner=reviewer · agent=codex · An autonomous AI dev team for your GitHub repos.runCodexImageGen mirrored codexAgentDef.buildArgs' sandbox and default-permissions args but skipped the OD_CODEX_DISABLE_PLUGINS=1 branch, so an operator who globally disabled Codex plugins would still get a plugin-enabled Codex turn on this image-gen path while it handles user prompt input. Append the same '--disable plugins' pair when the env var is set, in the same position as runtimes/defs/codex.ts. Extend media-codex-cli.test.ts: the fake Codex now records its argv, and two tests lock both directions (forwarded when the var is '1', absent when unset).
Siri-Ray
left a comment
There was a problem hiding this comment.
@fancy-agent thanks for the thoughtful follow-through on this provider. I reviewed the changed daemon image-provider path, the Codex auth/failure classification, launcher argument parity, the media model registries, and the New Project picker behavior. The current head addresses the earlier launcher and plugin-disable parity concerns, keeps codex-image-gen opt-in rather than a silent default, and has focused coverage for the main success/failure paths. I also ran the media registry verifier successfully; the focused Vitest commands could not run in this reviewer worktree because node_modules/vitest is not installed here. Nice work getting the no-API-key path wired through cleanly.
🔁 Powered by Looper · runner=reviewer · agent=codex · An autonomous AI dev team for your GitHub repos.
Fixes #4128
Why
I'm scratching my own itch: I run Open Design with Claude Code as the chat
agent, and I have a ChatGPT subscription with a signed-in Codex CLI — but no
OPENAI_API_KEY. Today that combination can't generate an image at all.The only no-key image path that exists is PR #622's
renderCodexImagegenOverride, and it only fires when the chat agent isCodex. Drive OD with Claude Code / Gemini / Cursor and you're back to needing
a provider API key, even though the machine already has a perfectly good,
already-authenticated image generator sitting in the Codex CLI.
This adds a local
codex-cliprovider so any agent can generate images throughthe operator's own Codex login — no
OPENAI_API_KEY, billed to their ChatGPTsubscription.
What users will see
codex-image-gen(provider "Codex CLI") in the modelpicker, alongside
gpt-image-2,dall-e-3, etc.signed-in Codex CLI's built-in
image_gentool.(same treatment as the
hyperframeslocal renderer).gpt-image-2);codex-image-genisstrictly opt-in via the picker.
relay where
image_genisn't mounted), generation fails with a clear,actionable error instead of a blank/placeholder image.
Surface area
page or dialog; it flows from the
apps/web/src/media/models.tsregistry)Screenshots
Entry point — New project → Media → Image → "Model". The new
codex-image-genmodel appears under a Codex CLI group markedConfigured (no key needed) and is selectable as the image model:
Filtering the picker confirms it resolves with no credentials required:
Bug fix verification
anyway: see
apps/daemon/tests/media-codex-cli.test.ts.)Validation
Run under Node 24 / pnpm 10.33.2:
pnpm guard→ pass (54/54)pnpm typecheck→ pass (all workspaces, including@open-design/web)node scripts/verify-media-models.mjs→ OK (TS + JS registries match)pnpm --filter @open-design/daemon test→ the newmedia-codex-cli.test.ts(5 cases) passes: success returns the real imagebytes; the four failure branches (image_gen unavailable, clean-exit-but-no-
file, non-PNG output, non-zero exit) all throw instead of falling back to a
stub. Tests use a fake Codex CLI injected via
CODEX_BIN— zero tokens.OPENAI_API_KEYset):od media generate --project … --surface image --model codex-image-gen --prompt "a cozy reading nook with a cat, warm flat illustration"produced areal 2.58 MB PNG in the project. The artifact came back as
providerId: "codex-cli",usedStubFallback: false,providerError: null,providerNote: "codex-cli/image_gen · 1:1 · 2579406 bytes · ChatGPT subscription (no API key)". Same path exercised throughgenerateMediadirectly with the real Codex CLI (independent
file/sipsconfirmed a1254×1254 PNG). The
image_gensmoke (orange-cat) under bothworkspace-writeand the default config showed the built-in tool path with no
scripts/image_gen.py/ API-key fallback.Notes / scope
NewProjectPanel.tsx(supportedModels);codex-clihad to be added to theimageset or the model stays hidden in the UI even though the daemondispatches it — exactly how
hyperframesis allowlisted forvideo. There'sa regression test for this in
apps/web/tests/components/NewProjectPanel.test.ts.the chat agent) decides to generate an image. The provider spawns its own
Codex process and is decoupled from the chat agent, and
od media generate --model codex-image-gen(the exact call any agent runtime makes) is verifiedend-to-end above — so agent-independence holds by construction — but I didn't
script a full Claude-Code-in-the-loop session.
workspace-write+ networkon macOS/Linux;
danger-full-accessonly on Windows/WSL via the existingcodexNeedsDangerFullAccessSandbox()helper). Verifiedimage_genworksunder
workspace-write.image_genleaves no distinct event incodex exec --json, so success isconfirmed from ground truth (exit 0 + the file exists at the path we handed
codex + a PNG signature), and failures throw — no silent stub fallback (same
policy as
hyperframes).