diff --git a/packages/cli/src/client.ts b/packages/cli/src/client.ts index 337d3d7b..c9074d97 100644 --- a/packages/cli/src/client.ts +++ b/packages/cli/src/client.ts @@ -1,12 +1,12 @@ import { treaty } from '@elysiajs/eden' import { getApiUrl, getToken } from './config' -// 使用动态导入来避免类型错误 +// Use dynamic import to avoid type errors export function createClient() { const apiUrl = getApiUrl() const token = getToken() - // Eden Treaty 配置 + // Eden Treaty configuration const client = treaty(apiUrl, { headers: token ? { 'Authorization': `Bearer ${token}`, @@ -20,7 +20,7 @@ export function createClient() { export function requireAuth(): string { const token = getToken() if (!token) { - throw new Error('未登录,请先使用 "memohome auth login" 命令登录') + throw new Error('Not logged in. Please use "memohome auth login" to login first') } return token } diff --git a/packages/cli/src/commands/agent.ts b/packages/cli/src/commands/agent.ts index 7a85cc7e..a822caa5 100644 --- a/packages/cli/src/commands/agent.ts +++ b/packages/cli/src/commands/agent.ts @@ -2,12 +2,139 @@ import type { Command } from 'commander' import chalk from 'chalk' import { requireAuth, getApiUrl, getToken } from '../client' +export async function startInteractiveMode(options: { maxContextTime?: string; language?: string } = {}) { + try { + requireAuth() + const token = getToken()! + const apiUrl = getApiUrl() + + console.log(chalk.green.bold('🤖 MemoHome Agent Interactive Mode')) + console.log(chalk.dim('Type /exit or /quit to exit, type /help for help\n')) + + const { createInterface } = await import('readline') + const rl = createInterface({ + input: process.stdin, + output: process.stdout, + prompt: chalk.blue('You: '), + }) + + rl.prompt() + + rl.on('line', async (line: string) => { + const input = line.trim() + + if (input === '/exit' || input === '/quit') { + console.log(chalk.yellow('Goodbye! 👋')) + rl.close() + process.exit(0) + return + } + + if (input === '/help') { + console.log(chalk.green('\nAvailable commands:')) + console.log(chalk.dim(' /exit, /quit - Exit interactive mode')) + console.log(chalk.dim(' /help - Show help information\n')) + rl.prompt() + return + } + + if (!input) { + rl.prompt() + return + } + + try { + console.log(chalk.green('Agent: ')) + + const response = await fetch(`${apiUrl}/agent/stream`, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + message: input, + maxContextLoadTime: parseInt(options.maxContextTime || '60'), + language: options.language || 'Chinese', + }), + }) + + if (!response.ok) { + const errorData = await response.json() as { error?: string } + console.error(chalk.red('Chat failed:'), errorData.error || 'Unknown error') + rl.prompt() + return + } + + const reader = response.body?.getReader() + const decoder = new TextDecoder() + + if (!reader) { + throw new Error('Unable to read response stream') + } + + let buffer = '' + + while (true) { + const { done, value } = await reader.read() + if (done) break + + const chunk = decoder.decode(value, { stream: true }) + buffer += chunk + + const lines = buffer.split('\n') + buffer = lines.pop() || '' + + for (const line of lines) { + if (line.startsWith('data: ')) { + const data = line.slice(6).trim() + + if (data === '[DONE]') { + console.log('\n') + rl.prompt() + return + } + + try { + const event = JSON.parse(data) + + if (event.type === 'text-delta' && event.text) { + process.stdout.write(event.text) + } else if (event.type === 'tool-call') { + console.log(chalk.dim(`\n[🔧 ${event.toolName}]`)) + } else if (event.type === 'error') { + console.error(chalk.red('\n❌'), event.error) + } + } catch { + // Skip unparseable JSON + } + } + } + } + } catch (error) { + const message = error instanceof Error ? error.message : String(error) + console.error(chalk.red('Error:'), message) + rl.prompt() + } + }) + + rl.on('close', () => { + console.log(chalk.yellow('\nGoodbye! 👋')) + process.exit(0) + }) + } catch (error) { + const message = error instanceof Error ? error.message : String(error) + console.error(chalk.red('Error:'), message) + process.exit(1) + } +} + export function agentCommands(program: Command) { program .command('chat ') - .description('与 AI Agent 对话') - .option('-t, --max-context-time ', '上下文加载时间(分钟)', '60') - .option('-l, --language ', '回复语言', 'Chinese') + .description('Chat with AI Agent') + .option('-t, --max-context-time ', 'Context load time (minutes)', '60') + .option('-l, --language ', 'Response language', 'Chinese') .action(async (message, options) => { try { requireAuth() @@ -31,7 +158,7 @@ export function agentCommands(program: Command) { if (!response.ok) { const errorData = await response.json() as { error?: string } - console.error(chalk.red('对话失败:'), errorData.error || '未知错误') + console.error(chalk.red('Chat failed:'), errorData.error || 'Unknown error') process.exit(1) } @@ -39,7 +166,7 @@ export function agentCommands(program: Command) { const decoder = new TextDecoder() if (!reader) { - throw new Error('无法读取响应流') + throw new Error('Unable to read response stream') } let buffer = '' @@ -51,7 +178,7 @@ export function agentCommands(program: Command) { const chunk = decoder.decode(value, { stream: true }) buffer += chunk - // 按行处理 + // Process line by line const lines = buffer.split('\n') buffer = lines.pop() || '' @@ -70,19 +197,19 @@ export function agentCommands(program: Command) { if (event.type === 'text-delta' && event.text) { process.stdout.write(event.text) } else if (event.type === 'tool-call') { - console.log(chalk.dim(`\n[🔧 使用工具: ${event.toolName}]`)) + console.log(chalk.dim(`\n[🔧 Using tool: ${event.toolName}]`)) } else if (event.type === 'error') { - console.error(chalk.red('\n❌ 错误:'), event.error) + console.error(chalk.red('\n❌ Error:'), event.error) } } catch { - // 跳过无法解析的JSON + // Skip unparseable JSON } } } } } catch (error) { const message = error instanceof Error ? error.message : String(error) - console.error(chalk.red('错误:'), message) + console.error(chalk.red('Error:'), message) process.exit(1) } }) @@ -90,134 +217,11 @@ export function agentCommands(program: Command) { program .command('interactive') .alias('i') - .description('进入交互式对话模式') - .option('-t, --max-context-time ', '上下文加载时间(分钟)', '60') - .option('-l, --language ', '回复语言', 'Chinese') + .description('Enter interactive conversation mode') + .option('-t, --max-context-time ', 'Context load time (minutes)', '60') + .option('-l, --language ', 'Response language', 'Chinese') .action(async (options) => { - try { - requireAuth() - const token = getToken()! - const apiUrl = getApiUrl() - - console.log(chalk.green.bold('🤖 MemoHome Agent 交互模式')) - console.log(chalk.dim('输入 /exit 或 /quit 退出,输入 /help 查看帮助\n')) - - const { createInterface } = await import('readline') - const rl = createInterface({ - input: process.stdin, - output: process.stdout, - prompt: chalk.blue('You: '), - }) - - rl.prompt() - - rl.on('line', async (line: string) => { - const input = line.trim() - - if (input === '/exit' || input === '/quit') { - console.log(chalk.yellow('再见!👋')) - rl.close() - process.exit(0) - return - } - - if (input === '/help') { - console.log(chalk.green('\n可用命令:')) - console.log(chalk.dim(' /exit, /quit - 退出交互模式')) - console.log(chalk.dim(' /help - 显示帮助信息\n')) - rl.prompt() - return - } - - if (!input) { - rl.prompt() - return - } - - try { - console.log(chalk.green('Agent: ')) - - const response = await fetch(`${apiUrl}/agent/stream`, { - method: 'POST', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - message: input, - maxContextLoadTime: parseInt(options.maxContextTime), - language: options.language, - }), - }) - - if (!response.ok) { - const errorData = await response.json() as { error?: string } - console.error(chalk.red('对话失败:'), errorData.error || '未知错误') - rl.prompt() - return - } - - const reader = response.body?.getReader() - const decoder = new TextDecoder() - - if (!reader) { - throw new Error('无法读取响应流') - } - - let buffer = '' - - while (true) { - const { done, value } = await reader.read() - if (done) break - - const chunk = decoder.decode(value, { stream: true }) - buffer += chunk - - const lines = buffer.split('\n') - buffer = lines.pop() || '' - - for (const line of lines) { - if (line.startsWith('data: ')) { - const data = line.slice(6).trim() - - if (data === '[DONE]') { - console.log('\n') - rl.prompt() - return - } - - try { - const event = JSON.parse(data) - - if (event.type === 'text-delta' && event.text) { - process.stdout.write(event.text) - } else if (event.type === 'tool-call') { - console.log(chalk.dim(`\n[🔧 ${event.toolName}]`)) - } else if (event.type === 'error') { - console.error(chalk.red('\n❌'), event.error) - } - } catch { - // 跳过无法解析的JSON - } - } - } - } - } catch (error) { - const message = error instanceof Error ? error.message : String(error) - console.error(chalk.red('错误:'), message) - rl.prompt() - } - }) - - rl.on('close', () => { - console.log(chalk.yellow('\n再见!👋')) - process.exit(0) - }) - } catch (error) { - const message = error instanceof Error ? error.message : String(error) - console.error(chalk.red('错误:'), message) - process.exit(1) - } + await startInteractiveMode(options) }) } diff --git a/packages/cli/src/commands/auth.ts b/packages/cli/src/commands/auth.ts index 8f27cedd..f57ed89c 100644 --- a/packages/cli/src/commands/auth.ts +++ b/packages/cli/src/commands/auth.ts @@ -9,9 +9,9 @@ import { formatError } from '../utils' export function authCommands(program: Command) { program .command('login') - .description('登录到 MemoHome') - .option('-u, --username ', '用户名') - .option('-p, --password ', '密码') + .description('Login to MemoHome') + .option('-u, --username ', 'Username') + .option('-p, --password ', 'Password') .action(async (options) => { try { let username = options.username @@ -22,13 +22,13 @@ export function authCommands(program: Command) { { type: 'input', name: 'username', - message: '请输入用户名:', + message: 'Please enter username:', when: !username, }, { type: 'password', name: 'password', - message: '请输入密码:', + message: 'Please enter password:', when: !password, mask: '*', }, @@ -37,7 +37,7 @@ export function authCommands(program: Command) { password = password || answers.password } - const spinner = ora('正在登录...').start() + const spinner = ora('Logging in...').start() const client = createClient() const response = await client.auth.login.post({ @@ -46,7 +46,7 @@ export function authCommands(program: Command) { }) if (response.error) { - spinner.fail(chalk.red('登录失败')) + spinner.fail(chalk.red('Login failed')) console.error(chalk.red(formatError(response.error.value))) process.exit(1) } @@ -54,89 +54,89 @@ export function authCommands(program: Command) { const data = response.data as { success?: boolean; data?: { token?: string; user?: { username: string; role: string } } } | null if (data?.success && data?.data?.token && data?.data?.user) { setToken(data.data.token) - spinner.succeed(chalk.green('登录成功!')) - console.log(chalk.blue(`用户: ${data.data.user.username}`)) - console.log(chalk.blue(`角色: ${data.data.user.role}`)) + spinner.succeed(chalk.green('Login successful!')) + console.log(chalk.blue(`User: ${data.data.user.username}`)) + console.log(chalk.blue(`Role: ${data.data.user.role}`)) } else { - spinner.fail(chalk.red('登录失败')) - console.error(chalk.red('无效的响应格式')) + spinner.fail(chalk.red('Login failed')) + console.error(chalk.red('Invalid response format')) process.exit(1) } } catch (error) { const message = error instanceof Error ? error.message : String(error) - console.error(chalk.red('登录错误:'), message) + console.error(chalk.red('Login error:'), message) process.exit(1) } }) program .command('logout') - .description('登出当前用户') + .description('Logout current user') .action(() => { const token = getToken() if (!token) { - console.log(chalk.yellow('当前未登录')) + console.log(chalk.yellow('Not currently logged in')) return } clearToken() - console.log(chalk.green('✓ 已登出')) + console.log(chalk.green('✓ Logged out')) }) program .command('whoami') - .description('查看当前登录用户') + .description('View current logged in user') .action(async () => { try { const token = getToken() if (!token) { - console.log(chalk.yellow('当前未登录')) - console.log(chalk.dim('使用 "memohome auth login" 登录')) + console.log(chalk.yellow('Not currently logged in')) + console.log(chalk.dim('Use "memohome auth login" to login')) return } - const spinner = ora('获取用户信息...').start() + const spinner = ora('Fetching user information...').start() const client = createClient() const response = await client.auth.me.get() if (response.error) { - spinner.fail(chalk.red('获取用户信息失败')) + spinner.fail(chalk.red('Failed to fetch user information')) console.error(chalk.red(formatError(response.error.value))) process.exit(1) } const data = response.data as { success?: boolean; data?: { username: string; role: string; id: string } } | null if (data?.success && data?.data) { - spinner.succeed(chalk.green('已登录')) - console.log(chalk.blue(`用户名: ${data.data.username}`)) - console.log(chalk.blue(`角色: ${data.data.role}`)) - console.log(chalk.blue(`用户ID: ${data.data.id}`)) + spinner.succeed(chalk.green('Logged in')) + console.log(chalk.blue(`Username: ${data.data.username}`)) + console.log(chalk.blue(`Role: ${data.data.role}`)) + console.log(chalk.blue(`User ID: ${data.data.id}`)) } else { - spinner.fail(chalk.red('获取用户信息失败')) + spinner.fail(chalk.red('Failed to fetch user information')) } } catch (error) { const message = error instanceof Error ? error.message : String(error) - console.error(chalk.red('错误:'), message) + console.error(chalk.red('Error:'), message) process.exit(1) } }) program .command('config') - .description('查看或设置 API 配置') - .option('-s, --set ', '设置 API URL') + .description('View or set API configuration') + .option('-s, --set ', 'Set API URL') .action((options) => { if (options.set) { const url = options.set setApiUrl(url) - console.log(chalk.green(`✓ API URL 已设置为: ${url}`)) + console.log(chalk.green(`✓ API URL set to: ${url}`)) } else { const apiUrl = getApiUrl() const token = getToken() - console.log(chalk.blue('当前配置:')) + console.log(chalk.blue('Current configuration:')) console.log(chalk.dim(`API URL: ${apiUrl}`)) - console.log(chalk.dim(`已登录: ${token ? '是' : '否'}`)) + console.log(chalk.dim(`Logged in: ${token ? 'Yes' : 'No'}`)) } }) } diff --git a/packages/cli/src/commands/settings.ts b/packages/cli/src/commands/config.ts similarity index 55% rename from packages/cli/src/commands/settings.ts rename to packages/cli/src/commands/config.ts index 790cdeb8..e4215964 100644 --- a/packages/cli/src/commands/settings.ts +++ b/packages/cli/src/commands/config.ts @@ -4,20 +4,20 @@ import inquirer from 'inquirer' import ora from 'ora' import { createClient, requireAuth } from '../client' -export function settingsCommands(program: Command) { +export function configCommands(program: Command) { program .command('get') - .description('获取当前用户设置') + .description('Get current user settings') .action(async () => { try { requireAuth() - const spinner = ora('获取设置...').start() + const spinner = ora('Fetching settings...').start() const client = createClient() const response = await client.settings.get() if (response.error) { - spinner.fail(chalk.red('获取设置失败')) + spinner.fail(chalk.red('Failed to fetch settings')) console.error(chalk.red(response.error.value)) process.exit(1) } @@ -25,36 +25,36 @@ export function settingsCommands(program: Command) { const data = response.data as any if (data?.success && data?.data) { const settings = data.data - spinner.succeed(chalk.green('当前设置')) + spinner.succeed(chalk.green('Current Settings')) console.log() - console.log(chalk.blue('🎯 Agent 配置:')) - console.log(chalk.dim(` 语言: ${settings.language || '未设置'}`)) - console.log(chalk.dim(` 上下文加载时间: ${settings.maxContextLoadTime || '未设置'} 分钟`)) + console.log(chalk.blue('🎯 Agent Configuration:')) + console.log(chalk.dim(` Language: ${settings.language || 'Not set'}`)) + console.log(chalk.dim(` Context Load Time: ${settings.maxContextLoadTime || 'Not set'} minutes`)) console.log() - console.log(chalk.blue('🤖 默认模型:')) - console.log(chalk.dim(` 聊天模型ID: ${settings.defaultChatModel || '未设置'}`)) - console.log(chalk.dim(` 摘要模型ID: ${settings.defaultSummaryModel || '未设置'}`)) - console.log(chalk.dim(` 嵌入模型ID: ${settings.defaultEmbeddingModel || '未设置'}`)) + console.log(chalk.blue('🤖 Default Models:')) + console.log(chalk.dim(` Chat Model ID: ${settings.defaultChatModel || 'Not set'}`)) + console.log(chalk.dim(` Summary Model ID: ${settings.defaultSummaryModel || 'Not set'}`)) + console.log(chalk.dim(` Embedding Model ID: ${settings.defaultEmbeddingModel || 'Not set'}`)) console.log() - console.log(chalk.blue('📊 其他:')) - console.log(chalk.dim(` 用户ID: ${settings.userId}`)) - console.log(chalk.dim(` 创建时间: ${new Date(settings.createdAt).toLocaleString('zh-CN')}`)) - console.log(chalk.dim(` 更新时间: ${new Date(settings.updatedAt).toLocaleString('zh-CN')}`)) + console.log(chalk.blue('📊 Other:')) + console.log(chalk.dim(` User ID: ${settings.userId}`)) + console.log(chalk.dim(` Created At: ${new Date(settings.createdAt).toLocaleString('en-US')}`)) + console.log(chalk.dim(` Updated At: ${new Date(settings.updatedAt).toLocaleString('en-US')}`)) } } catch (error: any) { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) process.exit(1) } }) program .command('set') - .description('更新用户设置') - .option('--language ', '首选语言') - .option('--max-context-time ', '上下文加载时间(分钟)') - .option('--chat-model ', '默认聊天模型ID') - .option('--summary-model ', '默认摘要模型ID') - .option('--embedding-model ', '默认嵌入模型ID') + .description('Update user settings') + .option('--language ', 'Preferred language') + .option('--max-context-time ', 'Context load time (minutes)') + .option('--chat-model ', 'Default chat model ID') + .option('--summary-model ', 'Default summary model ID') + .option('--embedding-model ', 'Default embedding model ID') .action(async (options) => { try { requireAuth() @@ -70,8 +70,8 @@ export function settingsCommands(program: Command) { updates.defaultEmbeddingModel = options.embeddingModel if (Object.keys(updates).length === 0) { - console.log(chalk.yellow('未提供任何更新参数')) - console.log(chalk.dim('\n可用选项:')) + console.log(chalk.yellow('No update parameters provided')) + console.log(chalk.dim('\nAvailable options:')) console.log(chalk.dim(' --language ')) console.log(chalk.dim(' --max-context-time ')) console.log(chalk.dim(' --chat-model ')) @@ -80,57 +80,57 @@ export function settingsCommands(program: Command) { return } - const spinner = ora('更新设置...').start() + const spinner = ora('Updating settings...').start() const client = createClient() const response = await client.settings.put(updates) if (response.error) { - spinner.fail(chalk.red('更新设置失败')) + spinner.fail(chalk.red('Failed to update settings')) console.error(chalk.red(response.error.value)) process.exit(1) } const data = response.data as any if (data?.success) { - spinner.succeed(chalk.green('设置已更新')) + spinner.succeed(chalk.green('Settings updated')) console.log() - console.log(chalk.blue('更新的设置:')) + console.log(chalk.blue('Updated settings:')) Object.entries(updates).forEach(([key, value]) => { console.log(chalk.dim(` ${key}: ${value}`)) }) } } catch (error: any) { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) process.exit(1) } }) program .command('setup') - .description('交互式设置向导') + .description('Interactive settings wizard') .action(async () => { try { requireAuth() - console.log(chalk.green.bold('\n🎨 设置向导\n')) + console.log(chalk.green.bold('\n🎨 Settings Wizard\n')) const answers = await inquirer.prompt([ { type: 'input', name: 'language', - message: '首选语言:', + message: 'Preferred language:', default: 'Chinese', }, { type: 'number', name: 'maxContextLoadTime', - message: '上下文加载时间(分钟):', + message: 'Context load time (minutes):', default: 60, validate: (value) => { const num = parseInt(value) if (num < 1 || num > 1440) { - return '请输入 1-1440 之间的数字' + return 'Please enter a number between 1-1440' } return true }, @@ -138,21 +138,21 @@ export function settingsCommands(program: Command) { { type: 'input', name: 'defaultChatModel', - message: '默认聊天模型ID (留空跳过):', + message: 'Default chat model ID (leave empty to skip):', }, { type: 'input', name: 'defaultSummaryModel', - message: '默认摘要模型ID (留空跳过):', + message: 'Default summary model ID (leave empty to skip):', }, { type: 'input', name: 'defaultEmbeddingModel', - message: '默认嵌入模型ID (留空跳过):', + message: 'Default embedding model ID (leave empty to skip):', }, ]) - // 过滤掉空值 + // Filter out empty values const updates: any = {} Object.entries(answers).forEach(([key, value]) => { if (value) { @@ -160,20 +160,20 @@ export function settingsCommands(program: Command) { } }) - const spinner = ora('保存设置...').start() + const spinner = ora('Saving settings...').start() const client = createClient() const response = await client.settings.put(updates) if (response.error) { - spinner.fail(chalk.red('保存设置失败')) + spinner.fail(chalk.red('Failed to save settings')) console.error(chalk.red(response.error.value)) process.exit(1) } - spinner.succeed(chalk.green('设置已保存')) + spinner.succeed(chalk.green('Settings saved')) } catch (error: any) { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) process.exit(1) } }) diff --git a/packages/cli/src/commands/debug.ts b/packages/cli/src/commands/debug.ts index 18a2eaa4..12f46cd6 100644 --- a/packages/cli/src/commands/debug.ts +++ b/packages/cli/src/commands/debug.ts @@ -6,20 +6,20 @@ import { getApiUrl, getToken } from '../client' export function debugCommands(program: Command) { program .command('ping') - .description('测试 API 服务器连接') + .description('Test API server connection') .action(async () => { const apiUrl = getApiUrl() const token = getToken() - console.log(chalk.blue('连接信息:')) + console.log(chalk.blue('Connection Info:')) console.log(chalk.dim(` API URL: ${apiUrl}`)) - console.log(chalk.dim(` Token: ${token ? '已设置' : '未设置'}`)) + console.log(chalk.dim(` Token: ${token ? 'Set' : 'Not set'}`)) console.log() - const spinner = ora('正在连接...').start() + const spinner = ora('Connecting...').start() try { - // 尝试直接 fetch + // Try direct fetch const controller = new AbortController() const timeoutId = setTimeout(() => controller.abort(), 5000) @@ -33,20 +33,20 @@ export function debugCommands(program: Command) { clearTimeout(timeoutId) if (response.ok) { - spinner.succeed(chalk.green('连接成功!')) + spinner.succeed(chalk.green('Connection successful!')) const text = await response.text() - console.log(chalk.dim('响应:'), text.substring(0, 100)) + console.log(chalk.dim('Response:'), text.substring(0, 100)) } else { - spinner.fail(chalk.red(`连接失败: HTTP ${response.status}`)) + spinner.fail(chalk.red(`Connection failed: HTTP ${response.status}`)) } } catch (error) { - spinner.fail(chalk.red('连接失败')) + spinner.fail(chalk.red('Connection failed')) if (error instanceof Error) { if (error.name === 'AbortError') { - console.error(chalk.yellow('连接超时 (5秒)')) - console.error(chalk.dim('请检查 API 服务器是否正在运行')) + console.error(chalk.yellow('Connection timeout (5 seconds)')) + console.error(chalk.dim('Please check if the API server is running')) } else { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) } } } diff --git a/packages/cli/src/commands/memory.ts b/packages/cli/src/commands/memory.ts index 34a31237..50e83f68 100644 --- a/packages/cli/src/commands/memory.ts +++ b/packages/cli/src/commands/memory.ts @@ -7,12 +7,12 @@ import { createClient, requireAuth } from '../client' export function memoryCommands(program: Command) { program .command('search ') - .description('搜索记忆') - .option('-l, --limit ', '返回结果数量', '10') + .description('Search memories') + .option('-l, --limit ', 'Number of results to return', '10') .action(async (query, options) => { try { requireAuth() - const spinner = ora('搜索记忆...').start() + const spinner = ora('Searching memories...').start() const client = createClient() const response = await client.memory.search.get({ @@ -23,40 +23,40 @@ export function memoryCommands(program: Command) { }) if (response.error) { - spinner.fail(chalk.red('搜索失败')) + spinner.fail(chalk.red('Search failed')) console.error(chalk.red(response.error.value)) process.exit(1) } const data = response.data as any if (data?.success && data?.data) { - spinner.succeed(chalk.green(`找到 ${data.data.length} 条记忆`)) + spinner.succeed(chalk.green(`Found ${data.data.length} memories`)) if (data.data.length === 0) { - console.log(chalk.yellow('未找到相关记忆')) + console.log(chalk.yellow('No related memories found')) return } data.data.forEach((item: any, index: number) => { console.log() - console.log(chalk.blue(`[${index + 1}] 相似度: ${(item.similarity * 100).toFixed(2)}%`)) - console.log(chalk.dim(`时间: ${new Date(item.timestamp).toLocaleString('zh-CN')}`)) + console.log(chalk.blue(`[${index + 1}] Similarity: ${(item.similarity * 100).toFixed(2)}%`)) + console.log(chalk.dim(`Time: ${new Date(item.timestamp).toLocaleString('en-US')}`)) console.log(chalk.white(item.content)) }) } } catch (error: any) { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) process.exit(1) } }) program .command('add ') - .description('添加记忆') + .description('Add memory') .action(async (content) => { try { requireAuth() - const spinner = ora('添加记忆...').start() + const spinner = ora('Adding memory...').start() const client = createClient() const response = await client.memory.post({ @@ -64,17 +64,17 @@ export function memoryCommands(program: Command) { }) if (response.error) { - spinner.fail(chalk.red('添加记忆失败')) + spinner.fail(chalk.red('Failed to add memory')) console.error(chalk.red(response.error.value)) process.exit(1) } const data = response.data as any if (data?.success) { - spinner.succeed(chalk.green('记忆已添加')) + spinner.succeed(chalk.green('Memory added')) } } catch (error: any) { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) process.exit(1) } }) @@ -82,13 +82,13 @@ export function memoryCommands(program: Command) { program .command('messages') .alias('msg') - .description('获取消息历史') - .option('-p, --page ', '页码', '1') - .option('-l, --limit ', '每页数量', '20') + .description('Get message history') + .option('-p, --page ', 'Page number', '1') + .option('-l, --limit ', 'Items per page', '20') .action(async (options) => { try { requireAuth() - const spinner = ora('获取消息历史...').start() + const spinner = ora('Fetching message history...').start() const client = createClient() const response = await client.memory.message.get({ @@ -99,7 +99,7 @@ export function memoryCommands(program: Command) { }) if (response.error) { - spinner.fail(chalk.red('获取消息失败')) + spinner.fail(chalk.red('Failed to fetch messages')) console.error(chalk.red(response.error.value)) process.exit(1) } @@ -107,46 +107,46 @@ export function memoryCommands(program: Command) { const data = response.data as any if (data?.success && data?.data) { const { messages, pagination } = data.data - spinner.succeed(chalk.green(`消息历史 (${pagination.page}/${pagination.totalPages} 页)`)) + spinner.succeed(chalk.green(`Message History (Page ${pagination.page}/${pagination.totalPages})`)) if (messages.length === 0) { - console.log(chalk.yellow('暂无消息')) + console.log(chalk.yellow('No messages')) return } - console.log(chalk.dim(`\n总计: ${pagination.total} 条消息\n`)) + console.log(chalk.dim(`\nTotal: ${pagination.total} messages\n`)) messages.forEach((msg: any) => { const roleColor = msg.role === 'user' ? chalk.blue : chalk.green const roleIcon = msg.role === 'user' ? '👤' : '🤖' console.log(roleColor(`${roleIcon} ${msg.role.toUpperCase()}`)) - console.log(chalk.dim(new Date(msg.timestamp).toLocaleString('zh-CN'))) + console.log(chalk.dim(new Date(msg.timestamp).toLocaleString('en-US'))) console.log(chalk.white(msg.content)) console.log() }) } } catch (error: any) { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) process.exit(1) } }) program .command('filter') - .description('按日期范围过滤消息') - .option('-s, --start ', '开始日期 (ISO 8601)') - .option('-e, --end ', '结束日期 (ISO 8601)') + .description('Filter messages by date range') + .option('-s, --start ', 'Start date (ISO 8601)') + .option('-e, --end ', 'End date (ISO 8601)') .action(async (options) => { try { requireAuth() if (!options.start || !options.end) { - console.error(chalk.red('请提供开始和结束日期')) - console.log(chalk.dim('示例: memohome memory filter -s 2024-01-01T00:00:00Z -e 2024-12-31T23:59:59Z')) + console.error(chalk.red('Please provide start and end dates')) + console.log(chalk.dim('Example: memohome memory filter -s 2024-01-01T00:00:00Z -e 2024-12-31T23:59:59Z')) process.exit(1) } - const spinner = ora('过滤消息...').start() + const spinner = ora('Filtering messages...').start() const client = createClient() const response = await client.memory.message.filter.get({ @@ -157,17 +157,17 @@ export function memoryCommands(program: Command) { }) if (response.error) { - spinner.fail(chalk.red('过滤消息失败')) + spinner.fail(chalk.red('Failed to filter messages')) console.error(chalk.red(response.error.value)) process.exit(1) } const data = response.data as any if (data?.success && data?.data) { - spinner.succeed(chalk.green(`找到 ${data.data.length} 条消息`)) + spinner.succeed(chalk.green(`Found ${data.data.length} messages`)) if (data.data.length === 0) { - console.log(chalk.yellow('未找到消息')) + console.log(chalk.yellow('No messages found')) return } @@ -177,13 +177,13 @@ export function memoryCommands(program: Command) { const roleColor = msg.role === 'user' ? chalk.blue : chalk.green const roleIcon = msg.role === 'user' ? '👤' : '🤖' console.log(roleColor(`${roleIcon} ${msg.role.toUpperCase()}`)) - console.log(chalk.dim(new Date(msg.timestamp).toLocaleString('zh-CN'))) + console.log(chalk.dim(new Date(msg.timestamp).toLocaleString('en-US'))) console.log(chalk.white(msg.content)) console.log() }) } } catch (error: any) { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) process.exit(1) } }) diff --git a/packages/cli/src/commands/model.ts b/packages/cli/src/commands/model.ts index 4e0041d7..8761a29b 100644 --- a/packages/cli/src/commands/model.ts +++ b/packages/cli/src/commands/model.ts @@ -10,9 +10,9 @@ import { formatError } from '../utils' export function modelCommands(program: Command) { program .command('list') - .description('列出所有模型配置') + .description('List all model configurations') .action(async () => { - const spinner = ora('获取模型列表...').start() + const spinner = ora('Fetching model list...').start() try { requireAuth() const client = createClient() @@ -20,24 +20,24 @@ export function modelCommands(program: Command) { const response = await client.model.get() if (response.error) { - spinner.fail(chalk.red('获取模型列表失败')) + spinner.fail(chalk.red('Failed to fetch model list')) console.error(chalk.red(formatError(response.error.value))) process.exit(1) } - // API 返回格式: { success, items, pagination } + // API response format: { success, items, pagination } const data = response.data as { success?: boolean; items?: Model[]; pagination?: unknown } | null if (data?.success && data?.items) { - spinner.succeed(chalk.green('模型列表')) + spinner.succeed(chalk.green('Model List')) const models = data.items if (models.length === 0) { - console.log(chalk.yellow('暂无模型配置')) + console.log(chalk.yellow('No model configurations found')) return } const tableData = [ - ['ID', '名称', '模型ID', '类型', '客户端'], + ['ID', 'Name', 'Model ID', 'Type', 'Client'], ...models.map((item: unknown) => { const modelItem = item as { id: string; model: Model } return [ @@ -53,19 +53,19 @@ export function modelCommands(program: Command) { console.log(table(tableData)) } } catch (error) { - spinner.fail(chalk.red('操作失败')) + spinner.fail(chalk.red('Operation failed')) if (error instanceof Error) { if (error.name === 'AbortError' || error.name === 'TimeoutError') { const { getApiUrl: getUrl } = await import('../config') - console.error(chalk.red('连接超时,请检查:')) - console.error(chalk.yellow(' 1. API 服务器是否正在运行')) - console.error(chalk.yellow(' 2. API 地址是否正确')) - console.error(chalk.dim(` 当前配置: ${getUrl()}`)) + console.error(chalk.red('Connection timeout, please check:')) + console.error(chalk.yellow(' 1. Is the API server running?')) + console.error(chalk.yellow(' 2. Is the API URL correct?')) + console.error(chalk.dim(` Current config: ${getUrl()}`)) } else { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) } } else { - console.error(chalk.red('错误:'), String(error)) + console.error(chalk.red('Error:'), String(error)) } process.exit(1) } @@ -73,16 +73,16 @@ export function modelCommands(program: Command) { program .command('create') - .description('创建模型配置') - .option('-n, --name ', '模型名称') - .option('-m, --model-id ', '模型ID') + .description('Create model configuration') + .option('-n, --name ', 'Model name') + .option('-m, --model-id ', 'Model ID') .option('-u, --base-url ', 'API Base URL') .option('-k, --api-key ', 'API Key') - .option('-c, --client-type ', '客户端类型 (openai/anthropic/google)') - .option('-t, --type ', '模型类型 (chat/embedding)', 'chat') - .option('-d, --dimensions ', 'Embedding 维度 (仅 embedding 类型需要)') + .option('-c, --client-type ', 'Client type (openai/anthropic/google)') + .option('-t, --type ', 'Model type (chat/embedding)', 'chat') + .option('-d, --dimensions ', 'Embedding dimensions (required for embedding type)') .action(async (options) => { - const spinner = ora('创建模型配置...').start() + const spinner = ora('Creating model configuration...').start() try { requireAuth() @@ -93,13 +93,13 @@ export function modelCommands(program: Command) { { type: 'input', name: 'name', - message: '模型名称:', + message: 'Model name:', when: !name, }, { type: 'input', name: 'modelId', - message: '模型ID (如 gpt-4 或 text-embedding-3-small):', + message: 'Model ID (e.g., gpt-4 or text-embedding-3-small):', when: !modelId, }, { @@ -119,7 +119,7 @@ export function modelCommands(program: Command) { { type: 'list', name: 'clientType', - message: '客户端类型:', + message: 'Client type:', choices: ['openai', 'anthropic', 'google'], default: 'openai', when: !clientType, @@ -127,7 +127,7 @@ export function modelCommands(program: Command) { { type: 'list', name: 'type', - message: '模型类型:', + message: 'Model type:', choices: ['chat', 'embedding'], default: 'chat', when: !type, @@ -142,23 +142,23 @@ export function modelCommands(program: Command) { type = type || answers.type } - // 如果是 embedding 类型,需要 dimensions + // If embedding type, dimensions is required if (type === 'embedding' && !dimensions) { const answer = await inquirer.prompt([ { type: 'number', name: 'dimensions', - message: 'Embedding 维度 (如 1536):', + message: 'Embedding dimensions (e.g., 1536):', validate: (value: number) => { if (value > 0) return true - return '维度必须是正整数' + return 'Dimensions must be a positive integer' }, }, ]) dimensions = answer.dimensions } - spinner.text = '创建模型配置...' + spinner.text = 'Creating model configuration...' const client = createClient() const payload: Record = { @@ -170,10 +170,10 @@ export function modelCommands(program: Command) { type, } - // 如果是 embedding 类型,添加 dimensions + // If embedding type, add dimensions if (type === 'embedding') { if (!dimensions) { - console.error(chalk.red('Embedding 模型需要指定 dimensions')) + console.error(chalk.red('Embedding models require dimensions to be specified')) process.exit(1) } payload.dimensions = typeof dimensions === 'number' ? dimensions : parseInt(dimensions) @@ -182,36 +182,36 @@ export function modelCommands(program: Command) { const response = await client.model.post(payload) if (response.error) { - spinner.fail(chalk.red('创建模型配置失败')) + spinner.fail(chalk.red('Failed to create model configuration')) console.error(chalk.red(response.error.value)) process.exit(1) } const data = response.data as ApiResponse | null if (data?.success && data?.data) { - spinner.succeed(chalk.green('模型配置创建成功')) - console.log(chalk.blue(`名称: ${data.data.name}`)) - console.log(chalk.blue(`模型ID: ${data.data.modelId}`)) - console.log(chalk.blue(`类型: ${data.data.type || 'chat'}`)) + spinner.succeed(chalk.green('Model configuration created successfully')) + console.log(chalk.blue(`Name: ${data.data.name}`)) + console.log(chalk.blue(`Model ID: ${data.data.modelId}`)) + console.log(chalk.blue(`Type: ${data.data.type || 'chat'}`)) if (data.data.type === 'embedding' && data.data.dimensions) { - console.log(chalk.blue(`维度: ${data.data.dimensions}`)) + console.log(chalk.blue(`Dimensions: ${data.data.dimensions}`)) } console.log(chalk.blue(`ID: ${data.data.id}`)) } } catch (error) { - spinner.fail(chalk.red('操作失败')) + spinner.fail(chalk.red('Operation failed')) if (error instanceof Error) { if (error.name === 'AbortError' || error.name === 'TimeoutError') { const { getApiUrl: getUrl } = await import('../config') - console.error(chalk.red('连接超时,请检查:')) - console.error(chalk.yellow(' 1. API 服务器是否正在运行')) - console.error(chalk.yellow(' 2. API 地址是否正确')) - console.error(chalk.dim(` 当前配置: ${getUrl()}`)) + console.error(chalk.red('Connection timeout, please check:')) + console.error(chalk.yellow(' 1. Is the API server running?')) + console.error(chalk.yellow(' 2. Is the API URL correct?')) + console.error(chalk.dim(` Current config: ${getUrl()}`)) } else { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) } } else { - console.error(chalk.red('错误:'), String(error)) + console.error(chalk.red('Error:'), String(error)) } process.exit(1) } @@ -219,7 +219,7 @@ export function modelCommands(program: Command) { program .command('delete ') - .description('删除模型配置') + .description('Delete model configuration') .action(async (id) => { let spinner: ReturnType | undefined try { @@ -229,42 +229,42 @@ export function modelCommands(program: Command) { { type: 'confirm', name: 'confirm', - message: chalk.yellow(`确定要删除模型配置 ${id} 吗?`), + message: chalk.yellow(`Are you sure you want to delete model configuration ${id}?`), default: false, }, ]) if (!confirm) { - console.log(chalk.yellow('已取消')) + console.log(chalk.yellow('Cancelled')) return } - spinner = ora('删除模型配置...').start() + spinner = ora('Deleting model configuration...').start() const client = createClient() const response = await client.model({ id }).delete() if (response.error) { - spinner.fail(chalk.red('删除模型配置失败')) + spinner.fail(chalk.red('Failed to delete model configuration')) console.error(chalk.red(response.error.value)) process.exit(1) } - if (spinner) spinner.succeed(chalk.green('模型配置已删除')) + if (spinner) spinner.succeed(chalk.green('Model configuration deleted')) } catch (error) { - if (spinner) spinner.fail(chalk.red('操作失败')) + if (spinner) spinner.fail(chalk.red('Operation failed')) if (error instanceof Error) { if (error.name === 'AbortError' || error.name === 'TimeoutError') { const { getApiUrl: getUrl } = await import('../config') - console.error(chalk.red('连接超时,请检查:')) - console.error(chalk.yellow(' 1. API 服务器是否正在运行')) - console.error(chalk.yellow(' 2. API 地址是否正确')) - console.error(chalk.dim(` 当前配置: ${getUrl()}`)) + console.error(chalk.red('Connection timeout, please check:')) + console.error(chalk.yellow(' 1. Is the API server running?')) + console.error(chalk.yellow(' 2. Is the API URL correct?')) + console.error(chalk.dim(` Current config: ${getUrl()}`)) } else { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) } } else { - console.error(chalk.red('错误:'), String(error)) + console.error(chalk.red('Error:'), String(error)) } process.exit(1) } @@ -272,9 +272,9 @@ export function modelCommands(program: Command) { program .command('get ') - .description('获取模型配置详情') + .description('Get model configuration details') .action(async (id) => { - const spinner = ora('获取模型配置...').start() + const spinner = ora('Fetching model configuration...').start() try { requireAuth() const client = createClient() @@ -282,7 +282,7 @@ export function modelCommands(program: Command) { const response = await client.model({ id }).get() if (response.error) { - spinner.fail(chalk.red('获取模型配置失败')) + spinner.fail(chalk.red('Failed to fetch model configuration')) console.error(chalk.red(response.error.value)) process.exit(1) } @@ -290,32 +290,32 @@ export function modelCommands(program: Command) { const data = response.data as ApiResponse | null if (data?.success && data?.data) { const model = data.data - spinner.succeed(chalk.green('模型配置')) + spinner.succeed(chalk.green('Model Configuration')) console.log(chalk.blue(`ID: ${model.id}`)) - console.log(chalk.blue(`名称: ${model.name}`)) - console.log(chalk.blue(`模型ID: ${model.modelId}`)) - console.log(chalk.blue(`类型: ${model.type || 'chat'}`)) + console.log(chalk.blue(`Name: ${model.name}`)) + console.log(chalk.blue(`Model ID: ${model.modelId}`)) + console.log(chalk.blue(`Type: ${model.type || 'chat'}`)) if (model.type === 'embedding' && model.dimensions) { - console.log(chalk.blue(`维度: ${model.dimensions}`)) + console.log(chalk.blue(`Dimensions: ${model.dimensions}`)) } console.log(chalk.blue(`Base URL: ${model.baseUrl}`)) - console.log(chalk.blue(`客户端类型: ${model.clientType}`)) - console.log(chalk.blue(`创建时间: ${new Date(model.createdAt).toLocaleString('zh-CN')}`)) + console.log(chalk.blue(`Client Type: ${model.clientType}`)) + console.log(chalk.blue(`Created At: ${new Date(model.createdAt).toLocaleString('en-US')}`)) } } catch (error) { - spinner.fail(chalk.red('操作失败')) + spinner.fail(chalk.red('Operation failed')) if (error instanceof Error) { if (error.name === 'AbortError' || error.name === 'TimeoutError') { const { getApiUrl: getUrl } = await import('../config') - console.error(chalk.red('连接超时,请检查:')) - console.error(chalk.yellow(' 1. API 服务器是否正在运行')) - console.error(chalk.yellow(' 2. API 地址是否正确')) - console.error(chalk.dim(` 当前配置: ${getUrl()}`)) + console.error(chalk.red('Connection timeout, please check:')) + console.error(chalk.yellow(' 1. Is the API server running?')) + console.error(chalk.yellow(' 2. Is the API URL correct?')) + console.error(chalk.dim(` Current config: ${getUrl()}`)) } else { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) } } else { - console.error(chalk.red('错误:'), String(error)) + console.error(chalk.red('Error:'), String(error)) } process.exit(1) } @@ -323,9 +323,9 @@ export function modelCommands(program: Command) { program .command('defaults') - .description('查看默认模型配置') + .description('View default model configurations') .action(async () => { - const spinner = ora('获取默认模型配置...').start() + const spinner = ora('Fetching default model configurations...').start() try { requireAuth() const client = createClient() @@ -338,19 +338,19 @@ export function modelCommands(program: Command) { spinner.stop() - console.log(chalk.green.bold('默认模型配置:')) + console.log(chalk.green.bold('Default Model Configurations:')) console.log() // Chat Model const chatData = chatRes.data as ApiResponse | null if (chatData?.success && chatData.data) { const model = chatData.data - console.log(chalk.blue('💬 聊天模型:')) - console.log(chalk.dim(` 名称: ${model.name}`)) - console.log(chalk.dim(` 模型ID: ${model.modelId}`)) + console.log(chalk.blue('💬 Chat Model:')) + console.log(chalk.dim(` Name: ${model.name}`)) + console.log(chalk.dim(` Model ID: ${model.modelId}`)) console.log(chalk.dim(` ID: ${model.id}`)) } else { - console.log(chalk.yellow('💬 聊天模型: 未配置')) + console.log(chalk.yellow('💬 Chat Model: Not configured')) } console.log() @@ -358,12 +358,12 @@ export function modelCommands(program: Command) { const summaryData = summaryRes.data as ApiResponse | null if (summaryData?.success && summaryData.data) { const model = summaryData.data - console.log(chalk.blue('📝 摘要模型:')) - console.log(chalk.dim(` 名称: ${model.name}`)) - console.log(chalk.dim(` 模型ID: ${model.modelId}`)) + console.log(chalk.blue('📝 Summary Model:')) + console.log(chalk.dim(` Name: ${model.name}`)) + console.log(chalk.dim(` Model ID: ${model.modelId}`)) console.log(chalk.dim(` ID: ${model.id}`)) } else { - console.log(chalk.yellow('📝 摘要模型: 未配置')) + console.log(chalk.yellow('📝 Summary Model: Not configured')) } console.log() @@ -371,27 +371,27 @@ export function modelCommands(program: Command) { const embeddingData = embeddingRes.data as ApiResponse | null if (embeddingData?.success && embeddingData.data) { const model = embeddingData.data - console.log(chalk.blue('🔍 嵌入模型:')) - console.log(chalk.dim(` 名称: ${model.name}`)) - console.log(chalk.dim(` 模型ID: ${model.modelId}`)) + console.log(chalk.blue('🔍 Embedding Model:')) + console.log(chalk.dim(` Name: ${model.name}`)) + console.log(chalk.dim(` Model ID: ${model.modelId}`)) console.log(chalk.dim(` ID: ${model.id}`)) } else { - console.log(chalk.yellow('🔍 嵌入模型: 未配置')) + console.log(chalk.yellow('🔍 Embedding Model: Not configured')) } } catch (error) { - spinner.fail(chalk.red('操作失败')) + spinner.fail(chalk.red('Operation failed')) if (error instanceof Error) { if (error.name === 'AbortError' || error.name === 'TimeoutError') { const { getApiUrl: getUrl } = await import('../config') - console.error(chalk.red('连接超时,请检查:')) - console.error(chalk.yellow(' 1. API 服务器是否正在运行')) - console.error(chalk.yellow(' 2. API 地址是否正确')) - console.error(chalk.dim(` 当前配置: ${getUrl()}`)) + console.error(chalk.red('Connection timeout, please check:')) + console.error(chalk.yellow(' 1. Is the API server running?')) + console.error(chalk.yellow(' 2. Is the API URL correct?')) + console.error(chalk.dim(` Current config: ${getUrl()}`)) } else { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) } } else { - console.error(chalk.red('错误:'), String(error)) + console.error(chalk.red('Error:'), String(error)) } process.exit(1) } diff --git a/packages/cli/src/commands/schedule.ts b/packages/cli/src/commands/schedule.ts index d960dba9..a3b7f330 100644 --- a/packages/cli/src/commands/schedule.ts +++ b/packages/cli/src/commands/schedule.ts @@ -8,57 +8,57 @@ import { createClient, requireAuth } from '../client' export function scheduleCommands(program: Command) { program .command('list') - .description('列出所有定时任务') + .description('List all scheduled tasks') .action(async () => { try { requireAuth() - const spinner = ora('获取定时任务列表...').start() + const spinner = ora('Fetching scheduled tasks list...').start() const client = createClient() const response = await client.schedule.get() if (response.error) { - spinner.fail(chalk.red('获取定时任务列表失败')) + spinner.fail(chalk.red('Failed to fetch scheduled tasks list')) console.error(chalk.red(response.error.value)) process.exit(1) } const data = response.data as any if (data?.success && data?.data) { - spinner.succeed(chalk.green('定时任务列表')) + spinner.succeed(chalk.green('Scheduled Tasks List')) const schedules = data.data if (schedules.length === 0) { - console.log(chalk.yellow('暂无定时任务')) + console.log(chalk.yellow('No scheduled tasks')) return } const tableData = [ - ['ID', '标题', 'Cron', '启用', '创建时间'], + ['ID', 'Title', 'Cron', 'Enabled', 'Created At'], ...schedules.map((schedule: any) => [ schedule.id.substring(0, 8) + '...', schedule.title, schedule.cronExpression, - schedule.enabled ? chalk.green('是') : chalk.red('否'), - new Date(schedule.createdAt).toLocaleString('zh-CN'), + schedule.enabled ? chalk.green('Yes') : chalk.red('No'), + new Date(schedule.createdAt).toLocaleString('en-US'), ]), ] console.log(table(tableData)) } } catch (error: any) { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) process.exit(1) } }) program .command('create') - .description('创建定时任务') - .option('-t, --title ', '任务标题') - .option('-d, --description <description>', '任务描述') - .option('-c, --cron <expression>', 'Cron 表达式') - .option('-e, --enabled', '启用任务', false) + .description('Create scheduled task') + .option('-t, --title <title>', 'Task title') + .option('-d, --description <description>', 'Task description') + .option('-c, --cron <expression>', 'Cron expression') + .option('-e, --enabled', 'Enable task', false) .action(async (options) => { try { requireAuth() @@ -70,25 +70,25 @@ export function scheduleCommands(program: Command) { { type: 'input', name: 'title', - message: '任务标题:', + message: 'Task title:', when: !title, }, { type: 'input', name: 'description', - message: '任务描述 (可选):', + message: 'Task description (optional):', when: !description, }, { type: 'input', name: 'cron', - message: 'Cron 表达式 (如: 0 9 * * *):', + message: 'Cron expression (e.g., 0 9 * * *):', when: !cron, }, { type: 'confirm', name: 'enabled', - message: '启用任务?', + message: 'Enable task?', default: false, when: enabled === undefined, }, @@ -100,7 +100,7 @@ export function scheduleCommands(program: Command) { enabled = enabled !== undefined ? enabled : answers.enabled } - const spinner = ora('创建定时任务...').start() + const spinner = ora('Creating scheduled task...').start() const client = createClient() const payload: any = { @@ -116,37 +116,37 @@ export function scheduleCommands(program: Command) { const response = await client.schedule.post(payload) if (response.error) { - spinner.fail(chalk.red('创建定时任务失败')) + spinner.fail(chalk.red('Failed to create scheduled task')) console.error(chalk.red(response.error.value)) process.exit(1) } const data = response.data as any if (data?.success && data?.data) { - spinner.succeed(chalk.green('定时任务创建成功')) - console.log(chalk.blue(`标题: ${data.data.title}`)) + spinner.succeed(chalk.green('Scheduled task created successfully')) + console.log(chalk.blue(`Title: ${data.data.title}`)) console.log(chalk.blue(`Cron: ${data.data.cronExpression}`)) console.log(chalk.blue(`ID: ${data.data.id}`)) } } catch (error: any) { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) process.exit(1) } }) program .command('get <id>') - .description('获取定时任务详情') + .description('Get scheduled task details') .action(async (id) => { try { requireAuth() - const spinner = ora('获取定时任务详情...').start() + const spinner = ora('Fetching scheduled task details...').start() const client = createClient() const response = await client.schedule({ id }).get() if (response.error) { - spinner.fail(chalk.red('获取定时任务失败')) + spinner.fail(chalk.red('Failed to fetch scheduled task')) console.error(chalk.red(response.error.value)) process.exit(1) } @@ -154,36 +154,36 @@ export function scheduleCommands(program: Command) { const data = response.data as any if (data?.success && data?.data) { const schedule = data.data - spinner.succeed(chalk.green('定时任务详情')) + spinner.succeed(chalk.green('Scheduled Task Details')) console.log(chalk.blue(`ID: ${schedule.id}`)) - console.log(chalk.blue(`标题: ${schedule.title}`)) + console.log(chalk.blue(`Title: ${schedule.title}`)) if (schedule.description) { - console.log(chalk.blue(`描述: ${schedule.description}`)) + console.log(chalk.blue(`Description: ${schedule.description}`)) } console.log(chalk.blue(`Cron: ${schedule.cronExpression}`)) console.log( - chalk.blue(`启用: ${schedule.enabled ? chalk.green('是') : chalk.red('否')}`) + chalk.blue(`Enabled: ${schedule.enabled ? chalk.green('Yes') : chalk.red('No')}`) ) console.log( - chalk.blue(`创建时间: ${new Date(schedule.createdAt).toLocaleString('zh-CN')}`) + chalk.blue(`Created At: ${new Date(schedule.createdAt).toLocaleString('en-US')}`) ) console.log( - chalk.blue(`更新时间: ${new Date(schedule.updatedAt).toLocaleString('zh-CN')}`) + chalk.blue(`Updated At: ${new Date(schedule.updatedAt).toLocaleString('en-US')}`) ) } } catch (error: any) { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) process.exit(1) } }) program .command('update <id>') - .description('更新定时任务') - .option('-t, --title <title>', '任务标题') - .option('-d, --description <description>', '任务描述') - .option('-c, --cron <expression>', 'Cron 表达式') - .option('-e, --enabled <boolean>', '启用任务 (true/false)') + .description('Update scheduled task') + .option('-t, --title <title>', 'Task title') + .option('-d, --description <description>', 'Task description') + .option('-c, --cron <expression>', 'Cron expression') + .option('-e, --enabled <boolean>', 'Enable task (true/false)') .action(async (id, options) => { try { requireAuth() @@ -198,31 +198,31 @@ export function scheduleCommands(program: Command) { } if (Object.keys(updates).length === 0) { - console.log(chalk.yellow('未提供任何更新参数')) + console.log(chalk.yellow('No update parameters provided')) return } - const spinner = ora('更新定时任务...').start() + const spinner = ora('Updating scheduled task...').start() const client = createClient() const response = await client.schedule({ id }).put(updates) if (response.error) { - spinner.fail(chalk.red('更新定时任务失败')) + spinner.fail(chalk.red('Failed to update scheduled task')) console.error(chalk.red(response.error.value)) process.exit(1) } - spinner.succeed(chalk.green('定时任务已更新')) + spinner.succeed(chalk.green('Scheduled task updated')) } catch (error: any) { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) process.exit(1) } }) program .command('delete <id>') - .description('删除定时任务') + .description('Delete scheduled task') .action(async (id) => { try { requireAuth() @@ -231,48 +231,48 @@ export function scheduleCommands(program: Command) { { type: 'confirm', name: 'confirm', - message: chalk.yellow(`确定要删除定时任务 ${id} 吗?`), + message: chalk.yellow(`Are you sure you want to delete scheduled task ${id}?`), default: false, }, ]) if (!confirm) { - console.log(chalk.yellow('已取消')) + console.log(chalk.yellow('Cancelled')) return } - const spinner = ora('删除定时任务...').start() + const spinner = ora('Deleting scheduled task...').start() const client = createClient() const response = await client.schedule({ id }).delete() if (response.error) { - spinner.fail(chalk.red('删除定时任务失败')) + spinner.fail(chalk.red('Failed to delete scheduled task')) console.error(chalk.red(response.error.value)) process.exit(1) } - spinner.succeed(chalk.green('定时任务已删除')) + spinner.succeed(chalk.green('Scheduled task deleted')) } catch (error: any) { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) process.exit(1) } }) program .command('toggle <id>') - .description('切换定时任务启用状态') + .description('Toggle scheduled task enabled status') .action(async (id) => { try { requireAuth() - const spinner = ora('切换任务状态...').start() + const spinner = ora('Toggling task status...').start() const client = createClient() - // 首先获取当前状态 + // First get current status const getResponse = await client.schedule({ id }).get() if (getResponse.error) { - spinner.fail(chalk.red('获取任务失败')) + spinner.fail(chalk.red('Failed to fetch task')) console.error(chalk.red(getResponse.error.value)) process.exit(1) } @@ -281,23 +281,23 @@ export function scheduleCommands(program: Command) { if (getData?.success && getData?.data) { const currentEnabled = getData.data.enabled - // 更新状态 + // Update status const updateResponse = await client.schedule({ id }).put({ enabled: !currentEnabled, }) if (updateResponse.error) { - spinner.fail(chalk.red('更新任务失败')) + spinner.fail(chalk.red('Failed to update task')) console.error(chalk.red(updateResponse.error.value)) process.exit(1) } spinner.succeed( - chalk.green(`任务已${!currentEnabled ? '启用' : '禁用'}`) + chalk.green(`Task ${!currentEnabled ? 'enabled' : 'disabled'}`) ) } } catch (error: any) { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) process.exit(1) } }) diff --git a/packages/cli/src/commands/user.ts b/packages/cli/src/commands/user.ts index b4727805..1787a27f 100644 --- a/packages/cli/src/commands/user.ts +++ b/packages/cli/src/commands/user.ts @@ -8,55 +8,55 @@ import { createClient, requireAuth } from '../client' export function userCommands(program: Command) { program .command('list') - .description('列出所有用户 (需要管理员权限)') + .description('List all users (requires admin privileges)') .action(async () => { try { requireAuth() - const spinner = ora('获取用户列表...').start() + const spinner = ora('Fetching user list...').start() const client = createClient() const response = await client.user.get() if (response.error) { - spinner.fail(chalk.red('获取用户列表失败')) + spinner.fail(chalk.red('Failed to fetch user list')) console.error(chalk.red(response.error.value)) process.exit(1) } const data = response.data as any if (data?.success && data?.data) { - spinner.succeed(chalk.green('用户列表')) + spinner.succeed(chalk.green('User List')) const users = data.data if (users.length === 0) { - console.log(chalk.yellow('暂无用户')) + console.log(chalk.yellow('No users')) return } const tableData = [ - ['ID', '用户名', '角色', '创建时间'], + ['ID', 'Username', 'Role', 'Created At'], ...users.map((user: any) => [ user.id, user.username, - user.role === 'admin' ? chalk.red('管理员') : chalk.blue('用户'), - new Date(user.createdAt).toLocaleString('zh-CN'), + user.role === 'admin' ? chalk.red('Admin') : chalk.blue('User'), + new Date(user.createdAt).toLocaleString('en-US'), ]), ] console.log(table(tableData)) } } catch (error: any) { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) process.exit(1) } }) program .command('create') - .description('创建新用户 (需要管理员权限)') - .option('-u, --username <username>', '用户名') - .option('-p, --password <password>', '密码') - .option('-r, --role <role>', '角色 (user/admin)', 'user') + .description('Create new user (requires admin privileges)') + .option('-u, --username <username>', 'Username') + .option('-p, --password <password>', 'Password') + .option('-r, --role <role>', 'Role (user/admin)', 'user') .action(async (options) => { try { requireAuth() @@ -70,20 +70,20 @@ export function userCommands(program: Command) { { type: 'input', name: 'username', - message: '用户名:', + message: 'Username:', when: !username, }, { type: 'password', name: 'password', - message: '密码:', + message: 'Password:', when: !password, mask: '*', }, { type: 'list', name: 'role', - message: '角色:', + message: 'Role:', choices: ['user', 'admin'], default: 'user', when: !role, @@ -94,7 +94,7 @@ export function userCommands(program: Command) { role = role || answers.role } - const spinner = ora('创建用户...').start() + const spinner = ora('Creating user...').start() const client = createClient() const response = await client.user.post({ @@ -104,27 +104,27 @@ export function userCommands(program: Command) { }) if (response.error) { - spinner.fail(chalk.red('创建用户失败')) + spinner.fail(chalk.red('Failed to create user')) console.error(chalk.red(response.error.value)) process.exit(1) } const data = response.data as any if (data?.success && data?.data) { - spinner.succeed(chalk.green('用户创建成功')) - console.log(chalk.blue(`用户名: ${data.data.username}`)) - console.log(chalk.blue(`角色: ${data.data.role}`)) + spinner.succeed(chalk.green('User created successfully')) + console.log(chalk.blue(`Username: ${data.data.username}`)) + console.log(chalk.blue(`Role: ${data.data.role}`)) console.log(chalk.blue(`ID: ${data.data.id}`)) } } catch (error: any) { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) process.exit(1) } }) program .command('delete <id>') - .description('删除用户 (需要管理员权限)') + .description('Delete user (requires admin privileges)') .action(async (id) => { try { requireAuth() @@ -133,47 +133,47 @@ export function userCommands(program: Command) { { type: 'confirm', name: 'confirm', - message: chalk.yellow(`确定要删除用户 ${id} 吗?`), + message: chalk.yellow(`Are you sure you want to delete user ${id}?`), default: false, }, ]) if (!confirm) { - console.log(chalk.yellow('已取消')) + console.log(chalk.yellow('Cancelled')) return } - const spinner = ora('删除用户...').start() + const spinner = ora('Deleting user...').start() const client = createClient() const response = await client.user({ id }).delete() if (response.error) { - spinner.fail(chalk.red('删除用户失败')) + spinner.fail(chalk.red('Failed to delete user')) console.error(chalk.red(response.error.value)) process.exit(1) } - spinner.succeed(chalk.green('用户已删除')) + spinner.succeed(chalk.green('User deleted')) } catch (error: any) { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) process.exit(1) } }) program .command('get <id>') - .description('获取用户详情') + .description('Get user details') .action(async (id) => { try { requireAuth() - const spinner = ora('获取用户信息...').start() + const spinner = ora('Fetching user information...').start() const client = createClient() const response = await client.user({ id }).get() if (response.error) { - spinner.fail(chalk.red('获取用户信息失败')) + spinner.fail(chalk.red('Failed to fetch user information')) console.error(chalk.red(response.error.value)) process.exit(1) } @@ -181,22 +181,22 @@ export function userCommands(program: Command) { const data = response.data as any if (data?.success && data?.data) { const user = data.data - spinner.succeed(chalk.green('用户信息')) + spinner.succeed(chalk.green('User Information')) console.log(chalk.blue(`ID: ${user.id}`)) - console.log(chalk.blue(`用户名: ${user.username}`)) - console.log(chalk.blue(`角色: ${user.role}`)) - console.log(chalk.blue(`创建时间: ${new Date(user.createdAt).toLocaleString('zh-CN')}`)) + console.log(chalk.blue(`Username: ${user.username}`)) + console.log(chalk.blue(`Role: ${user.role}`)) + console.log(chalk.blue(`Created At: ${new Date(user.createdAt).toLocaleString('en-US')}`)) } } catch (error: any) { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) process.exit(1) } }) program .command('update-password <id>') - .description('更新用户密码 (需要管理员权限)') - .option('-p, --password <password>', '新密码') + .description('Update user password (requires admin privileges)') + .option('-p, --password <password>', 'New password') .action(async (id, options) => { try { requireAuth() @@ -208,14 +208,14 @@ export function userCommands(program: Command) { { type: 'password', name: 'password', - message: '新密码:', + message: 'New password:', mask: '*', }, ]) password = answers.password } - const spinner = ora('更新密码...').start() + const spinner = ora('Updating password...').start() const client = createClient() const response = await client.user({ id }).password.patch({ @@ -223,14 +223,14 @@ export function userCommands(program: Command) { }) if (response.error) { - spinner.fail(chalk.red('更新密码失败')) + spinner.fail(chalk.red('Failed to update password')) console.error(chalk.red(response.error.value)) process.exit(1) } - spinner.succeed(chalk.green('密码已更新')) + spinner.succeed(chalk.green('Password updated')) } catch (error: any) { - console.error(chalk.red('错误:'), error.message) + console.error(chalk.red('Error:'), error.message) process.exit(1) } }) diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 890f5685..69f29153 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -5,9 +5,9 @@ import chalk from 'chalk' import { authCommands } from './commands/auth' import { userCommands } from './commands/user' import { modelCommands } from './commands/model' -import { agentCommands } from './commands/agent' +import { agentCommands, startInteractiveMode } from './commands/agent' import { memoryCommands } from './commands/memory' -import { settingsCommands } from './commands/settings' +import { configCommands } from './commands/config' import { scheduleCommands } from './commands/schedule' import { debugCommands } from './commands/debug' @@ -15,40 +15,48 @@ const program = new Command() program .name('memohome') - .description(chalk.bold.blue('🏠 MemoHome CLI - 智能记忆管理助手')) + .description(chalk.bold.blue('🏠 MemoHome Agent')) .version('1.0.0') -// 认证命令 -const auth = program.command('auth').description('用户认证管理') +// Authentication commands +const auth = program.command('auth').description('User authentication management') authCommands(auth) -// 用户管理命令 -const user = program.command('user').description('用户管理 (需要管理员权限)') +// User management commands +const user = program.command('user').description('User management (requires admin privileges)') userCommands(user) -// 模型管理命令 -const model = program.command('model').description('AI 模型配置管理') +// Model management commands +const model = program.command('model').description('AI model configuration management') modelCommands(model) -// Agent 对话命令 -const agent = program.command('agent').description('与 AI Agent 对话') +// Agent conversation commands +const agent = program.command('agent').description('Chat with AI Agent') agentCommands(agent) -// 记忆管理命令 -const memory = program.command('memory').description('记忆管理') +// Memory management commands +const memory = program.command('memory').description('Memory management') memoryCommands(memory) -// 设置管理命令 -const settings = program.command('settings').description('用户设置管理') -settingsCommands(settings) +// Config management commands +const config = program.command('config').description('User configuration management') +configCommands(config) -// 日程管理命令 -const schedule = program.command('schedule').description('日程管理') +// Schedule management commands +const schedule = program.command('schedule').description('Schedule management') scheduleCommands(schedule) -// 调试命令 -const debug = program.command('debug').description('调试工具') +// Debug commands +const debug = program.command('debug').description('Debug tools') debugCommands(debug) -program.parse() +// If no arguments provided, start interactive mode +if (process.argv.length === 2) { + startInteractiveMode().catch((error) => { + console.error('Failed to start interactive mode:', error) + process.exit(1) + }) +} else { + program.parse() +} diff --git a/packages/cli/src/types.ts b/packages/cli/src/types.ts index b3504236..7930f7ed 100644 --- a/packages/cli/src/types.ts +++ b/packages/cli/src/types.ts @@ -1,4 +1,4 @@ -// API 响应类型定义 +// API response type definitions export interface ApiResponse<T = unknown> { success?: boolean diff --git a/packages/cli/src/utils.ts b/packages/cli/src/utils.ts index 5666b2b4..a616062d 100644 --- a/packages/cli/src/utils.ts +++ b/packages/cli/src/utils.ts @@ -1,11 +1,11 @@ import chalk from 'chalk' /** - * 格式化 API 错误信息 + * Format API error information */ export function formatError(error: unknown): string { if (error === null || error === undefined) { - return '未知错误' + return 'Unknown error' } if (typeof error === 'string') { @@ -13,7 +13,7 @@ export function formatError(error: unknown): string { } if (typeof error === 'object') { - // 尝试提取常见的错误字段 + // Try to extract common error fields const errorObj = error as Record<string, unknown> if ('message' in errorObj && typeof errorObj.message === 'string') { @@ -24,12 +24,12 @@ export function formatError(error: unknown): string { return errorObj.error } - // 如果有 status 和 statusText + // If status and statusText exist if ('status' in errorObj && 'statusText' in errorObj) { return `${errorObj.status} ${errorObj.statusText}` } - // 否则返回格式化的 JSON + // Otherwise return formatted JSON try { return JSON.stringify(error, null, 2) } catch { @@ -41,7 +41,7 @@ export function formatError(error: unknown): string { } /** - * 打印错误并退出 + * Print error and exit */ export function exitWithError(message: string, error?: unknown): never { console.error(chalk.red(message)) @@ -52,12 +52,12 @@ export function exitWithError(message: string, error?: unknown): never { } /** - * 处理 Eden Treaty 响应错误 + * Handle Eden Treaty response errors */ -export function handleApiError(response: { error?: { value: unknown } }, defaultMessage = '操作失败'): never { +export function handleApiError(response: { error?: { value: unknown } }, defaultMessage = 'Operation failed'): never { if (response.error) { exitWithError(defaultMessage, response.error.value) } - exitWithError(defaultMessage, '未知错误') + exitWithError(defaultMessage, 'Unknown error') }