Files
lettabot/src/setup.ts
Sarah Wooders 22770e6e88 Initial commit - LettaBot multi-channel AI assistant
Co-authored-by: Cameron Pfiffer <cameron@pfiffer.org>
Co-authored-by: Caren Thomas <carenthomas@gmail.com>
Co-authored-by: Charles Packer <packercharles@gmail.com>
Co-authored-by: Sarah Wooders <sarahwooders@gmail.com>
2026-01-28 18:02:51 -08:00

210 lines
5.4 KiB
TypeScript

#!/usr/bin/env npx tsx
/**
* LettaBot Setup Wizard
*
* Interactive setup wizard
* Run with: npx tsx src/setup.ts
*/
import * as p from '@clack/prompts';
import { existsSync, writeFileSync, readFileSync } from 'node:fs';
import { spawn } from 'node:child_process';
import { resolve } from 'node:path';
const ENV_PATH = resolve(process.cwd(), '.env');
const ENV_EXAMPLE_PATH = resolve(process.cwd(), '.env.example');
interface SetupConfig {
telegramToken: string;
lettaApiKey: string;
allowedUsers: string;
workingDir: string;
}
async function main() {
console.clear();
p.intro('🤖 LettaBot Setup');
// Check if already configured
if (existsSync(ENV_PATH)) {
const overwrite = await p.confirm({
message: '.env already exists. Overwrite?',
initialValue: false,
});
if (p.isCancel(overwrite) || !overwrite) {
p.outro('Setup cancelled. Your existing config is preserved.');
process.exit(0);
}
}
// Security notice
p.note(
[
'LettaBot can execute tools on your machine (files, commands).',
'Only allow trusted users to interact with it.',
'',
'Recommended: Set ALLOWED_USERS to restrict access.',
].join('\n'),
'Security Note'
);
const proceed = await p.confirm({
message: 'I understand the security implications. Continue?',
initialValue: true,
});
if (p.isCancel(proceed) || !proceed) {
p.outro('Setup cancelled.');
process.exit(0);
}
// Collect configuration
const config = await p.group(
{
telegramToken: () => p.text({
message: 'Telegram Bot Token',
placeholder: 'Get from @BotFather on Telegram',
validate: (v) => {
if (!v) return 'Token is required';
if (!v.includes(':')) return 'Invalid token format (should contain ":")';
},
}),
lettaApiKey: () => p.text({
message: 'Letta API Key',
placeholder: 'Get from app.letta.com or leave empty for local server',
validate: () => undefined, // Optional
}),
allowedUsers: () => p.text({
message: 'Allowed Telegram User IDs (comma-separated, empty = all)',
placeholder: '123456789,987654321',
initialValue: '',
validate: (v) => {
if (!v) return undefined;
const ids = v.split(',').map(s => s.trim());
for (const id of ids) {
if (!/^\d+$/.test(id)) return `Invalid user ID: ${id}`;
}
},
}),
workingDir: () => p.text({
message: 'Working directory for agent workspaces',
initialValue: '/tmp/lettabot',
}),
},
{
onCancel: () => {
p.cancel('Setup cancelled.');
process.exit(0);
},
}
);
// Build .env content
const envContent = `# LettaBot Configuration
# Generated by setup wizard
# Required: Telegram Bot Token (from @BotFather)
TELEGRAM_BOT_TOKEN=${config.telegramToken}
# Letta API Key (from app.letta.com, or leave empty for local server)
LETTA_API_KEY=${config.lettaApiKey || ''}
# Optional: Letta server URL (default: https://api.letta.com)
# LETTA_BASE_URL=https://api.letta.com
# Security: Comma-separated Telegram user IDs (empty = allow all)
ALLOWED_USERS=${config.allowedUsers || ''}
# Working directory for user workspaces
WORKING_DIR=${config.workingDir || '/tmp/lettabot'}
# Optional: Default model
# DEFAULT_MODEL=claude-sonnet-4-20250514
`;
// Write .env
const s = p.spinner();
s.start('Writing configuration...');
try {
writeFileSync(ENV_PATH, envContent);
s.stop('Configuration saved to .env');
} catch (e) {
s.stop('Failed to write .env');
p.log.error(String(e));
process.exit(1);
}
// Validate Telegram token
s.start('Validating Telegram token...');
try {
const response = await fetch(`https://api.telegram.org/bot${config.telegramToken}/getMe`);
const data = await response.json() as { ok: boolean; result?: { username: string } };
if (data.ok && data.result) {
s.stop(`Telegram bot validated: @${data.result.username}`);
} else {
s.stop('Telegram token validation failed');
p.log.warn('Token might be invalid - check @BotFather');
}
} catch (e) {
s.stop('Could not validate Telegram token (network error)');
}
// Check Letta CLI (installed via @letta-ai/letta-code-sdk dependency chain)
s.start('Checking Letta Code CLI...');
try {
require.resolve('@letta-ai/letta-code/letta.js');
s.stop('Letta Code CLI found');
} catch {
s.stop('Letta Code CLI not found');
p.log.warn('Try running: npm install');
}
// Ask to start
const startNow = await p.confirm({
message: 'Start LettaBot now?',
initialValue: true,
});
if (p.isCancel(startNow)) {
p.outro('Setup complete! Run `npm run dev` to start.');
process.exit(0);
}
if (startNow) {
p.outro('Starting LettaBot...\n');
// Start the bot
const child = spawn('npx', ['tsx', 'src/index.ts'], {
stdio: 'inherit',
cwd: process.cwd(),
});
child.on('error', (e) => {
console.error('Failed to start:', e);
process.exit(1);
});
} else {
p.outro([
'Setup complete!',
'',
'Start the bot with:',
' npm run dev',
'',
'Or for production:',
' npm run build && npm start',
].join('\n'));
}
}
main().catch((e) => {
console.error('Setup failed:', e);
process.exit(1);
});