codex-imgen is a local-first Go CLI and async job service for Codex CLI $imagegen. It turns Codex image generation into a scriptable workflow for text-to-image, image-to-image, batch-style generation, job tracking, and agent/service integration.
codex-imgen is an independent community tool and is not affiliated with OpenAI.
Codex CLI already has image generation. codex-imgen focuses on the engineering layer around it:
- Run text-to-image and image-to-image from a simple local CLI
- Use local reference images with repeated
--imageflags - Submit async jobs and query them later with
status,get,list, andcancel - Control candidate generation with
--countand--concurrency - Subscribe to job-scoped WebSocket events from local tools and agents
- Check OpenClaw/TG original-file delivery contracts and skill sync drift
- Keep structured settings in
configs/config.yamland secrets in.env
If you only need one image once, native codex exec is enough. If you need repeatable generation, batching, job tracking, or local integration, use codex-imgen.
| Need | Native codex exec |
codex-imgen |
|---|---|---|
| One-off prompt | yes | yes |
| Simple CLI UX | limited | yes |
| Local reference images | manual | yes |
| Batch count/concurrency | manual | yes |
| Async job queue | no | yes |
| Status/list/cancel | no | yes |
| WebSocket events | no | yes |
| Agent/service integration | manual | yes |
| Local YAML config | no | yes |
OpenClaw/TG delivery can send generated images as original files, with captions preserved for each result.
Download the archive for your OS/arch from GitHub Releases, then unpack it:
tar -xzf codex-imgen_<tag>_<os>_<arch>.tar.gz
cd codex-imgen_<tag>_<os>_<arch>
cp configs/config.example.yaml configs/config.yaml
# Optional, only needed when email secrets are enabled:
cp .example.env .env
./imgen --helpOn Windows, run imgen.exe --help.
Release archives include the imgen and skill-sync binaries, configs/config.example.yaml, .example.env, README files, and LICENSE.
Requires Go and a logged-in Codex CLI.
git clone https://github.com/walker1211/codex-imgen.git
cd codex-imgen
cp configs/config.example.yaml configs/config.yaml
# Optional, only needed when email secrets are enabled:
cp .example.env .env
bash ./build.sh
./imgen --helpThe source-build commands above assume a Unix-like shell. On Windows, built binaries are named imgen.exe and skill-sync.exe.
Fill in configs/config.yaml before using service mode, custom backend settings, or email notifications. Put SMTP auth secrets in .env only.
Note: the binary reads configs/config.yaml from the current working directory. Adding the binary to PATH does not remove that requirement.
.claude/skills/imgen/ is the skill source; .openclaw/skills/imgen/ is the repository OpenClaw mirror; ~/.claude/skills/imgen/, ~/.openclaw/workspace/skills/imgen/, and ~/.codex/skills/imgen/ are local install artifacts.
Check whether local installs match the repository sources:
go run ./cmd/skill-sync --checkCopy repository sources into local Claude, OpenClaw, and Codex installs, and update the repository OpenClaw mirror:
go run ./cmd/skill-sync --applyYou can also build first with bash ./build.sh and then use the local binary:
./skill-sync --check
./skill-sync --applyThe default behavior is drift checking only; local skill install directories are overwritten only when --apply is passed explicitly.
Check whether the local OpenClaw setup satisfies the imgen / Telegram original-file delivery contract:
./imgen doctor openclawThis read-only command checks the image_generate deny rules, main agent message exposure, Telegram direct NO_REPLY silence, OpenClaw message send --force-document support, OpenClaw imgen skill installation/sync state, the IMGEN_DELIVERY_DIR / forceDocument / asDocument call contract, the synchronous CLI JSON success contract, and whether local backend.delivery_dir is under an OpenClaw-sendable root. WARN lines do not block; FAIL lines are actionable and mean the configuration, OpenClaw CLI capability, or skill sync needs fixing.
Repository config layout:
configs/config.example.yaml: structured config template committed to gitconfigs/config.yaml: real local structured config, not committed to git.example.env: secret template committed to git.env: real local secrets, not committed to git
Rules:
- Put only sensitive values in
.env. - Keep structured configuration in YAML.
EMAIL_SMTP_AUTH_CODEis the SMTP auth code used for email delivery.
Initialization:
cp configs/config.example.yaml configs/config.yaml
cp .example.env .envThen edit configs/config.yaml as needed:
server:
listen: 127.0.0.1:18080 # Service listen address; local-only by default
read_timeout: 5s # HTTP request read timeout
write_timeout: 30s # HTTP response write timeout
storage:
data_dir: "" # Service data directory; empty uses the OS user data directory
sqlite_path: "" # SQLite database path; empty uses data_dir/imgen.db
scheduler:
global_max_concurrency: 10 # Shared serve-mode generation queue cap for submit async and WebSocket realtime
default_job_concurrency: 2 # Default per-job concurrency when submit omits --concurrency
max_job_concurrency: 10 # Maximum per-job concurrency for submit async jobs
max_count_per_job: 10 # Maximum image count for one submit async job
maintenance_interval: 5m # Background maintenance interval
task_lease_timeout: 30m # Background task lease timeout
max_attempts: 3 # Maximum retry attempts per image in submit async jobs
backend:
type: built_in_codex # Use local Codex CLI with the built-in $imagegen skill
command: codex # Codex CLI command name or executable path
model: "" # Empty uses the Codex CLI default model; set this only when pinning a model
cwd: "" # Codex CLI working directory; empty uses the current process directory
timeout: 90s # Timeout for one Codex/imagegen invocation
delivery_dir: "" # Optional: copy generated images there; OpenClaw/TG can point this at an allowed workspace/media directory
delivery_max_files: 200 # Max files to retain in delivery_dir when set; 0 disables automatic cleanup
cleanup_source_thread_dir: false # When delivery_dir is set and copying succeeds, delete the source Codex generated_images thread directory for this image
prompt:
prefix: "$imagegen" # Prefix prepended to every prompt
prelude: | # Fixed prompt prelude for default style/output constraints
Use the built-in imagegen skill.
Output a single image.
Default to web or brand asset scenarios.
realtime:
enabled: true # Whether to enable the WebSocket realtime generation endpoint
max_sessions: 4 # Maximum active WebSocket generation sessions
max_items_per_session: 8 # Maximum items in one WebSocket generate.start frame
max_count_per_item: 1 # Maximum image count for one realtime item
item_timeout: 300s # Default timeout for one realtime item
max_item_timeout: 300s # Maximum client timeout_ms; usually keep this equal to item_timeout
email:
enabled: false # Whether to enable maintenance failure email notification
smtp_host: smtp.example.com # SMTP server host
smtp_port: 465 # SMTP port; 465 uses implicit TLS
from: from@example.com # Sender email and SMTP login identity
to: to@example.com # Recipient email
timeout: 3s # Timeout for one SMTP connection/send attempt
retry_times: 3 # Maximum email send attempts
retry_wait_time: 500ms # Wait duration between failed email attempts
use_proxy: false # SMTP proxying is not supported yet; keep falseConfiguration fields:
server.listen: service-mode listen address.127.0.0.1:18080allows local access only; use0.0.0.0:18080only when you intentionally expose it to the network.server.read_timeout: HTTP request read timeout.server.write_timeout: HTTP response write timeout.storage.data_dir: async service data directory. If empty, the user data directory is used. For local development,./.datais a good choice.storage.sqlite_path: SQLite database path. If empty,data_dir/imgen.dbis used. For local development,./.data/imgen.dbis a good choice.scheduler.global_max_concurrency: serve-mode bottom generation queue cap shared by asyncsubmitjobs and WebSocket realtime; it does not affect local sync shorthand generation withimgen "prompt".scheduler.default_job_concurrency: default asyncsubmitjob concurrency when--concurrencyis omitted.scheduler.max_job_concurrency: maximum asyncsubmitjob concurrency.scheduler.max_count_per_job: maximum image count for one async job; larger--countinput is clamped to this value.scheduler.maintenance_interval: service-mode maintenance interval for checks, failure progression, and failure notification.scheduler.task_lease_timeout: running-task lease timeout used to detect expired work.scheduler.max_attempts: maximum generation attempts per image in async jobs.realtime.enabled: whether to enable the WebSocket realtime generation endpoint.realtime.max_sessions: maximum active WebSocket generation sessions at the same time.realtime.max_items_per_session: maximum items in one WebSocketgenerate.startframe.realtime.max_count_per_item: maximum image count per realtime item.realtime.item_timeout: default timeout for one realtime item; realtime no longer has its own backend global queue.realtime.max_item_timeout: maximum clienttimeout_ms; usually keep it equal toitem_timeout.backend.type: generation backend type. Currently usebuilt_in_codex.backend.command: Codex CLI command. Defaults tocodex; the built-in backend currently requires this command to supportexec --json.backend.model: model name passed to Codex CLI. If empty, the configured Codex backend chooses its default model.backend.cwd: Codex CLI working directory. If empty, the current process working directory is used.~/is expanded.backend.timeout: timeout for one Codex/imagegen invocation. Increase it if generation frequently times out.backend.delivery_dir: optional delivery directory. When set, generated images are copied there and the copied path is returned. OpenClaw/TG can point this at an allowed workspace/media directory.backend.delivery_max_files: maximum retained files indelivery_dirwhen set. Defaults to200; set0to disable automatic cleanup.backend.cleanup_source_thread_dir: defaults tofalse; when set totrue, deletes the source Codexgenerated_images/<thread-id>directory for this image only afterdelivery_dircopying succeeds.backend.prompt.prefix: prefix automatically prepended to prompts, usually$imagegen.backend.prompt.prelude: fixed prompt prelude for default style and output constraints.email.enabled: whether to enable maintenance failure email notification.email.smtp_host: SMTP server host.email.smtp_port: SMTP server port. Port465uses implicit TLS; other ports use a timeout-controlled standard SMTP connection.email.from: sender email address and SMTP login identity.email.to: recipient email address.email.timeout: timeout for one SMTP connection/send attempt.email.retry_times: maximum email send attempts.email.retry_wait_time: wait duration between failed email attempts.email.use_proxy: email proxy switch. SMTP proxying is not supported yet; setting this totruereturns a config error..envEMAIL_SMTP_AUTH_CODE: SMTP auth code or password. Required when email is enabled.
./imgen "Generate a 3D-style baby dragon mascot for a web hero section, clean background, single image"
./imgen --count 4 --concurrency 2 "Kuroneko wearing a maid outfit in a cafe"
./imgen --count 4 --concurrency 2 --json "Kuroneko wearing a maid outfit in a cafe"Text mode prints one image path per line. --json prints structured output. Automation should treat ok: true plus non-empty images[].path values as success, not exit code alone.
Use local image files as references:
./imgen --image ./1.png "Keep the subject composition and pose, convert this image to a high-quality 3D figure render style, cleaner background, single image"
./imgen --json --image ./1.png "Keep the subject composition and pose, convert this image to a high-quality 3D figure render style, cleaner background, single image"Pass multiple reference images by repeating --image:
./imgen --image ./1.png --image ./2.png "Use these images as subject references and generate one consistent high-quality visual"Notes:
- Only local file paths are supported in this version. URLs and uploads are not supported.
- Synchronous
runand asynchronoussubmituse the same--imagesemantics. - The backend invokes Codex CLI as
<backend.command> exec --json --image ... -- '<prompt>'. - The
--separator is required for the native Codex CLI command because variadic--imagewould otherwise consume the prompt. - Wrappers such as
ccs codexare not automatically compatible; ifccs codex exec --jsonreportsunknown option '--json', the current built-in backend cannot use it directly.
When verifying native Codex CLI behavior, first confirm that the executable supports exec --json:
codex exec --help
codex exec --json -- '$imagegen Generate a cute baby dragon mascot, white background, single image'
codex exec --json --image ./1.png -- '$imagegen Keep the subject composition and pose, convert this image to a high-quality 3D figure render style, cleaner background, single image'This project only generates images and returns local file paths. Telegram, OpenClaw, or another agent must read the file pointed to by images[].path and upload the file bytes; a local path is not an image URL or a remote file id.
Integrations should follow this minimal contract:
- Resolve the config working directory before calling the CLI: prefer
IMGEN_REPO_ROOT; otherwise walk upward from the current directory until findingconfigs/config.yamlplus./imgen,build.sh, orgo.mod; then try explicit user-provided install paths. Do not scan the whole filesystem. - Run
./imgen --json ...from that directory, or use./imgen get --json <job-id>in service mode. - For OpenClaw/TG, use
IMGEN_DELIVERY_DIRorbackend.delivery_dirto copy images into an OpenClaw-sendable workspace/media directory, and usedelivery_max_filesto cap retained delivery files; explicitly enablecleanup_source_thread_dironly when you also want to remove the Codex source thread directory. - Synchronous success requires
ok: trueand non-emptyimages[].pathvalues for the expected images; service jobs expose final files throughimages[].pathafter completion. - If Telegram reports something like
Media failed, first check from the Telegram/OpenClaw runtime thatimages[].pathexists, is readable, has a valid image format, and is on a shared or copied filesystem.
For Telegram multi-image requests that need distinct themes, OpenClaw should run independent ./imgen --json --count 1 --concurrency 1 commands concurrently, send each completed images[].path immediately with the message tool, use forceDocument or asDocument for original PNG delivery, and return exactly NO_REPLY after direct delivery.
- Run
./skill-sync --applyto sync the imgen skill, then restart OpenClaw. - Run
./imgen doctor openclawand confirmmessage send supports --force-documentis OK and there are no FAIL lines. - Send a Telegram test message, for example:
Generate 3 cat Mac wallpapers with different moods. - Expect 3 image files/documents. Brief status text and captions are fine, but the literal
NO_REPLYshould not be visible.
NO_REPLY is the silent completion signal for OpenClaw: after files have been delivered directly to Telegram, the agent should not add a final text reply.
For the full OpenClaw reproduction and configuration checklist, see OpenClaw imgen Integration.
Start the local service in the foreground:
./imgen serveOr use the repository scripts to run it in the background:
./start.sh
./stop.sh
./restart.shThe background script uses nohup ./imgen serve and writes logs to logs/out.log.
Submit and query from another terminal:
./imgen submit --count 4 --concurrency 2 "Kuroneko wearing a maid outfit in a cafe"
./imgen submit --json --count 4 --concurrency 2 "Kuroneko wearing a maid outfit in a cafe"
./imgen submit --image ./1.png "Keep the subject composition and pose, convert this image to a high-quality 3D figure render style, cleaner background, single image"
./imgen status <job-id>
./imgen get <job-id>
./imgen get --json <job-id>
./imgen list
./imgen cancel <job-id>To inspect whether a job retried, query the SQLite attempt history:
sqlite3 .data/imgen.db \
"select job_id,image_index,attempt,status,duration_ms,path,last_error from job_image_attempts where job_id='<job-id>' order by image_index,attempt;"To locate which part of one Codex CLI invocation is slow, inspect phase details:
sqlite3 .data/imgen.db \
"select image_index,attempt,phase,elapsed_ms,detail from job_image_attempt_phases where job_id='<job-id>' order by image_index,attempt,occurred_at_ms;"Common interpretation:
- Late
process.started: Codex CLI startup or OS scheduling is slow. - Late
stdout.thread_started: Codex CLI initialization, network, or session creation is slow. - Long gap from
stdout.turn_startedtoimage.file_detected: most time is waiting for image generation or file availability. - Long gap from
image.file_detectedtostdout.turn_completed: the image file is already present, but Codex is still completing its final response or internal turn cleanup. - Long gap from
stdout.turn_completedtoprocess.exited: the Codex turn is complete, but the CLI process exit is slow. - If
stdout.turn_completedis missing, a long gap fromimage.file_detectedtoprocess.exitedmeans the image file is already present, but Codex CLI cleanup/exit is slow. - If
image.file_detectedis missing, a long gap fromstdout.turn_startedtostdout.saved_to/process.exitedstill points to the model or imagegen tool execution chain. - Long gap from
process.exitedtoparser.completed: local parsing or generated_images directory lookup is slow.
The service exposes /ws?job_id=<job-id> for job-scoped event subscriptions. Current event types include:
job.createdjob.startedimage.startedimage.completedimage.failedimage.cancelledjob.completedjob.partial_successjob.failedjob.cancelled
The WebSocket implementation is intentionally minimal: it supports connection upgrade, job_id subscriptions, and event pushes. Historical replay and reconnect recovery are future work.
- Text mode prints one image path per line.
--jsonprints structured output; automation should readimages[].path.- Multi-image sync mode prints one path per line.
- Service mode supports querying status and image paths by
job_id. - The maintenance ticker is wired into
servefor minimal checks, failure progression, and final failure notification. - Failure email notification is wired into the maintenance path; richer failure classification and immediate notification are future work.
go test ./...
bash ./build.shWhen changing CLI flags, config loading, or README content, also verify:
./imgen --help
./imgen --json "Generate a cute baby dragon mascot, white background, single image"