settings-store lists codex accounts as
index 0 = ~/.codex/auth.json (default)
index N = ~/.codex-accounts/{N}/auth.json
but codex-token-rotation only loads ~/.codex-accounts/{N} when those
dirs exist (it ignores ~/.codex/auth.json in that mode), so the
rotation array's indices are off-by-one vs settings indices.
Old buggy behavior:
- "사용중" badge pointed at the wrong account.
- Clicking 전환 on UI #N called setCurrentCodexAccountIndex(N) which
selected rotation array element N — typically a different
~/.codex-accounts/{N+1} entry. Effectively the UI told us a
different account was now active than what the next codex spawn
actually used.
Fix
- New findCodexAccountIndexByAuthPath(path) on the rotation module
exposes a path-based lookup.
- New getActiveCodexSettingsIndex() / setActiveCodexSettingsIndex(idx)
in settings-store translate via codexAuthPath() ↔
findCodexAccountIndexByAuthPath().
- /api/settings/accounts.codexCurrentIndex and PUT
/api/settings/accounts/codex/current both go through the
settings-side translation, so UI indices are now authoritative.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* Settings: clarify Codex 만료=결제, drop Claude expiry badge
- Codex expiry badge now says "결제 만료/까지" so it's unambiguous that
this is the ChatGPT Pro/Team subscription end-date, not an OAuth
access-token TTL.
- Claude rows no longer show an expiry badge: Claude credentials only
expose an OAuth access_token TTL (auto-refreshed every few minutes
by the existing claude token refresh loop), which is meaningless to
the user. Replace it with a static "토큰 자동갱신" tag so the column
alignment stays consistent.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Codex refresh: pull live plan_type from /wham/usage (sidecar override)
OAuth refresh_token grant returns a fresh JWT but auth0 caches the
chatgpt_plan_type and chatgpt_subscription_active_until claims at issue
time. So users who upgrade/downgrade their plan kept seeing the old
plan in the dashboard even after a forced refresh.
After each refreshCodexAccount() call, additionally GET
https://chatgpt.com/backend-api/wham/usage with the freshly-issued
access_token. That endpoint returns the current plan_type and email
from OpenAI's account service. Persist it to a sidecar
plan-status.json next to auth.json (not into auth.json itself, since
codex CLI may rewrite that file). readCodexAccount prefers the live
plan_type and uses the sidecar's checked_at as subscriptionLastChecked,
so the dashboard immediately reflects post-billing-event plan changes.
Confirmed end-to-end: account that was billed-down from Pro to Plus
now shows plus instead of stale pro after one refresh.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Auto refresh
- New refreshCodexAccount(index) calls https://auth.openai.com/oauth/token
with grant_type=refresh_token and persists rotated tokens back to
~/.codex-accounts/{N}/auth.json. JWT's subscription_active_until
reflects the latest plan state OpenAI hands back.
- startCodexAccountRefreshLoop() runs every 6h (first run delayed 60s
after boot to keep startup snappy). Hooked into main()/shutdown()
alongside the existing claude token refresh loop.
Manual controls (Settings → 계정 → Codex)
- "갱신" button per row: forces an immediate refresh for that account.
- "전체 갱신" button: refreshes all codex accounts in sequence.
- "전환" button: writes data/codex-rotation-state.json so the next codex
spawn picks the chosen account. Active account marked with a green-
bordered card and "사용중" badge.
Endpoints
- POST /api/settings/accounts/codex/{i}/refresh
- POST /api/settings/accounts/codex/refresh-all
- PUT /api/settings/accounts/codex/current { index }
- GET /api/settings/accounts now also returns codexCurrentIndex.
Why
JWTs cache subscription state at issue-time. When a Pro plan lapses or
a user upgrades/downgrades, the dashboard kept showing stale data until
the user logged in again. Periodic refresh + explicit "갱신" button
keeps the displayed state honest, and the manual switch unblocks the
user when rotation lands on a known-bad account.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Fast mode controls
- Codex: writes [features].fast_mode = true|false in ~/.codex/config.toml.
This matches the in-TUI /fast toggle ("toggle Fast mode to enable
fastest inference with increased plan usage").
- Claude: writes fastMode: bool to ~/.claude/settings.json. Mirrors the
/fast slash command preference (effective on opus-4-6).
UX polish
- Account rows switch from flex-wrap to a fixed grid (tag · email · plan ·
expiry · action) so emails of varying lengths don't cause ragged
baselines between rows.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>