| 485 | } |
| 486 | |
| 487 | func FormatAuthStatus(logger *zap.Logger) string { |
| 488 | store := LoadStore(logger) |
| 489 | |
| 490 | // pickProfile returns the most user-relevant profile for a provider: |
| 491 | // prefer ":default" (created by /auth login), otherwise the first match. |
| 492 | // This catches synced profiles (anthropic-oauth-sync, anthropic:claude-cli) |
| 493 | // that the legacy hardcoded lookup missed. |
| 494 | pickProfile := func(p ProviderID) *AuthProfileCredential { |
| 495 | var fallback *AuthProfileCredential |
| 496 | for id, c := range store.Profiles { |
| 497 | if c == nil || c.Provider != p { |
| 498 | continue |
| 499 | } |
| 500 | if strings.HasSuffix(id, ":default") { |
| 501 | return c |
| 502 | } |
| 503 | if fallback == nil { |
| 504 | fallback = c |
| 505 | } |
| 506 | } |
| 507 | return fallback |
| 508 | } |
| 509 | |
| 510 | fmtProvider := func(p ProviderID) string { |
| 511 | if c := pickProfile(p); c != nil { |
| 512 | if c.Expires == 0 { |
| 513 | return i18n.T("auth.login.status_no_expiry", c.CredType) |
| 514 | } |
| 515 | return i18n.T("auth.login.status_format", c.CredType, FormatExpiry(c.Expires)) |
| 516 | } |
| 517 | // No store profile: surface the env fallback the runtime would use, |
| 518 | // so /auth status matches what message sends actually resolve to. |
| 519 | if tp := envFallbackProvider(p); tp != nil { |
| 520 | return i18n.T("auth.login.status_env", tp.Source()) |
| 521 | } |
| 522 | return i18n.T("auth.login.status_not_connected") |
| 523 | } |
| 524 | |
| 525 | return i18n.T("auth.login.status_display", |
| 526 | fmtProvider(ProviderAnthropic), |
| 527 | fmtProvider(ProviderOpenAICodex), |
| 528 | fmtProvider(ProviderGitHubCopilot), |
| 529 | fmtProvider(ProviderGitHubModels)) |
| 530 | } |
| 531 | |
| 532 | func Logout(provider ProviderID, logger *zap.Logger) error { |
| 533 | store := LoadStore(logger) |