From c45dc62eaff9c6163bf3e173ca433bb09d21f3e6 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Mon, 20 Jul 2026 16:18:43 -0700 Subject: [PATCH] fix(codex): flag signed-out system default (#9619) * fix(codex): flag signed-out system default * fix(codex): distinguish missing sign-in warnings * test(codex): preserve system OAuth auth warning --- .../src/components/settings/AccountsPane.tsx | 75 +++++++++++-------- .../codex-account-auth-warning.test.ts | 44 ++++++++++- .../settings/codex-account-auth-warning.ts | 13 +++- src/renderer/src/i18n/locales/en.json | 3 +- src/renderer/src/i18n/locales/es.json | 3 +- src/renderer/src/i18n/locales/ja.json | 3 +- src/renderer/src/i18n/locales/ko.json | 3 +- src/renderer/src/i18n/locales/zh.json | 3 +- 8 files changed, 105 insertions(+), 42 deletions(-) diff --git a/src/renderer/src/components/settings/AccountsPane.tsx b/src/renderer/src/components/settings/AccountsPane.tsx index a0f80f5922a..651b03140b3 100644 --- a/src/renderer/src/components/settings/AccountsPane.tsx +++ b/src/renderer/src/components/settings/AccountsPane.tsx @@ -414,21 +414,20 @@ export function AccountsPane({ // WSL falls back to the generic label. const systemCodexIdentity = accountRuntime.runtime === 'host' ? codexAccounts.systemDefault : undefined - // Why: the auth warning is derived from the desktop's own rate-limit poll; - // with a remote owner it would misattribute local auth state to the server. - const activeCodexAuthWarning = - codexAccountsLoaded && !isRemoteAccountScope - ? getCodexAccountAuthWarning({ - limits: codexRateLimits, - target: codexRateLimitTarget, - runtime: accountRuntime, - activeAccountId: activeCodexAccountId, - accountId: activeCodexAccountId, - authKind: activeCodexAccountId === null ? systemCodexIdentity?.authKind : undefined - }) - : null - const systemCodexNeedsReauthentication = - activeCodexAccountId === null && Boolean(activeCodexAuthWarning) + // Why: remote snapshots own their system-default identity, but the desktop's + // rate-limit poll must not be misattributed to a remote account owner. + const activeCodexAuthWarning = codexAccountsLoaded + ? getCodexAccountAuthWarning({ + limits: isRemoteAccountScope ? null : codexRateLimits, + target: codexRateLimitTarget, + runtime: accountRuntime, + activeAccountId: activeCodexAccountId, + accountId: activeCodexAccountId, + authKind: activeCodexAccountId === null ? systemCodexIdentity?.authKind : undefined + }) + : null + const systemCodexMissingSignIn = activeCodexAuthWarning === 'missing-sign-in' + const systemCodexNeedsSignIn = activeCodexAccountId === null && Boolean(activeCodexAuthWarning) const accountRuntimeUnavailable = accountRuntime.runtime === 'wsl' && !wslAvailable && !wslCapabilitiesLoading @@ -1080,16 +1079,22 @@ export function AccountsPane({
- {activeCodexAccountId + {systemCodexMissingSignIn ? translate( - 'auto.components.settings.AccountsPane.75ca9b718e', - 'Codex reported that the active account needs a fresh sign-in. Re-authenticate it before starting new Codex sessions.' - ) - : translate( - 'auto.components.settings.AccountsPane.e4a28e8894', - 'Codex reported that the {{value0}} login needs a fresh sign-in. Sign in again before starting new Codex sessions.', + 'auto.components.settings.AccountsPane.codexSystemDefaultNeedsSignIn', + 'No Codex sign-in was found for {{value0}}.', { value0: accountRuntimeSentenceLabel } - )} + ) + : activeCodexAccountId + ? translate( + 'auto.components.settings.AccountsPane.75ca9b718e', + 'Codex reported that the active account needs a fresh sign-in. Re-authenticate it before starting new Codex sessions.' + ) + : translate( + 'auto.components.settings.AccountsPane.e4a28e8894', + 'Codex reported that the {{value0}} login needs a fresh sign-in. Sign in again before starting new Codex sessions.', + { value0: accountRuntimeSentenceLabel } + )}
) : null} @@ -1156,7 +1161,7 @@ export function AccountsPane({ } disabled={codexAction !== 'idle' || accountRuntimeUnavailable} className={`flex w-full items-center justify-between gap-3 rounded-md border px-3 py-2.5 text-left transition-colors ${ - systemCodexNeedsReauthentication + systemCodexNeedsSignIn ? 'border-destructive/50 bg-destructive/5' : systemCodexActive ? 'border-foreground/20 bg-accent/15' @@ -1179,7 +1184,7 @@ export function AccountsPane({ {translate('auto.components.settings.AccountsPane.e74831fb6b', 'Active')} ) : null} - {systemCodexNeedsReauthentication ? ( + {systemCodexNeedsSignIn ? ( - {systemCodexNeedsReauthentication - ? translate( - 'auto.components.settings.AccountsPane.fd62f37c24', - 'Codex reported this {{value0}} login is out of date.', - { value0: accountRuntimeSentenceLabel } - ) + {systemCodexNeedsSignIn + ? systemCodexMissingSignIn + ? translate( + 'auto.components.settings.AccountsPane.codexSystemDefaultNeedsSignIn', + 'No Codex sign-in was found for {{value0}}.', + { value0: accountRuntimeSentenceLabel } + ) + : translate( + 'auto.components.settings.AccountsPane.fd62f37c24', + 'Codex reported this {{value0}} login is out of date.', + { value0: accountRuntimeSentenceLabel } + ) : getCodexSystemDefaultSubtitle( systemCodexIdentity, accountRuntimeSentenceLabel diff --git a/src/renderer/src/components/settings/codex-account-auth-warning.test.ts b/src/renderer/src/components/settings/codex-account-auth-warning.test.ts index dcb14b039b4..4d1631fbaad 100644 --- a/src/renderer/src/components/settings/codex-account-auth-warning.test.ts +++ b/src/renderer/src/components/settings/codex-account-auth-warning.test.ts @@ -49,7 +49,22 @@ describe('codex account auth warning', () => { activeAccountId: 'account-1', accountId: 'account-1' }) - ).toContain('access token could not be refreshed') + ).toBe('stale-sign-in') + }) + + it('preserves stale-sign-in warnings for an OAuth system default', () => { + expect( + getCodexAccountAuthWarning({ + limits: codexLimits( + 'Your access token could not be refreshed. Please log out and sign in again.' + ), + target: { runtime: 'host', wslDistro: null }, + runtime: { runtime: 'host' }, + activeAccountId: null, + accountId: null, + authKind: 'oauth' + }) + ).toBe('stale-sign-in') }) it('does not warn for inactive accounts or a different runtime', () => { @@ -90,6 +105,33 @@ describe('codex account auth warning', () => { ).toBeNull() }) + it('warns only when the active system default has no usable login', () => { + const getWarning = (authKind: 'none' | 'api-key' | 'oauth') => + getCodexAccountAuthWarning({ + limits: null, + target: { runtime: 'host', wslDistro: null }, + runtime: { runtime: 'host' }, + activeAccountId: null, + accountId: null, + authKind + }) + + expect(getWarning('none')).toBe('missing-sign-in') + expect(getWarning('api-key')).toBeNull() + expect(getWarning('oauth')).toBeNull() + + expect( + getCodexAccountAuthWarning({ + limits: null, + target: { runtime: 'host', wslDistro: null }, + runtime: { runtime: 'host' }, + activeAccountId: 'account-1', + accountId: null, + authKind: 'none' + }) + ).toBeNull() + }) + it('allows a WSL default account location to receive the active WSL target warning', () => { expect( codexRateLimitTargetMatchesAccountRuntime( diff --git a/src/renderer/src/components/settings/codex-account-auth-warning.ts b/src/renderer/src/components/settings/codex-account-auth-warning.ts index cefccac3d5c..9db1e3ae8e5 100644 --- a/src/renderer/src/components/settings/codex-account-auth-warning.ts +++ b/src/renderer/src/components/settings/codex-account-auth-warning.ts @@ -10,6 +10,8 @@ type AccountRuntime = { wslDistro?: string | null } +type CodexAccountAuthWarning = 'missing-sign-in' | 'stale-sign-in' + export function codexRateLimitTargetMatchesAccountRuntime( target: RateLimitRuntimeTarget, runtime: AccountRuntime @@ -30,14 +32,17 @@ export function getCodexAccountAuthWarning(args: { activeAccountId: string | null accountId: string | null authKind?: CodexSystemDefaultIdentity['authKind'] -}): string | null { +}): CodexAccountAuthWarning | null { + if (args.accountId !== args.activeAccountId) { + return null + } // Why: app-server reports API-key homes as a ChatGPT-auth error because // usage is unsupported; that is not a stale sign-in the user can re-auth. if (args.accountId === null && args.authKind === 'api-key') { return null } - if (args.accountId !== args.activeAccountId) { - return null + if (args.accountId === null && args.authKind === 'none') { + return 'missing-sign-in' } if (!codexRateLimitTargetMatchesAccountRuntime(args.target, args.runtime)) { return null @@ -45,5 +50,5 @@ export function getCodexAccountAuthWarning(args: { if (args.limits?.status !== 'error' || !isCodexAuthError(args.limits.error)) { return null } - return args.limits.error?.trim() || 'Codex reported that this sign-in needs re-authentication.' + return 'stale-sign-in' } diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json index 1d7263b6a1d..1e0b811e0e5 100644 --- a/src/renderer/src/i18n/locales/en.json +++ b/src/renderer/src/i18n/locales/en.json @@ -4989,7 +4989,8 @@ "remoteScopeAuthContext": "Each account keeps its own sign-in context on {{value0}}.", "remoteEmptyClaudeAccounts": "No managed Claude accounts on {{value0}}. It uses its system default Claude login; add accounts on that server.", "remoteEmptyCodexAccounts": "No managed Codex accounts on {{value0}}. It uses its system default Codex login; add accounts on that server.", - "codexSystemDefaultCustomProvider": "Custom provider — no usage tracked." + "codexSystemDefaultCustomProvider": "Custom provider — no usage tracked.", + "codexSystemDefaultNeedsSignIn": "No Codex sign-in was found for {{value0}}." }, "AdvancedPane": { "40b29e0bf3": "Restart", diff --git a/src/renderer/src/i18n/locales/es.json b/src/renderer/src/i18n/locales/es.json index a10a0ea150a..7ad26a2f327 100644 --- a/src/renderer/src/i18n/locales/es.json +++ b/src/renderer/src/i18n/locales/es.json @@ -4966,7 +4966,8 @@ "remoteScopeAuthContext": "Cada cuenta mantiene su propio contexto de inicio de sesión en {{value0}}.", "remoteEmptyClaudeAccounts": "No hay cuentas de Claude administradas en {{value0}}. Usa su inicio de sesión de Claude predeterminado del sistema; agrega cuentas en ese servidor.", "remoteEmptyCodexAccounts": "No hay cuentas de Codex administradas en {{value0}}. Usa su inicio de sesión de Codex predeterminado del sistema; agrega cuentas en ese servidor.", - "codexSystemDefaultCustomProvider": "Proveedor personalizado — no se registra el uso." + "codexSystemDefaultCustomProvider": "Proveedor personalizado — no se registra el uso.", + "codexSystemDefaultNeedsSignIn": "No se encontró ningún inicio de sesión de Codex para {{value0}}." }, "AdvancedPane": { "40b29e0bf3": "Reiniciar", diff --git a/src/renderer/src/i18n/locales/ja.json b/src/renderer/src/i18n/locales/ja.json index 4531cc490a3..f7e7c05745e 100644 --- a/src/renderer/src/i18n/locales/ja.json +++ b/src/renderer/src/i18n/locales/ja.json @@ -4951,7 +4951,8 @@ "remoteScopeAuthContext": "各アカウントは {{value0}} 上に独自のサインインコンテキストを保持します。", "remoteEmptyClaudeAccounts": "{{value0}} に管理対象の Claude アカウントはありません。システム既定の Claude ログインを使用します。アカウントの追加はそのサーバー上で行ってください。", "remoteEmptyCodexAccounts": "{{value0}} に管理対象の Codex アカウントはありません。システム既定の Codex ログインを使用します。アカウントの追加はそのサーバー上で行ってください。", - "codexSystemDefaultCustomProvider": "カスタムプロバイダー — 使用量は追跡されません。" + "codexSystemDefaultCustomProvider": "カスタムプロバイダー — 使用量は追跡されません。", + "codexSystemDefaultNeedsSignIn": "{{value0}} の Codex サインインが見つかりません。" }, "AdvancedPane": { "40b29e0bf3": "再起動", diff --git a/src/renderer/src/i18n/locales/ko.json b/src/renderer/src/i18n/locales/ko.json index dd155b3a4bf..57adc540b89 100644 --- a/src/renderer/src/i18n/locales/ko.json +++ b/src/renderer/src/i18n/locales/ko.json @@ -4951,7 +4951,8 @@ "remoteScopeAuthContext": "각 계정은 {{value0}}에 자체 로그인 컨텍스트를 유지합니다.", "remoteEmptyClaudeAccounts": "{{value0}}에 관리되는 Claude 계정이 없습니다. 해당 서버의 시스템 기본 Claude 로그인을 사용하며, 계정 추가는 그 서버에서 진행하세요.", "remoteEmptyCodexAccounts": "{{value0}}에 관리되는 Codex 계정이 없습니다. 해당 서버의 시스템 기본 Codex 로그인을 사용하며, 계정 추가는 그 서버에서 진행하세요.", - "codexSystemDefaultCustomProvider": "사용자 지정 제공업체 — 사용량을 추적하지 않습니다." + "codexSystemDefaultCustomProvider": "사용자 지정 제공업체 — 사용량을 추적하지 않습니다.", + "codexSystemDefaultNeedsSignIn": "{{value0}}에 대한 Codex 로그인을 찾을 수 없습니다." }, "AdvancedPane": { "40b29e0bf3": "다시 시작", diff --git a/src/renderer/src/i18n/locales/zh.json b/src/renderer/src/i18n/locales/zh.json index 825f838ddd4..365a23efdf2 100644 --- a/src/renderer/src/i18n/locales/zh.json +++ b/src/renderer/src/i18n/locales/zh.json @@ -4951,7 +4951,8 @@ "remoteScopeAuthContext": "每个账户在 {{value0}} 上保留自己的登录上下文。", "remoteEmptyClaudeAccounts": "{{value0}} 上没有受管理的 Claude 账户。它使用其系统默认的 Claude 登录;请在该服务器上添加账户。", "remoteEmptyCodexAccounts": "{{value0}} 上没有受管理的 Codex 账户。它使用其系统默认的 Codex 登录;请在该服务器上添加账户。", - "codexSystemDefaultCustomProvider": "自定义提供商 — 不跟踪使用情况。" + "codexSystemDefaultCustomProvider": "自定义提供商 — 不跟踪使用情况。", + "codexSystemDefaultNeedsSignIn": "未找到 {{value0}} 的 Codex 登录信息。" }, "AdvancedPane": { "40b29e0bf3": "重新启动",