Codex Server Skill
Run OpenAI Codex App Server via @openai/codex-sdk on deno. Streams multi-turn
chat sessions, structured output, image input, and rich event logs. This is the
default ChatGPT/Codex skill — prefer it over codex-cli (which is reserved
for batch one-shot codex exec use).
Auth: ChatGPT subscription only
This skill is ChatGPT-subscription-only by design. There is no API-key
fallback — OPENAI_API_KEY is not in --allow-env, so deno cannot read it
even if exported.
- If
~/.codex/auth.jsonexists (i.e.,codex loginhas been completed), the skill works using the user's ChatGPT Plus / Pro / Team subscription. No API-key billing is consumed. - If
~/.codex/auth.jsonis missing, every subcommand fails with exit code 2 and prints the login guide. The user must runcodex login(browser flow) and re-invoke.
For the full login walkthrough (account types, troubleshooting) read
references/auth-setup.md — but only when auth.json is missing.
First-run setup
Run once after install. No env vars needed.
CODEX_SERVER_VERSION=$(ls ~/.claude/plugins/cache/hideki-plugins/codex-server/ | sort -V | tail -1)
deno run --allow-read --allow-write --allow-env --allow-run=mise,codex,which \
~/.claude/plugins/cache/hideki-plugins/codex-server/$CODEX_SERVER_VERSION/skills/codex-server/assets/lib/setup.ts
(Resolves the highest installed cache version at runtime via a version sort — never hardcode a specific version here, the plugin has already moved past 1.0.0 and will move again.)
setup.ts copies chat.ts / worker.ts / helpers.ts into ~/.codex-server/lib/,
pins the codex binary path into ~/.codex-server/config.json, and prepares the
turns directory. After setup, run subsequent invocations from
~/.codex-server/lib/chat.ts (stable path, version-independent).
Operational flow (≤3 lines — this is the whole point)
deno run … chat.ts new "<prompt>"→ returns{turn_id, out_path, ...}JSON in <1 s. A detached worker runs the actual turn.- Watch progress: use the
Monitortool on the returnedout_path(it grows as deltas arrive). - Done when the
donemarker file appears alongsideout.txt; onerror, theerrormarker appears instead.
No run_in_background: true. No bounded poll loops. No timeout knobs. The
deno invocation that Bash spawns exits in <1 s regardless of turn duration.
If you need a blocking call, use chat.ts wait <turn-id> — it polls the markers
and prints the final out.txt on completion.
Hang handling (idle watchdog)
The worker wraps the SDK event stream in an idle watchdog: if no stream
event arrives for CODEX_SERVER_IDLE_SECS (default 180s), the turn is
presumed hung (network drop, app-server protocol stall, wedged child), marked
error, and the worker hard-exits — so a hung turn surfaces as failed
instead of reading as running forever. Every event resets the clock, so
legitimate long reasoning (which still emits item.* events) is never killed.
Override the threshold by exporting CODEX_SERVER_IDLE_SECS before invoking.
turnState adds a backstop stalled state: worker alive, no marker, yet no
output progress for >5 min (longer than the watchdog, so it only appears if the
worker's own watchdog failed to fire — e.g. an old worker binary or wedged
I/O). wait/tail treat stalled as non-terminal and print a one-time notice;
status/doctor report it directly.
The SDK pin is machine-adaptive: setup.ts reads the local codex binary's
minor and writes a matching sdkSpec (npm:@openai/codex-sdk@^<minor>.0) into
~/.codex-server/config.json; the worker imports that spec dynamically. So a
repo synced across machines on different codex versions (e.g. one box on
0.142, another on 0.130) each tracks its own — no single hardcoded pin to go
stale. The only drift mode left is upgrading the codex binary without
re-running setup: new/continue warn (≤once/24h) and doctor reports it
when the live binary minor no longer matches config.codexMinor. The fix is
always the same — re-run setup.ts to re-resolve the SDK to the current binary.
CLI surface
All subcommands of ~/.codex-server/lib/chat.ts. See references/examples.md
for worked examples (only read when an unfamiliar invocation is needed).
new "<prompt>" [--model M] [--cwd PATH] [--schema FILE] [--image P]… [--skip-git-check]continue [--last | --thread <id>] "<prompt>" [same flags]tail <turn-id> [--follow]wait <turn-id> [--timeout SECS]status <turn-id>— JSON:running/complete/failed/abandoned/stalled/missinglist-turns [--limit N]list— recent threads from~/.codex/sessions/show <thread-id>— thread metadata + taildoctor— health report: auth, codex-binary-vs-SDK version skew, and anyrunning/stalled/abandonedturns. Run this first when hangs are suspected.
new / continue always return turn-id in <1 s. tail / wait are
explicitly blocking; if they exceed 2 min, invoke them with
run_in_background: true and use Monitor on the output file instead — same
pattern as any other streaming command.
Required deno permissions
The setup script pins the codex binary path. After setup, every
chat.ts invocation needs these flags:
--allow-readand--allow-write— workspace + working dir + turn files--allow-env=PATH,HOME,USERPROFILE— only; deliberately omitsOPENAI_API_KEY--allow-run=<codex-path>,<deno-path>,kill—<codex-path>from~/.codex-server/config.json;<deno-path>(Deno.execPath()) becausenew/continuefork a detached deno worker;killbecausewait/tail/statusliveness-check the worker viakill -0--allow-net=api.openai.com— defensive
Streaming and events
The worker writes one JSON event per line to events.jsonl and a
human-readable rendering to out.txt. Item types observed:
agent_message, reasoning, command_execution, file_change, todo_list.
For deep parsing or extending event rendering read
references/streaming-protocol.md — only when the default rendering is
insufficient.
Per-turn directory layout
~/.codex-server/turns/<turn-id>/ contains:
prompt.txt— the user promptmeta.json—{ turn_id, thread_id, started_at, cwd, model, pid, resumed_from }events.jsonl— everyThreadEvent(one JSON per line)out.txt— streamed human-readable textworker.log— worker diagnostics: a[boot]line written before anything can fail (so a post-mortem distinguishes "never started" from "started then died"), plus[fail]/[watchdog]reasons. This is how a detached worker preserves why it died (its real stderr is discarded by the client fork).done(marker) — present onceturn.completedfireserror(marker) — present onturn.failed, the idle watchdog, or uncaught worker errors
setup.ts GCs turn-dirs older than 7 days on every invocation.
Error handling
On unexpected behavior read references/error-handling.md. Common cases:
~/.codex/auth.jsonmissing → runcodex logincodexnot on PATH → install viabrew install codexornpm install -g @openai/codex- worker crashed →
statusreportsabandoned; safe to abandon, justnewagain
Deferred features
These are intentionally out of scope for v1.0.0. Pull them in only on demand.
- Interactive tool-approval flow (
turn/approve/turn/reject) via raw JSON-RPC. Currently auto-approved viaapproval_policy: on-failure. - WebSocket transport (
--listen ws://IP:PORT). - In-skill ChatGPT login orchestration (
account/login/start, device code) — users completecodex loginexternally. - Persistent app-server (warm process) — amortize ~500ms–2 s startup per turn.
- Per-session model upgrade detection à la openai-cli's
resolveModel.ts. Currently defers to~/.codex/config.toml. - Raw JSON-RPC sub-mode using
codex app-server generate-tsbindings. -
Track the installed codex binary instead of a hardcoded SDK pin.Done over v1.2.0–v1.3.0: v1.2.0 moved the pin^0.130.0→^0.142.x, restoredresumeThread'sThreadOptionsarg (non-git resume works), and surfaced fatalerrorstream events. v1.3.0 made resolution machine-adaptive —setup.tsderivessdkSpecfrom the local codex minor and the worker imports it dynamically, so a repo synced across boxes on different codex versions each self-tracks. Upgrading codex now just needs asetup.tsre-run (doctor/newwarn when it's overdue).
Behavior Scenarios
BDD spec lives in references/scenarios.feature. Read only when auditing or
amending the skill (e.g., via /skill-improve); not needed for normal
execution.
Feedback Check
Before doing any work, if feedback/log.md exists next to this SKILL.md and
has ≥5 entries, read the last 10. If a pattern is apparent (the same keyword
in 3+ entries, or average rating below 3), tell the user (in Japanese):
「過去のフィードバックで類似パターンを検出: [簡潔に]。/skill-improve --skill codex-server で改善案を分析できます。」
Otherwise proceed silently.
Retrospective
After completing a session, reflect:
- Were there mid-session corrections, errors, or surprises?
- Ask the user (in Japanese): 「今回のセッションのフィードバック (1-5の評価、気になった点、または何もなければEnter)」
- If the user provides feedback OR if corrections/issues actually occurred:
- Create
feedback/log.mdnext to this SKILL.md if missing (header:# Feedback Log+ blank line +<!-- Append new entries at the top. Do not edit previous entries. -->). - Prepend a new entry:
## <ISO-8601 timestamp> - **Skill Version**: <version from this file's frontmatter> - **Task**: <brief description> - **Outcome**: success | partial-success | failure | error - **Rating**: <N>/5 (or "—" if not provided) - **Corrections**: <mid-session corrections, or "none"> - **Issues**: <specific problems, or "none"> - **User Note**: <user's verbatim feedback, or "—"> ---
- Create
- If the user skips AND no corrections/issues occurred, end without recording.
References
Read these on-demand only — they are not auto-loaded.
references/scenarios.feature— Gherkin BDD spec. Only for skill-improve / audit. Never during normal execution.references/auth-setup.md—codex loginwalkthrough. Only when auth.json is missing.references/sdk-reference.md—Codex/Thread/TurnAPI quick-ref. Only when extending or debugging.references/streaming-protocol.md— event/item types. Only when default rendering is insufficient.references/execution-patterns.md— full rationale for decoupled async. Only when this 3-line summary is not enough.references/examples.md— worked invocations. Only when an unfamiliar pattern is needed.references/error-handling.md— error-to-fix lookup. Only on error.