Support for multiple Codex accounts, with sorting#588
Support for multiple Codex accounts, with sorting#588monterrr wants to merge 3 commits intosteipete:mainfrom
Conversation
Co-Authored-By: Craft Agent <agents-noreply@craft.do>
Co-Authored-By: Craft Agent <agents-noreply@craft.do>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c160acc027
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if let result = try? await self.persist( | ||
| candidate: candidate, | ||
| targetEmail: targetEmail, | ||
| workspaceLabel: context.targetWorkspaceLabel, | ||
| logger: context.log) |
There was a problem hiding this comment.
Match imported ChatGPT cookies by workspace as well as email
In the same-email multi-workspace case, this still treats any browser session with the right email as a match: evaluateCandidate only compares signedInEmail to targetEmail, and targetWorkspaceLabel is only used when persisting the winner. That means a browser currently focused on alice@example.com — Team can be saved under alice@example.com — Personal, so later refreshes will silently show the wrong quota/history for that Codex account.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Addressed in 72c6061 (Fix Codex workspace identity edge cases).
Candidate matching now uses workspace as well as email when a target workspace is known, so same-email personal/team cases no longer collapse onto email-only matching.
| let dashboard = try await Self.fetchOpenAIWebDashboard(request, logger: log) | ||
| guard let usage = dashboard.toUsageSnapshot(provider: .codex, accountEmail: request.accountEmail) else { |
There was a problem hiding this comment.
Preserve workspace labels in the selected Codex snapshot
The fetch request now carries workspaceLabel, but the provider-level usage snapshot is still built from toUsageSnapshot(... accountEmail:) only. In stacked mode the later applyAccountLabel call patches per-account cards, but in switcher/single-card mode (showAllTokenAccountsInMenu = false) the active Codex snapshot keeps accountOrganization == nil, so two accounts that share an email still render identically in the main menu and provider detail views.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Addressed in 72c6061 (Fix Codex workspace identity edge cases).
The selected Codex snapshot path now carries workspace identity through the dashboard-to-usage conversion as well, not just the stacked account-card path.
| private struct CredentialState: Equatable { | ||
| let cookieSource: ProviderCookieSource | ||
| let hasManualCookieHeader: Bool | ||
| let hasTokenAccounts: Bool |
There was a problem hiding this comment.
Reset OpenAI web state when the active Codex account changes
This credential fingerprint only records whether manual Codex credentials exist, not which token account is selected. When a user switches between two saved Codex accounts, CredentialState stays equal, so settingsDidChange skips resetOpenAIWebState(). The menu is repopulated immediately after setActiveTokenAccountIndex, which lets the newly selected account reuse the previous account’s dashboard-derived breakdown/credits until a later refresh completes.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Addressed in 72c6061 (Fix Codex workspace identity edge cases).
The Codex runtime credential-state tracking now includes the selected token account, so switching active Codex accounts resets the OpenAI web state instead of reusing the previous account’s dashboard-derived state.
Co-Authored-By: Craft Agent <agents-noreply@craft.do>
|
Pushed follow-up commit 72c6061 (Fix Codex workspace identity edge cases) to address the workspace/account-identity review points:
Also added focused tests around the snapshot/workspace behavior. @codex review |
There was a problem hiding this comment.
💡 Codex Review
CodexBar/Sources/CodexBar/UsageStore+TokenAccounts.swift
Lines 156 to 158 in 72c6061
When the user switches from account A to account B, self.snapshots[provider] still contains A until B finishes refreshing. Here hadPriorData treats that stale provider snapshot as reusable history, so ConsecutiveFailureGate suppresses B's first refresh error and leaves A's usage visible. In the account-switch flow this mixes data between accounts instead of surfacing that the newly selected account failed to load.
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| let disabledOpenAIWeb = previous.cookieSource.isEnabled && !current.cookieSource.isEnabled | ||
|
|
||
| guard removedAllManualCodexCredentials || disabledOpenAIWeb else { return } |
There was a problem hiding this comment.
Reset Codex web state when the selected token account changes
CredentialState now tracks selectedTokenAccountID, but this guard still only clears caches when manual credentials are removed or web access is turned off. That means switching Codex token accounts keeps the previous openAIDashboard/cookie state alive; for same-email personal-vs-workspace accounts, the later refresh path in UsageStore only notices email changes, so the breakdown/credits panel can stay attached to the old workspace after the switch.
Useful? React with 👍 / 👎.
Closes #545
This is a draft PR mainly to show the implementation approach I took for Codex multi-account support.
Context
My personal problem was simple: I often have more than one Codex account available, and I wanted to see them together in the menu bar, with each account’s usage/limits visible at the same time.
What this adds
For Codex, this turns the menu from a single-account usage view into a multi-account view.
In practice, it adds three things:
A possible improvement here would be to allow manually adding labels to accounts, instead of only relying on the detected account identity/context.
Why sorting is part of it
Once multiple Codex accounts are visible together, sorting becomes practically useful.
For example:
That was part of the actual use case for me, not just a cosmetic extra.
Implementation approach
I built this with AI assistance, and the route I took was to reuse the existing token-account pipeline for Codex rather than inventing a separate account model.
Concretely, the implementation does this:
TokenAccountOverrideUsageSnapshotSo the basic idea was not to build a completely separate multi-account subsystem, but to reuse the existing token-account flow and make Codex participate in it.
Scope / limitations
I only implemented this for Codex, because that was the only thing I personally needed.
I did not try to generalize the same behavior across the other providers, so I don’t want to overclaim here. My guess is that a similar approach could be extended to other providers that fit the existing token-account/session model, but I have not done that work.
Note
This draft PR is mainly intended as a concrete reference for the implementation approach that solved this problem for me.
If some parts are useful, feel free to reuse, adapt, or reimplement them however makes the most sense upstream.