mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-25 07:00:48 +09:00
feat: improve client
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
+141
-137
@@ -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 <message>')
|
||||
.description('与 AI Agent 对话')
|
||||
.option('-t, --max-context-time <minutes>', '上下文加载时间(分钟)', '60')
|
||||
.option('-l, --language <language>', '回复语言', 'Chinese')
|
||||
.description('Chat with AI Agent')
|
||||
.option('-t, --max-context-time <minutes>', 'Context load time (minutes)', '60')
|
||||
.option('-l, --language <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 <minutes>', '上下文加载时间(分钟)', '60')
|
||||
.option('-l, --language <language>', '回复语言', 'Chinese')
|
||||
.description('Enter interactive conversation mode')
|
||||
.option('-t, --max-context-time <minutes>', 'Context load time (minutes)', '60')
|
||||
.option('-l, --language <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)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@ import { formatError } from '../utils'
|
||||
export function authCommands(program: Command) {
|
||||
program
|
||||
.command('login')
|
||||
.description('登录到 MemoHome')
|
||||
.option('-u, --username <username>', '用户名')
|
||||
.option('-p, --password <password>', '密码')
|
||||
.description('Login to MemoHome')
|
||||
.option('-u, --username <username>', 'Username')
|
||||
.option('-p, --password <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 <url>', '设置 API URL')
|
||||
.description('View or set API configuration')
|
||||
.option('-s, --set <url>', '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'}`))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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 <language>', '首选语言')
|
||||
.option('--max-context-time <minutes>', '上下文加载时间(分钟)')
|
||||
.option('--chat-model <id>', '默认聊天模型ID')
|
||||
.option('--summary-model <id>', '默认摘要模型ID')
|
||||
.option('--embedding-model <id>', '默认嵌入模型ID')
|
||||
.description('Update user settings')
|
||||
.option('--language <language>', 'Preferred language')
|
||||
.option('--max-context-time <minutes>', 'Context load time (minutes)')
|
||||
.option('--chat-model <id>', 'Default chat model ID')
|
||||
.option('--summary-model <id>', 'Default summary model ID')
|
||||
.option('--embedding-model <id>', '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 <language>'))
|
||||
console.log(chalk.dim(' --max-context-time <minutes>'))
|
||||
console.log(chalk.dim(' --chat-model <id>'))
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@ import { createClient, requireAuth } from '../client'
|
||||
export function memoryCommands(program: Command) {
|
||||
program
|
||||
.command('search <query>')
|
||||
.description('搜索记忆')
|
||||
.option('-l, --limit <limit>', '返回结果数量', '10')
|
||||
.description('Search memories')
|
||||
.option('-l, --limit <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 <content>')
|
||||
.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 <page>', '页码', '1')
|
||||
.option('-l, --limit <limit>', '每页数量', '20')
|
||||
.description('Get message history')
|
||||
.option('-p, --page <page>', 'Page number', '1')
|
||||
.option('-l, --limit <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 <date>', '开始日期 (ISO 8601)')
|
||||
.option('-e, --end <date>', '结束日期 (ISO 8601)')
|
||||
.description('Filter messages by date range')
|
||||
.option('-s, --start <date>', 'Start date (ISO 8601)')
|
||||
.option('-e, --end <date>', '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)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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 <name>', '模型名称')
|
||||
.option('-m, --model-id <modelId>', '模型ID')
|
||||
.description('Create model configuration')
|
||||
.option('-n, --name <name>', 'Model name')
|
||||
.option('-m, --model-id <modelId>', 'Model ID')
|
||||
.option('-u, --base-url <baseUrl>', 'API Base URL')
|
||||
.option('-k, --api-key <apiKey>', 'API Key')
|
||||
.option('-c, --client-type <clientType>', '客户端类型 (openai/anthropic/google)')
|
||||
.option('-t, --type <type>', '模型类型 (chat/embedding)', 'chat')
|
||||
.option('-d, --dimensions <dimensions>', 'Embedding 维度 (仅 embedding 类型需要)')
|
||||
.option('-c, --client-type <clientType>', 'Client type (openai/anthropic/google)')
|
||||
.option('-t, --type <type>', 'Model type (chat/embedding)', 'chat')
|
||||
.option('-d, --dimensions <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<string, unknown> = {
|
||||
@@ -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<Model> | 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 <id>')
|
||||
.description('删除模型配置')
|
||||
.description('Delete model configuration')
|
||||
.action(async (id) => {
|
||||
let spinner: ReturnType<typeof ora> | 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 <id>')
|
||||
.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<Model> | 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<Model> | 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<Model> | 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<Model> | 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)
|
||||
}
|
||||
|
||||
@@ -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 <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)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
|
||||
+29
-21
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// API 响应类型定义
|
||||
// API response type definitions
|
||||
|
||||
export interface ApiResponse<T = unknown> {
|
||||
success?: boolean
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user