feat: ChatGPT multi-account parity (fallback, quota, cachekeep, modals, CLI)#7
Conversation
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
There was a problem hiding this comment.
13 issues found and verified against the latest diff
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
All 13 cubic findings addressed (commit
|
@iceteaSA Thanks for the thorough follow-up — I've gone through the code to verify each fix, and they all check out. The ones I can confirm from the diffs here:
The rest (lock staleness fallback, destroyed-socket guard, absolute-path resolve, NaN guards, idle reset) match the descriptions but live in files I don't have the full diff for — the patterns are consistent with what I see elsewhere in the codebase. 364 tests, clean The codebase is in good shape. No remaining concerns from my side. Tip: get faster answers by chatting with cubic’s review copilot in the review UI |
081aaf4 to
9611249
Compare
ualtinok
left a comment
There was a problem hiding this comment.
Thanks for the very thorough PR — design, tests, and the cubic follow-up all look strong. I reviewed it locally against current main (now at v0.1.2). Requesting one required change plus a couple of items to confirm before merge.
Blocker (must fix)
Full local test suite fails on this branch:
src/tests/oauth.test.ts
(fail) OAuth server concurrency (C1/C2) > startOAuthServer clears failed in-flight starts so a later retry can succeed
This first failure cascades into the other OAuth concurrency tests (unknown-state 400, missing-state 400, idle-server close, timeout cleanup). Each of those passes in isolation — they only fail because the blocker test above leaves a listener bound and never reaches its blocker.close().
Root cause looks like a test bug, not an implementation bug. The implementation binds explicitly to loopback:
// core/oauth.ts
server.listen(OAUTH_PORT, '127.0.0.1', ...)but the test's blocker binds to the unspecified address:
// tests/oauth.test.ts ~419
await new Promise<void>((resolve) => blocker.listen(OAUTH_PORT, resolve))
await expect(startOAuthServer()).rejects.toThrow()On macOS (and any host where listen(port) does not pre-empt a separate explicit 127.0.0.1 bind), startOAuthServer() then succeeds, so the rejects.toThrow() assertion fails and blocker is never closed. I verified directly: a plain listen(1455) does not block a subsequent listen(1455, '127.0.0.1') on this platform.
Suggested fix — bind the blocker to the same address the server uses, and always close it:
await new Promise<void>((resolve) =>
blocker.listen(OAUTH_PORT, '127.0.0.1', resolve),
)
try {
await expect(startOAuthServer()).rejects.toThrow()
} finally {
await new Promise<void>((resolve) => blocker.close(() => resolve()))
}Please confirm bun run test passes the full suite locally (and in CI) after the fix.
Please confirm before merge
-
CI visibility — the PR currently only shows Socket checks; I don't see the repo CI workflow (typecheck/build/test/format/lint) reporting on this PR. Please make sure the full CI run is green and visible here. (
typecheck,build,format:check,lintall pass locally for me; only the test suite is red because of the above.) -
New TUI dependency alerts — Socket flagged High "obfuscated code" warnings on the new deps (
@opentui/core@0.4.1, transitiveentities,seroval). These are new runtime deps for the sidebar. Please confirm these are acceptable/triaged, since they widen the plugin's supply-chain surface. -
./tuiexport ships source —package.jsonexposes"./tui"as./src/tui.tsx(not a built artifact). Confirm that's intentional and that OpenCode's TUI plugin loader consumes the.tsxsource directly at runtime.
Looked good (no action)
- Merges cleanly onto current
main; thev0.1.2retryable-websocket / header-timeout handling andcodexApiEndpointconfig are preserved in the merge. - Account/state writes go through
writeJsonAtomic(..., mode: 0o600), and refresh tokens are SHA-256 hashed before being persisted in operation-error records — no plaintext-token-at-rest issue found. typecheck/format:check/lint/buildall pass; package builds and packs with the newcli.js,tui-preferences.js, andrpc/*entry points.
Once the OAuth test is green in CI and the three confirmations above are settled, I'm happy to merge.
…s, CLI) Adds multi-account OAuth with reactive fallback, push-based Codex quota tracking (x-codex-* headers + codex.rate_limits WS frame), idle prompt-cache keep-warm (main + optional subagents), a TUI sidebar, 7 /openai-* command modals, an openai-auth CLI, leveled redacting logging, and a loopback RPC between loader and TUI. Preserves upstream's configurable codexApiEndpoint and retryable-websocket handling.
|
Thanks for the careful local review — the blocker diagnosis was exactly right, and I've rebased the branch onto current Blocker — fixedYour root-cause was precise: the test bug, not the implementation. The impl correctly binds Applied your suggested fix verbatim — bind the blocker to the same await new Promise<void>((resolve) =>
blocker.listen(OAUTH_PORT, '127.0.0.1', resolve),
)
try {
await expect(startOAuthServer()).rejects.toThrow()
} finally {
await new Promise<void>((resolve) => blocker.close(() => resolve()))
}Transparency note: I'm on Linux, where (392, up from the 390 you'd have seen — the rebase picked up your two new Confirmations1. CI visibility. This is the expected behavior for a fork PR ( 2. New TUI dependency alerts. Triaged — these aren't novel supply-chain surface:
All three are OSI-licensed and reputable, and this is the same 3. Let me know once you've kicked the CI run — happy to address anything the full workflow surfaces. |
Brings the OpenAI Codex OAuth plugin to feature parity with the Anthropic sibling, on a shared design + log structure agreed across both plugins.
Account management
main-first/fallback-first) with non-destructiveactiveIdswitching; per-account quota killswitch with 5h/weekly thresholds./openai-account addvia OAuth (browser + headless device-code);openai-authCLI for headless fallback-account management.Quota
x-codex-*HTTP headers + WScodex.rate_limitsframe), normalized 5h + weekly windows./openai-quotapollswham/usagefor main + every fallback.Cache keep-warm (
/openai-cachekeep)store:falseshadow request just before cache expiry. Optional subagent mode (30-min idle cap). Proven live: ~99% cache hit on idle warms, negligible quota impact.Token refresh
Cost
costZeroing.enabled: false(default on).Observability
TUI / RPC
DialogSelectcontrol surfaces for all/openai-*commands (account L1→L2 submenus, OSC-52 auth-URL copy). Multi-session RPC fix: disambiguate concurrent opencode instances byprocess.pidso command modals reach their own server.Config coexistence
openai-auth.jsonholds transport settings + the account store, content-discriminated so neither clobbers the other.Verification
tscclean, biome 0 warnings; both bundles build. Full-suite run repeated 20× with 0 flakes.Notes for reviewers
/openai-*command nouns, modal UX, log structure, and config keys are intentionally aligned with the Anthropic sibling plugin so both behave identically at a given log level.web_searchprompt-cache stabilizer, WS / raw-WS transports, and Codex request rewriting.Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.Summary by cubic
Adds multi‑account ChatGPT OAuth with automatic fallback, live quota, cache keep‑warm, a TUI sidebar with modals, and an
openai-authCLI. Preserves the configurable Codex endpoint and retryable WebSocket behavior while reaching parity with the Anthropic plugin.New Features
activeIdswitching, per‑account killswitch, and ChatGPT identity dedup.x-codex-*) and WS (codex.rate_limits), normalized 5‑hour + weekly windows; sidebar readout with pacing and persisted 429 backoff;/openai-quotarefresh for main and fallbacks.store:falseshadow request before expiry; optional subagent mode; dedup with error backoff./openai-*modals over a project‑scoped, per‑process loopback RPC (port‑file + bearer token); preferences control account order;openai-authCLI (login,list,remove).ARCHITECTURE.md,STRUCTURE.md).Migration
/openai-account addoropenai-auth login; manage with/openai-accountoropenai-auth list/remove./openai-cachekeep(optional subagent).openai-auth.jsonmigrates in place. Optional envs:OPENCODE_OPENAI_AUTH_LOG_LEVEL,OPENCODE_OPENAI_AUTH_LOG_FILE,OPENCODE_OPENAI_AUTH_STATE_FILE,OPENCODE_TUI_PREFERENCES_FILE,OPENCODE_OPENAI_AUTH_RPC_DIR.Written for commit 2e0e7b2. Summary will update on new commits.