import { homedir } from "node:os"; import type { Letta } from "@letta-ai/letta-client"; import { Box } from "ink"; import { useEffect, useState } from "react"; import type { AgentProvenance } from "../../agent/create"; import { getModelDisplayName } from "../../agent/model"; import { settingsManager } from "../../settings-manager"; import { getVersion } from "../../version"; import { useTerminalWidth } from "../hooks/useTerminalWidth"; import { AnimatedLogo } from "./AnimatedLogo"; import { colors } from "./colors"; import { Text } from "./Text"; /** * Convert absolute path to use ~ for home directory */ function toTildePath(absolutePath: string): string { const home = homedir(); if (absolutePath.startsWith(home)) { return `~${absolutePath.slice(home.length)}`; } return absolutePath; } /** * Synchronously determine auth method from env vars (for initial render). * Returns null if we need to check keychain/settings asynchronously. */ function getInitialAuthMethod(): "url" | "api-key" | null { if (process.env.LETTA_BASE_URL) return "url"; if (process.env.LETTA_API_KEY) return "api-key"; return null; // Need async check for keychain/settings } /** * Determine the auth method used (async for keychain access) */ async function getAuthMethod(): Promise<"url" | "api-key" | "oauth"> { // Check if custom URL is being used if (process.env.LETTA_BASE_URL) { return "url"; } // Check if API key from env if (process.env.LETTA_API_KEY) { return "api-key"; } // Check settings for refresh token (OAuth) from keychain tokens const settings = await settingsManager.getSettingsWithSecureTokens(); if (settings.refreshToken) { return "oauth"; } // Check if API key stored in settings or keychain if (settings.env?.LETTA_API_KEY) { return "api-key"; } return "oauth"; // default } type LoadingState = | "loading_profiles" | "assembling" | "importing" | "initializing" | "checking" | "selecting_global" | "ready"; export function WelcomeScreen({ loadingState, continueSession, agentState, agentProvenance: _agentProvenance, }: { loadingState: LoadingState; continueSession?: boolean; agentState?: Letta.AgentState | null; agentProvenance?: AgentProvenance | null; }) { // Keep hook call for potential future responsive behavior useTerminalWidth(); const cwd = process.cwd(); const version = getVersion(); const tildePath = toTildePath(cwd); // Get model display name (pretty name if available, otherwise last part of handle) // Build full model handle from llm_config (model_endpoint_type/model) like App.tsx does const llmConfig = agentState?.llm_config; const fullModel = llmConfig?.model_endpoint_type && llmConfig?.model ? `${llmConfig.model_endpoint_type}/${llmConfig.model}` : (llmConfig?.model ?? null); const model = fullModel ? (getModelDisplayName(fullModel) ?? fullModel.split("/").pop()) : undefined; // Get auth method - use sync check for env vars, async only for keychain const initialAuth = getInitialAuthMethod(); const [authMethod, setAuthMethod] = useState<"url" | "api-key" | "oauth">( initialAuth ?? "oauth", ); useEffect(() => { // Only run async check if env vars didn't determine auth method if (!initialAuth) { getAuthMethod().then(setAuthMethod); } }, [initialAuth]); const authDisplay = authMethod === "url" ? process.env.LETTA_BASE_URL || "Custom URL" : authMethod === "api-key" ? "API key auth" : "OAuth"; return ( {/* Left column: Logo */} {/* Right column: Text info */} {/* Row 1: Letta Code + version */} Letta Code v{version} {/* Row 2: model · auth (or just auth while loading) */} {model ? `${model} · ${authDisplay}` : authDisplay} {/* Row 3: loading status, then path once ready */} {loadingState === "ready" ? tildePath : getLoadingMessage(loadingState, !!continueSession)} ); } function getLoadingMessage( loadingState: LoadingState, continueSession: boolean, ): string { switch (loadingState) { case "loading_profiles": return "Loading pinned agents..."; case "initializing": return continueSession ? "Resuming agent..." : "Creating agent..."; case "assembling": return "Assembling tools..."; case "importing": return "Importing agent..."; case "checking": return "Checking for pending approvals..."; default: return "Loading..."; } }