Codex accounts: 6h auto-refresh, manual refresh, manual switch (#48)
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>
This commit is contained in:
@@ -897,6 +897,13 @@ button:disabled {
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.settings-account-group-head {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.settings-account-row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
@@ -908,6 +915,38 @@ button:disabled {
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
|
||||
.settings-account-row.is-active-account {
|
||||
border-color: rgba(80, 180, 130, 0.45);
|
||||
background: rgba(80, 180, 130, 0.06);
|
||||
}
|
||||
|
||||
.settings-account-actions {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.settings-secondary {
|
||||
min-height: 28px;
|
||||
padding: 0 10px;
|
||||
border: 1px solid var(--panel-border-strong);
|
||||
border-radius: 6px;
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
color: var(--bg-ink);
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.settings-secondary:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.settings-secondary:hover:not(:disabled) {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.settings-account-main {
|
||||
display: grid;
|
||||
grid-template-columns: 28px minmax(0, 1fr) 60px 180px;
|
||||
|
||||
Reference in New Issue
Block a user