fix(codex): add Cloudflare-required headers to primary and auxiliary client paths#12664
Merged
Conversation
Add ChatGPT-Account-Id and originator headers when using chatgpt.com backend-api endpoint. Matches official codex-rs CLI behavior to prevent Cloudflare JavaScript challenges on non-residential IPs (VPS, Mac Mini, always-on servers). Applied in AIAgent.__init__ and _update_base_url_headers to cover both initial setup and credential rotation paths.
…ient
The cherry-picked salvage (admin28980's commit) added codex headers only on the
primary chat client path, with two inaccuracies:
- originator was 'hermes-agent' — Cloudflare whitelists codex_cli_rs,
codex_vscode, codex_sdk_ts, and Codex* prefixes. 'hermes-agent' isn't on
the list, so the header had no mitigating effect on the 403 (the
account-id header alone may have been carrying the fix).
- account-id header was 'ChatGPT-Account-Id' — upstream codex-rs auth.rs
uses canonical 'ChatGPT-Account-ID' (PascalCase, trailing -ID).
Also, the auxiliary client (_try_codex + resolve_provider_client raw_codex
branch) constructs OpenAI clients against the same chatgpt.com endpoint with
no default headers at all — so compression, title generation, vision, session
search, and web_extract all still 403 from VPS IPs.
Consolidate the header set into _codex_cloudflare_headers() in
agent/auxiliary_client.py (natural home next to _read_codex_access_token and
the existing JWT decode logic) and call it from all four insertion points:
- run_agent.py: AIAgent.__init__ (initial construction)
- run_agent.py: _apply_client_headers_for_base_url (credential rotation)
- agent/auxiliary_client.py: _try_codex (aux client)
- agent/auxiliary_client.py: resolve_provider_client raw_codex branch
Net: -36/+55 lines, -25 lines of duplicated inline JWT decode replaced by a
single helper. User-Agent switched to 'codex_cli_rs/0.0.0 (Hermes Agent)' to
match the codex-rs shape while keeping product attribution.
Tests in tests/agent/test_codex_cloudflare_headers.py cover:
- originator value, User-Agent shape, canonical header casing
- account-ID extraction from a real JWT fixture
- graceful handling of malformed / non-string / claim-missing tokens
- wiring at all four insertion points (primary init, rotation, both aux paths)
- non-chatgpt base URLs (openrouter) do NOT get codex headers
- switching away from chatgpt.com drops the headers
Contributor
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Codex provider no longer 403s from non-residential IPs. Requests to
chatgpt.com/backend-api/codexnow advertiseoriginator: codex_cli_rs, a codex_cli_rs-shapedUser-Agent, andChatGPT-Account-ID(canonical PascalCase) extracted from the OAuth JWT — matching the whitelisted header set Cloudflare expects.Closes the Cloudflare 403 regression originally reported in #6391.
Changes
agent/auxiliary_client.py: new_codex_cloudflare_headers(access_token)helper (originator + User-Agent + account-ID extraction). Wired into_try_codexand theraw_codexbranch ofresolve_provider_client.run_agent.py: replaced two inlined JWT-decode blocks (AIAgent.__init__+_apply_client_headers_for_base_url) with calls to the shared helper. Net -25 lines of duplicated inline logic.tests/agent/test_codex_cloudflare_headers.py: 13 regression tests — helper correctness, token edge cases, wiring at all four insertion points.Salvage notes
Cherry-picked @admin28980's commit from #6391 preserving their authorship via
--author=271152998+admin28980@users.noreply.github.com(their commit shipped with a genericUser <user@local>identity; public noreply email used to route credit to their GitHub account). Fix-up commit on top corrects the header values and extends coverage to the aux client.Corrections vs the original PR
originator: hermes-agent→codex_cli_rs(Cloudflare whitelistscodex_cli_rs,codex_vscode,codex_sdk_ts, andCodex*prefixes —hermes-agentisn't on the list, so the original value wasn't actually mitigating the block).ChatGPT-Account-Id→ChatGPT-Account-ID(canonical casing from upstream codex-rsauth.rs).User-Agent: hermes-agent/1.0→codex_cli_rs/0.0.0 (Hermes Agent)(matches the codex-rs UA shape while keeping product attribution).web_extractno longer 403 from VPS IPs when routed through Codex.Validation
codex_cli_rs+ canonical account-id header_try_codex)Targeted tests:
tests/agent/test_codex_cloudflare_headers.py13/13 pass. Adjacent: 78/78 (auxiliary_client, create_openai_client kwargs-isolation / reuse / proxy-env).