
Gpt Image 2
- 194k installs
- 14 repo stars
- Updated April 28, 2026
- agentspace-so/agent-skills
GPT Image 2 is a skill that generates images via your ChatGPT subscription using the Codex CLI, supporting text-to-image and editing modes.
About
GPT Image 2 skill for image generation using ChatGPT Plus/Pro subscription. Supports text-to-image, image-to-image editing with reference images, style transfer, and multi-reference composition through local Codex CLI. No separate API billing or credentials required.
- No separate OpenAI API keys required - uses existing ChatGPT subscription
- Four generation modes: text-to-image, image-to-image, style transfer, multi-reference composition
- Runs locally through codex CLI; only outbound traffic is to OpenAI via ChatGPT login
Gpt Image 2 by the numbers
- 194,249 all-time installs (skills.sh)
- Ranked #43 of 1,340 Generative Media skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
gpt-image-2 capabilities & compatibility
Included in ChatGPT Plus/Pro subscription - no per-image billing
- Works with
- openai
- Use cases
- image generation
- Pricing
- Free
npx skills add https://github.com/agentspace-so/agent-skills --skill gpt-image-2Add your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 194k |
|---|---|
| repo stars | ★ 14 |
| Security audit | 3 / 3 scanners passed |
| Last updated | April 28, 2026 |
| Repository | agentspace-so/agent-skills ↗ |
How do you extract images from Codex JSONL logs?
Generate and edit images using GPT Image 2 technology through your existing ChatGPT subscription without separate API costs.
Who is it for?
Users with ChatGPT Plus/Pro subscriptions; cost-conscious image generation; local CLI workflows.
Skip if: Users without ChatGPT subscription; those needing DALL-E 3 specifically; high-volume batch generation.
When should I use this skill?
User explicitly requests GPT Image 2, ChatGPT Images 2.0, or asks to generate/edit images through ChatGPT plan.
What you get
Decoded PNG, JPG, or WebP image files recovered from Codex CLI session rollout JSONL payloads.
- Generated PNG/JPEG/WebP image file at specified path
By the numbers
- Minimum base64 blob length: 200 characters
- Detects 3 image formats via magic prefixes: PNG, JPG, WebP
Files
🪞 GPT Image 2 — Image Generation via Your ChatGPT Subscription
Generate images with GPT Image 2 (ChatGPT Images 2.0) inside your agent, using your existing ChatGPT Plus or Pro subscription — no separate OpenAI access, no Fal or Replicate tokens, no per-image billing.
Text-to-image, image-to-image editing, style transfer, and multi-reference composition. Runs entirely through the local codex CLI you're already logged into.
Heads up — this skill requires a ChatGPT Plus or Pro subscription _plus_ the Codex CLI installed locally. If you have neither, you can use GPT Image 2 in the browser via RunComfy instead — hosted, no ChatGPT subscription or local install needed (RunComfy account required):
>
- Text-to-image: runcomfy.com/models/openai/gpt-image-2/text-to-image
- Image edit (i2i): runcomfy.com/models/openai/gpt-image-2/edit
>
The rest of this document covers the local Codex CLI flow for agents whose user has a ChatGPT subscription.

Example output: a plain flat-color icon repainted via `--ref` in ukiyo-e style — composition preserved, rendering swapped, period-appropriate red seal added by the model unprompted.
When to trigger
Trigger when the user explicitly asks for GPT Image 2 via their ChatGPT subscription, for example:
- "use GPT Image 2" / "use gpt-image-2" / "use ChatGPT Images 2.0"
- "use Image 2" / "image 2 this"
- attached a reference image and asked to remix / edit / restyle it
Do not auto-trigger for a plain "generate an image" request if the user didn't specify this route. If they did specify it, do not silently fall back to HTML mockups, screenshots, or a different image model.
How to invoke
A single bash script handles everything: runs codex exec with the right flags, then decodes the generated image from the persisted session rollout.
Text-to-image:
bash scripts/gen.sh \
--prompt "<user's raw prompt>" \
--out <absolute/path/to/output.png>Image-to-image (reference flag is repeatable for multi-reference composition):
bash scripts/gen.sh \
--prompt "<user's raw prompt, e.g. 'repaint in watercolor'>" \
--ref /absolute/path/to/reference.png \
--out <absolute/path/to/output.png>Optional: --timeout-sec 300 (default 300).
Default behavior
- Pass the user's prompt through raw. Do not translate, polish, or add style modifiers unless the user asked for it.
- Choose the output path. Default to
./image-<YYYYMMDD-HHMMSS>.pngin the current working directory if the user didn't specify. - Deliver the image. After the script succeeds, display / attach the output file. Do not stop at "done, see path X".
- Text-heavy layouts are fine. Image 2 handles infographics and timeline prompts well. Do not preemptively warn just because a prompt has a lot of text.
Hard constraints
- Do not switch routes without permission. If the user said "use GPT Image 2", do not substitute DALL·E, Midjourney, an HTML mockup, or a manual screenshot workflow.
- Do not rewrite the prompt unless asked.
- Do not imply this skill works without a local
codexlogin and a valid ChatGPT subscription with image-generation entitlement.
Prerequisites
1. codex CLI installed — brew install codex or see openai/codex. 2. Logged in with a ChatGPT plan that includes Image 2 — codex login. 3. python3 on PATH (ships with macOS; apt install python3 on Linux).
This skill does not grant image-generation capability on its own. It exposes the capability the user already has through their ChatGPT subscription.
Exit codes
| code | meaning |
|---|---|
| 0 | success — output path printed on stdout |
| 2 | bad args |
| 3 | codex or python3 CLI missing |
| 4 | --ref file does not exist |
| 5 | codex exec failed (auth? network? model?) |
| 6 | no new session file detected |
| 7 | imagegen did not produce an image payload (feature not enabled, quota, or capability refused) |
On failure, name the layer in one sentence instead of dumping the full stderr at the user.
How it works
The codex CLI reuses the logged-in ChatGPT session and exposes an imagegen tool (gated behind the image_generation feature flag). The script:
1. snapshots ~/.codex/sessions/ before the run 2. runs codex exec --enable image_generation --sandbox read-only ... (with -i <file> for each reference image) 3. diffs the sessions directory, then invokes scripts/extract_image.py to scan every new rollout JSONL for a base64 image payload (PNG / JPEG / WebP magic-header match) 4. decodes the largest matching blob and writes it to --out
Two non-obvious flags other wrappers get wrong on codex-cli 0.111.0+:
--enable image_generationis required; the feature is still under-development and off by default.--ephemeralmust not be used — ephemeral sessions aren't persisted, so the image payload has nowhere to live.
Data handling
The script is narrowly scoped on purpose:
- It reads only session rollout files created by its own
codex execinvocation. The sessions directory is snapshotted before the call and diffed after, so any prior~/.codex/sessions/*files (which may contain unrelated Codex conversations) are never touched, read, or transmitted. - It writes only two kinds of file: the output PNG at the caller's
--outpath, and short-livedmktemplogs that are auto-deleted on exit via a trap. - No environment variables are read. No credentials are requested. No other paths under
~/.codex/are accessed. - No network calls leave this skill. The only outbound traffic is the one made by the
codexCLI itself (to OpenAI, using the user's existing ChatGPT login) — this skill does not add endpoints, telemetry, or callbacks.
What this skill is not
Not a direct OpenAI API client. Not a capability grant — it depends on the user's working Codex CLI login. Not a multi-tenant service (one call per invocation; concurrent calls are serialized by the filesystem-snapshot diff).
#!/usr/bin/env python3
"""Extract a generated image from a Codex CLI session rollout JSONL."""
from __future__ import annotations
import base64
import json
import pathlib
import re
import sys
IMAGE_MAGIC_PREFIXES: dict[str, str] = {
"iVBORw0KGgo": "png",
"/9j/": "jpg",
"UklGR": "webp",
}
MIN_BLOB_LENGTH = 200
BASE64_BLOB_PATTERN = re.compile(r'"([A-Za-z0-9+/=]{' + str(MIN_BLOB_LENGTH) + r',})"')
def find_best_image_blob(session_paths: list[pathlib.Path]) -> tuple[str, str] | None:
"""Return the largest (base64, ext) image payload found across given files."""
best: tuple[str, str, int] | None = None
for session_path in session_paths:
try:
text = session_path.read_text(errors="replace")
except OSError:
continue
for line in text.splitlines():
try:
obj = json.loads(line)
except ValueError:
continue
flat = json.dumps(obj)
for match in BASE64_BLOB_PATTERN.finditer(flat):
blob = match.group(1)
for magic, ext in IMAGE_MAGIC_PREFIXES.items():
if blob.startswith(magic):
if best is None or len(blob) > best[2]:
best = (blob, ext, len(blob))
break
if best is None:
return None
return best[0], best[1]
ALLOWED_OUTPUT_EXTENSIONS: frozenset[str] = frozenset({".png", ".jpg", ".jpeg", ".webp"})
FORBIDDEN_OUTPUT_PREFIXES: tuple[str, ...] = (
"/bin", "/boot", "/dev", "/etc", "/lib", "/proc",
"/sbin", "/sys", "/usr", "/System", "/Library",
"/var/root", "/var/log", "/var/db",
)
def validate_output_path(raw_out: str) -> pathlib.Path:
"""Canonicalise the output path; reject non-image extensions and system dirs."""
candidate = pathlib.Path(raw_out)
ext = candidate.suffix.lower()
if ext not in ALLOWED_OUTPUT_EXTENSIONS:
raise ValueError(
f"output path must end in one of {sorted(ALLOWED_OUTPUT_EXTENSIONS)}; got {ext!r}"
)
resolved = candidate.expanduser().resolve()
resolved_str = str(resolved)
alt_str = (
resolved_str[len("/private"):] if resolved_str.startswith("/private/") else None
)
def _is_under_forbidden(path_str: str) -> bool:
return any(
path_str == f or path_str.startswith(f + "/")
for f in FORBIDDEN_OUTPUT_PREFIXES
)
if _is_under_forbidden(resolved_str) or (alt_str and _is_under_forbidden(alt_str)):
raise ValueError(f"refusing to write under a system directory: {resolved}")
return resolved
def main(argv: list[str]) -> int:
if len(argv) != 3:
print(
"usage: extract_image.py <out_path> <sessions_list_file>",
file=sys.stderr,
)
return 2
try:
out_path = validate_output_path(argv[1])
except ValueError as err:
print(f"invalid output path: {err}", file=sys.stderr)
return 2
sessions_list_path = pathlib.Path(argv[2])
session_paths = [
pathlib.Path(line)
for line in sessions_list_path.read_text().splitlines()
if line.strip()
]
result = find_best_image_blob(session_paths)
if result is None:
print("IMAGE_NOT_FOUND_IN_SESSION", file=sys.stderr)
return 1
blob, _ext = result
image_bytes = base64.b64decode(blob)
out_path.parent.mkdir(parents=True, exist_ok=True)
out_path.write_bytes(image_bytes)
print(out_path)
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv))
#!/usr/bin/env bash
# Generate an image via Codex CLI's imagegen tool, reusing the user's
# ChatGPT subscription session. Supports text-to-image and image-to-image.
#
# Implementation note: on codex-cli 0.111.0 the `imagegen` tool does NOT
# write a PNG file to disk. The generated image is embedded as base64 inside
# the session rollout jsonl under ~/.codex/sessions/YYYY/MM/DD/. This script
# captures the new session file created by the run and decodes the image
# out of it. Flags: `--enable image_generation` turns the under-development
# tool on; `--ephemeral` is intentionally NOT passed so the session is
# persisted and we can read it back.
#
# Usage:
# gen.sh --prompt "<text>" --out <path.png> [--ref <image>]... [--timeout-sec N]
#
# Exit codes:
# 0 success (path printed on stdout)
# 2 bad args
# 3 required CLI missing (codex / python3)
# 4 reference image not found
# 5 codex exec failed
# 6 no new session file detected
# 7 image payload not found in session file (imagegen likely did not run)
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PROMPT=""
OUT=""
REF_IMAGES=()
TIMEOUT_SEC=300
while [[ $# -gt 0 ]]; do
case "$1" in
--prompt) PROMPT="$2"; shift 2 ;;
--out) OUT="$2"; shift 2 ;;
--ref) REF_IMAGES+=("$2"); shift 2 ;;
--timeout-sec) TIMEOUT_SEC="$2"; shift 2 ;;
-h|--help) sed -n '2,24p' "$0"; exit 0 ;;
*) echo "Unknown arg: $1" >&2; exit 2 ;;
esac
done
[[ -z "$PROMPT" ]] && { echo "Missing --prompt" >&2; exit 2; }
[[ -z "$OUT" ]] && { echo "Missing --out" >&2; exit 2; }
command -v codex >/dev/null 2>&1 || {
echo "codex CLI not found. Install Codex CLI and run 'codex login' first." >&2
exit 3
}
command -v python3 >/dev/null 2>&1 || { echo "python3 not found" >&2; exit 3; }
SESSIONS_ROOT="$HOME/.codex/sessions"
mkdir -p "$SESSIONS_ROOT"
before="$(mktemp)"; after="$(mktemp)"
stdout_log="$(mktemp)"; stderr_log="$(mktemp)"
trap 'rm -f "$before" "$after" "$stdout_log" "$stderr_log"' EXIT
find "$SESSIONS_ROOT" -type f -name 'rollout-*.jsonl' -print 2>/dev/null | sort > "$before" || true
# Intentionally NOT using --ephemeral: we need the session rollout on disk.
args=(exec --skip-git-repo-check --sandbox read-only --color never --enable image_generation)
if [[ ${#REF_IMAGES[@]} -gt 0 ]]; then
for img in "${REF_IMAGES[@]}"; do
[[ -f "$img" ]] || { echo "Reference image not found: $img" >&2; exit 4; }
args+=(-i "$img")
done
fi
instruction="Use the imagegen tool to generate the image for the following request."
if [[ ${#REF_IMAGES[@]} -gt 0 ]]; then
instruction+=" Use the attached image(s) as visual reference / input for image-to-image."
fi
instruction+=$'\nRequirements: generate the image directly, return only the image, no explanation.\n\nRequest:\n'"$PROMPT"
# `-i` is a variadic flag (<FILE>...), so passing the prompt as the trailing
# positional would be consumed as another image file. Feed the prompt via
# stdin instead (codex exec reads from stdin when no prompt positional is
# given).
TO=""
if command -v timeout >/dev/null 2>&1; then TO="timeout"
elif command -v gtimeout >/dev/null 2>&1; then TO="gtimeout"
fi
set +e
if [[ -n "$TO" ]]; then
printf '%s' "$instruction" | "$TO" "$TIMEOUT_SEC" codex "${args[@]}" >"$stdout_log" 2>"$stderr_log"
else
printf '%s' "$instruction" | codex "${args[@]}" >"$stdout_log" 2>"$stderr_log"
fi
rc=$?
set -e
if [[ $rc -ne 0 ]]; then
echo "codex exec failed (exit=$rc). stderr tail:" >&2
tail -n 40 "$stderr_log" >&2 || true
exit 5
fi
find "$SESSIONS_ROOT" -type f -name 'rollout-*.jsonl' -print 2>/dev/null | sort > "$after" || true
# Collect ALL new session files. A single `codex exec` call can spawn more
# than one session rollout (e.g. when the imagegen tool runs in a sub-turn),
# so we must scan every new one rather than blindly picking the last.
new_sessions_file="$(mktemp)"
trap 'rm -f "$before" "$after" "$stdout_log" "$stderr_log" "$new_sessions_file"' EXIT
comm -13 "$before" "$after" > "$new_sessions_file" || true
if [[ ! -s "$new_sessions_file" ]]; then
echo "No new session rollout file under $SESSIONS_ROOT" >&2
tail -n 40 "$stderr_log" >&2 || true
exit 6
fi
# Extract the image from the new session rollout(s). Extraction logic lives
# in a separate Python module; see scripts/extract_image.py for details.
set +e
python3 "$SCRIPT_DIR/extract_image.py" "$OUT" "$new_sessions_file"
py_rc=$?
set -e
if [[ $py_rc -ne 0 ]]; then
echo "Image payload not found in any new session file" >&2
echo "(imagegen likely did not run; stderr tail:)" >&2
tail -n 30 "$stderr_log" >&2 || true
exit 7
fi
Related skills
FAQ
What image formats does gpt-image-2 detect?
gpt-image-2 identifies PNG, JPEG, and WebP from base64 magic prefixes `iVBORw0KGgo`, `/9j/`, and `UklGR` after scanning Codex CLI rollout JSONL for blobs at least 200 characters long.
Which file does gpt-image-2 read?
gpt-image-2 reads Codex CLI session rollout JSONL files. It regex-scans embedded base64 strings and picks the largest valid image payload across the provided session paths.
Is Gpt Image 2 safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.