mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-25 07:00:48 +09:00
feat: cli
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
"db:migrate": "pnpm --filter @memohome/db migrate",
|
||||
"db:generate": "pnpm --filter @memohome/db generate",
|
||||
"db:studio": "pnpm --filter @memohome/db studio",
|
||||
"cli": "pnpm --filter @memohome/cli start",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"test": "vitest"
|
||||
|
||||
Binary file not shown.
@@ -11,20 +11,51 @@ export const validateUser = async (username: string, password: string) => {
|
||||
const rootUser = process.env.ROOT_USER
|
||||
const rootPassword = process.env.ROOT_USER_PASSWORD
|
||||
|
||||
let userId: string | null = null
|
||||
|
||||
if (rootUser && rootPassword && username === rootUser) {
|
||||
if (password === rootPassword) {
|
||||
// 检查 root 用户是否存在于数据库中
|
||||
const [existingUser] = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.username, rootUser))
|
||||
|
||||
userId = existingUser?.id
|
||||
if (!existingUser) {
|
||||
// 为 root 用户创建数据库记录
|
||||
// 使用占位符密码哈希,因为实际密码在环境变量中
|
||||
const [newUser] = await db
|
||||
.insert(users)
|
||||
.values({
|
||||
username: rootUser,
|
||||
passwordHash: 'ENV_BASED_AUTH', // 占位符,实际使用环境变量验证
|
||||
role: 'admin',
|
||||
displayName: 'Root User',
|
||||
email: null,
|
||||
avatarUrl: null,
|
||||
isActive: true,
|
||||
})
|
||||
.onConflictDoNothing() // 避免并发创建导致的冲突
|
||||
.returning({
|
||||
id: users.id,
|
||||
})
|
||||
|
||||
userId = newUser.id
|
||||
}
|
||||
|
||||
// 检查 root 用户的 settings 是否存在,不存在则创建
|
||||
const [existingSettings] = await db
|
||||
.select()
|
||||
.from(settings)
|
||||
.where(eq(settings.userId, 'root'))
|
||||
.where(eq(settings.userId, userId))
|
||||
|
||||
if (!existingSettings) {
|
||||
// 为 root 用户创建默认 settings
|
||||
await db
|
||||
.insert(settings)
|
||||
.values({
|
||||
userId: 'root',
|
||||
userId: userId,
|
||||
defaultChatModel: null,
|
||||
defaultEmbeddingModel: null,
|
||||
defaultSummaryModel: null,
|
||||
@@ -36,7 +67,7 @@ export const validateUser = async (username: string, password: string) => {
|
||||
|
||||
// 返回 ROOT 用户信息
|
||||
return {
|
||||
id: 'root',
|
||||
id: userId,
|
||||
username: rootUser,
|
||||
role: 'admin' as const,
|
||||
displayName: 'Root User',
|
||||
@@ -49,7 +80,7 @@ export const validateUser = async (username: string, password: string) => {
|
||||
const [user] = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.username, username))
|
||||
.where(eq(users.id, userId!))
|
||||
|
||||
if (!user) {
|
||||
return null
|
||||
|
||||
@@ -94,6 +94,7 @@ export const createSchedule = async (
|
||||
userId: string,
|
||||
data: CreateScheduleInput
|
||||
) => {
|
||||
const { scheduleTask } = createScheduler()
|
||||
const [newSchedule] = await db
|
||||
.insert(schedule)
|
||||
.values({
|
||||
@@ -107,6 +108,15 @@ export const createSchedule = async (
|
||||
})
|
||||
.returning()
|
||||
|
||||
scheduleTask(userId, {
|
||||
id: newSchedule.id!,
|
||||
pattern: newSchedule.pattern,
|
||||
name: newSchedule.name,
|
||||
description: newSchedule.description,
|
||||
command: newSchedule.command,
|
||||
maxCalls: newSchedule.maxCalls || undefined,
|
||||
})
|
||||
|
||||
return newSchedule
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": ["../../eslint.config.mjs"],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-explicit-any": "off"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# dependencies (bun install)
|
||||
node_modules
|
||||
|
||||
# output
|
||||
out
|
||||
dist
|
||||
*.tgz
|
||||
|
||||
# code coverage
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# logs
|
||||
logs
|
||||
_.log
|
||||
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# caches
|
||||
.eslintcache
|
||||
.cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# IntelliJ based IDEs
|
||||
.idea
|
||||
|
||||
# Finder (MacOS) folder config
|
||||
.DS_Store
|
||||
+338
-1
@@ -1 +1,338 @@
|
||||
# @memohome/shared
|
||||
# @memohome/cli
|
||||
|
||||
MemoHome 的命令行工具,使用 Elysia Eden 与 API 服务器通信。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 🔐 **用户认证** - 登录、登出、查看当前用户
|
||||
- 👥 **用户管理** - 完整的用户 CRUD 操作(管理员)
|
||||
- 🤖 **模型管理** - AI 模型配置管理
|
||||
- 💬 **Agent 对话** - 与 AI Agent 进行对话,支持流式响应
|
||||
- 🧠 **记忆管理** - 搜索、添加、查看对话记忆
|
||||
- ⚙️ **设置管理** - 个性化配置
|
||||
- 📅 **日程管理** - 定时任务管理
|
||||
|
||||
## 安装
|
||||
|
||||
```bash
|
||||
# 在项目根目录
|
||||
pnpm install
|
||||
|
||||
# 进入 CLI 目录
|
||||
cd packages/cli
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 配置 API 地址(可选)
|
||||
|
||||
默认连接到 `http://localhost:7002`,如需修改:
|
||||
|
||||
```bash
|
||||
bun run src/index.ts auth config --set http://your-api-url:port
|
||||
```
|
||||
|
||||
### 2. 登录
|
||||
|
||||
```bash
|
||||
bun run src/index.ts auth login
|
||||
# 或直接提供用户名和密码
|
||||
bun run src/index.ts auth login -u admin -p password
|
||||
```
|
||||
|
||||
### 3. 开始使用
|
||||
|
||||
```bash
|
||||
# 查看帮助
|
||||
bun run src/index.ts --help
|
||||
|
||||
# 与 Agent 对话
|
||||
bun run src/index.ts agent chat "你好"
|
||||
|
||||
# 进入交互模式
|
||||
bun run src/index.ts agent interactive
|
||||
```
|
||||
|
||||
## 命令参考
|
||||
|
||||
### 认证命令 (`auth`)
|
||||
|
||||
```bash
|
||||
# 登录
|
||||
memohome auth login [-u username] [-p password]
|
||||
|
||||
# 登出
|
||||
memohome auth logout
|
||||
|
||||
# 查看当前登录用户
|
||||
memohome auth whoami
|
||||
|
||||
# 查看/设置 API 配置
|
||||
memohome auth config [--set <url>]
|
||||
```
|
||||
|
||||
### 用户管理 (`user`) 🔒 需要管理员权限
|
||||
|
||||
```bash
|
||||
# 列出所有用户
|
||||
memohome user list
|
||||
|
||||
# 创建用户
|
||||
memohome user create [-u username] [-p password] [-r role]
|
||||
|
||||
# 获取用户详情
|
||||
memohome user get <id>
|
||||
|
||||
# 删除用户
|
||||
memohome user delete <id>
|
||||
|
||||
# 更新用户密码
|
||||
memohome user update-password <id> [-p password]
|
||||
```
|
||||
|
||||
### 模型管理 (`model`)
|
||||
|
||||
```bash
|
||||
# 列出所有模型
|
||||
memohome model list
|
||||
|
||||
# 创建聊天模型配置
|
||||
memohome model create \
|
||||
-n "GPT-4" \
|
||||
-m "gpt-4" \
|
||||
-u "https://api.openai.com/v1" \
|
||||
-k "sk-xxx" \
|
||||
-c "openai" \
|
||||
-t "chat"
|
||||
|
||||
# 创建 Embedding 模型配置
|
||||
memohome model create \
|
||||
-n "Text Embedding 3 Small" \
|
||||
-m "text-embedding-3-small" \
|
||||
-u "https://api.openai.com/v1" \
|
||||
-k "sk-xxx" \
|
||||
-c "openai" \
|
||||
-t "embedding" \
|
||||
-d 1536
|
||||
|
||||
# 获取模型详情
|
||||
memohome model get <id>
|
||||
|
||||
# 删除模型
|
||||
memohome model delete <id>
|
||||
|
||||
# 查看默认模型配置
|
||||
memohome model defaults
|
||||
```
|
||||
|
||||
### Agent 对话 (`agent`)
|
||||
|
||||
```bash
|
||||
# 发送单条消息
|
||||
memohome agent chat "你好,介绍一下你自己" \
|
||||
[-t 60] \
|
||||
[-l Chinese]
|
||||
|
||||
# 进入交互模式
|
||||
memohome agent interactive
|
||||
memohome agent i # 简写
|
||||
|
||||
# 交互模式命令:
|
||||
# /exit, /quit - 退出
|
||||
# /help - 帮助
|
||||
```
|
||||
|
||||
### 记忆管理 (`memory`)
|
||||
|
||||
```bash
|
||||
# 搜索记忆
|
||||
memohome memory search "关键词" [-l 10]
|
||||
|
||||
# 添加记忆
|
||||
memohome memory add "这是一条记忆"
|
||||
|
||||
# 查看消息历史
|
||||
memohome memory messages [-p 1] [-l 20]
|
||||
memohome memory msg # 简写
|
||||
|
||||
# 按日期过滤消息
|
||||
memohome memory filter \
|
||||
-s 2024-01-01T00:00:00Z \
|
||||
-e 2024-12-31T23:59:59Z
|
||||
```
|
||||
|
||||
### 设置管理 (`settings`)
|
||||
|
||||
```bash
|
||||
# 查看当前设置
|
||||
memohome settings get
|
||||
|
||||
# 更新设置
|
||||
memohome settings set \
|
||||
[--language Chinese] \
|
||||
[--max-context-time 60] \
|
||||
[--chat-model <id>] \
|
||||
[--summary-model <id>] \
|
||||
[--embedding-model <id>]
|
||||
|
||||
# 交互式设置向导
|
||||
memohome settings setup
|
||||
```
|
||||
|
||||
### 日程管理 (`schedule`)
|
||||
|
||||
```bash
|
||||
# 列出所有定时任务
|
||||
memohome schedule list
|
||||
|
||||
# 创建定时任务
|
||||
memohome schedule create \
|
||||
-t "每日提醒" \
|
||||
-d "每天早上9点的提醒" \
|
||||
-c "0 9 * * *" \
|
||||
-e
|
||||
|
||||
# 获取任务详情
|
||||
memohome schedule get <id>
|
||||
|
||||
# 更新任务
|
||||
memohome schedule update <id> \
|
||||
[-t title] \
|
||||
[-d description] \
|
||||
[-c cron] \
|
||||
[-e true/false]
|
||||
|
||||
# 删除任务
|
||||
memohome schedule delete <id>
|
||||
|
||||
# 切换任务启用状态
|
||||
memohome schedule toggle <id>
|
||||
```
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 完整工作流程
|
||||
|
||||
```bash
|
||||
# 1. 登录
|
||||
memohome auth login -u admin -p password
|
||||
|
||||
# 2. 创建模型配置(聊天模型)
|
||||
memohome model create \
|
||||
-n "GPT-4" \
|
||||
-m "gpt-4" \
|
||||
-u "https://api.openai.com/v1" \
|
||||
-k "your-api-key" \
|
||||
-c "openai" \
|
||||
-t "chat"
|
||||
|
||||
# 如果需要 embedding 模型
|
||||
memohome model create \
|
||||
-n "Text Embedding" \
|
||||
-m "text-embedding-3-small" \
|
||||
-u "https://api.openai.com/v1" \
|
||||
-k "your-api-key" \
|
||||
-c "openai" \
|
||||
-t "embedding" \
|
||||
-d 1536
|
||||
|
||||
# 3. 配置设置(使用模型ID)
|
||||
memohome settings set \
|
||||
--language Chinese \
|
||||
--max-context-time 60 \
|
||||
--chat-model <model-id-from-step-2>
|
||||
|
||||
# 4. 开始对话
|
||||
memohome agent chat "你好"
|
||||
|
||||
# 5. 进入交互模式
|
||||
memohome agent i
|
||||
```
|
||||
|
||||
### Agent 交互模式示例
|
||||
|
||||
```bash
|
||||
$ memohome agent interactive
|
||||
|
||||
🤖 MemoHome Agent 交互模式
|
||||
输入 /exit 或 /quit 退出,输入 /help 查看帮助
|
||||
|
||||
You: 你好
|
||||
Agent: 你好!我是 MemoHome AI 助手,很高兴为你服务...
|
||||
|
||||
You: 帮我总结一下今天的对话
|
||||
Agent: [🔧 使用工具: search_memory]
|
||||
根据我们的对话记录...
|
||||
|
||||
You: /exit
|
||||
再见!👋
|
||||
```
|
||||
|
||||
### 搜索记忆示例
|
||||
|
||||
```bash
|
||||
$ memohome memory search "项目计划"
|
||||
|
||||
✓ 找到 3 条记忆
|
||||
|
||||
[1] 相似度: 92.50%
|
||||
时间: 2024-01-15 10:30:00
|
||||
讨论了项目的初步计划和时间线...
|
||||
|
||||
[2] 相似度: 85.20%
|
||||
时间: 2024-01-14 15:20:00
|
||||
确定了项目的主要里程碑...
|
||||
|
||||
[3] 相似度: 78.90%
|
||||
时间: 2024-01-13 09:00:00
|
||||
项目启动会议记录...
|
||||
```
|
||||
|
||||
## 配置文件
|
||||
|
||||
CLI 配置保存在 `~/.memohome/config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"apiUrl": "http://localhost:7002",
|
||||
"token": "your_jwt_token"
|
||||
}
|
||||
```
|
||||
|
||||
## 开发
|
||||
|
||||
```bash
|
||||
# 开发模式(带热重载)
|
||||
pnpm run dev
|
||||
|
||||
# 直接运行
|
||||
pnpm run start
|
||||
```
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **Bun** - JavaScript 运行时
|
||||
- **Elysia Eden** - 类型安全的 HTTP 客户端
|
||||
- **Commander** - 命令行参数解析
|
||||
- **Chalk** - 终端颜色输出
|
||||
- **Inquirer** - 交互式提示
|
||||
- **Ora** - 加载动画
|
||||
- **Table** - 表格输出
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **认证要求**: 大部分命令需要先登录
|
||||
2. **管理员权限**: 用户管理命令需要管理员角色
|
||||
3. **模型配置**: 使用 Agent 前需要配置模型
|
||||
4. **流式响应**: Agent 对话使用 SSE 流式传输
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [API 文档](../api/README.md)
|
||||
- [认证系统](../api/AUTH_README.md)
|
||||
- [Agent API](../api/AGENT_API.md)
|
||||
- [用户管理](../api/USER_MANAGEMENT.md)
|
||||
|
||||
## 许可证
|
||||
|
||||
MIT
|
||||
|
||||
@@ -1,16 +1,39 @@
|
||||
{
|
||||
"name": "@memohome/cli",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"description": "Command line interface for MemoHome API",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"bin": {
|
||||
"memohome": "./src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "bun run src/index.ts"
|
||||
"start": "bun run src/index.ts",
|
||||
"dev": "bun run --watch src/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@elysiajs/eden": "^1.4.6",
|
||||
"@memohome/api": "workspace:*",
|
||||
"@memohome/shared": "workspace:*"
|
||||
"@memohome/shared": "workspace:*",
|
||||
"elysia": "latest",
|
||||
"commander": "^12.1.0",
|
||||
"chalk": "^5.4.1",
|
||||
"ora": "^8.1.1",
|
||||
"inquirer": "^12.3.0",
|
||||
"table": "^6.8.2",
|
||||
"node-fetch": "^3.3.2"
|
||||
},
|
||||
"packageManager": "pnpm@10.27.0"
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.10.5",
|
||||
"bun-types": "latest",
|
||||
"@types/bun": "latest"
|
||||
},
|
||||
"packageManager": "pnpm@10.27.0",
|
||||
"module": "src/index.ts",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { treaty } from '@elysiajs/eden'
|
||||
import { getApiUrl, getToken } from './config'
|
||||
|
||||
// 使用动态导入来避免类型错误
|
||||
export function createClient() {
|
||||
const apiUrl = getApiUrl()
|
||||
const token = getToken()
|
||||
|
||||
// Eden Treaty 配置
|
||||
const client = treaty(apiUrl, {
|
||||
headers: token ? {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
} : undefined,
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return client as any
|
||||
}
|
||||
|
||||
export function requireAuth(): string {
|
||||
const token = getToken()
|
||||
if (!token) {
|
||||
throw new Error('未登录,请先使用 "memohome auth login" 命令登录')
|
||||
}
|
||||
return token
|
||||
}
|
||||
|
||||
export { getApiUrl, getToken }
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
import type { Command } from 'commander'
|
||||
import chalk from 'chalk'
|
||||
import { requireAuth, getApiUrl, getToken } from '../client'
|
||||
|
||||
export function agentCommands(program: Command) {
|
||||
program
|
||||
.command('chat <message>')
|
||||
.description('与 AI Agent 对话')
|
||||
.option('-t, --max-context-time <minutes>', '上下文加载时间(分钟)', '60')
|
||||
.option('-l, --language <language>', '回复语言', 'Chinese')
|
||||
.action(async (message, options) => {
|
||||
try {
|
||||
requireAuth()
|
||||
const token = getToken()!
|
||||
const apiUrl = getApiUrl()
|
||||
|
||||
console.log(chalk.blue('🤖 Agent: '))
|
||||
|
||||
const response = await fetch(`${apiUrl}/agent/stream`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
message,
|
||||
maxContextLoadTime: parseInt(options.maxContextTime),
|
||||
language: options.language,
|
||||
}),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json() as { error?: string }
|
||||
console.error(chalk.red('对话失败:'), errorData.error || '未知错误')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
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')
|
||||
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)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command('interactive')
|
||||
.alias('i')
|
||||
.description('进入交互式对话模式')
|
||||
.option('-t, --max-context-time <minutes>', '上下文加载时间(分钟)', '60')
|
||||
.option('-l, --language <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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
import type { Command } from 'commander'
|
||||
import chalk from 'chalk'
|
||||
import inquirer from 'inquirer'
|
||||
import ora from 'ora'
|
||||
import { createClient } from '../client'
|
||||
import { setToken, clearToken, getToken, getApiUrl, setApiUrl } from '../config'
|
||||
import { formatError } from '../utils'
|
||||
|
||||
export function authCommands(program: Command) {
|
||||
program
|
||||
.command('login')
|
||||
.description('登录到 MemoHome')
|
||||
.option('-u, --username <username>', '用户名')
|
||||
.option('-p, --password <password>', '密码')
|
||||
.action(async (options) => {
|
||||
try {
|
||||
let username = options.username
|
||||
let password = options.password
|
||||
|
||||
if (!username || !password) {
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'username',
|
||||
message: '请输入用户名:',
|
||||
when: !username,
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'password',
|
||||
message: '请输入密码:',
|
||||
when: !password,
|
||||
mask: '*',
|
||||
},
|
||||
])
|
||||
username = username || answers.username
|
||||
password = password || answers.password
|
||||
}
|
||||
|
||||
const spinner = ora('正在登录...').start()
|
||||
const client = createClient()
|
||||
|
||||
const response = await client.auth.login.post({
|
||||
username,
|
||||
password,
|
||||
})
|
||||
|
||||
if (response.error) {
|
||||
spinner.fail(chalk.red('登录失败'))
|
||||
console.error(chalk.red(formatError(response.error.value)))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
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}`))
|
||||
} else {
|
||||
spinner.fail(chalk.red('登录失败'))
|
||||
console.error(chalk.red('无效的响应格式'))
|
||||
process.exit(1)
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error)
|
||||
console.error(chalk.red('登录错误:'), message)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command('logout')
|
||||
.description('登出当前用户')
|
||||
.action(() => {
|
||||
const token = getToken()
|
||||
if (!token) {
|
||||
console.log(chalk.yellow('当前未登录'))
|
||||
return
|
||||
}
|
||||
|
||||
clearToken()
|
||||
console.log(chalk.green('✓ 已登出'))
|
||||
})
|
||||
|
||||
program
|
||||
.command('whoami')
|
||||
.description('查看当前登录用户')
|
||||
.action(async () => {
|
||||
try {
|
||||
const token = getToken()
|
||||
if (!token) {
|
||||
console.log(chalk.yellow('当前未登录'))
|
||||
console.log(chalk.dim('使用 "memohome auth login" 登录'))
|
||||
return
|
||||
}
|
||||
|
||||
const spinner = ora('获取用户信息...').start()
|
||||
const client = createClient()
|
||||
|
||||
const response = await client.auth.me.get()
|
||||
|
||||
if (response.error) {
|
||||
spinner.fail(chalk.red('获取用户信息失败'))
|
||||
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}`))
|
||||
} else {
|
||||
spinner.fail(chalk.red('获取用户信息失败'))
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error)
|
||||
console.error(chalk.red('错误:'), message)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command('config')
|
||||
.description('查看或设置 API 配置')
|
||||
.option('-s, --set <url>', '设置 API URL')
|
||||
.action((options) => {
|
||||
if (options.set) {
|
||||
const url = options.set
|
||||
setApiUrl(url)
|
||||
console.log(chalk.green(`✓ API URL 已设置为: ${url}`))
|
||||
} else {
|
||||
const apiUrl = getApiUrl()
|
||||
const token = getToken()
|
||||
console.log(chalk.blue('当前配置:'))
|
||||
console.log(chalk.dim(`API URL: ${apiUrl}`))
|
||||
console.log(chalk.dim(`已登录: ${token ? '是' : '否'}`))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import type { Command } from 'commander'
|
||||
import chalk from 'chalk'
|
||||
import ora from 'ora'
|
||||
import { getApiUrl, getToken } from '../client'
|
||||
|
||||
export function debugCommands(program: Command) {
|
||||
program
|
||||
.command('ping')
|
||||
.description('测试 API 服务器连接')
|
||||
.action(async () => {
|
||||
const apiUrl = getApiUrl()
|
||||
const token = getToken()
|
||||
|
||||
console.log(chalk.blue('连接信息:'))
|
||||
console.log(chalk.dim(` API URL: ${apiUrl}`))
|
||||
console.log(chalk.dim(` Token: ${token ? '已设置' : '未设置'}`))
|
||||
console.log()
|
||||
|
||||
const spinner = ora('正在连接...').start()
|
||||
|
||||
try {
|
||||
// 尝试直接 fetch
|
||||
const controller = new AbortController()
|
||||
const timeoutId = setTimeout(() => controller.abort(), 5000)
|
||||
|
||||
const response = await fetch(`${apiUrl}/`, {
|
||||
signal: controller.signal,
|
||||
headers: token ? {
|
||||
'Authorization': `Bearer ${token}`
|
||||
} : {}
|
||||
})
|
||||
|
||||
clearTimeout(timeoutId)
|
||||
|
||||
if (response.ok) {
|
||||
spinner.succeed(chalk.green('连接成功!'))
|
||||
const text = await response.text()
|
||||
console.log(chalk.dim('响应:'), text.substring(0, 100))
|
||||
} else {
|
||||
spinner.fail(chalk.red(`连接失败: HTTP ${response.status}`))
|
||||
}
|
||||
} catch (error) {
|
||||
spinner.fail(chalk.red('连接失败'))
|
||||
if (error instanceof Error) {
|
||||
if (error.name === 'AbortError') {
|
||||
console.error(chalk.yellow('连接超时 (5秒)'))
|
||||
console.error(chalk.dim('请检查 API 服务器是否正在运行'))
|
||||
} else {
|
||||
console.error(chalk.red('错误:'), error.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
import type { Command } from 'commander'
|
||||
import chalk from 'chalk'
|
||||
import ora from 'ora'
|
||||
import { table } from 'table'
|
||||
import { createClient, requireAuth } from '../client'
|
||||
|
||||
export function memoryCommands(program: Command) {
|
||||
program
|
||||
.command('search <query>')
|
||||
.description('搜索记忆')
|
||||
.option('-l, --limit <limit>', '返回结果数量', '10')
|
||||
.action(async (query, options) => {
|
||||
try {
|
||||
requireAuth()
|
||||
const spinner = ora('搜索记忆...').start()
|
||||
const client = createClient()
|
||||
|
||||
const response = await client.memory.search.get({
|
||||
query: {
|
||||
q: query,
|
||||
limit: parseInt(options.limit),
|
||||
},
|
||||
})
|
||||
|
||||
if (response.error) {
|
||||
spinner.fail(chalk.red('搜索失败'))
|
||||
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} 条记忆`))
|
||||
|
||||
if (data.data.length === 0) {
|
||||
console.log(chalk.yellow('未找到相关记忆'))
|
||||
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.white(item.content))
|
||||
})
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(chalk.red('错误:'), error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command('add <content>')
|
||||
.description('添加记忆')
|
||||
.action(async (content) => {
|
||||
try {
|
||||
requireAuth()
|
||||
const spinner = ora('添加记忆...').start()
|
||||
const client = createClient()
|
||||
|
||||
const response = await client.memory.post({
|
||||
content,
|
||||
})
|
||||
|
||||
if (response.error) {
|
||||
spinner.fail(chalk.red('添加记忆失败'))
|
||||
console.error(chalk.red(response.error.value))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const data = response.data as any
|
||||
if (data?.success) {
|
||||
spinner.succeed(chalk.green('记忆已添加'))
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(chalk.red('错误:'), error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command('messages')
|
||||
.alias('msg')
|
||||
.description('获取消息历史')
|
||||
.option('-p, --page <page>', '页码', '1')
|
||||
.option('-l, --limit <limit>', '每页数量', '20')
|
||||
.action(async (options) => {
|
||||
try {
|
||||
requireAuth()
|
||||
const spinner = ora('获取消息历史...').start()
|
||||
const client = createClient()
|
||||
|
||||
const response = await client.memory.message.get({
|
||||
query: {
|
||||
page: parseInt(options.page),
|
||||
limit: parseInt(options.limit),
|
||||
},
|
||||
})
|
||||
|
||||
if (response.error) {
|
||||
spinner.fail(chalk.red('获取消息失败'))
|
||||
console.error(chalk.red(response.error.value))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const data = response.data as any
|
||||
if (data?.success && data?.data) {
|
||||
const { messages, pagination } = data.data
|
||||
spinner.succeed(chalk.green(`消息历史 (${pagination.page}/${pagination.totalPages} 页)`))
|
||||
|
||||
if (messages.length === 0) {
|
||||
console.log(chalk.yellow('暂无消息'))
|
||||
return
|
||||
}
|
||||
|
||||
console.log(chalk.dim(`\n总计: ${pagination.total} 条消息\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.white(msg.content))
|
||||
console.log()
|
||||
})
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(chalk.red('错误:'), error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command('filter')
|
||||
.description('按日期范围过滤消息')
|
||||
.option('-s, --start <date>', '开始日期 (ISO 8601)')
|
||||
.option('-e, --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'))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const spinner = ora('过滤消息...').start()
|
||||
const client = createClient()
|
||||
|
||||
const response = await client.memory.message.filter.get({
|
||||
query: {
|
||||
startDate: options.start,
|
||||
endDate: options.end,
|
||||
},
|
||||
})
|
||||
|
||||
if (response.error) {
|
||||
spinner.fail(chalk.red('过滤消息失败'))
|
||||
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} 条消息`))
|
||||
|
||||
if (data.data.length === 0) {
|
||||
console.log(chalk.yellow('未找到消息'))
|
||||
return
|
||||
}
|
||||
|
||||
console.log()
|
||||
|
||||
data.data.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.white(msg.content))
|
||||
console.log()
|
||||
})
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(chalk.red('错误:'), error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,400 @@
|
||||
import type { Command } from 'commander'
|
||||
import chalk from 'chalk'
|
||||
import inquirer from 'inquirer'
|
||||
import ora from 'ora'
|
||||
import { table } from 'table'
|
||||
import { createClient, requireAuth } from '../client'
|
||||
import type { ApiResponse, Model } from '../types'
|
||||
import { formatError } from '../utils'
|
||||
|
||||
export function modelCommands(program: Command) {
|
||||
program
|
||||
.command('list')
|
||||
.description('列出所有模型配置')
|
||||
.action(async () => {
|
||||
const spinner = ora('获取模型列表...').start()
|
||||
try {
|
||||
requireAuth()
|
||||
const client = createClient()
|
||||
|
||||
const response = await client.model.get()
|
||||
|
||||
if (response.error) {
|
||||
spinner.fail(chalk.red('获取模型列表失败'))
|
||||
console.error(chalk.red(formatError(response.error.value)))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// API 返回格式: { success, items, pagination }
|
||||
const data = response.data as { success?: boolean; items?: Model[]; pagination?: unknown } | null
|
||||
if (data?.success && data?.items) {
|
||||
spinner.succeed(chalk.green('模型列表'))
|
||||
|
||||
const models = data.items
|
||||
if (models.length === 0) {
|
||||
console.log(chalk.yellow('暂无模型配置'))
|
||||
return
|
||||
}
|
||||
|
||||
const tableData = [
|
||||
['ID', '名称', '模型ID', '类型', '客户端'],
|
||||
...models.map((item: unknown) => {
|
||||
const modelItem = item as { id: string; model: Model }
|
||||
return [
|
||||
modelItem.id.substring(0, 8) + '...',
|
||||
modelItem.model.name || '-',
|
||||
modelItem.model.modelId,
|
||||
modelItem.model.type === 'embedding' ? chalk.yellow('embedding') : chalk.blue('chat'),
|
||||
modelItem.model.clientType,
|
||||
]
|
||||
}),
|
||||
]
|
||||
|
||||
console.log(table(tableData))
|
||||
}
|
||||
} catch (error) {
|
||||
spinner.fail(chalk.red('操作失败'))
|
||||
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()}`))
|
||||
} else {
|
||||
console.error(chalk.red('错误:'), error.message)
|
||||
}
|
||||
} else {
|
||||
console.error(chalk.red('错误:'), String(error))
|
||||
}
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command('create')
|
||||
.description('创建模型配置')
|
||||
.option('-n, --name <name>', '模型名称')
|
||||
.option('-m, --model-id <modelId>', '模型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 类型需要)')
|
||||
.action(async (options) => {
|
||||
const spinner = ora('创建模型配置...').start()
|
||||
try {
|
||||
requireAuth()
|
||||
|
||||
let { name, modelId, baseUrl, apiKey, clientType, type, dimensions } = options
|
||||
|
||||
if (!name || !modelId || !baseUrl || !apiKey || !clientType) {
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'name',
|
||||
message: '模型名称:',
|
||||
when: !name,
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'modelId',
|
||||
message: '模型ID (如 gpt-4 或 text-embedding-3-small):',
|
||||
when: !modelId,
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'baseUrl',
|
||||
message: 'API Base URL:',
|
||||
default: 'https://api.openai.com/v1',
|
||||
when: !baseUrl,
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'apiKey',
|
||||
message: 'API Key:',
|
||||
when: !apiKey,
|
||||
mask: '*',
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'clientType',
|
||||
message: '客户端类型:',
|
||||
choices: ['openai', 'anthropic', 'google'],
|
||||
default: 'openai',
|
||||
when: !clientType,
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'type',
|
||||
message: '模型类型:',
|
||||
choices: ['chat', 'embedding'],
|
||||
default: 'chat',
|
||||
when: !type,
|
||||
},
|
||||
])
|
||||
|
||||
name = name || answers.name
|
||||
modelId = modelId || answers.modelId
|
||||
baseUrl = baseUrl || answers.baseUrl
|
||||
apiKey = apiKey || answers.apiKey
|
||||
clientType = clientType || answers.clientType
|
||||
type = type || answers.type
|
||||
}
|
||||
|
||||
// 如果是 embedding 类型,需要 dimensions
|
||||
if (type === 'embedding' && !dimensions) {
|
||||
const answer = await inquirer.prompt([
|
||||
{
|
||||
type: 'number',
|
||||
name: 'dimensions',
|
||||
message: 'Embedding 维度 (如 1536):',
|
||||
validate: (value: number) => {
|
||||
if (value > 0) return true
|
||||
return '维度必须是正整数'
|
||||
},
|
||||
},
|
||||
])
|
||||
dimensions = answer.dimensions
|
||||
}
|
||||
|
||||
spinner.text = '创建模型配置...'
|
||||
const client = createClient()
|
||||
|
||||
const payload: Record<string, unknown> = {
|
||||
name,
|
||||
modelId,
|
||||
baseUrl,
|
||||
apiKey,
|
||||
clientType,
|
||||
type,
|
||||
}
|
||||
|
||||
// 如果是 embedding 类型,添加 dimensions
|
||||
if (type === 'embedding') {
|
||||
if (!dimensions) {
|
||||
console.error(chalk.red('Embedding 模型需要指定 dimensions'))
|
||||
process.exit(1)
|
||||
}
|
||||
payload.dimensions = typeof dimensions === 'number' ? dimensions : parseInt(dimensions)
|
||||
}
|
||||
|
||||
const response = await client.model.post(payload)
|
||||
|
||||
if (response.error) {
|
||||
spinner.fail(chalk.red('创建模型配置失败'))
|
||||
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'}`))
|
||||
if (data.data.type === 'embedding' && data.data.dimensions) {
|
||||
console.log(chalk.blue(`维度: ${data.data.dimensions}`))
|
||||
}
|
||||
console.log(chalk.blue(`ID: ${data.data.id}`))
|
||||
}
|
||||
} catch (error) {
|
||||
spinner.fail(chalk.red('操作失败'))
|
||||
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()}`))
|
||||
} else {
|
||||
console.error(chalk.red('错误:'), error.message)
|
||||
}
|
||||
} else {
|
||||
console.error(chalk.red('错误:'), String(error))
|
||||
}
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command('delete <id>')
|
||||
.description('删除模型配置')
|
||||
.action(async (id) => {
|
||||
let spinner: ReturnType<typeof ora> | undefined
|
||||
try {
|
||||
requireAuth()
|
||||
|
||||
const { confirm } = await inquirer.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: chalk.yellow(`确定要删除模型配置 ${id} 吗?`),
|
||||
default: false,
|
||||
},
|
||||
])
|
||||
|
||||
if (!confirm) {
|
||||
console.log(chalk.yellow('已取消'))
|
||||
return
|
||||
}
|
||||
|
||||
spinner = ora('删除模型配置...').start()
|
||||
const client = createClient()
|
||||
|
||||
const response = await client.model({ id }).delete()
|
||||
|
||||
if (response.error) {
|
||||
spinner.fail(chalk.red('删除模型配置失败'))
|
||||
console.error(chalk.red(response.error.value))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (spinner) spinner.succeed(chalk.green('模型配置已删除'))
|
||||
} catch (error) {
|
||||
if (spinner) spinner.fail(chalk.red('操作失败'))
|
||||
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()}`))
|
||||
} else {
|
||||
console.error(chalk.red('错误:'), error.message)
|
||||
}
|
||||
} else {
|
||||
console.error(chalk.red('错误:'), String(error))
|
||||
}
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command('get <id>')
|
||||
.description('获取模型配置详情')
|
||||
.action(async (id) => {
|
||||
const spinner = ora('获取模型配置...').start()
|
||||
try {
|
||||
requireAuth()
|
||||
const client = createClient()
|
||||
|
||||
const response = await client.model({ id }).get()
|
||||
|
||||
if (response.error) {
|
||||
spinner.fail(chalk.red('获取模型配置失败'))
|
||||
console.error(chalk.red(response.error.value))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const data = response.data as ApiResponse<Model> | null
|
||||
if (data?.success && data?.data) {
|
||||
const model = data.data
|
||||
spinner.succeed(chalk.green('模型配置'))
|
||||
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'}`))
|
||||
if (model.type === 'embedding' && model.dimensions) {
|
||||
console.log(chalk.blue(`维度: ${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')}`))
|
||||
}
|
||||
} catch (error) {
|
||||
spinner.fail(chalk.red('操作失败'))
|
||||
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()}`))
|
||||
} else {
|
||||
console.error(chalk.red('错误:'), error.message)
|
||||
}
|
||||
} else {
|
||||
console.error(chalk.red('错误:'), String(error))
|
||||
}
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command('defaults')
|
||||
.description('查看默认模型配置')
|
||||
.action(async () => {
|
||||
const spinner = ora('获取默认模型配置...').start()
|
||||
try {
|
||||
requireAuth()
|
||||
const client = createClient()
|
||||
|
||||
const [chatRes, summaryRes, embeddingRes] = await Promise.all([
|
||||
client.model.chat.default.get(),
|
||||
client.model.summary.default.get(),
|
||||
client.model.embedding.default.get(),
|
||||
])
|
||||
|
||||
spinner.stop()
|
||||
|
||||
console.log(chalk.green.bold('默认模型配置:'))
|
||||
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.dim(` ID: ${model.id}`))
|
||||
} else {
|
||||
console.log(chalk.yellow('💬 聊天模型: 未配置'))
|
||||
}
|
||||
console.log()
|
||||
|
||||
// Summary Model
|
||||
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.dim(` ID: ${model.id}`))
|
||||
} else {
|
||||
console.log(chalk.yellow('📝 摘要模型: 未配置'))
|
||||
}
|
||||
console.log()
|
||||
|
||||
// Embedding Model
|
||||
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.dim(` ID: ${model.id}`))
|
||||
} else {
|
||||
console.log(chalk.yellow('🔍 嵌入模型: 未配置'))
|
||||
}
|
||||
} catch (error) {
|
||||
spinner.fail(chalk.red('操作失败'))
|
||||
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()}`))
|
||||
} else {
|
||||
console.error(chalk.red('错误:'), error.message)
|
||||
}
|
||||
} else {
|
||||
console.error(chalk.red('错误:'), String(error))
|
||||
}
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
import type { Command } from 'commander'
|
||||
import chalk from 'chalk'
|
||||
import inquirer from 'inquirer'
|
||||
import ora from 'ora'
|
||||
import { table } from 'table'
|
||||
import { createClient, requireAuth } from '../client'
|
||||
|
||||
export function scheduleCommands(program: Command) {
|
||||
program
|
||||
.command('list')
|
||||
.description('列出所有定时任务')
|
||||
.action(async () => {
|
||||
try {
|
||||
requireAuth()
|
||||
const spinner = ora('获取定时任务列表...').start()
|
||||
const client = createClient()
|
||||
|
||||
const response = await client.schedule.get()
|
||||
|
||||
if (response.error) {
|
||||
spinner.fail(chalk.red('获取定时任务列表失败'))
|
||||
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('定时任务列表'))
|
||||
|
||||
const schedules = data.data
|
||||
if (schedules.length === 0) {
|
||||
console.log(chalk.yellow('暂无定时任务'))
|
||||
return
|
||||
}
|
||||
|
||||
const tableData = [
|
||||
['ID', '标题', 'Cron', '启用', '创建时间'],
|
||||
...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'),
|
||||
]),
|
||||
]
|
||||
|
||||
console.log(table(tableData))
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(chalk.red('错误:'), 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)
|
||||
.action(async (options) => {
|
||||
try {
|
||||
requireAuth()
|
||||
|
||||
let { title, description, cron, enabled } = options
|
||||
|
||||
if (!title || !cron) {
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'title',
|
||||
message: '任务标题:',
|
||||
when: !title,
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'description',
|
||||
message: '任务描述 (可选):',
|
||||
when: !description,
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'cron',
|
||||
message: 'Cron 表达式 (如: 0 9 * * *):',
|
||||
when: !cron,
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'enabled',
|
||||
message: '启用任务?',
|
||||
default: false,
|
||||
when: enabled === undefined,
|
||||
},
|
||||
])
|
||||
|
||||
title = title || answers.title
|
||||
description = description || answers.description
|
||||
cron = cron || answers.cron
|
||||
enabled = enabled !== undefined ? enabled : answers.enabled
|
||||
}
|
||||
|
||||
const spinner = ora('创建定时任务...').start()
|
||||
const client = createClient()
|
||||
|
||||
const payload: any = {
|
||||
title,
|
||||
cronExpression: cron,
|
||||
enabled,
|
||||
}
|
||||
|
||||
if (description) {
|
||||
payload.description = description
|
||||
}
|
||||
|
||||
const response = await client.schedule.post(payload)
|
||||
|
||||
if (response.error) {
|
||||
spinner.fail(chalk.red('创建定时任务失败'))
|
||||
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}`))
|
||||
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)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command('get <id>')
|
||||
.description('获取定时任务详情')
|
||||
.action(async (id) => {
|
||||
try {
|
||||
requireAuth()
|
||||
const spinner = ora('获取定时任务详情...').start()
|
||||
const client = createClient()
|
||||
|
||||
const response = await client.schedule({ id }).get()
|
||||
|
||||
if (response.error) {
|
||||
spinner.fail(chalk.red('获取定时任务失败'))
|
||||
console.error(chalk.red(response.error.value))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const data = response.data as any
|
||||
if (data?.success && data?.data) {
|
||||
const schedule = data.data
|
||||
spinner.succeed(chalk.green('定时任务详情'))
|
||||
console.log(chalk.blue(`ID: ${schedule.id}`))
|
||||
console.log(chalk.blue(`标题: ${schedule.title}`))
|
||||
if (schedule.description) {
|
||||
console.log(chalk.blue(`描述: ${schedule.description}`))
|
||||
}
|
||||
console.log(chalk.blue(`Cron: ${schedule.cronExpression}`))
|
||||
console.log(
|
||||
chalk.blue(`启用: ${schedule.enabled ? chalk.green('是') : chalk.red('否')}`)
|
||||
)
|
||||
console.log(
|
||||
chalk.blue(`创建时间: ${new Date(schedule.createdAt).toLocaleString('zh-CN')}`)
|
||||
)
|
||||
console.log(
|
||||
chalk.blue(`更新时间: ${new Date(schedule.updatedAt).toLocaleString('zh-CN')}`)
|
||||
)
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(chalk.red('错误:'), 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)')
|
||||
.action(async (id, options) => {
|
||||
try {
|
||||
requireAuth()
|
||||
|
||||
const updates: any = {}
|
||||
|
||||
if (options.title) updates.title = options.title
|
||||
if (options.description) updates.description = options.description
|
||||
if (options.cron) updates.cronExpression = options.cron
|
||||
if (options.enabled !== undefined) {
|
||||
updates.enabled = options.enabled === 'true' || options.enabled === true
|
||||
}
|
||||
|
||||
if (Object.keys(updates).length === 0) {
|
||||
console.log(chalk.yellow('未提供任何更新参数'))
|
||||
return
|
||||
}
|
||||
|
||||
const spinner = ora('更新定时任务...').start()
|
||||
const client = createClient()
|
||||
|
||||
const response = await client.schedule({ id }).put(updates)
|
||||
|
||||
if (response.error) {
|
||||
spinner.fail(chalk.red('更新定时任务失败'))
|
||||
console.error(chalk.red(response.error.value))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
spinner.succeed(chalk.green('定时任务已更新'))
|
||||
} catch (error: any) {
|
||||
console.error(chalk.red('错误:'), error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command('delete <id>')
|
||||
.description('删除定时任务')
|
||||
.action(async (id) => {
|
||||
try {
|
||||
requireAuth()
|
||||
|
||||
const { confirm } = await inquirer.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: chalk.yellow(`确定要删除定时任务 ${id} 吗?`),
|
||||
default: false,
|
||||
},
|
||||
])
|
||||
|
||||
if (!confirm) {
|
||||
console.log(chalk.yellow('已取消'))
|
||||
return
|
||||
}
|
||||
|
||||
const spinner = ora('删除定时任务...').start()
|
||||
const client = createClient()
|
||||
|
||||
const response = await client.schedule({ id }).delete()
|
||||
|
||||
if (response.error) {
|
||||
spinner.fail(chalk.red('删除定时任务失败'))
|
||||
console.error(chalk.red(response.error.value))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
spinner.succeed(chalk.green('定时任务已删除'))
|
||||
} catch (error: any) {
|
||||
console.error(chalk.red('错误:'), error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command('toggle <id>')
|
||||
.description('切换定时任务启用状态')
|
||||
.action(async (id) => {
|
||||
try {
|
||||
requireAuth()
|
||||
const spinner = ora('切换任务状态...').start()
|
||||
const client = createClient()
|
||||
|
||||
// 首先获取当前状态
|
||||
const getResponse = await client.schedule({ id }).get()
|
||||
|
||||
if (getResponse.error) {
|
||||
spinner.fail(chalk.red('获取任务失败'))
|
||||
console.error(chalk.red(getResponse.error.value))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const getData = getResponse.data as any
|
||||
if (getData?.success && getData?.data) {
|
||||
const currentEnabled = getData.data.enabled
|
||||
|
||||
// 更新状态
|
||||
const updateResponse = await client.schedule({ id }).put({
|
||||
enabled: !currentEnabled,
|
||||
})
|
||||
|
||||
if (updateResponse.error) {
|
||||
spinner.fail(chalk.red('更新任务失败'))
|
||||
console.error(chalk.red(updateResponse.error.value))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
spinner.succeed(
|
||||
chalk.green(`任务已${!currentEnabled ? '启用' : '禁用'}`)
|
||||
)
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(chalk.red('错误:'), error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
import type { Command } from 'commander'
|
||||
import chalk from 'chalk'
|
||||
import inquirer from 'inquirer'
|
||||
import ora from 'ora'
|
||||
import { createClient, requireAuth } from '../client'
|
||||
|
||||
export function settingsCommands(program: Command) {
|
||||
program
|
||||
.command('get')
|
||||
.description('获取当前用户设置')
|
||||
.action(async () => {
|
||||
try {
|
||||
requireAuth()
|
||||
const spinner = ora('获取设置...').start()
|
||||
const client = createClient()
|
||||
|
||||
const response = await client.settings.get()
|
||||
|
||||
if (response.error) {
|
||||
spinner.fail(chalk.red('获取设置失败'))
|
||||
console.error(chalk.red(response.error.value))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const data = response.data as any
|
||||
if (data?.success && data?.data) {
|
||||
const settings = data.data
|
||||
spinner.succeed(chalk.green('当前设置'))
|
||||
console.log()
|
||||
console.log(chalk.blue('🎯 Agent 配置:'))
|
||||
console.log(chalk.dim(` 语言: ${settings.language || '未设置'}`))
|
||||
console.log(chalk.dim(` 上下文加载时间: ${settings.maxContextLoadTime || '未设置'} 分钟`))
|
||||
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()
|
||||
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')}`))
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(chalk.red('错误:'), 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')
|
||||
.action(async (options) => {
|
||||
try {
|
||||
requireAuth()
|
||||
|
||||
const updates: any = {}
|
||||
|
||||
if (options.language) updates.language = options.language
|
||||
if (options.maxContextTime)
|
||||
updates.maxContextLoadTime = parseInt(options.maxContextTime)
|
||||
if (options.chatModel) updates.defaultChatModel = options.chatModel
|
||||
if (options.summaryModel) updates.defaultSummaryModel = options.summaryModel
|
||||
if (options.embeddingModel)
|
||||
updates.defaultEmbeddingModel = options.embeddingModel
|
||||
|
||||
if (Object.keys(updates).length === 0) {
|
||||
console.log(chalk.yellow('未提供任何更新参数'))
|
||||
console.log(chalk.dim('\n可用选项:'))
|
||||
console.log(chalk.dim(' --language <language>'))
|
||||
console.log(chalk.dim(' --max-context-time <minutes>'))
|
||||
console.log(chalk.dim(' --chat-model <id>'))
|
||||
console.log(chalk.dim(' --summary-model <id>'))
|
||||
console.log(chalk.dim(' --embedding-model <id>'))
|
||||
return
|
||||
}
|
||||
|
||||
const spinner = ora('更新设置...').start()
|
||||
const client = createClient()
|
||||
|
||||
const response = await client.settings.put(updates)
|
||||
|
||||
if (response.error) {
|
||||
spinner.fail(chalk.red('更新设置失败'))
|
||||
console.error(chalk.red(response.error.value))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const data = response.data as any
|
||||
if (data?.success) {
|
||||
spinner.succeed(chalk.green('设置已更新'))
|
||||
console.log()
|
||||
console.log(chalk.blue('更新的设置:'))
|
||||
Object.entries(updates).forEach(([key, value]) => {
|
||||
console.log(chalk.dim(` ${key}: ${value}`))
|
||||
})
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(chalk.red('错误:'), error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command('setup')
|
||||
.description('交互式设置向导')
|
||||
.action(async () => {
|
||||
try {
|
||||
requireAuth()
|
||||
|
||||
console.log(chalk.green.bold('\n🎨 设置向导\n'))
|
||||
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'language',
|
||||
message: '首选语言:',
|
||||
default: 'Chinese',
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
name: 'maxContextLoadTime',
|
||||
message: '上下文加载时间(分钟):',
|
||||
default: 60,
|
||||
validate: (value) => {
|
||||
const num = parseInt(value)
|
||||
if (num < 1 || num > 1440) {
|
||||
return '请输入 1-1440 之间的数字'
|
||||
}
|
||||
return true
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'defaultChatModel',
|
||||
message: '默认聊天模型ID (留空跳过):',
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'defaultSummaryModel',
|
||||
message: '默认摘要模型ID (留空跳过):',
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'defaultEmbeddingModel',
|
||||
message: '默认嵌入模型ID (留空跳过):',
|
||||
},
|
||||
])
|
||||
|
||||
// 过滤掉空值
|
||||
const updates: any = {}
|
||||
Object.entries(answers).forEach(([key, value]) => {
|
||||
if (value) {
|
||||
updates[key] = value
|
||||
}
|
||||
})
|
||||
|
||||
const spinner = ora('保存设置...').start()
|
||||
const client = createClient()
|
||||
|
||||
const response = await client.settings.put(updates)
|
||||
|
||||
if (response.error) {
|
||||
spinner.fail(chalk.red('保存设置失败'))
|
||||
console.error(chalk.red(response.error.value))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
spinner.succeed(chalk.green('设置已保存'))
|
||||
} catch (error: any) {
|
||||
console.error(chalk.red('错误:'), error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
import type { Command } from 'commander'
|
||||
import chalk from 'chalk'
|
||||
import inquirer from 'inquirer'
|
||||
import ora from 'ora'
|
||||
import { table } from 'table'
|
||||
import { createClient, requireAuth } from '../client'
|
||||
|
||||
export function userCommands(program: Command) {
|
||||
program
|
||||
.command('list')
|
||||
.description('列出所有用户 (需要管理员权限)')
|
||||
.action(async () => {
|
||||
try {
|
||||
requireAuth()
|
||||
const spinner = ora('获取用户列表...').start()
|
||||
const client = createClient()
|
||||
|
||||
const response = await client.user.get()
|
||||
|
||||
if (response.error) {
|
||||
spinner.fail(chalk.red('获取用户列表失败'))
|
||||
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('用户列表'))
|
||||
|
||||
const users = data.data
|
||||
if (users.length === 0) {
|
||||
console.log(chalk.yellow('暂无用户'))
|
||||
return
|
||||
}
|
||||
|
||||
const tableData = [
|
||||
['ID', '用户名', '角色', '创建时间'],
|
||||
...users.map((user: any) => [
|
||||
user.id,
|
||||
user.username,
|
||||
user.role === 'admin' ? chalk.red('管理员') : chalk.blue('用户'),
|
||||
new Date(user.createdAt).toLocaleString('zh-CN'),
|
||||
]),
|
||||
]
|
||||
|
||||
console.log(table(tableData))
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(chalk.red('错误:'), error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command('create')
|
||||
.description('创建新用户 (需要管理员权限)')
|
||||
.option('-u, --username <username>', '用户名')
|
||||
.option('-p, --password <password>', '密码')
|
||||
.option('-r, --role <role>', '角色 (user/admin)', 'user')
|
||||
.action(async (options) => {
|
||||
try {
|
||||
requireAuth()
|
||||
|
||||
let username = options.username
|
||||
let password = options.password
|
||||
let role = options.role
|
||||
|
||||
if (!username || !password) {
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'username',
|
||||
message: '用户名:',
|
||||
when: !username,
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'password',
|
||||
message: '密码:',
|
||||
when: !password,
|
||||
mask: '*',
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'role',
|
||||
message: '角色:',
|
||||
choices: ['user', 'admin'],
|
||||
default: 'user',
|
||||
when: !role,
|
||||
},
|
||||
])
|
||||
username = username || answers.username
|
||||
password = password || answers.password
|
||||
role = role || answers.role
|
||||
}
|
||||
|
||||
const spinner = ora('创建用户...').start()
|
||||
const client = createClient()
|
||||
|
||||
const response = await client.user.post({
|
||||
username,
|
||||
password,
|
||||
role,
|
||||
})
|
||||
|
||||
if (response.error) {
|
||||
spinner.fail(chalk.red('创建用户失败'))
|
||||
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}`))
|
||||
console.log(chalk.blue(`ID: ${data.data.id}`))
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(chalk.red('错误:'), error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command('delete <id>')
|
||||
.description('删除用户 (需要管理员权限)')
|
||||
.action(async (id) => {
|
||||
try {
|
||||
requireAuth()
|
||||
|
||||
const { confirm } = await inquirer.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: chalk.yellow(`确定要删除用户 ${id} 吗?`),
|
||||
default: false,
|
||||
},
|
||||
])
|
||||
|
||||
if (!confirm) {
|
||||
console.log(chalk.yellow('已取消'))
|
||||
return
|
||||
}
|
||||
|
||||
const spinner = ora('删除用户...').start()
|
||||
const client = createClient()
|
||||
|
||||
const response = await client.user({ id }).delete()
|
||||
|
||||
if (response.error) {
|
||||
spinner.fail(chalk.red('删除用户失败'))
|
||||
console.error(chalk.red(response.error.value))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
spinner.succeed(chalk.green('用户已删除'))
|
||||
} catch (error: any) {
|
||||
console.error(chalk.red('错误:'), error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command('get <id>')
|
||||
.description('获取用户详情')
|
||||
.action(async (id) => {
|
||||
try {
|
||||
requireAuth()
|
||||
const spinner = ora('获取用户信息...').start()
|
||||
const client = createClient()
|
||||
|
||||
const response = await client.user({ id }).get()
|
||||
|
||||
if (response.error) {
|
||||
spinner.fail(chalk.red('获取用户信息失败'))
|
||||
console.error(chalk.red(response.error.value))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const data = response.data as any
|
||||
if (data?.success && data?.data) {
|
||||
const user = data.data
|
||||
spinner.succeed(chalk.green('用户信息'))
|
||||
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')}`))
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(chalk.red('错误:'), error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command('update-password <id>')
|
||||
.description('更新用户密码 (需要管理员权限)')
|
||||
.option('-p, --password <password>', '新密码')
|
||||
.action(async (id, options) => {
|
||||
try {
|
||||
requireAuth()
|
||||
|
||||
let password = options.password
|
||||
|
||||
if (!password) {
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'password',
|
||||
name: 'password',
|
||||
message: '新密码:',
|
||||
mask: '*',
|
||||
},
|
||||
])
|
||||
password = answers.password
|
||||
}
|
||||
|
||||
const spinner = ora('更新密码...').start()
|
||||
const client = createClient()
|
||||
|
||||
const response = await client.user({ id }).password.patch({
|
||||
password,
|
||||
})
|
||||
|
||||
if (response.error) {
|
||||
spinner.fail(chalk.red('更新密码失败'))
|
||||
console.error(chalk.red(response.error.value))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
spinner.succeed(chalk.green('密码已更新'))
|
||||
} catch (error: any) {
|
||||
console.error(chalk.red('错误:'), error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import { homedir } from 'os'
|
||||
import { join } from 'path'
|
||||
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'
|
||||
|
||||
const CONFIG_DIR = join(homedir(), '.memohome')
|
||||
const CONFIG_FILE = join(CONFIG_DIR, 'config.json')
|
||||
|
||||
export interface Config {
|
||||
apiUrl: string
|
||||
token?: string
|
||||
}
|
||||
|
||||
const DEFAULT_CONFIG: Config = {
|
||||
apiUrl: process.env.API_BASE_URL || 'http://localhost:7002',
|
||||
}
|
||||
|
||||
export function ensureConfigDir() {
|
||||
if (!existsSync(CONFIG_DIR)) {
|
||||
mkdirSync(CONFIG_DIR, { recursive: true })
|
||||
}
|
||||
}
|
||||
|
||||
export function loadConfig(): Config {
|
||||
ensureConfigDir()
|
||||
|
||||
if (!existsSync(CONFIG_FILE)) {
|
||||
saveConfig(DEFAULT_CONFIG)
|
||||
return DEFAULT_CONFIG
|
||||
}
|
||||
|
||||
try {
|
||||
const data = readFileSync(CONFIG_FILE, 'utf-8')
|
||||
return { ...DEFAULT_CONFIG, ...JSON.parse(data) }
|
||||
} catch {
|
||||
console.error('Failed to load config, using defaults')
|
||||
return DEFAULT_CONFIG
|
||||
}
|
||||
}
|
||||
|
||||
export function saveConfig(config: Config) {
|
||||
ensureConfigDir()
|
||||
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2))
|
||||
}
|
||||
|
||||
export function getToken(): string | null {
|
||||
const config = loadConfig()
|
||||
return config.token || null
|
||||
}
|
||||
|
||||
export function setToken(token: string) {
|
||||
const config = loadConfig()
|
||||
config.token = token
|
||||
saveConfig(config)
|
||||
}
|
||||
|
||||
export function clearToken() {
|
||||
const config = loadConfig()
|
||||
delete config.token
|
||||
saveConfig(config)
|
||||
}
|
||||
|
||||
export function getApiUrl(): string {
|
||||
const config = loadConfig()
|
||||
return config.apiUrl
|
||||
}
|
||||
|
||||
export function setApiUrl(url: string) {
|
||||
const config = loadConfig()
|
||||
config.apiUrl = url
|
||||
saveConfig(config)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import { Command } from 'commander'
|
||||
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 { memoryCommands } from './commands/memory'
|
||||
import { settingsCommands } from './commands/settings'
|
||||
import { scheduleCommands } from './commands/schedule'
|
||||
import { debugCommands } from './commands/debug'
|
||||
|
||||
const program = new Command()
|
||||
|
||||
program
|
||||
.name('memohome')
|
||||
.description(chalk.bold.blue('🏠 MemoHome CLI - 智能记忆管理助手'))
|
||||
.version('1.0.0')
|
||||
|
||||
// 认证命令
|
||||
const auth = program.command('auth').description('用户认证管理')
|
||||
authCommands(auth)
|
||||
|
||||
// 用户管理命令
|
||||
const user = program.command('user').description('用户管理 (需要管理员权限)')
|
||||
userCommands(user)
|
||||
|
||||
// 模型管理命令
|
||||
const model = program.command('model').description('AI 模型配置管理')
|
||||
modelCommands(model)
|
||||
|
||||
// Agent 对话命令
|
||||
const agent = program.command('agent').description('与 AI Agent 对话')
|
||||
agentCommands(agent)
|
||||
|
||||
// 记忆管理命令
|
||||
const memory = program.command('memory').description('记忆管理')
|
||||
memoryCommands(memory)
|
||||
|
||||
// 设置管理命令
|
||||
const settings = program.command('settings').description('用户设置管理')
|
||||
settingsCommands(settings)
|
||||
|
||||
// 日程管理命令
|
||||
const schedule = program.command('schedule').description('日程管理')
|
||||
scheduleCommands(schedule)
|
||||
|
||||
// 调试命令
|
||||
const debug = program.command('debug').description('调试工具')
|
||||
debugCommands(debug)
|
||||
|
||||
program.parse()
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// API 响应类型定义
|
||||
|
||||
export interface ApiResponse<T = unknown> {
|
||||
success?: boolean
|
||||
data?: T
|
||||
error?: string
|
||||
}
|
||||
|
||||
export interface User {
|
||||
id: string
|
||||
username: string
|
||||
role: string
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export interface Model {
|
||||
id: string
|
||||
name: string
|
||||
modelId: string
|
||||
baseUrl: string
|
||||
apiKey?: string
|
||||
clientType: string
|
||||
type?: 'chat' | 'embedding'
|
||||
dimensions?: number
|
||||
createdAt: string
|
||||
updatedAt?: string
|
||||
}
|
||||
|
||||
export interface Memory {
|
||||
content: string
|
||||
timestamp: string
|
||||
similarity?: number
|
||||
}
|
||||
|
||||
export interface Message {
|
||||
role: 'user' | 'assistant'
|
||||
content: string
|
||||
timestamp: string
|
||||
}
|
||||
|
||||
export interface MessageListResponse {
|
||||
messages: Message[]
|
||||
pagination: {
|
||||
page: number
|
||||
limit: number
|
||||
total: number
|
||||
totalPages: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface Settings {
|
||||
userId: string
|
||||
language?: string
|
||||
maxContextLoadTime?: number
|
||||
defaultChatModel?: string
|
||||
defaultSummaryModel?: string
|
||||
defaultEmbeddingModel?: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface Schedule {
|
||||
id: string
|
||||
title: string
|
||||
description?: string
|
||||
cronExpression: string
|
||||
enabled: boolean
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import chalk from 'chalk'
|
||||
|
||||
/**
|
||||
* 格式化 API 错误信息
|
||||
*/
|
||||
export function formatError(error: unknown): string {
|
||||
if (error === null || error === undefined) {
|
||||
return '未知错误'
|
||||
}
|
||||
|
||||
if (typeof error === 'string') {
|
||||
return error
|
||||
}
|
||||
|
||||
if (typeof error === 'object') {
|
||||
// 尝试提取常见的错误字段
|
||||
const errorObj = error as Record<string, unknown>
|
||||
|
||||
if ('message' in errorObj && typeof errorObj.message === 'string') {
|
||||
return errorObj.message
|
||||
}
|
||||
|
||||
if ('error' in errorObj && typeof errorObj.error === 'string') {
|
||||
return errorObj.error
|
||||
}
|
||||
|
||||
// 如果有 status 和 statusText
|
||||
if ('status' in errorObj && 'statusText' in errorObj) {
|
||||
return `${errorObj.status} ${errorObj.statusText}`
|
||||
}
|
||||
|
||||
// 否则返回格式化的 JSON
|
||||
try {
|
||||
return JSON.stringify(error, null, 2)
|
||||
} catch {
|
||||
return String(error)
|
||||
}
|
||||
}
|
||||
|
||||
return String(error)
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印错误并退出
|
||||
*/
|
||||
export function exitWithError(message: string, error?: unknown): never {
|
||||
console.error(chalk.red(message))
|
||||
if (error) {
|
||||
console.error(chalk.red(formatError(error)))
|
||||
}
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 Eden Treaty 响应错误
|
||||
*/
|
||||
export function handleApiError(response: { error?: { value: unknown } }, defaultMessage = '操作失败'): never {
|
||||
if (response.error) {
|
||||
exitWithError(defaultMessage, response.error.value)
|
||||
}
|
||||
exitWithError(defaultMessage, '未知错误')
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2022"],
|
||||
"moduleResolution": "bundler",
|
||||
"types": ["bun-types"],
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"jsx": "react-jsx",
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
|
||||
Generated
+533
-15
@@ -137,6 +137,46 @@ importers:
|
||||
specifier: latest
|
||||
version: 1.3.5
|
||||
|
||||
packages/cli:
|
||||
dependencies:
|
||||
'@elysiajs/eden':
|
||||
specifier: ^1.4.6
|
||||
version: 1.4.6(elysia@1.4.21(@sinclair/typebox@0.34.47)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3))
|
||||
'@memohome/api':
|
||||
specifier: workspace:*
|
||||
version: link:../api
|
||||
'@memohome/shared':
|
||||
specifier: workspace:*
|
||||
version: link:../shared
|
||||
chalk:
|
||||
specifier: ^5.4.1
|
||||
version: 5.6.2
|
||||
commander:
|
||||
specifier: ^12.1.0
|
||||
version: 12.1.0
|
||||
elysia:
|
||||
specifier: latest
|
||||
version: 1.4.21(@sinclair/typebox@0.34.47)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3)
|
||||
inquirer:
|
||||
specifier: ^12.3.0
|
||||
version: 12.11.1(@types/node@22.19.5)
|
||||
node-fetch:
|
||||
specifier: ^3.3.2
|
||||
version: 3.3.2
|
||||
ora:
|
||||
specifier: ^8.1.1
|
||||
version: 8.2.0
|
||||
table:
|
||||
specifier: ^6.8.2
|
||||
version: 6.9.0
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^22.10.5
|
||||
version: 22.19.5
|
||||
bun-types:
|
||||
specifier: latest
|
||||
version: 1.3.5
|
||||
|
||||
packages/db:
|
||||
dependencies:
|
||||
'@memohome/shared':
|
||||
@@ -189,18 +229,6 @@ importers:
|
||||
specifier: ^0.4.1
|
||||
version: 0.4.1(zod-to-json-schema@3.25.1(zod@4.3.5))(zod@4.3.5)
|
||||
|
||||
packages/schedule:
|
||||
dependencies:
|
||||
'@memohome/db':
|
||||
specifier: workspace:*
|
||||
version: link:../db
|
||||
'@memohome/shared':
|
||||
specifier: workspace:*
|
||||
version: link:../shared
|
||||
drizzle-orm:
|
||||
specifier: ^0.45.1
|
||||
version: 0.45.1(@cloudflare/workers-types@4.20260109.0)(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(bun-types@1.3.5)(pg@8.16.3)(sqlite3@5.1.7)
|
||||
|
||||
packages/shared: {}
|
||||
|
||||
packages/ui:
|
||||
@@ -1142,6 +1170,140 @@ packages:
|
||||
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
|
||||
engines: {node: '>=18.18'}
|
||||
|
||||
'@inquirer/ansi@1.0.2':
|
||||
resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@inquirer/checkbox@4.3.2':
|
||||
resolution: {integrity: sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/confirm@5.1.21':
|
||||
resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/core@10.3.2':
|
||||
resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/editor@4.2.23':
|
||||
resolution: {integrity: sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/expand@4.0.23':
|
||||
resolution: {integrity: sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/external-editor@1.0.3':
|
||||
resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/figures@1.0.15':
|
||||
resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@inquirer/input@4.3.1':
|
||||
resolution: {integrity: sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/number@3.0.23':
|
||||
resolution: {integrity: sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/password@4.0.23':
|
||||
resolution: {integrity: sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/prompts@7.10.1':
|
||||
resolution: {integrity: sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/rawlist@4.1.11':
|
||||
resolution: {integrity: sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/search@3.2.2':
|
||||
resolution: {integrity: sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/select@4.4.2':
|
||||
resolution: {integrity: sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/type@3.0.10':
|
||||
resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@internationalized/date@3.10.1':
|
||||
resolution: {integrity: sha512-oJrXtQiAXLvT9clCf1K4kxp3eKsQhIaZqxEyowkBcsvZDdZkbWrVmnGknxs5flTD0VGsxrxKgBCZty1EzoiMzA==}
|
||||
|
||||
@@ -1629,6 +1791,9 @@ packages:
|
||||
'@types/node@18.19.130':
|
||||
resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==}
|
||||
|
||||
'@types/node@22.19.5':
|
||||
resolution: {integrity: sha512-HfF8+mYcHPcPypui3w3mvzuIErlNOh2OAG+BCeBZCEwyiD5ls2SiCwEyT47OELtf7M3nHxBdu0FsmzdKxkN52Q==}
|
||||
|
||||
'@types/node@24.10.4':
|
||||
resolution: {integrity: sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==}
|
||||
|
||||
@@ -2042,6 +2207,10 @@ packages:
|
||||
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
astral-regex@2.0.0:
|
||||
resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
asynckit@0.4.0:
|
||||
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
||||
|
||||
@@ -2137,6 +2306,13 @@ packages:
|
||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
chalk@5.6.2:
|
||||
resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==}
|
||||
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
|
||||
|
||||
chardet@2.1.1:
|
||||
resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==}
|
||||
|
||||
charenc@0.0.2:
|
||||
resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==}
|
||||
|
||||
@@ -2158,6 +2334,18 @@ packages:
|
||||
resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
cli-cursor@5.0.0:
|
||||
resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
cli-spinners@2.9.2:
|
||||
resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
cli-width@4.1.0:
|
||||
resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==}
|
||||
engines: {node: '>= 12'}
|
||||
|
||||
cloudflare@4.5.0:
|
||||
resolution: {integrity: sha512-fPcbPKx4zF45jBvQ0z7PCdgejVAPBBCZxwqk1k7krQNfpM07Cfj97/Q6wBzvYqlWXx/zt1S9+m8vnfCe06umbQ==}
|
||||
|
||||
@@ -2184,6 +2372,10 @@ packages:
|
||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
commander@12.1.0:
|
||||
resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
compare-versions@6.1.1:
|
||||
resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==}
|
||||
|
||||
@@ -2425,6 +2617,9 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
emoji-regex@10.6.0:
|
||||
resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
|
||||
|
||||
emoji-regex@8.0.0:
|
||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||
|
||||
@@ -2731,6 +2926,10 @@ packages:
|
||||
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
get-east-asian-width@1.4.0:
|
||||
resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
get-intrinsic@1.3.0:
|
||||
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -2835,6 +3034,10 @@ packages:
|
||||
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
iconv-lite@0.7.2:
|
||||
resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
ieee754@1.2.1:
|
||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||
|
||||
@@ -2875,6 +3078,15 @@ packages:
|
||||
ini@1.3.8:
|
||||
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
|
||||
|
||||
inquirer@12.11.1:
|
||||
resolution: {integrity: sha512-9VF7mrY+3OmsAfjH3yKz/pLbJ5z22E23hENKw3/LNSaA/sAt3v49bDRY+Ygct1xwuKT+U+cBfTzjCPySna69Qw==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
ip-address@10.1.0:
|
||||
resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==}
|
||||
engines: {node: '>= 12'}
|
||||
@@ -2908,6 +3120,10 @@ packages:
|
||||
engines: {node: '>=14.16'}
|
||||
hasBin: true
|
||||
|
||||
is-interactive@2.0.0:
|
||||
resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
is-lambda@1.0.1:
|
||||
resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==}
|
||||
|
||||
@@ -2915,6 +3131,14 @@ packages:
|
||||
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||
engines: {node: '>=0.12.0'}
|
||||
|
||||
is-unicode-supported@1.3.0:
|
||||
resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
is-unicode-supported@2.1.0:
|
||||
resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
is-what@5.5.0:
|
||||
resolution: {integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -3151,9 +3375,16 @@ packages:
|
||||
lodash.once@4.1.1:
|
||||
resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==}
|
||||
|
||||
lodash.truncate@4.4.2:
|
||||
resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==}
|
||||
|
||||
lodash@4.17.21:
|
||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||
|
||||
log-symbols@6.0.0:
|
||||
resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
lru-cache@10.4.3:
|
||||
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
|
||||
|
||||
@@ -3226,6 +3457,10 @@ packages:
|
||||
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
mimic-function@5.0.1:
|
||||
resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
mimic-response@3.1.0:
|
||||
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -3308,6 +3543,10 @@ packages:
|
||||
resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==}
|
||||
hasBin: true
|
||||
|
||||
mute-stream@2.0.0:
|
||||
resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==}
|
||||
engines: {node: ^18.17.0 || >=20.5.0}
|
||||
|
||||
nanoid@3.3.11:
|
||||
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
@@ -3408,6 +3647,10 @@ packages:
|
||||
once@1.4.0:
|
||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||
|
||||
onetime@7.0.0:
|
||||
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
open@10.2.0:
|
||||
resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -3431,6 +3674,10 @@ packages:
|
||||
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
ora@8.2.0:
|
||||
resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
p-finally@1.0.0:
|
||||
resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -3663,6 +3910,10 @@ packages:
|
||||
engines: {node: '>= 0.4'}
|
||||
hasBin: true
|
||||
|
||||
restore-cursor@5.1.0:
|
||||
resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
retry@0.12.0:
|
||||
resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==}
|
||||
engines: {node: '>= 4'}
|
||||
@@ -3692,6 +3943,10 @@ packages:
|
||||
resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
run-async@4.0.6:
|
||||
resolution: {integrity: sha512-IoDlSLTs3Yq593mb3ZoKWKXMNu3UpObxhgA/Xuid5p4bbfi2jdY1Hj0m1K+0/tEuQTxIGMhQDqGjKb7RuxGpAQ==}
|
||||
engines: {node: '>=0.12.0'}
|
||||
|
||||
rxjs@7.8.2:
|
||||
resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
|
||||
|
||||
@@ -3757,6 +4012,10 @@ packages:
|
||||
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
slice-ansi@4.0.0:
|
||||
resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
smart-buffer@4.2.0:
|
||||
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
|
||||
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
|
||||
@@ -3808,6 +4067,10 @@ packages:
|
||||
std-env@3.10.0:
|
||||
resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==}
|
||||
|
||||
stdin-discarder@0.2.2:
|
||||
resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
string-argv@0.3.2:
|
||||
resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
|
||||
engines: {node: '>=0.6.19'}
|
||||
@@ -3820,6 +4083,10 @@ packages:
|
||||
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
string-width@7.2.0:
|
||||
resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
string_decoder@1.3.0:
|
||||
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
|
||||
|
||||
@@ -3859,6 +4126,10 @@ packages:
|
||||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
table@6.9.0:
|
||||
resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
||||
tailwind-merge@3.4.0:
|
||||
resolution: {integrity: sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==}
|
||||
|
||||
@@ -3961,6 +4232,9 @@ packages:
|
||||
undici-types@5.26.5:
|
||||
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
|
||||
|
||||
undici-types@6.21.0:
|
||||
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
||||
|
||||
undici-types@7.16.0:
|
||||
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
|
||||
|
||||
@@ -4246,6 +4520,10 @@ packages:
|
||||
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
wrap-ansi@6.2.0:
|
||||
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
wrap-ansi@7.0.0:
|
||||
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -4317,6 +4595,10 @@ packages:
|
||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
yoctocolors-cjs@2.1.3:
|
||||
resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
zod-to-json-schema@3.25.1:
|
||||
resolution: {integrity: sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==}
|
||||
peerDependencies:
|
||||
@@ -5036,6 +5318,131 @@ snapshots:
|
||||
|
||||
'@humanwhocodes/retry@0.4.3': {}
|
||||
|
||||
'@inquirer/ansi@1.0.2': {}
|
||||
|
||||
'@inquirer/checkbox@4.3.2(@types/node@22.19.5)':
|
||||
dependencies:
|
||||
'@inquirer/ansi': 1.0.2
|
||||
'@inquirer/core': 10.3.2(@types/node@22.19.5)
|
||||
'@inquirer/figures': 1.0.15
|
||||
'@inquirer/type': 3.0.10(@types/node@22.19.5)
|
||||
yoctocolors-cjs: 2.1.3
|
||||
optionalDependencies:
|
||||
'@types/node': 22.19.5
|
||||
|
||||
'@inquirer/confirm@5.1.21(@types/node@22.19.5)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.3.2(@types/node@22.19.5)
|
||||
'@inquirer/type': 3.0.10(@types/node@22.19.5)
|
||||
optionalDependencies:
|
||||
'@types/node': 22.19.5
|
||||
|
||||
'@inquirer/core@10.3.2(@types/node@22.19.5)':
|
||||
dependencies:
|
||||
'@inquirer/ansi': 1.0.2
|
||||
'@inquirer/figures': 1.0.15
|
||||
'@inquirer/type': 3.0.10(@types/node@22.19.5)
|
||||
cli-width: 4.1.0
|
||||
mute-stream: 2.0.0
|
||||
signal-exit: 4.1.0
|
||||
wrap-ansi: 6.2.0
|
||||
yoctocolors-cjs: 2.1.3
|
||||
optionalDependencies:
|
||||
'@types/node': 22.19.5
|
||||
|
||||
'@inquirer/editor@4.2.23(@types/node@22.19.5)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.3.2(@types/node@22.19.5)
|
||||
'@inquirer/external-editor': 1.0.3(@types/node@22.19.5)
|
||||
'@inquirer/type': 3.0.10(@types/node@22.19.5)
|
||||
optionalDependencies:
|
||||
'@types/node': 22.19.5
|
||||
|
||||
'@inquirer/expand@4.0.23(@types/node@22.19.5)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.3.2(@types/node@22.19.5)
|
||||
'@inquirer/type': 3.0.10(@types/node@22.19.5)
|
||||
yoctocolors-cjs: 2.1.3
|
||||
optionalDependencies:
|
||||
'@types/node': 22.19.5
|
||||
|
||||
'@inquirer/external-editor@1.0.3(@types/node@22.19.5)':
|
||||
dependencies:
|
||||
chardet: 2.1.1
|
||||
iconv-lite: 0.7.2
|
||||
optionalDependencies:
|
||||
'@types/node': 22.19.5
|
||||
|
||||
'@inquirer/figures@1.0.15': {}
|
||||
|
||||
'@inquirer/input@4.3.1(@types/node@22.19.5)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.3.2(@types/node@22.19.5)
|
||||
'@inquirer/type': 3.0.10(@types/node@22.19.5)
|
||||
optionalDependencies:
|
||||
'@types/node': 22.19.5
|
||||
|
||||
'@inquirer/number@3.0.23(@types/node@22.19.5)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.3.2(@types/node@22.19.5)
|
||||
'@inquirer/type': 3.0.10(@types/node@22.19.5)
|
||||
optionalDependencies:
|
||||
'@types/node': 22.19.5
|
||||
|
||||
'@inquirer/password@4.0.23(@types/node@22.19.5)':
|
||||
dependencies:
|
||||
'@inquirer/ansi': 1.0.2
|
||||
'@inquirer/core': 10.3.2(@types/node@22.19.5)
|
||||
'@inquirer/type': 3.0.10(@types/node@22.19.5)
|
||||
optionalDependencies:
|
||||
'@types/node': 22.19.5
|
||||
|
||||
'@inquirer/prompts@7.10.1(@types/node@22.19.5)':
|
||||
dependencies:
|
||||
'@inquirer/checkbox': 4.3.2(@types/node@22.19.5)
|
||||
'@inquirer/confirm': 5.1.21(@types/node@22.19.5)
|
||||
'@inquirer/editor': 4.2.23(@types/node@22.19.5)
|
||||
'@inquirer/expand': 4.0.23(@types/node@22.19.5)
|
||||
'@inquirer/input': 4.3.1(@types/node@22.19.5)
|
||||
'@inquirer/number': 3.0.23(@types/node@22.19.5)
|
||||
'@inquirer/password': 4.0.23(@types/node@22.19.5)
|
||||
'@inquirer/rawlist': 4.1.11(@types/node@22.19.5)
|
||||
'@inquirer/search': 3.2.2(@types/node@22.19.5)
|
||||
'@inquirer/select': 4.4.2(@types/node@22.19.5)
|
||||
optionalDependencies:
|
||||
'@types/node': 22.19.5
|
||||
|
||||
'@inquirer/rawlist@4.1.11(@types/node@22.19.5)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.3.2(@types/node@22.19.5)
|
||||
'@inquirer/type': 3.0.10(@types/node@22.19.5)
|
||||
yoctocolors-cjs: 2.1.3
|
||||
optionalDependencies:
|
||||
'@types/node': 22.19.5
|
||||
|
||||
'@inquirer/search@3.2.2(@types/node@22.19.5)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.3.2(@types/node@22.19.5)
|
||||
'@inquirer/figures': 1.0.15
|
||||
'@inquirer/type': 3.0.10(@types/node@22.19.5)
|
||||
yoctocolors-cjs: 2.1.3
|
||||
optionalDependencies:
|
||||
'@types/node': 22.19.5
|
||||
|
||||
'@inquirer/select@4.4.2(@types/node@22.19.5)':
|
||||
dependencies:
|
||||
'@inquirer/ansi': 1.0.2
|
||||
'@inquirer/core': 10.3.2(@types/node@22.19.5)
|
||||
'@inquirer/figures': 1.0.15
|
||||
'@inquirer/type': 3.0.10(@types/node@22.19.5)
|
||||
yoctocolors-cjs: 2.1.3
|
||||
optionalDependencies:
|
||||
'@types/node': 22.19.5
|
||||
|
||||
'@inquirer/type@3.0.10(@types/node@22.19.5)':
|
||||
optionalDependencies:
|
||||
'@types/node': 22.19.5
|
||||
|
||||
'@internationalized/date@3.10.1':
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.18
|
||||
@@ -5513,6 +5920,10 @@ snapshots:
|
||||
dependencies:
|
||||
undici-types: 5.26.5
|
||||
|
||||
'@types/node@22.19.5':
|
||||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
|
||||
'@types/node@24.10.4':
|
||||
dependencies:
|
||||
undici-types: 7.16.0
|
||||
@@ -5523,7 +5934,7 @@ snapshots:
|
||||
|
||||
'@types/pg@8.16.0':
|
||||
dependencies:
|
||||
'@types/node': 25.0.3
|
||||
'@types/node': 24.10.4
|
||||
pg-protocol: 1.10.3
|
||||
pg-types: 2.2.0
|
||||
|
||||
@@ -6074,6 +6485,8 @@ snapshots:
|
||||
|
||||
assertion-error@2.0.1: {}
|
||||
|
||||
astral-regex@2.0.0: {}
|
||||
|
||||
asynckit@0.4.0: {}
|
||||
|
||||
axios@1.7.7:
|
||||
@@ -6193,6 +6606,10 @@ snapshots:
|
||||
ansi-styles: 4.3.0
|
||||
supports-color: 7.2.0
|
||||
|
||||
chalk@5.6.2: {}
|
||||
|
||||
chardet@2.1.1: {}
|
||||
|
||||
charenc@0.0.2: {}
|
||||
|
||||
chownr@1.1.4: {}
|
||||
@@ -6208,6 +6625,14 @@ snapshots:
|
||||
clean-stack@2.2.0:
|
||||
optional: true
|
||||
|
||||
cli-cursor@5.0.0:
|
||||
dependencies:
|
||||
restore-cursor: 5.1.0
|
||||
|
||||
cli-spinners@2.9.2: {}
|
||||
|
||||
cli-width@4.1.0: {}
|
||||
|
||||
cloudflare@4.5.0(encoding@0.1.13):
|
||||
dependencies:
|
||||
'@types/node': 18.19.130
|
||||
@@ -6237,6 +6662,8 @@ snapshots:
|
||||
dependencies:
|
||||
delayed-stream: 1.0.0
|
||||
|
||||
commander@12.1.0: {}
|
||||
|
||||
compare-versions@6.1.1: {}
|
||||
|
||||
concat-map@0.0.1: {}
|
||||
@@ -6363,6 +6790,8 @@ snapshots:
|
||||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
emoji-regex@10.6.0: {}
|
||||
|
||||
emoji-regex@8.0.0: {}
|
||||
|
||||
emoji-regex@9.2.2: {}
|
||||
@@ -6746,6 +7175,8 @@ snapshots:
|
||||
|
||||
gensync@1.0.0-beta.2: {}
|
||||
|
||||
get-east-asian-width@1.4.0: {}
|
||||
|
||||
get-intrinsic@1.3.0:
|
||||
dependencies:
|
||||
call-bind-apply-helpers: 1.0.2
|
||||
@@ -6896,6 +7327,10 @@ snapshots:
|
||||
safer-buffer: 2.1.2
|
||||
optional: true
|
||||
|
||||
iconv-lite@0.7.2:
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
|
||||
ieee754@1.2.1: {}
|
||||
|
||||
ignore@5.3.2: {}
|
||||
@@ -6927,6 +7362,18 @@ snapshots:
|
||||
|
||||
ini@1.3.8: {}
|
||||
|
||||
inquirer@12.11.1(@types/node@22.19.5):
|
||||
dependencies:
|
||||
'@inquirer/ansi': 1.0.2
|
||||
'@inquirer/core': 10.3.2(@types/node@22.19.5)
|
||||
'@inquirer/prompts': 7.10.1(@types/node@22.19.5)
|
||||
'@inquirer/type': 3.0.10(@types/node@22.19.5)
|
||||
mute-stream: 2.0.0
|
||||
run-async: 4.0.6
|
||||
rxjs: 7.8.2
|
||||
optionalDependencies:
|
||||
'@types/node': 22.19.5
|
||||
|
||||
ip-address@10.1.0:
|
||||
optional: true
|
||||
|
||||
@@ -6950,11 +7397,17 @@ snapshots:
|
||||
dependencies:
|
||||
is-docker: 3.0.0
|
||||
|
||||
is-interactive@2.0.0: {}
|
||||
|
||||
is-lambda@1.0.1:
|
||||
optional: true
|
||||
|
||||
is-number@7.0.0: {}
|
||||
|
||||
is-unicode-supported@1.3.0: {}
|
||||
|
||||
is-unicode-supported@2.1.0: {}
|
||||
|
||||
is-what@5.5.0: {}
|
||||
|
||||
is-wsl@3.1.0:
|
||||
@@ -7172,8 +7625,15 @@ snapshots:
|
||||
|
||||
lodash.once@4.1.1: {}
|
||||
|
||||
lodash.truncate@4.4.2: {}
|
||||
|
||||
lodash@4.17.21: {}
|
||||
|
||||
log-symbols@6.0.0:
|
||||
dependencies:
|
||||
chalk: 5.6.2
|
||||
is-unicode-supported: 1.3.0
|
||||
|
||||
lru-cache@10.4.3: {}
|
||||
|
||||
lru-cache@5.1.1:
|
||||
@@ -7268,6 +7728,8 @@ snapshots:
|
||||
dependencies:
|
||||
mime-db: 1.52.0
|
||||
|
||||
mimic-function@5.0.1: {}
|
||||
|
||||
mimic-response@3.1.0: {}
|
||||
|
||||
minimatch@10.0.3:
|
||||
@@ -7347,6 +7809,8 @@ snapshots:
|
||||
|
||||
mustache@4.2.0: {}
|
||||
|
||||
mute-stream@2.0.0: {}
|
||||
|
||||
nanoid@3.3.11: {}
|
||||
|
||||
nanoid@5.1.6: {}
|
||||
@@ -7455,6 +7919,10 @@ snapshots:
|
||||
dependencies:
|
||||
wrappy: 1.0.2
|
||||
|
||||
onetime@7.0.0:
|
||||
dependencies:
|
||||
mimic-function: 5.0.1
|
||||
|
||||
open@10.2.0:
|
||||
dependencies:
|
||||
default-browser: 5.4.0
|
||||
@@ -7504,6 +7972,18 @@ snapshots:
|
||||
type-check: 0.4.0
|
||||
word-wrap: 1.2.5
|
||||
|
||||
ora@8.2.0:
|
||||
dependencies:
|
||||
chalk: 5.6.2
|
||||
cli-cursor: 5.0.0
|
||||
cli-spinners: 2.9.2
|
||||
is-interactive: 2.0.0
|
||||
is-unicode-supported: 2.1.0
|
||||
log-symbols: 6.0.0
|
||||
stdin-discarder: 0.2.2
|
||||
string-width: 7.2.0
|
||||
strip-ansi: 7.1.2
|
||||
|
||||
p-finally@1.0.0: {}
|
||||
|
||||
p-limit@3.1.0:
|
||||
@@ -7745,6 +8225,11 @@ snapshots:
|
||||
path-parse: 1.0.7
|
||||
supports-preserve-symlinks-flag: 1.0.0
|
||||
|
||||
restore-cursor@5.1.0:
|
||||
dependencies:
|
||||
onetime: 7.0.0
|
||||
signal-exit: 4.1.0
|
||||
|
||||
retry@0.12.0:
|
||||
optional: true
|
||||
|
||||
@@ -7791,14 +8276,15 @@ snapshots:
|
||||
|
||||
run-applescript@7.1.0: {}
|
||||
|
||||
run-async@4.0.6: {}
|
||||
|
||||
rxjs@7.8.2:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
safe-buffer@5.2.1: {}
|
||||
|
||||
safer-buffer@2.1.2:
|
||||
optional: true
|
||||
safer-buffer@2.1.2: {}
|
||||
|
||||
semver@6.3.1: {}
|
||||
|
||||
@@ -7844,6 +8330,12 @@ snapshots:
|
||||
|
||||
slash@3.0.0: {}
|
||||
|
||||
slice-ansi@4.0.0:
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
astral-regex: 2.0.0
|
||||
is-fullwidth-code-point: 3.0.0
|
||||
|
||||
smart-buffer@4.2.0:
|
||||
optional: true
|
||||
|
||||
@@ -7902,6 +8394,8 @@ snapshots:
|
||||
|
||||
std-env@3.10.0: {}
|
||||
|
||||
stdin-discarder@0.2.2: {}
|
||||
|
||||
string-argv@0.3.2: {}
|
||||
|
||||
string-width@4.2.3:
|
||||
@@ -7916,6 +8410,12 @@ snapshots:
|
||||
emoji-regex: 9.2.2
|
||||
strip-ansi: 7.1.2
|
||||
|
||||
string-width@7.2.0:
|
||||
dependencies:
|
||||
emoji-regex: 10.6.0
|
||||
get-east-asian-width: 1.4.0
|
||||
strip-ansi: 7.1.2
|
||||
|
||||
string_decoder@1.3.0:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
@@ -7950,6 +8450,14 @@ snapshots:
|
||||
|
||||
supports-preserve-symlinks-flag@1.0.0: {}
|
||||
|
||||
table@6.9.0:
|
||||
dependencies:
|
||||
ajv: 8.13.0
|
||||
lodash.truncate: 4.4.2
|
||||
slice-ansi: 4.0.0
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
|
||||
tailwind-merge@3.4.0: {}
|
||||
|
||||
tailwindcss@4.1.18: {}
|
||||
@@ -8049,6 +8557,8 @@ snapshots:
|
||||
|
||||
undici-types@5.26.5: {}
|
||||
|
||||
undici-types@6.21.0: {}
|
||||
|
||||
undici-types@7.16.0: {}
|
||||
|
||||
undici@5.28.5:
|
||||
@@ -8322,6 +8832,12 @@ snapshots:
|
||||
|
||||
word-wrap@1.2.5: {}
|
||||
|
||||
wrap-ansi@6.2.0:
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
|
||||
wrap-ansi@7.0.0:
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
@@ -8383,6 +8899,8 @@ snapshots:
|
||||
|
||||
yocto-queue@0.1.0: {}
|
||||
|
||||
yoctocolors-cjs@2.1.3: {}
|
||||
|
||||
zod-to-json-schema@3.25.1(zod@3.25.76):
|
||||
dependencies:
|
||||
zod: 3.25.76
|
||||
|
||||
Reference in New Issue
Block a user