feat: Codex (ChatGPT subscription) provider#16
Conversation
OAuth PKCE flow to auth w/ ChatGPT Plus/Pro; stores tokens in plugin settings. Calls chatgpt.com/backend-api/codex/responses (responses API) instead of chat completions. Auto-refreshes tokens. Sign in/out UI in settings tab. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis PR adds Codex (ChatGPT subscription) as a new provider alongside existing LangChain clients. It includes OAuth PKCE authentication with token refresh, a direct Codex API client for streaming responses, settings UI controls for sign-in/sign-out, and integration into the ChatApiManager request flow. ChangesCodex Provider Integration
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/api.ts`:
- Around line 237-241: The token refresh updates
(this.settings.codexAccess/codexRefresh/codexExpires) but aren't persisted;
modify ChatApiManager to accept a persist callback (e.g., onPersist or
saveSettingsCallback) in its constructor or init, pass () => this.saveSettings()
from main.ts, and invoke that callback inside the getValidCodexToken refresh
handler used by callCodexProvider (immediately after updating this.settings.*)
so refreshed Codex tokens are saved to disk via
plugin.saveSettings()/saveData().
In `@src/codex-auth.ts`:
- Around line 100-108: The refresh response handling currently computes expires
as Date.now() + json.expires_in * 1000 without validating expires_in, which can
produce NaN; update the refresh flow (the same block that reads json and returns
{ access, refresh, expires }) to validate json.expires_in (e.g. typeof
json.expires_in === 'number' && Number.isFinite(json.expires_in') or parse and
check) and return null if it's missing/invalid, mirroring the initial token
acquisition guard so expires is always a valid timestamp.
- Around line 81-86: The token factory returns expires based on json.expires_in
without validation, causing NaN if expires_in is missing; update the code that
builds the token object (the return block in the function that constructs
access/refresh/expires/accountId used by getValidCodexToken) to validate and
normalize json.expires_in (e.g., ensure it's a finite positive number via
Number(...) check or default to 0 or a conservative TTL) before computing
Date.now() + expires_in*1000, and if it's invalid consider either throwing a
clear error or setting expires to Date.now() (or Date.now() + 0) so subsequent
getValidCodexToken logic behaves predictably. Ensure you reference the same
returned fields (access, refresh, expires, accountId) so callers remain
unchanged.
In `@src/settings.ts`:
- Around line 90-93: The UI's isSignedIn calculation (variable isSignedIn using
this.plugin.settings.codexAccess and codexAccountId) is inconsistent with API
validation in callCodexProvider which also requires codexRefresh; update the
sign-in check to include this.plugin.settings.codexRefresh so the UI only
reports signed-in when codexAccess, codexAccountId and codexRefresh are present,
and ensure any related UI text/flows that rely on isSignedIn still behave
correctly.
- Around line 113-125: Wrap the call to startCodexOAuthFlow() in a try/catch
inside the block where you open the browser for sign-in; if it throws, catch the
error, log it (e.g., console.error or process/plugin logger) and show a
user-facing Notice with an error message, and return or abort without mutating
settings. Keep the existing logic that, on success, assigns tokens to
this.plugin.settings.codexAccess/codexRefresh/codexExpires/codexAccountId, calls
await this.saveSettings(), shows the success Notice, and calls this.display();
ensure any cleanup or UI state update still happens in the error path as
appropriate.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1a1c9c46-8b60-463e-b445-690cfd14f027
📒 Files selected for processing (4)
src/api.tssrc/codex-auth.tssrc/codex-client.tssrc/settings.ts
|
Thanks for this PR. I'm probably going to use this myself! There are some issues highlighted by Code Rabbit, but they are more code quality things that are not really needed rn. |
|
Thanks for the super quick merge! 🙏 Found a couple of bugs while testing locally — opened #17 with fixes (Obsidian CSP blocks native fetch so switched to requestUrl, SSE delta parsing fix, and a model dropdown for Codex). Working well now! |
Summary
localhost:1455to capture the auth code; tokens are stored in plugin settings and auto-refreshedhttps://chatgpt.com/backend-api/codex/responses(the responses API) with the required Codex headers, bypassing LangChain for this providerHow it works
auth.openai.comNew files
src/codex-auth.ts— PKCE OAuth flow, token refresh, token validationsrc/codex-client.ts— Codex responses API client + SSE parserNotes
CLIENT_IDand endpoints as the official openai/codex CLIgpt-5.1-codexorcodex-mini-latestTest plan
🤖 Generated with Claude Code
Summary by CodeRabbit