Add self-hosted server option to onboard wizard

- Add "Enter self-hosted URL" option in authentication step
- Defaults to http://localhost:8283
- Sets LETTA_BASE_URL so model listing works from local server
- Validates connection to self-hosted server
- Shows server URL in configuration summary

🤖 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>
This commit is contained in:
Sarah Wooders
2026-01-28 21:22:55 -08:00
parent 7707363dd3
commit 4a024082cc

View File

@@ -16,8 +16,9 @@ const ENV_EXAMPLE_PATH = resolve(process.cwd(), '.env.example');
interface OnboardConfig {
// Auth
authMethod: 'keep' | 'oauth' | 'apikey' | 'skip';
authMethod: 'keep' | 'oauth' | 'apikey' | 'selfhosted' | 'skip';
apiKey?: string;
baseUrl?: string;
// Agent
agentChoice: 'new' | 'existing' | 'env' | 'skip';
@@ -123,7 +124,8 @@ async function stepAuth(config: OnboardConfig, env: Record<string, string>): Pro
...(hasExistingAuth ? [{ value: 'keep', label: getAuthLabel(), hint: displayKey?.slice(0, 20) + '...' }] : []),
...(isLettaCloud ? [{ value: 'oauth', label: 'Login to Letta Platform', hint: 'Opens browser' }] : []),
{ value: 'apikey', label: 'Enter API Key manually', hint: 'Paste your key' },
{ value: 'skip', label: 'Skip', hint: 'Local server without auth' },
{ value: 'selfhosted', label: 'Enter self-hosted URL', hint: 'Local Letta server' },
{ value: 'skip', label: 'Skip', hint: 'Continue without auth' },
];
const authMethod = await p.select({
@@ -194,6 +196,22 @@ async function stepAuth(config: OnboardConfig, env: Record<string, string>): Pro
config.apiKey = apiKey;
env.LETTA_API_KEY = apiKey;
}
} else if (authMethod === 'selfhosted') {
const serverUrl = await p.text({
message: 'Letta server URL',
placeholder: 'http://localhost:8283',
initialValue: 'http://localhost:8283',
});
if (p.isCancel(serverUrl)) { p.cancel('Setup cancelled'); process.exit(0); }
const url = serverUrl || 'http://localhost:8283';
config.baseUrl = url;
env.LETTA_BASE_URL = url;
process.env.LETTA_BASE_URL = url; // Set immediately so model listing works
// Clear any cloud API key since we're using self-hosted
delete env.LETTA_API_KEY;
delete process.env.LETTA_API_KEY;
} else if (authMethod === 'keep') {
// For OAuth tokens, refresh if needed
if (existingTokens?.refreshToken) {
@@ -238,7 +256,7 @@ async function stepAuth(config: OnboardConfig, env: Record<string, string>): Pro
}
}
// Validate connection (only if not skipping auth)
// Validate connection (skip if 'skip' was chosen)
if (config.authMethod !== 'skip') {
const keyToValidate = config.apiKey || env.LETTA_API_KEY;
if (keyToValidate) {
@@ -246,11 +264,16 @@ async function stepAuth(config: OnboardConfig, env: Record<string, string>): Pro
}
const spinner = p.spinner();
spinner.start('Checking connection...');
const serverLabel = config.baseUrl || 'Letta Cloud';
spinner.start(`Checking connection to ${serverLabel}...`);
try {
const { testConnection } = await import('./tools/letta-api.js');
const ok = await testConnection();
spinner.stop(ok ? 'Connected to server' : 'Connection issue');
spinner.stop(ok ? `Connected to ${serverLabel}` : 'Connection issue');
if (!ok && config.authMethod === 'selfhosted') {
p.log.warn(`Could not connect to ${config.baseUrl}. Make sure the server is running.`);
}
} catch {
spinner.stop('Connection check skipped');
}
@@ -623,7 +646,8 @@ function showSummary(config: OnboardConfig): void {
keep: 'Keep existing',
oauth: 'OAuth login',
apikey: config.apiKey ? `API Key (${config.apiKey.slice(0, 10)}...)` : 'API Key',
skip: 'None (local server)',
selfhosted: config.baseUrl ? `Self-hosted (${config.baseUrl})` : 'Self-hosted',
skip: 'None',
}[config.authMethod];
lines.push(`Auth: ${authLabel}`);