diff --git a/src/components/LogoV2/CondensedLogo.tsx b/src/components/LogoV2/CondensedLogo.tsx index 13419d62c..0e7891200 100644 --- a/src/components/LogoV2/CondensedLogo.tsx +++ b/src/components/LogoV2/CondensedLogo.tsx @@ -7,7 +7,7 @@ import { useAppState } from '../../state/AppState.js'; import { getEffortSuffix } from '../../utils/effort.js'; import { truncate } from '../../utils/format.js'; import { isFullscreenEnvEnabled } from '../../utils/fullscreen.js'; -import { formatModelAndBilling, getLogoDisplayData, truncatePath } from '../../utils/logoV2Utils.js'; +import { formatModelAndBilling, getLogoDisplayData, isNotLoggedIn, truncatePath } from '../../utils/logoV2Utils.js'; import { renderModelSetting } from '../../utils/model/model.js'; import { OffscreenFreeze } from '../OffscreenFreeze.js'; import { AnimatedClawd } from './AnimatedClawd.js'; @@ -23,7 +23,10 @@ export function CondensedLogo(): ReactNode { const { columns } = useTerminalSize(); const agent = useAppState(s => s.agent); const effortValue = useAppState(s => s.effortValue); + // Subscribe to authVersion to re-render after login/logout + useAppState(s => s.authVersion); const model = useMainLoopModel(); + const notLoggedIn = isNotLoggedIn(); const modelDisplayName = renderModelSetting(model); const { version, cwd, billingType, agentName: agentNameFromSettings } = getLogoDisplayData(); @@ -92,7 +95,9 @@ export function CondensedLogo(): ReactNode { {' '} v{truncatedVersion} - {shouldSplit ? ( + {notLoggedIn ? ( + Not logged in + ) : shouldSplit ? ( <> {truncatedModel} {truncatedBilling} diff --git a/src/components/LogoV2/LogoV2.tsx b/src/components/LogoV2/LogoV2.tsx index 2c998a224..cfec387fc 100644 --- a/src/components/LogoV2/LogoV2.tsx +++ b/src/components/LogoV2/LogoV2.tsx @@ -11,6 +11,7 @@ import { getRecentActivitySync, getRecentReleaseNotesSync, getLogoDisplayData, + isNotLoggedIn, } from '../../utils/logoV2Utils.js'; import { truncate } from '../../utils/format.js'; import { getDisplayPath } from '../../utils/file.js'; @@ -82,6 +83,8 @@ export function LogoV2(): React.ReactNode { const showOverageCreditUpsell = useShowOverageCreditUpsell(); const agent = useAppState(s => s.agent); const effortValue = useAppState(s => s.effortValue); + // Subscribe to authVersion to re-render after login/logout + useAppState(s => s.authVersion); const config = getGlobalConfig(); @@ -136,6 +139,7 @@ export function LogoV2(): React.ReactNode { }, [showOverageCreditUpsell, showOnboarding, showGuestPassesUpsell, isCondensedMode]); const model = useMainLoopModel(); + const notLoggedIn = isNotLoggedIn(); const fullModelDisplayName = renderModelSetting(model); const { version, cwd, billingType, agentName: agentNameFromSettings } = getLogoDisplayData(); // Prefer AppState.agent (set from --agent CLI flag) over settings @@ -247,8 +251,8 @@ export function LogoV2(): React.ReactNode { - {modelDisplayName} - {billingType} + {notLoggedIn ? 'Not logged in' : modelDisplayName} + {!notLoggedIn && {billingType}} {agentName ? `@${agentName} · ${truncatedCwd}` : truncatedCwd} @@ -269,8 +273,9 @@ export function LogoV2(): React.ReactNode { } const welcomeMessage = formatWelcomeMessage(username); - const modelLine = - !process.env.IS_DEMO && config.oauthAccount?.organizationName + const modelLine = notLoggedIn + ? 'Not logged in' + : !process.env.IS_DEMO && config.oauthAccount?.organizationName ? `${modelDisplayName} · ${billingType} · ${config.oauthAccount.organizationName}` : `${modelDisplayName} · ${billingType}`; // Calculate cwd width accounting for agent name if present diff --git a/src/utils/logoV2Utils.ts b/src/utils/logoV2Utils.ts index 4357119d7..861d45200 100644 --- a/src/utils/logoV2Utils.ts +++ b/src/utils/logoV2Utils.ts @@ -1,7 +1,8 @@ import { getDirectConnectServerUrl, getSessionId } from '../bootstrap/state.js' import { stringWidth } from '@anthropic/ink' import type { LogOption } from '../types/logs.js' -import { getSubscriptionName, isClaudeAISubscriber } from './auth.js' +import { getAnthropicApiKey } from './auth.js' +import { hasCoStrictCredentialsSync } from '../costrict/provider/credentials.js' import { getCwd } from './cwd.js' import { getDisplayPath } from './file.js' import { @@ -9,6 +10,7 @@ import { truncateToWidth, truncateToWidthNoEllipsis, } from './format.js' +import { getAPIProvider, getProviderDisplayName } from './model/providers.js' import { getStoredChangelogFromMemory, parseChangelog } from './releaseNotes.js' import { gt } from './semver.js' import { loadMessageLogs } from './sessionStorage.js' @@ -237,12 +239,33 @@ export function formatReleaseNoteForDisplay( } /** - * Gets the common logo display data used by both LogoV2 and CondensedLogo + * Check if user is not logged in + * User is considered logged in if they have CoStrict credentials or API key */ +export function isNotLoggedIn(): boolean { + // 1. CoStrict login (credentials file exists) + if (hasCoStrictCredentialsSync()) return false + + const settings = getInitialSettings() + + // 2. Any provider configured via modelType (包括 anthropic/openai/bedrock 等) + // 用户通过 /login 选择 provider 后会设置 modelType + if (settings.modelType) { + return false + } + + // 3. Anthropic API Key configured (legacy way without modelType) + if (getAnthropicApiKey() !== null) return false + + // None of the above - not logged in + return true +} + export function getLogoDisplayData(): { version: string cwd: string billingType: string + isLoggedIn: boolean agentName: string | undefined } { const version = process.env.DEMO_VERSION ?? MACRO.VERSION @@ -253,15 +276,18 @@ export function getLogoDisplayData(): { const cwd = serverUrl ? `${displayPath} in ${serverUrl.replace(/^https?:\/\//, '')}` : displayPath - const billingType = isClaudeAISubscriber() - ? getSubscriptionName() - : 'API Usage Billing' + + const loggedIn = !isNotLoggedIn() + const provider = getAPIProvider() + const providerName = getProviderDisplayName(provider) + const billingType = loggedIn ? providerName : 'Not logged in' const agentName = getInitialSettings().agent return { version, cwd, billingType, + isLoggedIn: loggedIn, agentName, } } diff --git a/src/utils/model/providers.ts b/src/utils/model/providers.ts index b5b55444c..45c76d3ca 100644 --- a/src/utils/model/providers.ts +++ b/src/utils/model/providers.ts @@ -35,6 +35,32 @@ export function getAPIProviderForStatsig(): AnalyticsMetadata_I_VERIFIED_THIS_IS return getAPIProvider() as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS } +/** + * Get a human-readable display name for the current API provider + */ +export function getProviderDisplayName(provider: APIProvider): string { + switch (provider) { + case 'firstParty': + return 'Anthropic' + case 'bedrock': + return 'AWS Bedrock' + case 'vertex': + return 'Google Vertex' + case 'foundry': + return 'Foundry' + case 'openai': + return 'OpenAI' + case 'gemini': + return 'Google Gemini' + case 'grok': + return 'xAI Grok' + case 'costrict': + return 'CoStrict' + default: + return provider + } +} + /** * Check if ANTHROPIC_BASE_URL is a first-party Anthropic API URL. * Returns true if not set (default API) or points to api.anthropic.com