Skip to content

Support for multiple Codex accounts, with sorting#588

Open
monterrr wants to merge 3 commits intosteipete:mainfrom
monterrr:codex-multi-account-approach
Open

Support for multiple Codex accounts, with sorting#588
monterrr wants to merge 3 commits intosteipete:mainfrom
monterrr:codex-multi-account-approach

Conversation

@monterrr
Copy link

@monterrr monterrr commented Mar 22, 2026

Closes #545

This is a draft PR mainly to show the implementation approach I took for Codex multi-account support.

CodexBar multiple Codex accounts

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:

  • multiple Codex accounts can be shown together in the menu, each with its own usage card
  • accounts are distinguished in the menu using the Codex account email and whether the account is personal vs team/workspace-backed
  • account cards can be sorted in useful ways

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:

  • sorting by reset soonest helps prioritize the account whose quota/window will reset first
  • sorting by most remaining usage helps prioritize the account with the most available capacity

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:

  • plugs Codex into the token-account infrastructure
  • fetches usage separately for each stored Codex account via TokenAccountOverride
  • forces Codex account-specific fetches through the web path when an override is active
  • attaches account identity back onto each resulting UsageSnapshot
  • stores the resulting per-account snapshots and renders them together in the Codex menu
  • adds sorting and refresh handling needed to keep that usable in practice

So 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.

Co-Authored-By: Craft Agent <agents-noreply@craft.do>
Co-Authored-By: Craft Agent <agents-noreply@craft.do>
@monterrr monterrr marked this pull request as ready for review March 22, 2026 17:55
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +430 to +434
if let result = try? await self.persist(
candidate: candidate,
targetEmail: targetEmail,
workspaceLabel: context.targetWorkspaceLabel,
logger: context.log)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Copy link
Author

@monterrr monterrr Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +130 to +131
let dashboard = try await Self.fetchOpenAIWebDashboard(request, logger: log)
guard let usage = dashboard.toUsageSnapshot(provider: .codex, accountEmail: request.accountEmail) else {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Copy link
Author

@monterrr monterrr Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +8 to +11
private struct CredentialState: Equatable {
let cookieSource: ProviderCookieSource
let hasManualCookieHeader: Bool
let hasTokenAccounts: Bool

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Copy link
Author

@monterrr monterrr Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
@monterrr
Copy link
Author

monterrr commented Mar 23, 2026

Pushed follow-up commit 72c6061 (Fix Codex workspace identity edge cases) to address the workspace/account-identity review points:

  • match imported OpenAI web sessions by workspace as well as email when a target workspace is known
  • preserve workspace identity in the selected Codex snapshot path, not just the stacked account-card path
  • reset OpenAI web state when the active Codex token account changes

Also added focused tests around the snapshot/workspace behavior. @codex review

@monterrr monterrr changed the title Draft: Show multiple Codex accounts in the menu Support for multiple Codex accounts Mar 23, 2026
@monterrr monterrr changed the title Support for multiple Codex accounts Support for multiple Codex accounts, with sorting Mar 23, 2026
@monterrr
Copy link
Author

@codex review
Follow-up fixes are in 72c6061.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

let hadPriorData = self.snapshots[provider] != nil || fallbackSnapshot != nil
let shouldSurface = self.failureGates[provider]?
.shouldSurfaceError(onFailureWithPriorData: hadPriorData) ?? true

P1 Badge Stop reusing a different account's snapshot after a switch fails

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".

Comment on lines +44 to +46
let disabledOpenAIWeb = previous.cookieSource.isEnabled && !current.cookieSource.isEnabled

guard removedAllManualCodexCredentials || disabledOpenAIWeb else { return }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Would you be open to a PR for Codex multi-account support?

2 participants