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
This commit is contained in:
Brennan Benson
2026-07-20 16:18:43 -07:00
committed by GitHub
parent f655ae3929
commit c45dc62eaf
8 changed files with 105 additions and 42 deletions
@@ -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({
<div className="flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/5 px-3 py-2 text-xs text-destructive">
<AlertTriangle className="mt-0.5 size-3.5 shrink-0" />
<span>
{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 }
)}
</span>
</div>
) : 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')}
</Badge>
) : null}
{systemCodexNeedsReauthentication ? (
{systemCodexNeedsSignIn ? (
<Badge
variant="destructive"
className="h-4 shrink-0 rounded px-1.5 text-[10px] font-medium leading-none"
@@ -1193,15 +1198,21 @@ export function AccountsPane({
</div>
<span
className={`truncate text-[11px] ${
systemCodexNeedsReauthentication ? 'text-destructive' : 'text-muted-foreground'
systemCodexNeedsSignIn ? 'text-destructive' : 'text-muted-foreground'
}`}
>
{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
@@ -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(
@@ -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'
}
+2 -1
View File
@@ -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",
+2 -1
View File
@@ -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",
+2 -1
View File
@@ -4951,7 +4951,8 @@
"remoteScopeAuthContext": "各アカウントは {{value0}} 上に独自のサインインコンテキストを保持します。",
"remoteEmptyClaudeAccounts": "{{value0}} に管理対象の Claude アカウントはありません。システム既定の Claude ログインを使用します。アカウントの追加はそのサーバー上で行ってください。",
"remoteEmptyCodexAccounts": "{{value0}} に管理対象の Codex アカウントはありません。システム既定の Codex ログインを使用します。アカウントの追加はそのサーバー上で行ってください。",
"codexSystemDefaultCustomProvider": "カスタムプロバイダー — 使用量は追跡されません。"
"codexSystemDefaultCustomProvider": "カスタムプロバイダー — 使用量は追跡されません。",
"codexSystemDefaultNeedsSignIn": "{{value0}} の Codex サインインが見つかりません。"
},
"AdvancedPane": {
"40b29e0bf3": "再起動",
+2 -1
View File
@@ -4951,7 +4951,8 @@
"remoteScopeAuthContext": "각 계정은 {{value0}}에 자체 로그인 컨텍스트를 유지합니다.",
"remoteEmptyClaudeAccounts": "{{value0}}에 관리되는 Claude 계정이 없습니다. 해당 서버의 시스템 기본 Claude 로그인을 사용하며, 계정 추가는 그 서버에서 진행하세요.",
"remoteEmptyCodexAccounts": "{{value0}}에 관리되는 Codex 계정이 없습니다. 해당 서버의 시스템 기본 Codex 로그인을 사용하며, 계정 추가는 그 서버에서 진행하세요.",
"codexSystemDefaultCustomProvider": "사용자 지정 제공업체 — 사용량을 추적하지 않습니다."
"codexSystemDefaultCustomProvider": "사용자 지정 제공업체 — 사용량을 추적하지 않습니다.",
"codexSystemDefaultNeedsSignIn": "{{value0}}에 대한 Codex 로그인을 찾을 수 없습니다."
},
"AdvancedPane": {
"40b29e0bf3": "다시 시작",
+2 -1
View File
@@ -4951,7 +4951,8 @@
"remoteScopeAuthContext": "每个账户在 {{value0}} 上保留自己的登录上下文。",
"remoteEmptyClaudeAccounts": "{{value0}} 上没有受管理的 Claude 账户。它使用其系统默认的 Claude 登录;请在该服务器上添加账户。",
"remoteEmptyCodexAccounts": "{{value0}} 上没有受管理的 Codex 账户。它使用其系统默认的 Codex 登录;请在该服务器上添加账户。",
"codexSystemDefaultCustomProvider": "自定义提供商 — 不跟踪使用情况。"
"codexSystemDefaultCustomProvider": "自定义提供商 — 不跟踪使用情况。",
"codexSystemDefaultNeedsSignIn": "未找到 {{value0}} 的 Codex 登录信息。"
},
"AdvancedPane": {
"40b29e0bf3": "重新启动",