@xiaozhi-client/cli 1.9.4-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +98 -0
- package/fix-imports.js +32 -0
- package/package.json +26 -0
- package/project.json +75 -0
- package/src/Constants.ts +105 -0
- package/src/Container.ts +212 -0
- package/src/Types.ts +79 -0
- package/src/commands/CommandHandlerFactory.ts +98 -0
- package/src/commands/ConfigCommandHandler.ts +279 -0
- package/src/commands/EndpointCommandHandler.ts +158 -0
- package/src/commands/McpCommandHandler.ts +778 -0
- package/src/commands/ProjectCommandHandler.ts +254 -0
- package/src/commands/ServiceCommandHandler.ts +182 -0
- package/src/commands/__tests__/CommandHandlerFactory.test.ts +323 -0
- package/src/commands/__tests__/CommandRegistry.test.ts +287 -0
- package/src/commands/__tests__/ConfigCommandHandler.test.ts +844 -0
- package/src/commands/__tests__/EndpointCommandHandler.test.ts +426 -0
- package/src/commands/__tests__/McpCommandHandler.test.ts +753 -0
- package/src/commands/__tests__/ProjectCommandHandler.test.ts +230 -0
- package/src/commands/__tests__/ServiceCommands.integration.test.ts +408 -0
- package/src/commands/index.ts +351 -0
- package/src/errors/ErrorHandlers.ts +141 -0
- package/src/errors/ErrorMessages.ts +121 -0
- package/src/errors/__tests__/index.test.ts +186 -0
- package/src/errors/index.ts +163 -0
- package/src/global.d.ts +19 -0
- package/src/index.ts +53 -0
- package/src/interfaces/Command.ts +128 -0
- package/src/interfaces/CommandTypes.ts +95 -0
- package/src/interfaces/Config.ts +25 -0
- package/src/interfaces/Service.ts +99 -0
- package/src/services/DaemonManager.ts +318 -0
- package/src/services/ProcessManager.ts +235 -0
- package/src/services/ServiceManager.ts +319 -0
- package/src/services/TemplateManager.ts +382 -0
- package/src/services/__tests__/DaemonManager.test.ts +378 -0
- package/src/services/__tests__/DaemonMode.integration.test.ts +321 -0
- package/src/services/__tests__/ProcessManager.test.ts +296 -0
- package/src/services/__tests__/ServiceManager.test.ts +774 -0
- package/src/services/__tests__/TemplateManager.test.ts +337 -0
- package/src/types/backend.d.ts +48 -0
- package/src/utils/FileUtils.ts +320 -0
- package/src/utils/FormatUtils.ts +198 -0
- package/src/utils/PathUtils.ts +255 -0
- package/src/utils/PlatformUtils.ts +217 -0
- package/src/utils/Validation.ts +274 -0
- package/src/utils/VersionUtils.ts +141 -0
- package/src/utils/__tests__/FileUtils.test.ts +728 -0
- package/src/utils/__tests__/FormatUtils.test.ts +243 -0
- package/src/utils/__tests__/PathUtils.test.ts +1165 -0
- package/src/utils/__tests__/PlatformUtils.test.ts +723 -0
- package/src/utils/__tests__/Validation.test.ts +560 -0
- package/src/utils/__tests__/VersionUtils.test.ts +410 -0
- package/tsconfig.json +32 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/tsup.config.ts +107 -0
- package/vitest.config.ts +97 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 命令注册器
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Command } from "commander";
|
|
6
|
+
import { ErrorHandler } from "../errors/ErrorHandlers";
|
|
7
|
+
import type {
|
|
8
|
+
CommandHandler,
|
|
9
|
+
ICommandHandlerFactory,
|
|
10
|
+
ICommandRegistry,
|
|
11
|
+
} from "../interfaces/Command";
|
|
12
|
+
import type { IDIContainer } from "../interfaces/Config";
|
|
13
|
+
import { CommandHandlerFactory } from "./CommandHandlerFactory";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 命令注册器实现
|
|
17
|
+
*/
|
|
18
|
+
export class CommandRegistry implements ICommandRegistry {
|
|
19
|
+
private handlers: CommandHandler[] = [];
|
|
20
|
+
private handlerFactory: ICommandHandlerFactory;
|
|
21
|
+
|
|
22
|
+
constructor(private container: IDIContainer) {
|
|
23
|
+
this.handlerFactory = new CommandHandlerFactory(container);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 注册所有命令到 Commander 程序
|
|
28
|
+
*/
|
|
29
|
+
async registerCommands(program: Command): Promise<void> {
|
|
30
|
+
try {
|
|
31
|
+
// 注册基本命令
|
|
32
|
+
this.registerVersionCommand(program);
|
|
33
|
+
this.registerHelpCommand(program);
|
|
34
|
+
|
|
35
|
+
// 创建并注册所有功能命令处理器
|
|
36
|
+
const handlers = this.handlerFactory.createHandlers();
|
|
37
|
+
for (const handler of handlers) {
|
|
38
|
+
this.registerHandler(handler);
|
|
39
|
+
this.registerCommand(program, handler);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 注册向后兼容的顶级服务命令
|
|
43
|
+
this.registerLegacyServiceCommands(program, handlers);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
ErrorHandler.handle(error as Error);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 注册命令处理器
|
|
51
|
+
*/
|
|
52
|
+
registerHandler(handler: CommandHandler): void {
|
|
53
|
+
this.handlers.push(handler);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 注册单个命令
|
|
58
|
+
*/
|
|
59
|
+
registerCommand(program: Command, handler: CommandHandler): void {
|
|
60
|
+
// 如果有子命令,创建命令组
|
|
61
|
+
if (handler.subcommands && handler.subcommands.length > 0) {
|
|
62
|
+
const commandGroup = program
|
|
63
|
+
.command(handler.name)
|
|
64
|
+
.description(handler.description);
|
|
65
|
+
|
|
66
|
+
for (const subcommand of handler.subcommands) {
|
|
67
|
+
let subcommandName = subcommand.name;
|
|
68
|
+
|
|
69
|
+
// 特殊处理需要参数的子命令
|
|
70
|
+
if (subcommand.name === "get") {
|
|
71
|
+
subcommandName = "get <key>";
|
|
72
|
+
} else if (subcommand.name === "set") {
|
|
73
|
+
subcommandName = "set <key> <value>";
|
|
74
|
+
} else if (subcommand.name === "call") {
|
|
75
|
+
subcommandName = "call <serviceName> <toolName>";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const cmd = commandGroup
|
|
79
|
+
.command(subcommandName)
|
|
80
|
+
.description(subcommand.description);
|
|
81
|
+
|
|
82
|
+
// 添加子命令选项
|
|
83
|
+
if (subcommand.options) {
|
|
84
|
+
for (const option of subcommand.options) {
|
|
85
|
+
cmd.option(option.flags, option.description, option.defaultValue);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 设置子命令处理函数
|
|
90
|
+
cmd.action(async (...args) => {
|
|
91
|
+
try {
|
|
92
|
+
// Commander.js 传递的最后一个参数是 Command 对象,包含选项值
|
|
93
|
+
const command = args[args.length - 1];
|
|
94
|
+
const options = command.opts(); // 获取解析后的选项
|
|
95
|
+
await subcommand.execute(args.slice(0, -1), options);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
ErrorHandler.handle(error as Error);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 设置主命令的默认行为
|
|
103
|
+
commandGroup.action(async (...args) => {
|
|
104
|
+
try {
|
|
105
|
+
const command = args[args.length - 1];
|
|
106
|
+
const options = command.opts(); // 获取解析后的选项
|
|
107
|
+
await handler.execute(args.slice(0, -1), options);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
ErrorHandler.handle(error as Error);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
} else {
|
|
113
|
+
// 没有子命令,注册为普通命令
|
|
114
|
+
let commandName = handler.name;
|
|
115
|
+
|
|
116
|
+
// 特殊处理 create 命令,需要接受项目名称参数
|
|
117
|
+
if (handler.name === "create") {
|
|
118
|
+
commandName = "create <projectName>";
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const command = program
|
|
122
|
+
.command(commandName)
|
|
123
|
+
.description(handler.description);
|
|
124
|
+
|
|
125
|
+
// 添加选项
|
|
126
|
+
if (handler.options) {
|
|
127
|
+
for (const option of handler.options) {
|
|
128
|
+
command.option(option.flags, option.description, option.defaultValue);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// 设置主命令处理函数
|
|
133
|
+
command.action(async (...args) => {
|
|
134
|
+
try {
|
|
135
|
+
const command = args[args.length - 1];
|
|
136
|
+
const options = command.opts(); // 获取解析后的选项
|
|
137
|
+
await handler.execute(args.slice(0, -1), options);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
ErrorHandler.handle(error as Error);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 注册版本命令
|
|
147
|
+
*/
|
|
148
|
+
private registerVersionCommand(program: Command): void {
|
|
149
|
+
const versionUtils = this.container.get("versionUtils") as any;
|
|
150
|
+
|
|
151
|
+
program.version(versionUtils.getVersion(), "-v, --version", "显示版本信息");
|
|
152
|
+
|
|
153
|
+
// 注册 --info 选项
|
|
154
|
+
program.option("--info", "显示详细信息");
|
|
155
|
+
|
|
156
|
+
// 注册 --version-info 选项
|
|
157
|
+
program.option("--version-info", "显示详细版本信息");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* 注册帮助命令
|
|
162
|
+
*/
|
|
163
|
+
private registerHelpCommand(program: Command): void {
|
|
164
|
+
program.helpOption("-h, --help", "显示帮助信息").addHelpText(
|
|
165
|
+
"after",
|
|
166
|
+
`
|
|
167
|
+
示例:
|
|
168
|
+
xiaozhi init # 初始化配置文件
|
|
169
|
+
xiaozhi start # 启动服务(包含 Web UI)
|
|
170
|
+
xiaozhi start -d # 后台启动服务
|
|
171
|
+
xiaozhi start -s 3000 # 以 MCP Server 模式启动
|
|
172
|
+
xiaozhi stop # 停止服务
|
|
173
|
+
xiaozhi status # 检查服务状态
|
|
174
|
+
xiaozhi restart -d # 重启服务(后台模式)
|
|
175
|
+
xiaozhi config mcpEndpoint <url> # 设置 MCP 端点
|
|
176
|
+
xiaozhi create my-project # 创建项目
|
|
177
|
+
xiaozhi mcp list # 列出 MCP 服务
|
|
178
|
+
|
|
179
|
+
更多信息请访问: https://github.com/your-org/xiaozhi-client
|
|
180
|
+
`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* 注册向后兼容的顶级服务命令
|
|
186
|
+
*/
|
|
187
|
+
private registerLegacyServiceCommands(
|
|
188
|
+
program: Command,
|
|
189
|
+
handlers: CommandHandler[]
|
|
190
|
+
): void {
|
|
191
|
+
// 找到服务命令处理器
|
|
192
|
+
const serviceHandler = handlers.find((h) => h.name === "service");
|
|
193
|
+
if (!serviceHandler || !serviceHandler.subcommands) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 为每个服务子命令创建顶级命令
|
|
198
|
+
for (const subcommand of serviceHandler.subcommands) {
|
|
199
|
+
const command = program
|
|
200
|
+
.command(subcommand.name)
|
|
201
|
+
.description(subcommand.description);
|
|
202
|
+
|
|
203
|
+
// 添加选项
|
|
204
|
+
if (subcommand.options) {
|
|
205
|
+
for (const option of subcommand.options) {
|
|
206
|
+
command.option(option.flags, option.description, option.defaultValue);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// 设置命令处理函数
|
|
211
|
+
command.action(async (...args) => {
|
|
212
|
+
try {
|
|
213
|
+
// Commander.js 传递的最后一个参数是 Command 对象,包含选项值
|
|
214
|
+
const command = args[args.length - 1];
|
|
215
|
+
const options = command.opts(); // 获取解析后的选项
|
|
216
|
+
await subcommand.execute(args.slice(0, -1), options);
|
|
217
|
+
} catch (error) {
|
|
218
|
+
ErrorHandler.handle(error as Error);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* 注册服务管理命令(稍后实现)
|
|
226
|
+
*/
|
|
227
|
+
// private async registerServiceCommands(program: Command): Promise<void> {
|
|
228
|
+
// const serviceCommand = this.container.get('serviceCommand');
|
|
229
|
+
|
|
230
|
+
// // start 命令
|
|
231
|
+
// program
|
|
232
|
+
// .command('start')
|
|
233
|
+
// .description('启动服务')
|
|
234
|
+
// .option('-d, --daemon', '在后台运行服务')
|
|
235
|
+
// .option('-u, --ui', '同时启动 Web UI 服务')
|
|
236
|
+
// .option('-s, --server [port]', '以 MCP Server 模式启动 (可选指定端口,默认 3000)')
|
|
237
|
+
// .option('--stdio', '以 stdio 模式运行 MCP Server (用于 Cursor 等客户端)')
|
|
238
|
+
// .action(async (options) => {
|
|
239
|
+
// await serviceCommand.start(options);
|
|
240
|
+
// });
|
|
241
|
+
|
|
242
|
+
// // stop 命令
|
|
243
|
+
// program
|
|
244
|
+
// .command('stop')
|
|
245
|
+
// .description('停止服务')
|
|
246
|
+
// .action(async () => {
|
|
247
|
+
// await serviceCommand.stop();
|
|
248
|
+
// });
|
|
249
|
+
|
|
250
|
+
// // status 命令
|
|
251
|
+
// program
|
|
252
|
+
// .command('status')
|
|
253
|
+
// .description('检查服务状态')
|
|
254
|
+
// .action(async () => {
|
|
255
|
+
// await serviceCommand.status();
|
|
256
|
+
// });
|
|
257
|
+
|
|
258
|
+
// // restart 命令
|
|
259
|
+
// program
|
|
260
|
+
// .command('restart')
|
|
261
|
+
// .description('重启服务')
|
|
262
|
+
// .option('-d, --daemon', '在后台运行服务')
|
|
263
|
+
// .option('-u, --ui', '同时启动 Web UI 服务')
|
|
264
|
+
// .action(async (options) => {
|
|
265
|
+
// await serviceCommand.restart(options);
|
|
266
|
+
// });
|
|
267
|
+
|
|
268
|
+
// // attach 命令
|
|
269
|
+
// program
|
|
270
|
+
// .command('attach')
|
|
271
|
+
// .description('连接到后台服务查看日志')
|
|
272
|
+
// .action(async () => {
|
|
273
|
+
// await serviceCommand.attach();
|
|
274
|
+
// });
|
|
275
|
+
// }
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* 注册配置管理命令(稍后实现)
|
|
279
|
+
*/
|
|
280
|
+
// private async registerConfigCommands(program: Command): Promise<void> {
|
|
281
|
+
// const configCommand = this.container.get('configCommand');
|
|
282
|
+
|
|
283
|
+
// // init 命令
|
|
284
|
+
// program
|
|
285
|
+
// .command('init')
|
|
286
|
+
// .description('初始化配置文件')
|
|
287
|
+
// .option('-f, --format <format>', '配置文件格式 (json, json5, jsonc)', 'json')
|
|
288
|
+
// .action(async (options) => {
|
|
289
|
+
// await configCommand.init(options);
|
|
290
|
+
// });
|
|
291
|
+
|
|
292
|
+
// // config 命令
|
|
293
|
+
// program
|
|
294
|
+
// .command('config <key> [value]')
|
|
295
|
+
// .description('查看或设置配置')
|
|
296
|
+
// .action(async (key, value) => {
|
|
297
|
+
// await configCommand.manage(key, value);
|
|
298
|
+
// });
|
|
299
|
+
// }
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* 注册项目管理命令(稍后实现)
|
|
303
|
+
*/
|
|
304
|
+
// private async registerProjectCommands(program: Command): Promise<void> {
|
|
305
|
+
// const projectCommand = this.container.get('projectCommand');
|
|
306
|
+
|
|
307
|
+
// // create 命令
|
|
308
|
+
// program
|
|
309
|
+
// .command('create <projectName>')
|
|
310
|
+
// .description('创建项目')
|
|
311
|
+
// .option('-t, --template <templateName>', '使用指定模板创建项目')
|
|
312
|
+
// .action(async (projectName, options) => {
|
|
313
|
+
// await projectCommand.create(projectName, options);
|
|
314
|
+
// });
|
|
315
|
+
// }
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* 注册 MCP 管理命令(稍后实现)
|
|
319
|
+
*/
|
|
320
|
+
// private async registerMcpCommands(program: Command): Promise<void> {
|
|
321
|
+
// const mcpCommand = this.container.get('mcpCommand');
|
|
322
|
+
|
|
323
|
+
// // mcp 命令组
|
|
324
|
+
// const mcpGroup = program.command('mcp').description('MCP 服务和工具管理');
|
|
325
|
+
|
|
326
|
+
// // mcp list 命令
|
|
327
|
+
// mcpGroup
|
|
328
|
+
// .command('list')
|
|
329
|
+
// .description('列出 MCP 服务')
|
|
330
|
+
// .option('--tools', '显示所有服务的工具列表')
|
|
331
|
+
// .action(async (options) => {
|
|
332
|
+
// await mcpCommand.list(options);
|
|
333
|
+
// });
|
|
334
|
+
|
|
335
|
+
// // mcp server 命令
|
|
336
|
+
// mcpGroup
|
|
337
|
+
// .command('server <serverName>')
|
|
338
|
+
// .description('管理指定的 MCP 服务')
|
|
339
|
+
// .action(async (serverName) => {
|
|
340
|
+
// await mcpCommand.server(serverName);
|
|
341
|
+
// });
|
|
342
|
+
|
|
343
|
+
// // mcp tool 命令
|
|
344
|
+
// mcpGroup
|
|
345
|
+
// .command('tool <serverName> <toolName> <action>')
|
|
346
|
+
// .description('管理 MCP 工具 (enable/disable)')
|
|
347
|
+
// .action(async (serverName, toolName, action) => {
|
|
348
|
+
// await mcpCommand.tool(serverName, toolName, action);
|
|
349
|
+
// });
|
|
350
|
+
// }
|
|
351
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 错误处理器
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import { ERROR_MESSAGES } from "./ErrorMessages";
|
|
7
|
+
import { CLIError } from "./index";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 错误处理器类
|
|
11
|
+
*/
|
|
12
|
+
export class ErrorHandler {
|
|
13
|
+
/**
|
|
14
|
+
* 处理错误并退出程序
|
|
15
|
+
*/
|
|
16
|
+
static handle(error: Error): never {
|
|
17
|
+
if (error instanceof CLIError) {
|
|
18
|
+
ErrorHandler.handleCLIError(error);
|
|
19
|
+
} else {
|
|
20
|
+
ErrorHandler.handleUnknownError(error);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 处理 CLI 错误
|
|
28
|
+
*/
|
|
29
|
+
private static handleCLIError(error: CLIError): void {
|
|
30
|
+
console.error(chalk.red(`❌ 错误: ${error.message}`));
|
|
31
|
+
|
|
32
|
+
// 显示错误码(调试模式)
|
|
33
|
+
if (process.env.DEBUG) {
|
|
34
|
+
console.error(chalk.gray(`错误码: ${error.code}`));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 显示建议
|
|
38
|
+
if (error.suggestions && error.suggestions.length > 0) {
|
|
39
|
+
console.log(chalk.yellow("💡 建议:"));
|
|
40
|
+
for (const suggestion of error.suggestions) {
|
|
41
|
+
console.log(chalk.gray(` ${suggestion}`));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 显示相关帮助信息
|
|
46
|
+
const helpMessage = ERROR_MESSAGES.getHelpMessage(error.code);
|
|
47
|
+
if (helpMessage) {
|
|
48
|
+
console.log(chalk.blue(`ℹ️ ${helpMessage}`));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 处理未知错误
|
|
54
|
+
*/
|
|
55
|
+
private static handleUnknownError(error: Error): void {
|
|
56
|
+
console.error(chalk.red(`❌ 未知错误: ${error.message}`));
|
|
57
|
+
|
|
58
|
+
// 在调试模式下显示完整堆栈
|
|
59
|
+
if (process.env.DEBUG || process.env.NODE_ENV === "development") {
|
|
60
|
+
console.error(chalk.gray("堆栈信息:"));
|
|
61
|
+
console.error(chalk.gray(error.stack));
|
|
62
|
+
} else {
|
|
63
|
+
console.log(
|
|
64
|
+
chalk.yellow("💡 提示: 设置 DEBUG=1 环境变量查看详细错误信息")
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 异步操作错误处理包装器
|
|
71
|
+
*/
|
|
72
|
+
static async handleAsync<T>(
|
|
73
|
+
operation: () => Promise<T>,
|
|
74
|
+
context: string
|
|
75
|
+
): Promise<T> {
|
|
76
|
+
try {
|
|
77
|
+
return await operation();
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if (error instanceof CLIError) {
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
if (error instanceof Error) {
|
|
83
|
+
throw new CLIError(
|
|
84
|
+
`${context}失败: ${error.message}`,
|
|
85
|
+
"OPERATION_FAILED",
|
|
86
|
+
1
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
throw new CLIError(`${context}失败: 未知错误`, "OPERATION_FAILED", 1);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* 同步操作错误处理包装器
|
|
95
|
+
*/
|
|
96
|
+
static handleSync<T>(operation: () => T, context: string): T {
|
|
97
|
+
try {
|
|
98
|
+
return operation();
|
|
99
|
+
} catch (error) {
|
|
100
|
+
if (error instanceof CLIError) {
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
if (error instanceof Error) {
|
|
104
|
+
throw new CLIError(
|
|
105
|
+
`${context}失败: ${error.message}`,
|
|
106
|
+
"OPERATION_FAILED",
|
|
107
|
+
1
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
throw new CLIError(`${context}失败: 未知错误`, "OPERATION_FAILED", 1);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 警告处理
|
|
116
|
+
*/
|
|
117
|
+
static warn(message: string, suggestions?: string[]): void {
|
|
118
|
+
console.warn(chalk.yellow(`⚠️ 警告: ${message}`));
|
|
119
|
+
|
|
120
|
+
if (suggestions && suggestions.length > 0) {
|
|
121
|
+
console.log(chalk.yellow("💡 建议:"));
|
|
122
|
+
for (const suggestion of suggestions) {
|
|
123
|
+
console.log(chalk.gray(` ${suggestion}`));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 信息提示
|
|
130
|
+
*/
|
|
131
|
+
static info(message: string): void {
|
|
132
|
+
console.log(chalk.blue(`ℹ️ ${message}`));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 成功提示
|
|
137
|
+
*/
|
|
138
|
+
static success(message: string): void {
|
|
139
|
+
console.log(chalk.green(`✅ ${message}`));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 错误消息管理
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { ERROR_CODES } from "../Constants";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 错误消息映射
|
|
9
|
+
*/
|
|
10
|
+
const ERROR_HELP_MESSAGES: Record<string, string> = {
|
|
11
|
+
[ERROR_CODES.CONFIG_ERROR]: '运行 "xiaozhi --help" 查看配置相关命令',
|
|
12
|
+
[ERROR_CODES.SERVICE_ERROR]: '运行 "xiaozhi status" 检查服务状态',
|
|
13
|
+
[ERROR_CODES.VALIDATION_ERROR]: "检查输入参数是否正确",
|
|
14
|
+
[ERROR_CODES.FILE_ERROR]: "检查文件路径和权限",
|
|
15
|
+
[ERROR_CODES.PROCESS_ERROR]: "检查进程状态和权限",
|
|
16
|
+
[ERROR_CODES.NETWORK_ERROR]: "检查网络连接和防火墙设置",
|
|
17
|
+
[ERROR_CODES.PERMISSION_ERROR]: "尝试使用管理员权限运行",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 常见问题解决方案
|
|
22
|
+
*/
|
|
23
|
+
const COMMON_SOLUTIONS: Record<string, string[]> = {
|
|
24
|
+
config_not_found: [
|
|
25
|
+
'运行 "xiaozhi init" 初始化配置文件',
|
|
26
|
+
"检查当前目录是否为项目根目录",
|
|
27
|
+
"设置 XIAOZHI_CONFIG_DIR 环境变量指定配置目录",
|
|
28
|
+
],
|
|
29
|
+
service_port_occupied: [
|
|
30
|
+
"检查端口是否被其他程序占用",
|
|
31
|
+
'使用 "lsof -i :端口号" 查看端口使用情况',
|
|
32
|
+
"更改配置文件中的端口设置",
|
|
33
|
+
],
|
|
34
|
+
permission_denied: [
|
|
35
|
+
"检查文件和目录权限",
|
|
36
|
+
"使用 sudo 或管理员权限运行",
|
|
37
|
+
"确保当前用户有足够的权限",
|
|
38
|
+
],
|
|
39
|
+
service_start_failed: [
|
|
40
|
+
"检查配置文件格式是否正确",
|
|
41
|
+
"查看日志文件获取详细错误信息",
|
|
42
|
+
"确保所有依赖服务正常运行",
|
|
43
|
+
],
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 错误消息管理类
|
|
48
|
+
*/
|
|
49
|
+
export class ERROR_MESSAGES {
|
|
50
|
+
/**
|
|
51
|
+
* 获取错误码对应的帮助信息
|
|
52
|
+
*/
|
|
53
|
+
static getHelpMessage(errorCode: string): string | undefined {
|
|
54
|
+
return ERROR_HELP_MESSAGES[errorCode];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 获取常见问题的解决方案
|
|
59
|
+
*/
|
|
60
|
+
static getSolutions(problemKey: string): string[] {
|
|
61
|
+
return COMMON_SOLUTIONS[problemKey] || [];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 格式化错误消息
|
|
66
|
+
*/
|
|
67
|
+
static formatError(error: Error, context?: string): string {
|
|
68
|
+
const contextPrefix = context ? `[${context}] ` : "";
|
|
69
|
+
return `${contextPrefix}${error.message}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 获取友好的错误描述
|
|
74
|
+
*/
|
|
75
|
+
static getFriendlyMessage(errorCode: string): string {
|
|
76
|
+
const friendlyMessages: Record<string, string> = {
|
|
77
|
+
[ERROR_CODES.CONFIG_ERROR]: "配置文件相关错误",
|
|
78
|
+
[ERROR_CODES.SERVICE_ERROR]: "服务运行相关错误",
|
|
79
|
+
[ERROR_CODES.VALIDATION_ERROR]: "输入验证错误",
|
|
80
|
+
[ERROR_CODES.FILE_ERROR]: "文件操作错误",
|
|
81
|
+
[ERROR_CODES.PROCESS_ERROR]: "进程管理错误",
|
|
82
|
+
[ERROR_CODES.NETWORK_ERROR]: "网络连接错误",
|
|
83
|
+
[ERROR_CODES.PERMISSION_ERROR]: "权限不足错误",
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
return friendlyMessages[errorCode] || "未知错误";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 检查是否为可恢复错误
|
|
91
|
+
*/
|
|
92
|
+
static isRecoverable(errorCode: string): boolean {
|
|
93
|
+
const recoverableErrors: string[] = [
|
|
94
|
+
ERROR_CODES.NETWORK_ERROR,
|
|
95
|
+
ERROR_CODES.FILE_ERROR,
|
|
96
|
+
ERROR_CODES.SERVICE_ERROR,
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
return recoverableErrors.includes(errorCode);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 获取错误的严重程度
|
|
104
|
+
*/
|
|
105
|
+
static getSeverity(
|
|
106
|
+
errorCode: string
|
|
107
|
+
): "low" | "medium" | "high" | "critical" {
|
|
108
|
+
const severityMap: Record<string, "low" | "medium" | "high" | "critical"> =
|
|
109
|
+
{
|
|
110
|
+
[ERROR_CODES.VALIDATION_ERROR]: "low",
|
|
111
|
+
[ERROR_CODES.FILE_ERROR]: "medium",
|
|
112
|
+
[ERROR_CODES.CONFIG_ERROR]: "medium",
|
|
113
|
+
[ERROR_CODES.NETWORK_ERROR]: "medium",
|
|
114
|
+
[ERROR_CODES.SERVICE_ERROR]: "high",
|
|
115
|
+
[ERROR_CODES.PROCESS_ERROR]: "high",
|
|
116
|
+
[ERROR_CODES.PERMISSION_ERROR]: "critical",
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
return severityMap[errorCode] || "medium";
|
|
120
|
+
}
|
|
121
|
+
}
|