replace ChatGPT localhost login with device auth - #3
Conversation
Follow the Codex device-code flow instead of requiring a localhost callback server for ChatGPT sign-in. This makes remote and headless usage much simpler by showing a browser URL plus one-time code, polling the Codex device endpoints, and exchanging the resulting authorization code for the same refreshable token state egaki already uses. The current blocker is upstream account settings: the tested account reaches the Codex device consent screen but OpenAI then requires device code authorization to be enabled in ChatGPT security settings before the flow can complete.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f4337e7355
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (!response.ok) { | ||
| throw new Error(`Device code request failed: ${response.status}`) | ||
| } |
There was a problem hiding this comment.
Keep OAuth fallback when device-code provisioning fails
The new login flow now aborts immediately on any non-2xx response from /api/accounts/deviceauth/usercode, and there is no remaining fallback to the previous localhost OAuth path. In environments/accounts where device auth is not enabled (commonly surfaced as 404/forbidden from this endpoint), users can no longer sign in to ChatGPT at all, which is a functional regression from the prior implementation.
Useful? React with 👍 / 👎.
**CLI imports (#1):** cli.ts now imports *Uncached variants so the CLI commands (egaki image, video, speech, transcribe) continue to work with raw result types instead of the cached { src } shape. **SSE race condition (#2):** subscribe to onProgressChange before reading the first snapshot, then loop: read -> yield -> check total -> await. This prevents the stream from hanging when a generation finishes between yield and subscribe. **Stale progress cleanup (#3):** guard setState calls in connectToProgress with `progressAbortController === controller` so an old aborted stream does not clear state from a newer active connection. **Error auto-clear by unique ID (#4):** each error gets a unique _id (key + timestamp + random). The 8s auto-clear timer removes by _id, not by generation key, so rapid duplicate failures are cleared independently. **Documented normalization limits (#5):** added AGENTS.md section for cachedGenerate with explicit note that params must be JSON-serializable; Date, Map, Set serialize to {} and should be passed as primitives. **getCacheInfo from wrapper (#6):** cachedGenerate now returns a callable with a .getCacheInfo(params) method that uses the same cacheKey config. Server components use wrapper.getCacheInfo() instead of the standalone getCacheInfo() to guarantee hash consistency. Session: ses_12ab29c60ffeiueHKd83cdmM4r
What changed
egaki login --provider chatgptnow shows:Why
The localhost callback flow is awkward for remote and headless usage. Device auth is much simpler for agents running on another machine because the terminal only needs to display a URL and short code.
Current blocker
I tested the flow end to end with this account. The new device-code flow works up to the Codex consent screen, but OpenAI then blocks completion with a settings requirement:
So the implementation matches Codex’s device-auth path, but successful completion depends on that upstream account setting being enabled.
Notes