codexthropic
v0.0.5
Published
Local proxy: Anthropic Messages API → Codex Responses API using local Codex OAuth
Readme
codexthropic
A local Node proxy that exposes the Anthropic Messages API and forwards each request to OpenAI's Codex Responses API at the ChatGPT backend, using your local Codex OAuth tokens (~/.codex/auth.json). Drop-in for clients that speak the Anthropic API (e.g. Claude Code).
Quickstart
npx codexthropic # foreground; banner shows listening URL
ANTHROPIC_BASE_URL=http://127.0.0.1:8765 ANTHROPIC_API_KEY=any claude # second terminalPrerequisites: Node 20+ and a working codex login (writes ~/.codex/auth.json).
Global install also works: npm i -g codexthropic.
Features
- Streaming SSE — Anthropic
message_start/content_block_*/message_delta/message_stopevents emitted as Codex events arrive. - Tool use — Anthropic
toolsandtool_use/tool_resultround-trip to Codexfunctiontools andfunction_call/function_call_outputitems.tool_choiceis mapped:auto→auto,any→required,none→none,{type:"tool", name}→{type:"function", name}. - Multi-turn reasoning continuity — Codex
reasoning.encrypted_contentround-trips through Anthropic'sthinking.signatureso chain-of-thought survives across tool-call rounds (see Reasoning). - Image input — Anthropic
imageblocks on user messages forward as Codexinput_image(base64; orurlsource restricted tohttp(s)/data:). - OAuth auto-refresh — auto-refresh with single-flight coalescing, reactive 401 retry, and rotated-
refresh_tokenre-read from disk (see Auth). - FAST mode —
--fast(orCODEX_FAST=1) setsservice_tier: "priority"upstream; banner + access log show a yellowFASTsignal in TTY mode.
CLI
Run codexthropic --help for the full structured help (USAGE / OPTIONS / ENVIRONMENT / AUTH / EXAMPLES).
Options
--port <n>— port (default8765, or$PORT)--host <addr>— host (default127.0.0.1, or$HOST). Localhost-only by default.--fast— includeservice_tier: "priority"in upstream requests (or setCODEX_FAST=1). When on, the startup banner shows a bold yellowFAST modeline and every access-log entry gains a trailingfasttoken plus a yellow model name (TTY only).--verbose— log each SSE event in/out (or setDEBUG=codexthropic). Note:-vno longer aliases--verbose; it now means--version.--version,-v— print the version and exit--help,-h— show help
Environment
PORT,HOST,CODEX_FAST,DEBUG,CODEX_HOME— see--help.NO_COLOR(any value) — disable color output everywhere.FORCE_COLOR(any value) — force color even when stderr/stdout is not a TTY.
Startup banner
On startup codexthropic prints a banner to stderr with name + version, the listening URL, the auth status (with refresh-time hint), the model-mapping summary, and a one-line client setup hint. The banner is informational only — it does not preflight refresh expired tokens.
Auth
- Reads
~/.codex/auth.json(override path withCODEX_HOME). - Auto-refreshes the access_token when its JWT
expis within 60s. - Concurrent expired requests are coalesced via single-flight; refreshed tokens are persisted back atomically (tmp + fsync + rename, mode
0600, onlytokens.access_token/tokens.id_token/tokens.refresh_token/last_refreshare touched — every other field is preserved byte-for-byte so this stays compatible with the Codex CLI). - After a refresh failure, subsequent requests within a 30-second window return cached 401 without re-hitting
auth.openai.com(prevents lockout-storm). - Reactive 401 retry: a 401 from
chatgpt.com/backend-api/codex/responsestriggers one force-refresh + replay before the error surfaces. If the Codex CLI rotatedrefresh_tokenon disk while we still held a stale copy, the disk version is reloaded transparently — no restart needed.
Model mapping
| Client model contains | Upstream model |
| ------------------------------ | -------------- |
| ^gpt- or ^o\d+ (lowercased) | passthrough |
| opus | gpt-5.5 |
| sonnet | gpt-5.5 |
| haiku | gpt-5.5 |
| (default) | gpt-5.5 |
gpt-5.5 is the default because the ChatGPT-Pro Codex backend currently rejects gpt-5-codex and gpt-5 for ChatGPT accounts. Edit src/model-map.ts to flip.
Reasoning
Anthropic thinking.budget_tokens → Codex reasoning.effort:
< 4000→low< 16000→medium< 32000→high≥ 32000(ortype: "adaptive", oroutput_config.effort: "max") →xhigh
Only emitted when the upstream-mapped model matches ^(o\d+|gpt-5).
reasoning.encrypted_content is always included so reasoning carries across multi-turn tool-call rounds. reasoning.summary: "auto" is also requested so Codex emits the human-readable reasoning summary, which the proxy surfaces as the thinking block's text.
Multi-turn reasoning continuity works under codex-oauth's store: false constraint by round-tripping the encrypted reasoning state through Anthropic's thinking.signature field: Codex's reasoning.encrypted_content on the response side becomes thinking.signature on the Anthropic block; on the next request, the signature is converted back to a Codex input[].type: "reasoning" item. Clients that round-trip thinking blocks (e.g., Claude Code) preserve chain-of-thought across tool-call rounds.
Limitations (out of v1 scope)
- No file / document content blocks (silently dropped).
- Image input is supported on user messages (see Features). Caveats: assistant-role images, unrecognized source variants, and malformed/empty source fields are silently dropped.
- No
web_search,computer_use, MCP tools. count_tokensreturns a chars/4 heuristic (Claude Code probes this endpoint to validate the model). No prompt-cache translation.- No upstream 5xx retries or 429 backoff.
max_tokensfrom the client is silently dropped (Codex backend caps responses on its own).- No daemon mode, no PID file, no config file. KISS.
Develop
bun install
bun run buildTests
bun test88 tests, all offline. Live path is manual smoke (see --verbose).
Release
bun run release # npm whoami || npm login && bumpp && npm publishprepublishOnly rebuilds the bundle (dist/cli.js) before each publish; only dist/ and README.md are shipped to npm.
Design
Design docs live in docs/designs/.
