Run Claude Code over your ChatGPT subscription.
claudex is a local proxy that translates Anthropic Messages API to the OpenAI Responses API used by the Codex backend, and authenticates with your ChatGPT Plus/Pro account via the same OAuth device flow the codex CLI uses. Your Claude Code stays unchanged — it just points at ANTHROPIC_BASE_URL=http://127.0.0.1:<port> and talks to the proxy.
Status: works for me, not battle-tested. SSE event shape from the Codex backend drifts; expect rough edges around tool-use and reasoning streams.
cargo install --git https://github.com/m0n0x41d/claudexor from a clone:
cargo install --path .claudex auth # one-time: ChatGPT device-flow login
claudex init # write default config to ~/.claudex/config.yaml
claudex # spawn the proxy and run `claude` through it
claudex status # show current token state
claudex --resume <session_id> # any unknown args are forwarded to `claude`
claudex -- --print "hello" # use `--` to be explicitArg order to claude: claude.default_args from config, then whatever you passed to claudex.
claudex with no subcommand binds an ephemeral port, exports ANTHROPIC_BASE_URL + a dummy ANTHROPIC_API_KEY, then execs claude with whatever args you pass after it. When claude exits, the proxy is dropped. No daemon, no persistent ports.
Logs go to ~/.claudex/claudex.log while claude is running (stderr is left alone so the TUI stays clean). tail -f ~/.claudex/claudex.log to watch traffic.
If claude is already signed in to claude.ai, run claude /logout inside it once and answer No to the API key approval prompt — otherwise the claude.ai token takes precedence over the proxy.
~/.claudex/config.yaml:
proxy:
bind: 127.0.0.1
port: 0 # 0 = ephemeral
log_level: info
codex:
base_url: https://chatgpt.com/backend-api/codex
default_model: gpt-5.5
reasoning_effort: medium # low | medium | high
reasoning_summary: concise # auto | concise | detailed
originator: claudex
claude:
command: claude
default_args: []
model_map: {} # claude-opus-4-7: gpt-5.5Available models depend on your ChatGPT tier — the Codex backend gates them by client_version. Probe with:
curl -s -H "Authorization: Bearer $(jq -r .access_token ~/.claudex/auth.json)" \
-H "chatgpt-account-id: $(jq -r .account_id ~/.claudex/auth.json)" \
'https://chatgpt.com/backend-api/codex/models?client_version=1.0.0' | jq '.models[].slug'Layered, bottom-up. Each layer depends only on the layer below.
types/ L0 algebraic data (Anthropic + Responses wire types, TokenSet, Config)
translate/ L1 pure Anthropic↔Responses (incl. StreamReconciler state machine)
auth/ L2 pure auth state machine (classify, device-flow parsing)
ports/ L3 effect traits + their concrete impls (http, fs, subprocess, …)
app/ L4 use cases: authenticate, AuthService, serve, launch_session
cli/ L5 argv dispatch
Side effects live only in ports/ and app/. Translation and auth state are pure and unit-tested. The proxy is spawn-per-invocation: one claudex process = one claude session = one ephemeral listener.
MIT.