xiaozhi-client 1.9.4-beta.1 → 1.9.4-beta.3

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.
Files changed (87) hide show
  1. package/dist/backend/Logger.js +3 -0
  2. package/dist/backend/Logger.js.map +1 -0
  3. package/dist/backend/WebServer.js +93 -0
  4. package/dist/backend/WebServer.js.map +1 -0
  5. package/dist/backend/WebServerLauncher.js +9 -15
  6. package/dist/backend/WebServerLauncher.js.map +1 -1
  7. package/dist/backend/lib/config/manager.js +3 -0
  8. package/dist/backend/lib/config/manager.js.map +1 -0
  9. package/dist/backend/managers/MCPServiceManagerSingleton.js +27 -0
  10. package/dist/backend/managers/MCPServiceManagerSingleton.js.map +1 -0
  11. package/dist/backend/package.json +20 -31
  12. package/dist/backend/templates/json5/xiaozhi.config.json5 +14 -14
  13. package/dist/cli/index.js +4473 -0
  14. package/dist/cli/index.js.map +1 -0
  15. package/package.json +20 -31
  16. package/templates/json5/xiaozhi.config.json5 +14 -14
  17. package/dist/backend/WebServerLauncher.d.ts +0 -1
  18. package/dist/backend/cli.d.ts +0 -1
  19. package/dist/backend/cli.js +0 -129
  20. package/dist/backend/cli.js.map +0 -1
  21. package/dist/cli.js +0 -2
  22. package/dist/docs/404/index.html +0 -19
  23. package/dist/docs/404.html +0 -19
  24. package/dist/docs/_next/static/JZ0ESgtaHnsqkxSabOqqU/_buildManifest.js +0 -1
  25. package/dist/docs/_next/static/JZ0ESgtaHnsqkxSabOqqU/_ssgManifest.js +0 -1
  26. package/dist/docs/_next/static/chunks/112-c9cbd8401d35f825.js +0 -4
  27. package/dist/docs/_next/static/chunks/2a9bc5d7-4c434acf20ba934a.js +0 -1
  28. package/dist/docs/_next/static/chunks/782-c26ca6c69e488d48.js +0 -1
  29. package/dist/docs/_next/static/chunks/799-fe0d35806fd12012.js +0 -1
  30. package/dist/docs/_next/static/chunks/9b1cb2c3-cc9ed703e6aef1a2.js +0 -1
  31. package/dist/docs/_next/static/chunks/app/[[...mdxPath]]/page-48f5c8f3210e0a8a.js +0 -1
  32. package/dist/docs/_next/static/chunks/app/_not-found/page-2e38866a1cbb77e4.js +0 -1
  33. package/dist/docs/_next/static/chunks/app/layout-e8f420537fd59e8d.js +0 -1
  34. package/dist/docs/_next/static/chunks/framework-b73126dabbf07067.js +0 -1
  35. package/dist/docs/_next/static/chunks/main-75dc65850b89d90d.js +0 -1
  36. package/dist/docs/_next/static/chunks/main-app-3303134270964ce6.js +0 -1
  37. package/dist/docs/_next/static/chunks/pages/_app-e698a68d07c8993d.js +0 -1
  38. package/dist/docs/_next/static/chunks/pages/_error-189a41ab5833da03.js +0 -1
  39. package/dist/docs/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
  40. package/dist/docs/_next/static/chunks/webpack-10e2bf7d852ddb6e.js +0 -1
  41. package/dist/docs/_next/static/css/2d82b615fcca1590.css +0 -1
  42. package/dist/docs/_next/static/css/b03484a3c350cf6e.css +0 -1
  43. package/dist/docs/_next/static/css/b289318ef4b60b0a.css +0 -1
  44. package/dist/docs/_next/static/media/get-parameter.62eee93d.png +0 -0
  45. package/dist/docs/changelog/index.html +0 -585
  46. package/dist/docs/changelog/index.txt +0 -1079
  47. package/dist/docs/images/add-to-cherry-studio/step-1.png +0 -0
  48. package/dist/docs/images/add-to-cherry-studio/step-2.png +0 -0
  49. package/dist/docs/images/add-to-cherry-studio/step-3.png +0 -0
  50. package/dist/docs/images/add-to-cherry-studio/step-4.png +0 -0
  51. package/dist/docs/images/add-to-cherry-studio/step-5.png +0 -0
  52. package/dist/docs/images/add-to-cursor/step-1.png +0 -0
  53. package/dist/docs/images/add-to-cursor/step-2.png +0 -0
  54. package/dist/docs/images/add-to-cursor/step-3.png +0 -0
  55. package/dist/docs/images/coze-workflow/config-workflow-step-1.png +0 -0
  56. package/dist/docs/images/coze-workflow/config-workflow-step-2.png +0 -0
  57. package/dist/docs/images/coze-workflow/config-workflow-step-3.png +0 -0
  58. package/dist/docs/images/coze-workflow/get-parameter.png +0 -0
  59. package/dist/docs/images/integrate-to-cherry-studio.png +0 -0
  60. package/dist/docs/images/integrate-to-cursor.png +0 -0
  61. package/dist/docs/images/modelscope/step-1.png +0 -0
  62. package/dist/docs/images/modelscope/step-2.png +0 -0
  63. package/dist/docs/images/modelscope/step-3.png +0 -0
  64. package/dist/docs/images/modelscope/step-4.png +0 -0
  65. package/dist/docs/images/preview.png +0 -0
  66. package/dist/docs/images/use-multi-xiaozhi-mcp-endpoints/step-1.png +0 -0
  67. package/dist/docs/images/use-multi-xiaozhi-mcp-endpoints/step-2.png +0 -0
  68. package/dist/docs/images/use-multi-xiaozhi-mcp-endpoints/step-3.png +0 -0
  69. package/dist/docs/images/use-multi-xiaozhi-mcp-endpoints/step-4.png +0 -0
  70. package/dist/docs/images/use-multi-xiaozhi-mcp-endpoints/step-5.png +0 -0
  71. package/dist/docs/images/web-ui-preview.png +0 -0
  72. package/dist/docs/index.html +0 -22
  73. package/dist/docs/index.txt +0 -41
  74. package/dist/docs/quickstart/index.html +0 -64
  75. package/dist/docs/quickstart/index.txt +0 -185
  76. package/dist/docs/reference/command/index.html +0 -20
  77. package/dist/docs/reference/command/index.txt +0 -42
  78. package/dist/docs/usage/as-mcp/index.html +0 -36
  79. package/dist/docs/usage/as-mcp/index.txt +0 -101
  80. package/dist/docs/usage/coze-workflow/index.html +0 -35
  81. package/dist/docs/usage/coze-workflow/index.txt +0 -120
  82. package/dist/docs/usage/docker/index.html +0 -40
  83. package/dist/docs/usage/docker/index.txt +0 -154
  84. package/dist/docs/usage/modelscope/index.html +0 -32
  85. package/dist/docs/usage/modelscope/index.txt +0 -109
  86. package/dist/docs/usage/multi-endpoint/index.html +0 -32
  87. package/dist/docs/usage/multi-endpoint/index.txt +0 -118
@@ -0,0 +1,4473 @@
1
+ #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
+ var __esm = (fn, res) => function __init() {
8
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
9
+ };
10
+ var __export = (target, all) => {
11
+ for (var name in all)
12
+ __defProp(target, name, { get: all[name], enumerable: true });
13
+ };
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") {
16
+ for (let key of __getOwnPropNames(from))
17
+ if (!__hasOwnProp.call(to, key) && key !== except)
18
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
19
+ }
20
+ return to;
21
+ };
22
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
23
+
24
+ // src/Constants.ts
25
+ var SERVICE_CONSTANTS, CONFIG_CONSTANTS, PATH_CONSTANTS, ERROR_CODES, TIMEOUT_CONSTANTS;
26
+ var init_Constants = __esm({
27
+ "src/Constants.ts"() {
28
+ SERVICE_CONSTANTS = {
29
+ /** 服务名称 */
30
+ NAME: "xiaozhi-mcp-service",
31
+ /** 默认端口 */
32
+ DEFAULT_PORT: 3e3,
33
+ /** Web UI 默认端口 */
34
+ DEFAULT_WEB_UI_PORT: 9999,
35
+ /** PID 文件名 */
36
+ PID_FILE: "xiaozhi.pid",
37
+ /** 日志文件名 */
38
+ LOG_FILE: "xiaozhi.log"
39
+ };
40
+ CONFIG_CONSTANTS = {
41
+ /** 配置文件名(按优先级排序) */
42
+ FILE_NAMES: [
43
+ "xiaozhi.config.json5",
44
+ "xiaozhi.config.jsonc",
45
+ "xiaozhi.config.json"
46
+ ],
47
+ /** 默认配置文件名 */
48
+ DEFAULT_FILE: "xiaozhi.config.default.json",
49
+ /** 配置目录环境变量 */
50
+ DIR_ENV_VAR: "XIAOZHI_CONFIG_DIR"
51
+ };
52
+ PATH_CONSTANTS = {
53
+ /** 工作目录名 */
54
+ WORK_DIR: ".xiaozhi",
55
+ /** 模板目录名 */
56
+ TEMPLATES_DIR: "templates",
57
+ /** 日志目录名 */
58
+ LOGS_DIR: "logs"
59
+ };
60
+ ERROR_CODES = {
61
+ /** 通用错误 */
62
+ GENERAL_ERROR: "GENERAL_ERROR",
63
+ /** 配置错误 */
64
+ CONFIG_ERROR: "CONFIG_ERROR",
65
+ /** 服务错误 */
66
+ SERVICE_ERROR: "SERVICE_ERROR",
67
+ /** 验证错误 */
68
+ VALIDATION_ERROR: "VALIDATION_ERROR",
69
+ /** 文件操作错误 */
70
+ FILE_ERROR: "FILE_ERROR",
71
+ /** 进程错误 */
72
+ PROCESS_ERROR: "PROCESS_ERROR",
73
+ /** 网络错误 */
74
+ NETWORK_ERROR: "NETWORK_ERROR",
75
+ /** 权限错误 */
76
+ PERMISSION_ERROR: "PERMISSION_ERROR"
77
+ };
78
+ TIMEOUT_CONSTANTS = {
79
+ /** 进程停止超时 */
80
+ PROCESS_STOP: 3e3,
81
+ /** 服务启动超时 */
82
+ SERVICE_START: 1e4,
83
+ /** 网络请求超时 */
84
+ NETWORK_REQUEST: 5e3,
85
+ /** 文件操作超时 */
86
+ FILE_OPERATION: 2e3
87
+ };
88
+ }
89
+ });
90
+
91
+ // src/errors/index.ts
92
+ var CLIError, ConfigError, ServiceError, ValidationError, FileError, ProcessError;
93
+ var init_errors = __esm({
94
+ "src/errors/index.ts"() {
95
+ init_Constants();
96
+ CLIError = class _CLIError extends Error {
97
+ constructor(message, code, exitCode = 1, suggestions) {
98
+ super(message);
99
+ this.code = code;
100
+ this.exitCode = exitCode;
101
+ this.suggestions = suggestions;
102
+ this.name = "CLIError";
103
+ if (Error.captureStackTrace) {
104
+ Error.captureStackTrace(this, _CLIError);
105
+ }
106
+ }
107
+ static {
108
+ __name(this, "CLIError");
109
+ }
110
+ /**
111
+ * 创建带建议的错误
112
+ */
113
+ static withSuggestions(message, code, suggestions) {
114
+ return new _CLIError(message, code, 1, suggestions);
115
+ }
116
+ };
117
+ ConfigError = class _ConfigError extends CLIError {
118
+ static {
119
+ __name(this, "ConfigError");
120
+ }
121
+ constructor(message, suggestions) {
122
+ super(message, ERROR_CODES.CONFIG_ERROR, 1, suggestions);
123
+ this.name = "ConfigError";
124
+ }
125
+ static configNotFound() {
126
+ return new _ConfigError("\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728", [
127
+ '\u8BF7\u8FD0\u884C "xiaozhi init" \u521D\u59CB\u5316\u914D\u7F6E\u6587\u4EF6'
128
+ ]);
129
+ }
130
+ static invalidFormat(format) {
131
+ return new _ConfigError(`\u65E0\u6548\u7684\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F: ${format}`, [
132
+ "\u652F\u6301\u7684\u683C\u5F0F: json, json5, jsonc"
133
+ ]);
134
+ }
135
+ };
136
+ ServiceError = class _ServiceError extends CLIError {
137
+ static {
138
+ __name(this, "ServiceError");
139
+ }
140
+ constructor(message, suggestions) {
141
+ super(message, ERROR_CODES.SERVICE_ERROR, 1, suggestions);
142
+ this.name = "ServiceError";
143
+ }
144
+ static alreadyRunning(pid) {
145
+ return new _ServiceError(`\u670D\u52A1\u5DF2\u7ECF\u5728\u8FD0\u884C (PID: ${pid})`, [
146
+ '\u8BF7\u5148\u8FD0\u884C "xiaozhi stop" \u505C\u6B62\u73B0\u6709\u670D\u52A1',
147
+ '\u6216\u8005\u4F7F\u7528 "xiaozhi restart" \u91CD\u542F\u670D\u52A1'
148
+ ]);
149
+ }
150
+ static autoRestarting(pid) {
151
+ return new _ServiceError(
152
+ `\u68C0\u6D4B\u5230\u670D\u52A1\u5DF2\u5728\u8FD0\u884C (PID: ${pid})\uFF0C\u6B63\u5728\u81EA\u52A8\u91CD\u542F...`,
153
+ ["\u5982\u679C\u4E0D\u5E0C\u671B\u81EA\u52A8\u91CD\u542F\uFF0C\u8BF7\u4F7F\u7528 xiaozhi stop \u624B\u52A8\u505C\u6B62\u670D\u52A1"]
154
+ );
155
+ }
156
+ static notRunning() {
157
+ return new _ServiceError("\u670D\u52A1\u672A\u8FD0\u884C", ['\u8BF7\u8FD0\u884C "xiaozhi start" \u542F\u52A8\u670D\u52A1']);
158
+ }
159
+ static startFailed(reason) {
160
+ return new _ServiceError(`\u670D\u52A1\u542F\u52A8\u5931\u8D25: ${reason}`, [
161
+ "\u68C0\u67E5\u914D\u7F6E\u6587\u4EF6\u662F\u5426\u6B63\u786E",
162
+ "\u786E\u4FDD\u7AEF\u53E3\u672A\u88AB\u5360\u7528",
163
+ "\u67E5\u770B\u65E5\u5FD7\u6587\u4EF6\u83B7\u53D6\u8BE6\u7EC6\u4FE1\u606F"
164
+ ]);
165
+ }
166
+ };
167
+ ValidationError = class _ValidationError extends CLIError {
168
+ static {
169
+ __name(this, "ValidationError");
170
+ }
171
+ constructor(message, field) {
172
+ super(`\u9A8C\u8BC1\u5931\u8D25: ${field} - ${message}`, ERROR_CODES.VALIDATION_ERROR, 1);
173
+ this.name = "ValidationError";
174
+ }
175
+ static invalidPort(port) {
176
+ return new _ValidationError(
177
+ `\u7AEF\u53E3\u53F7\u5FC5\u987B\u5728 1-65535 \u8303\u56F4\u5185\uFF0C\u5F53\u524D\u503C: ${port}`,
178
+ "port"
179
+ );
180
+ }
181
+ static requiredField(field) {
182
+ return new _ValidationError("\u5FC5\u586B\u5B57\u6BB5\u4E0D\u80FD\u4E3A\u7A7A", field);
183
+ }
184
+ };
185
+ FileError = class _FileError extends CLIError {
186
+ static {
187
+ __name(this, "FileError");
188
+ }
189
+ constructor(message, filePath, suggestions) {
190
+ const fullMessage = filePath ? `${message}: ${filePath}` : message;
191
+ super(fullMessage, ERROR_CODES.FILE_ERROR, 1, suggestions);
192
+ this.name = "FileError";
193
+ }
194
+ static notFound(filePath) {
195
+ return new _FileError("\u6587\u4EF6\u4E0D\u5B58\u5728", filePath, ["\u68C0\u67E5\u6587\u4EF6\u8DEF\u5F84\u662F\u5426\u6B63\u786E"]);
196
+ }
197
+ static permissionDenied(filePath) {
198
+ return new _FileError("\u6743\u9650\u4E0D\u8DB3", filePath, [
199
+ "\u68C0\u67E5\u6587\u4EF6\u6743\u9650\u6216\u4F7F\u7528\u7BA1\u7406\u5458\u6743\u9650\u8FD0\u884C"
200
+ ]);
201
+ }
202
+ static alreadyExists(filePath) {
203
+ return new _FileError("\u6587\u4EF6\u5DF2\u5B58\u5728", filePath, [
204
+ "\u4F7F\u7528\u4E0D\u540C\u7684\u6587\u4EF6\u540D\u6216\u5220\u9664\u73B0\u6709\u6587\u4EF6"
205
+ ]);
206
+ }
207
+ };
208
+ ProcessError = class _ProcessError extends CLIError {
209
+ static {
210
+ __name(this, "ProcessError");
211
+ }
212
+ constructor(message, pid, suggestions) {
213
+ const fullMessage = pid ? `${message} (PID: ${pid})` : message;
214
+ super(fullMessage, ERROR_CODES.PROCESS_ERROR, 1, suggestions);
215
+ this.name = "ProcessError";
216
+ }
217
+ static killFailed(pid) {
218
+ return new _ProcessError("\u65E0\u6CD5\u7EC8\u6B62\u8FDB\u7A0B", pid, [
219
+ "\u8FDB\u7A0B\u53EF\u80FD\u5DF2\u7ECF\u505C\u6B62\u6216\u6743\u9650\u4E0D\u8DB3"
220
+ ]);
221
+ }
222
+ static notFound(pid) {
223
+ return new _ProcessError("\u8FDB\u7A0B\u4E0D\u5B58\u5728", pid);
224
+ }
225
+ };
226
+ }
227
+ });
228
+
229
+ // src/utils/FileUtils.ts
230
+ import fs from "fs";
231
+ import { tmpdir } from "os";
232
+ import path from "path";
233
+ var FileUtils;
234
+ var init_FileUtils = __esm({
235
+ "src/utils/FileUtils.ts"() {
236
+ init_errors();
237
+ FileUtils = class _FileUtils {
238
+ static {
239
+ __name(this, "FileUtils");
240
+ }
241
+ /**
242
+ * 检查文件是否存在
243
+ */
244
+ static exists(filePath) {
245
+ try {
246
+ return fs.existsSync(filePath);
247
+ } catch {
248
+ return false;
249
+ }
250
+ }
251
+ /**
252
+ * 确保目录存在
253
+ */
254
+ static ensureDir(dirPath) {
255
+ try {
256
+ if (!fs.existsSync(dirPath)) {
257
+ fs.mkdirSync(dirPath, { recursive: true });
258
+ }
259
+ } catch (error) {
260
+ throw new FileError("\u65E0\u6CD5\u521B\u5EFA\u76EE\u5F55", dirPath);
261
+ }
262
+ }
263
+ /**
264
+ * 读取文件内容
265
+ */
266
+ static readFile(filePath, encoding = "utf8") {
267
+ try {
268
+ if (!_FileUtils.exists(filePath)) {
269
+ throw FileError.notFound(filePath);
270
+ }
271
+ return fs.readFileSync(filePath, encoding);
272
+ } catch (error) {
273
+ if (error instanceof FileError) {
274
+ throw error;
275
+ }
276
+ throw new FileError("\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6", filePath);
277
+ }
278
+ }
279
+ /**
280
+ * 写入文件内容
281
+ */
282
+ static writeFile(filePath, content, options) {
283
+ try {
284
+ if (!options?.overwrite && _FileUtils.exists(filePath)) {
285
+ throw FileError.alreadyExists(filePath);
286
+ }
287
+ const dir = path.dirname(filePath);
288
+ _FileUtils.ensureDir(dir);
289
+ fs.writeFileSync(filePath, content, "utf8");
290
+ } catch (error) {
291
+ if (error instanceof FileError) {
292
+ throw error;
293
+ }
294
+ throw new FileError("\u65E0\u6CD5\u5199\u5165\u6587\u4EF6", filePath);
295
+ }
296
+ }
297
+ /**
298
+ * 复制文件
299
+ */
300
+ static copyFile(srcPath, destPath, options) {
301
+ try {
302
+ if (!_FileUtils.exists(srcPath)) {
303
+ throw FileError.notFound(srcPath);
304
+ }
305
+ if (!options?.overwrite && _FileUtils.exists(destPath)) {
306
+ throw FileError.alreadyExists(destPath);
307
+ }
308
+ const destDir = path.dirname(destPath);
309
+ _FileUtils.ensureDir(destDir);
310
+ fs.copyFileSync(srcPath, destPath);
311
+ } catch (error) {
312
+ if (error instanceof FileError) {
313
+ throw error;
314
+ }
315
+ throw new FileError("\u65E0\u6CD5\u590D\u5236\u6587\u4EF6", srcPath);
316
+ }
317
+ }
318
+ /**
319
+ * 删除文件
320
+ */
321
+ static deleteFile(filePath) {
322
+ try {
323
+ if (_FileUtils.exists(filePath)) {
324
+ fs.unlinkSync(filePath);
325
+ }
326
+ } catch (error) {
327
+ throw new FileError("\u65E0\u6CD5\u5220\u9664\u6587\u4EF6", filePath);
328
+ }
329
+ }
330
+ /**
331
+ * 复制目录
332
+ */
333
+ static copyDirectory(srcDir, destDir, options = {}) {
334
+ try {
335
+ if (!_FileUtils.exists(srcDir)) {
336
+ throw FileError.notFound(srcDir);
337
+ }
338
+ _FileUtils.ensureDir(destDir);
339
+ const items = fs.readdirSync(srcDir);
340
+ for (const item of items) {
341
+ if (options.exclude?.includes(item)) {
342
+ continue;
343
+ }
344
+ const srcPath = path.join(srcDir, item);
345
+ const destPath = path.join(destDir, item);
346
+ const stat = fs.statSync(srcPath);
347
+ if (stat.isDirectory()) {
348
+ if (options.recursive !== false) {
349
+ _FileUtils.copyDirectory(srcPath, destPath, options);
350
+ }
351
+ } else {
352
+ _FileUtils.copyFile(srcPath, destPath, {
353
+ overwrite: options.overwrite
354
+ });
355
+ }
356
+ }
357
+ } catch (error) {
358
+ if (error instanceof FileError) {
359
+ throw error;
360
+ }
361
+ throw new FileError("\u65E0\u6CD5\u590D\u5236\u76EE\u5F55", srcDir);
362
+ }
363
+ }
364
+ /**
365
+ * 删除目录
366
+ */
367
+ static deleteDirectory(dirPath, options = {}) {
368
+ try {
369
+ if (_FileUtils.exists(dirPath)) {
370
+ fs.rmSync(dirPath, {
371
+ recursive: options.recursive ?? true,
372
+ force: true
373
+ });
374
+ }
375
+ } catch (error) {
376
+ throw new FileError("\u65E0\u6CD5\u5220\u9664\u76EE\u5F55", dirPath);
377
+ }
378
+ }
379
+ /**
380
+ * 获取文件信息
381
+ */
382
+ static getFileInfo(filePath) {
383
+ try {
384
+ if (!_FileUtils.exists(filePath)) {
385
+ throw FileError.notFound(filePath);
386
+ }
387
+ const stats = fs.statSync(filePath);
388
+ return {
389
+ size: stats.size,
390
+ isFile: stats.isFile(),
391
+ isDirectory: stats.isDirectory(),
392
+ mtime: stats.mtime,
393
+ ctime: stats.ctime
394
+ };
395
+ } catch (error) {
396
+ if (error instanceof FileError) {
397
+ throw error;
398
+ }
399
+ throw new FileError("\u65E0\u6CD5\u83B7\u53D6\u6587\u4EF6\u4FE1\u606F", filePath);
400
+ }
401
+ }
402
+ /**
403
+ * 列出目录内容
404
+ */
405
+ static listDirectory(dirPath, options = {}) {
406
+ try {
407
+ if (!_FileUtils.exists(dirPath)) {
408
+ throw FileError.notFound(dirPath);
409
+ }
410
+ const items = fs.readdirSync(dirPath);
411
+ let result = [];
412
+ for (const item of items) {
413
+ if (!options.includeHidden && item.startsWith(".")) {
414
+ continue;
415
+ }
416
+ const itemPath = path.join(dirPath, item);
417
+ result.push(itemPath);
418
+ if (options.recursive && fs.statSync(itemPath).isDirectory()) {
419
+ const subItems = _FileUtils.listDirectory(itemPath, options);
420
+ result = result.concat(subItems);
421
+ }
422
+ }
423
+ return result;
424
+ } catch (error) {
425
+ if (error instanceof FileError) {
426
+ throw error;
427
+ }
428
+ throw new FileError("\u65E0\u6CD5\u5217\u51FA\u76EE\u5F55\u5185\u5BB9", dirPath);
429
+ }
430
+ }
431
+ /**
432
+ * 创建临时文件
433
+ */
434
+ static createTempFile(prefix = "xiaozhi-", suffix = ".tmp") {
435
+ const tempDir = process.env.TMPDIR || process.env.TEMP || tmpdir();
436
+ const timestamp = Date.now();
437
+ const random = Math.random().toString(36).substring(2);
438
+ const fileName = `${prefix}${timestamp}-${random}${suffix}`;
439
+ return path.join(tempDir, fileName);
440
+ }
441
+ /**
442
+ * 检查文件权限
443
+ */
444
+ static checkPermissions(filePath, mode = fs.constants.R_OK | fs.constants.W_OK) {
445
+ try {
446
+ fs.accessSync(filePath, mode);
447
+ return true;
448
+ } catch {
449
+ return false;
450
+ }
451
+ }
452
+ /**
453
+ * 获取文件扩展名
454
+ */
455
+ static getExtension(filePath) {
456
+ return path.extname(filePath).toLowerCase();
457
+ }
458
+ /**
459
+ * 获取文件名(不含扩展名)
460
+ */
461
+ static getBaseName(filePath) {
462
+ return path.basename(filePath, path.extname(filePath));
463
+ }
464
+ /**
465
+ * 规范化路径
466
+ */
467
+ static normalizePath(filePath) {
468
+ return path.normalize(filePath);
469
+ }
470
+ /**
471
+ * 解析相对路径为绝对路径
472
+ */
473
+ static resolvePath(filePath, basePath) {
474
+ if (basePath) {
475
+ return path.resolve(basePath, filePath);
476
+ }
477
+ return path.resolve(filePath);
478
+ }
479
+ };
480
+ }
481
+ });
482
+
483
+ // src/utils/FormatUtils.ts
484
+ var FormatUtils;
485
+ var init_FormatUtils = __esm({
486
+ "src/utils/FormatUtils.ts"() {
487
+ FormatUtils = class {
488
+ static {
489
+ __name(this, "FormatUtils");
490
+ }
491
+ /**
492
+ * 格式化运行时间
493
+ */
494
+ static formatUptime(ms) {
495
+ const seconds = Math.floor(ms / 1e3);
496
+ const minutes = Math.floor(seconds / 60);
497
+ const hours = Math.floor(minutes / 60);
498
+ const days = Math.floor(hours / 24);
499
+ if (days > 0) {
500
+ return `${days}\u5929 ${hours % 24}\u5C0F\u65F6 ${minutes % 60}\u5206\u949F`;
501
+ }
502
+ if (hours > 0) {
503
+ return `${hours}\u5C0F\u65F6 ${minutes % 60}\u5206\u949F`;
504
+ }
505
+ if (minutes > 0) {
506
+ return `${minutes}\u5206\u949F ${seconds % 60}\u79D2`;
507
+ }
508
+ return `${seconds}\u79D2`;
509
+ }
510
+ /**
511
+ * 格式化文件大小
512
+ */
513
+ static formatFileSize(bytes) {
514
+ const units = ["B", "KB", "MB", "GB", "TB"];
515
+ let size = bytes;
516
+ let unitIndex = 0;
517
+ while (size >= 1024 && unitIndex < units.length - 1) {
518
+ size /= 1024;
519
+ unitIndex++;
520
+ }
521
+ return `${size.toFixed(unitIndex === 0 ? 0 : 1)} ${units[unitIndex]}`;
522
+ }
523
+ /**
524
+ * 格式化时间戳
525
+ */
526
+ static formatTimestamp(timestamp, format = "full") {
527
+ const date = new Date(timestamp);
528
+ switch (format) {
529
+ case "date":
530
+ return date.toLocaleDateString("zh-CN");
531
+ case "time":
532
+ return date.toLocaleTimeString("zh-CN");
533
+ default:
534
+ return date.toLocaleString("zh-CN");
535
+ }
536
+ }
537
+ /**
538
+ * 格式化进程 ID
539
+ */
540
+ static formatPid(pid) {
541
+ return `PID: ${pid}`;
542
+ }
543
+ /**
544
+ * 格式化端口号
545
+ */
546
+ static formatPort(port) {
547
+ return `\u7AEF\u53E3: ${port}`;
548
+ }
549
+ /**
550
+ * 格式化 URL
551
+ */
552
+ static formatUrl(protocol, host, port, path7) {
553
+ const url = `${protocol}://${host}:${port}`;
554
+ return path7 ? `${url}${path7}` : url;
555
+ }
556
+ /**
557
+ * 格式化配置键值对
558
+ */
559
+ static formatConfigPair(key, value) {
560
+ if (typeof value === "object") {
561
+ return `${key}: ${JSON.stringify(value, null, 2)}`;
562
+ }
563
+ return `${key}: ${value}`;
564
+ }
565
+ /**
566
+ * 格式化错误消息
567
+ */
568
+ static formatError(error, includeStack = false) {
569
+ let message = `\u9519\u8BEF: ${error.message}`;
570
+ if (includeStack && error.stack) {
571
+ message += `
572
+ \u5806\u6808\u4FE1\u606F:
573
+ ${error.stack}`;
574
+ }
575
+ return message;
576
+ }
577
+ /**
578
+ * 格式化列表
579
+ */
580
+ static formatList(items, bullet = "\u2022") {
581
+ return items.map((item) => `${bullet} ${item}`).join("\n");
582
+ }
583
+ /**
584
+ * 格式化表格数据
585
+ */
586
+ static formatTable(data) {
587
+ if (data.length === 0) return "";
588
+ const keys = Object.keys(data[0]);
589
+ const maxWidths = keys.map(
590
+ (key) => Math.max(key.length, ...data.map((row) => String(row[key]).length))
591
+ );
592
+ const header = keys.map((key, i) => key.padEnd(maxWidths[i])).join(" | ");
593
+ const separator = maxWidths.map((width) => "-".repeat(width)).join("-|-");
594
+ const rows = data.map(
595
+ (row) => keys.map((key, i) => String(row[key]).padEnd(maxWidths[i])).join(" | ")
596
+ );
597
+ return [header, separator, ...rows].join("\n");
598
+ }
599
+ /**
600
+ * 格式化进度条
601
+ */
602
+ static formatProgressBar(current, total, width = 20) {
603
+ const percentage = Math.min(current / total, 1);
604
+ const filled = Math.floor(percentage * width);
605
+ const empty = width - filled;
606
+ const bar = "\u2588".repeat(filled) + "\u2591".repeat(empty);
607
+ const percent = Math.floor(percentage * 100);
608
+ return `[${bar}] ${percent}% (${current}/${total})`;
609
+ }
610
+ /**
611
+ * 格式化命令行参数
612
+ */
613
+ static formatCommandArgs(command, args) {
614
+ const quotedArgs = args.map(
615
+ (arg) => arg.includes(" ") ? `"${arg}"` : arg
616
+ );
617
+ return `${command} ${quotedArgs.join(" ")}`;
618
+ }
619
+ /**
620
+ * 截断长文本
621
+ */
622
+ static truncateText(text, maxLength, suffix = "...") {
623
+ if (text.length <= maxLength) return text;
624
+ return text.substring(0, maxLength - suffix.length) + suffix;
625
+ }
626
+ /**
627
+ * 格式化 JSON
628
+ */
629
+ static formatJson(obj, indent = 2) {
630
+ try {
631
+ return JSON.stringify(obj, null, indent);
632
+ } catch (error) {
633
+ return String(obj);
634
+ }
635
+ }
636
+ /**
637
+ * 格式化布尔值
638
+ */
639
+ static formatBoolean(value, trueText = "\u662F", falseText = "\u5426") {
640
+ return value ? trueText : falseText;
641
+ }
642
+ };
643
+ }
644
+ });
645
+
646
+ // src/utils/PathUtils.ts
647
+ import { realpathSync } from "fs";
648
+ import { tmpdir as tmpdir2 } from "os";
649
+ import path2 from "path";
650
+ import { fileURLToPath } from "url";
651
+ var PathUtils;
652
+ var init_PathUtils = __esm({
653
+ "src/utils/PathUtils.ts"() {
654
+ init_Constants();
655
+ init_FileUtils();
656
+ PathUtils = class _PathUtils {
657
+ static {
658
+ __name(this, "PathUtils");
659
+ }
660
+ /**
661
+ * 获取 PID 文件路径
662
+ */
663
+ static getPidFile() {
664
+ const configDir = process.env[CONFIG_CONSTANTS.DIR_ENV_VAR] || process.cwd();
665
+ return path2.join(configDir, `.${SERVICE_CONSTANTS.NAME}.pid`);
666
+ }
667
+ /**
668
+ * 获取日志文件路径
669
+ */
670
+ static getLogFile(projectDir) {
671
+ const baseDir = projectDir || process.cwd();
672
+ return path2.join(baseDir, SERVICE_CONSTANTS.LOG_FILE);
673
+ }
674
+ /**
675
+ * 获取配置目录路径
676
+ */
677
+ static getConfigDir() {
678
+ return process.env[CONFIG_CONSTANTS.DIR_ENV_VAR] || process.cwd();
679
+ }
680
+ /**
681
+ * 获取工作目录路径
682
+ */
683
+ static getWorkDir() {
684
+ const configDir = _PathUtils.getConfigDir();
685
+ return path2.join(configDir, PATH_CONSTANTS.WORK_DIR);
686
+ }
687
+ /**
688
+ * 获取模板目录路径
689
+ */
690
+ static getTemplatesDir() {
691
+ const __filename = fileURLToPath(import.meta.url);
692
+ const scriptDir = path2.dirname(__filename);
693
+ return [
694
+ // 构建后的环境:dist/cli.js -> dist/templates
695
+ path2.join(scriptDir, PATH_CONSTANTS.TEMPLATES_DIR),
696
+ // 构建环境:dist/cli/index.js -> dist/backend/templates
697
+ path2.join(scriptDir, "..", "backend", PATH_CONSTANTS.TEMPLATES_DIR),
698
+ // npm 全局安装
699
+ path2.join(
700
+ scriptDir,
701
+ "..",
702
+ "..",
703
+ "..",
704
+ "..",
705
+ PATH_CONSTANTS.TEMPLATES_DIR
706
+ )
707
+ ];
708
+ }
709
+ /**
710
+ * 查找模板目录
711
+ */
712
+ static findTemplatesDir() {
713
+ const possiblePaths = _PathUtils.getTemplatesDir();
714
+ for (const templatesDir of possiblePaths) {
715
+ if (FileUtils.exists(templatesDir)) {
716
+ return templatesDir;
717
+ }
718
+ }
719
+ return null;
720
+ }
721
+ /**
722
+ * 获取模板路径
723
+ */
724
+ static getTemplatePath(templateName) {
725
+ const templatesDir = _PathUtils.findTemplatesDir();
726
+ if (!templatesDir) {
727
+ return null;
728
+ }
729
+ const templatePath = path2.join(templatesDir, templateName);
730
+ return FileUtils.exists(templatePath) ? templatePath : null;
731
+ }
732
+ /**
733
+ * 获取脚本目录路径
734
+ */
735
+ static getScriptDir() {
736
+ const __filename = fileURLToPath(import.meta.url);
737
+ return path2.dirname(__filename);
738
+ }
739
+ /**
740
+ * 获取项目根目录路径
741
+ */
742
+ static getProjectRoot() {
743
+ const scriptDir = _PathUtils.getScriptDir();
744
+ return path2.join(scriptDir, "..", "..", "..");
745
+ }
746
+ /**
747
+ * 获取构建输出目录路径
748
+ */
749
+ static getDistDir() {
750
+ const projectRoot = _PathUtils.getProjectRoot();
751
+ return path2.join(projectRoot, "dist");
752
+ }
753
+ /**
754
+ * 获取相对于项目根目录的路径
755
+ */
756
+ static getRelativePath(filePath) {
757
+ const projectRoot = _PathUtils.getProjectRoot();
758
+ return path2.relative(projectRoot, filePath);
759
+ }
760
+ /**
761
+ * 解析配置文件路径
762
+ */
763
+ static resolveConfigPath(format) {
764
+ const configDir = _PathUtils.getConfigDir();
765
+ if (format) {
766
+ return path2.join(configDir, `xiaozhi.config.${format}`);
767
+ }
768
+ for (const fileName of CONFIG_CONSTANTS.FILE_NAMES) {
769
+ const filePath = path2.join(configDir, fileName);
770
+ if (FileUtils.exists(filePath)) {
771
+ return filePath;
772
+ }
773
+ }
774
+ return path2.join(configDir, CONFIG_CONSTANTS.FILE_NAMES[2]);
775
+ }
776
+ /**
777
+ * 获取默认配置文件路径
778
+ */
779
+ static getDefaultConfigPath() {
780
+ const projectRoot = _PathUtils.getProjectRoot();
781
+ return path2.join(projectRoot, CONFIG_CONSTANTS.DEFAULT_FILE);
782
+ }
783
+ /**
784
+ * 验证路径安全性(防止路径遍历攻击)
785
+ */
786
+ static validatePath(inputPath) {
787
+ const normalizedPath = path2.normalize(inputPath);
788
+ return !normalizedPath.includes("..");
789
+ }
790
+ /**
791
+ * 确保路径在指定目录内
792
+ */
793
+ static ensurePathWithin(inputPath, baseDir) {
794
+ const resolvedPath = path2.resolve(baseDir, inputPath);
795
+ const resolvedBase = path2.resolve(baseDir);
796
+ if (!resolvedPath.startsWith(resolvedBase)) {
797
+ throw new Error(`\u8DEF\u5F84 ${inputPath} \u8D85\u51FA\u4E86\u5141\u8BB8\u7684\u8303\u56F4`);
798
+ }
799
+ return resolvedPath;
800
+ }
801
+ /**
802
+ * 获取可执行文件路径
803
+ */
804
+ static getExecutablePath(name) {
805
+ const cliPath = process.argv[1];
806
+ if (!cliPath) {
807
+ return path2.join(process.cwd(), `${name}.js`);
808
+ }
809
+ let realCliPath;
810
+ try {
811
+ realCliPath = realpathSync(cliPath);
812
+ } catch (error) {
813
+ realCliPath = cliPath;
814
+ }
815
+ const distDir = path2.dirname(realCliPath);
816
+ return path2.join(distDir, `${name}.js`);
817
+ }
818
+ /**
819
+ * 获取 Web 服务器启动器路径
820
+ */
821
+ static getWebServerLauncherPath() {
822
+ return _PathUtils.getExecutablePath("WebServerLauncher");
823
+ }
824
+ /**
825
+ * 创建安全的文件路径
826
+ */
827
+ static createSafePath(...segments) {
828
+ const joinedPath = path2.join(...segments);
829
+ const normalizedPath = path2.normalize(joinedPath);
830
+ if (normalizedPath.includes("..") || normalizedPath.includes("~")) {
831
+ throw new Error(`\u4E0D\u5B89\u5168\u7684\u8DEF\u5F84: ${normalizedPath}`);
832
+ }
833
+ return normalizedPath;
834
+ }
835
+ /**
836
+ * 获取临时目录路径
837
+ */
838
+ static getTempDir() {
839
+ return process.env.TMPDIR || process.env.TEMP || tmpdir2();
840
+ }
841
+ /**
842
+ * 获取用户主目录路径
843
+ */
844
+ static getHomeDir() {
845
+ return process.env.HOME || process.env.USERPROFILE || "";
846
+ }
847
+ };
848
+ }
849
+ });
850
+
851
+ // src/utils/PlatformUtils.ts
852
+ import { execSync } from "child_process";
853
+ var PlatformUtils;
854
+ var init_PlatformUtils = __esm({
855
+ "src/utils/PlatformUtils.ts"() {
856
+ init_Constants();
857
+ init_errors();
858
+ PlatformUtils = class _PlatformUtils {
859
+ static {
860
+ __name(this, "PlatformUtils");
861
+ }
862
+ /**
863
+ * 获取当前平台
864
+ */
865
+ static getCurrentPlatform() {
866
+ return process.platform;
867
+ }
868
+ /**
869
+ * 检查是否为 Windows 平台
870
+ */
871
+ static isWindows() {
872
+ return process.platform === "win32";
873
+ }
874
+ /**
875
+ * 检查是否为 macOS 平台
876
+ */
877
+ static isMacOS() {
878
+ return process.platform === "darwin";
879
+ }
880
+ /**
881
+ * 检查是否为 Linux 平台
882
+ */
883
+ static isLinux() {
884
+ return process.platform === "linux";
885
+ }
886
+ /**
887
+ * 检查是否为类 Unix 系统
888
+ */
889
+ static isUnixLike() {
890
+ return !_PlatformUtils.isWindows();
891
+ }
892
+ /**
893
+ * 检查进程是否为 xiaozhi-client 进程
894
+ */
895
+ static isXiaozhiProcess(pid) {
896
+ try {
897
+ if (process.env.XIAOZHI_CONTAINER === "true" || process.env.NODE_ENV === "test") {
898
+ process.kill(pid, 0);
899
+ return true;
900
+ }
901
+ try {
902
+ let cmdline = "";
903
+ if (_PlatformUtils.isWindows()) {
904
+ const result = execSync(`tasklist /FI "PID eq ${pid}" /FO CSV /NH`, {
905
+ encoding: "utf8",
906
+ timeout: TIMEOUT_CONSTANTS.PROCESS_STOP
907
+ });
908
+ cmdline = result.toLowerCase();
909
+ } else {
910
+ const result = execSync(`ps -p ${pid} -o comm=`, {
911
+ encoding: "utf8",
912
+ timeout: TIMEOUT_CONSTANTS.PROCESS_STOP
913
+ });
914
+ cmdline = result.toLowerCase();
915
+ }
916
+ return cmdline.includes("node") || cmdline.includes("xiaozhi");
917
+ } catch (error) {
918
+ process.kill(pid, 0);
919
+ return true;
920
+ }
921
+ } catch (error) {
922
+ return false;
923
+ }
924
+ }
925
+ /**
926
+ * 杀死进程
927
+ */
928
+ static async killProcess(pid, signal = "SIGTERM") {
929
+ try {
930
+ process.kill(pid, signal);
931
+ let attempts = 0;
932
+ const maxAttempts = 30;
933
+ while (attempts < maxAttempts) {
934
+ await new Promise((resolve) => setTimeout(resolve, 100));
935
+ try {
936
+ process.kill(pid, 0);
937
+ attempts++;
938
+ } catch {
939
+ return;
940
+ }
941
+ }
942
+ try {
943
+ process.kill(pid, 0);
944
+ process.kill(pid, "SIGKILL");
945
+ await new Promise((resolve) => setTimeout(resolve, 500));
946
+ } catch {
947
+ }
948
+ } catch (error) {
949
+ throw new ProcessError(
950
+ `\u65E0\u6CD5\u7EC8\u6B62\u8FDB\u7A0B: ${error instanceof Error ? error.message : String(error)}`,
951
+ pid
952
+ );
953
+ }
954
+ }
955
+ /**
956
+ * 检查进程是否存在
957
+ */
958
+ static processExists(pid) {
959
+ try {
960
+ process.kill(pid, 0);
961
+ return true;
962
+ } catch {
963
+ return false;
964
+ }
965
+ }
966
+ /**
967
+ * 获取系统信息
968
+ */
969
+ static getSystemInfo() {
970
+ return {
971
+ platform: _PlatformUtils.getCurrentPlatform(),
972
+ arch: process.arch,
973
+ nodeVersion: process.version,
974
+ isContainer: process.env.XIAOZHI_CONTAINER === "true"
975
+ };
976
+ }
977
+ /**
978
+ * 获取环境变量
979
+ */
980
+ static getEnvVar(name, defaultValue) {
981
+ return process.env[name] || defaultValue;
982
+ }
983
+ /**
984
+ * 设置环境变量
985
+ */
986
+ static setEnvVar(name, value) {
987
+ process.env[name] = value;
988
+ }
989
+ /**
990
+ * 检查是否在容器环境中运行
991
+ */
992
+ static isContainerEnvironment() {
993
+ return process.env.XIAOZHI_CONTAINER === "true";
994
+ }
995
+ /**
996
+ * 检查是否在测试环境中运行
997
+ */
998
+ static isTestEnvironment() {
999
+ return process.env.NODE_ENV === "test";
1000
+ }
1001
+ /**
1002
+ * 检查是否在开发环境中运行
1003
+ */
1004
+ static isDevelopmentEnvironment() {
1005
+ return process.env.NODE_ENV === "development";
1006
+ }
1007
+ /**
1008
+ * 获取合适的 tail 命令
1009
+ */
1010
+ static getTailCommand(filePath) {
1011
+ if (_PlatformUtils.isWindows()) {
1012
+ return {
1013
+ command: "powershell",
1014
+ args: ["-Command", `Get-Content -Path "${filePath}" -Wait`]
1015
+ };
1016
+ }
1017
+ return {
1018
+ command: "tail",
1019
+ args: ["-f", filePath]
1020
+ };
1021
+ }
1022
+ };
1023
+ }
1024
+ });
1025
+
1026
+ // src/utils/Validation.ts
1027
+ var Validation;
1028
+ var init_Validation = __esm({
1029
+ "src/utils/Validation.ts"() {
1030
+ init_errors();
1031
+ Validation = class _Validation {
1032
+ static {
1033
+ __name(this, "Validation");
1034
+ }
1035
+ /**
1036
+ * 验证端口号
1037
+ */
1038
+ static validatePort(port) {
1039
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
1040
+ throw ValidationError.invalidPort(port);
1041
+ }
1042
+ }
1043
+ /**
1044
+ * 验证配置文件格式
1045
+ */
1046
+ static validateConfigFormat(format) {
1047
+ const validFormats = ["json", "json5", "jsonc"];
1048
+ if (!validFormats.includes(format)) {
1049
+ throw new ValidationError(
1050
+ `\u65E0\u6548\u7684\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F: ${format}\uFF0C\u652F\u6301\u7684\u683C\u5F0F: ${validFormats.join(", ")}`,
1051
+ "format"
1052
+ );
1053
+ }
1054
+ return format;
1055
+ }
1056
+ /**
1057
+ * 验证必填字段
1058
+ */
1059
+ static validateRequired(value, fieldName) {
1060
+ if (value === void 0 || value === null || value === "") {
1061
+ throw ValidationError.requiredField(fieldName);
1062
+ }
1063
+ }
1064
+ /**
1065
+ * 验证字符串长度
1066
+ */
1067
+ static validateStringLength(value, fieldName, options = {}) {
1068
+ if (options.min !== void 0 && value.length < options.min) {
1069
+ throw new ValidationError(
1070
+ `\u957F\u5EA6\u4E0D\u80FD\u5C11\u4E8E ${options.min} \u4E2A\u5B57\u7B26\uFF0C\u5F53\u524D\u957F\u5EA6: ${value.length}`,
1071
+ fieldName
1072
+ );
1073
+ }
1074
+ if (options.max !== void 0 && value.length > options.max) {
1075
+ throw new ValidationError(
1076
+ `\u957F\u5EA6\u4E0D\u80FD\u8D85\u8FC7 ${options.max} \u4E2A\u5B57\u7B26\uFF0C\u5F53\u524D\u957F\u5EA6: ${value.length}`,
1077
+ fieldName
1078
+ );
1079
+ }
1080
+ }
1081
+ /**
1082
+ * 验证 URL 格式
1083
+ */
1084
+ static validateUrl(url, fieldName = "url") {
1085
+ try {
1086
+ new URL(url);
1087
+ } catch {
1088
+ throw new ValidationError(`\u65E0\u6548\u7684 URL \u683C\u5F0F: ${url}`, fieldName);
1089
+ }
1090
+ }
1091
+ /**
1092
+ * 验证 WebSocket URL 格式
1093
+ */
1094
+ static validateWebSocketUrl(url, fieldName = "websocket_url") {
1095
+ _Validation.validateUrl(url, fieldName);
1096
+ const parsedUrl = new URL(url);
1097
+ if (!["ws:", "wss:"].includes(parsedUrl.protocol)) {
1098
+ throw new ValidationError(
1099
+ `WebSocket URL \u5FC5\u987B\u4F7F\u7528 ws:// \u6216 wss:// \u534F\u8BAE\uFF0C\u5F53\u524D\u534F\u8BAE: ${parsedUrl.protocol}`,
1100
+ fieldName
1101
+ );
1102
+ }
1103
+ }
1104
+ /**
1105
+ * 验证 HTTP URL 格式
1106
+ */
1107
+ static validateHttpUrl(url, fieldName = "http_url") {
1108
+ _Validation.validateUrl(url, fieldName);
1109
+ const parsedUrl = new URL(url);
1110
+ if (!["http:", "https:"].includes(parsedUrl.protocol)) {
1111
+ throw new ValidationError(
1112
+ `HTTP URL \u5FC5\u987B\u4F7F\u7528 http:// \u6216 https:// \u534F\u8BAE\uFF0C\u5F53\u524D\u534F\u8BAE: ${parsedUrl.protocol}`,
1113
+ fieldName
1114
+ );
1115
+ }
1116
+ }
1117
+ /**
1118
+ * 验证项目名称
1119
+ */
1120
+ static validateProjectName(name) {
1121
+ _Validation.validateRequired(name, "projectName");
1122
+ _Validation.validateStringLength(name, "projectName", { min: 1, max: 100 });
1123
+ const invalidChars = /[<>:"/\\|?*]/;
1124
+ const hasControlChars = name.split("").some((char) => char.charCodeAt(0) < 32);
1125
+ if (invalidChars.test(name) || hasControlChars) {
1126
+ throw new ValidationError(
1127
+ '\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u5305\u542B\u4EE5\u4E0B\u5B57\u7B26: < > : " / \\ | ? * \u4EE5\u53CA\u63A7\u5236\u5B57\u7B26',
1128
+ "projectName"
1129
+ );
1130
+ }
1131
+ if (name.startsWith(".")) {
1132
+ throw new ValidationError("\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4EE5\u70B9\u5F00\u5934", "projectName");
1133
+ }
1134
+ }
1135
+ /**
1136
+ * 验证模板名称
1137
+ */
1138
+ static validateTemplateName(name) {
1139
+ _Validation.validateRequired(name, "templateName");
1140
+ _Validation.validateStringLength(name, "templateName", { min: 1, max: 50 });
1141
+ const validPattern = /^[a-zA-Z0-9_-]+$/;
1142
+ if (!validPattern.test(name)) {
1143
+ throw new ValidationError(
1144
+ "\u6A21\u677F\u540D\u79F0\u53EA\u80FD\u5305\u542B\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u8FDE\u5B57\u7B26\u548C\u4E0B\u5212\u7EBF",
1145
+ "templateName"
1146
+ );
1147
+ }
1148
+ }
1149
+ /**
1150
+ * 验证环境变量名称
1151
+ */
1152
+ static validateEnvVarName(name) {
1153
+ _Validation.validateRequired(name, "envVarName");
1154
+ const validPattern = /^[A-Z_][A-Z0-9_]*$/;
1155
+ if (!validPattern.test(name)) {
1156
+ throw new ValidationError(
1157
+ "\u73AF\u5883\u53D8\u91CF\u540D\u79F0\u53EA\u80FD\u5305\u542B\u5927\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u548C\u4E0B\u5212\u7EBF\uFF0C\u4E14\u4E0D\u80FD\u4EE5\u6570\u5B57\u5F00\u5934",
1158
+ "envVarName"
1159
+ );
1160
+ }
1161
+ }
1162
+ /**
1163
+ * 验证 JSON 格式
1164
+ */
1165
+ static validateJson(jsonString, fieldName = "json") {
1166
+ try {
1167
+ return JSON.parse(jsonString);
1168
+ } catch (error) {
1169
+ throw new ValidationError(
1170
+ `\u65E0\u6548\u7684 JSON \u683C\u5F0F: ${error instanceof Error ? error.message : String(error)}`,
1171
+ fieldName
1172
+ );
1173
+ }
1174
+ }
1175
+ /**
1176
+ * 验证数字范围
1177
+ */
1178
+ static validateNumberRange(value, fieldName, options = {}) {
1179
+ if (options.min !== void 0 && value < options.min) {
1180
+ throw new ValidationError(
1181
+ `\u503C\u4E0D\u80FD\u5C0F\u4E8E ${options.min}\uFF0C\u5F53\u524D\u503C: ${value}`,
1182
+ fieldName
1183
+ );
1184
+ }
1185
+ if (options.max !== void 0 && value > options.max) {
1186
+ throw new ValidationError(
1187
+ `\u503C\u4E0D\u80FD\u5927\u4E8E ${options.max}\uFF0C\u5F53\u524D\u503C: ${value}`,
1188
+ fieldName
1189
+ );
1190
+ }
1191
+ }
1192
+ /**
1193
+ * 验证数组长度
1194
+ */
1195
+ static validateArrayLength(array, fieldName, options = {}) {
1196
+ if (options.min !== void 0 && array.length < options.min) {
1197
+ throw new ValidationError(
1198
+ `\u6570\u7EC4\u957F\u5EA6\u4E0D\u80FD\u5C11\u4E8E ${options.min}\uFF0C\u5F53\u524D\u957F\u5EA6: ${array.length}`,
1199
+ fieldName
1200
+ );
1201
+ }
1202
+ if (options.max !== void 0 && array.length > options.max) {
1203
+ throw new ValidationError(
1204
+ `\u6570\u7EC4\u957F\u5EA6\u4E0D\u80FD\u8D85\u8FC7 ${options.max}\uFF0C\u5F53\u524D\u957F\u5EA6: ${array.length}`,
1205
+ fieldName
1206
+ );
1207
+ }
1208
+ }
1209
+ /**
1210
+ * 验证对象属性
1211
+ */
1212
+ static validateObjectProperties(obj, requiredProps, fieldName = "object") {
1213
+ for (const prop of requiredProps) {
1214
+ if (!(prop in obj)) {
1215
+ throw new ValidationError(`\u7F3A\u5C11\u5FC5\u9700\u7684\u5C5E\u6027: ${prop}`, fieldName);
1216
+ }
1217
+ }
1218
+ }
1219
+ /**
1220
+ * 验证枚举值
1221
+ */
1222
+ static validateEnum(value, validValues, fieldName) {
1223
+ if (!validValues.includes(value)) {
1224
+ throw new ValidationError(
1225
+ `\u65E0\u6548\u7684\u503C: ${value}\uFF0C\u6709\u6548\u503C: ${validValues.join(", ")}`,
1226
+ fieldName
1227
+ );
1228
+ }
1229
+ return value;
1230
+ }
1231
+ /**
1232
+ * 验证正则表达式
1233
+ */
1234
+ static validateRegex(pattern, fieldName = "regex") {
1235
+ try {
1236
+ return new RegExp(pattern);
1237
+ } catch (error) {
1238
+ throw new ValidationError(
1239
+ `\u65E0\u6548\u7684\u6B63\u5219\u8868\u8FBE\u5F0F: ${error instanceof Error ? error.message : String(error)}`,
1240
+ fieldName
1241
+ );
1242
+ }
1243
+ }
1244
+ };
1245
+ }
1246
+ });
1247
+
1248
+ // src/services/ProcessManager.ts
1249
+ var ProcessManager_exports = {};
1250
+ __export(ProcessManager_exports, {
1251
+ ProcessManagerImpl: () => ProcessManagerImpl
1252
+ });
1253
+ var ProcessManagerImpl;
1254
+ var init_ProcessManager = __esm({
1255
+ "src/services/ProcessManager.ts"() {
1256
+ init_errors();
1257
+ init_FileUtils();
1258
+ init_FormatUtils();
1259
+ init_PathUtils();
1260
+ init_PlatformUtils();
1261
+ ProcessManagerImpl = class {
1262
+ static {
1263
+ __name(this, "ProcessManagerImpl");
1264
+ }
1265
+ /**
1266
+ * 获取 PID 文件路径
1267
+ */
1268
+ getPidFilePath() {
1269
+ return PathUtils.getPidFile();
1270
+ }
1271
+ /**
1272
+ * 读取 PID 文件信息
1273
+ */
1274
+ readPidFile() {
1275
+ try {
1276
+ const pidFilePath = this.getPidFilePath();
1277
+ if (!FileUtils.exists(pidFilePath)) {
1278
+ return null;
1279
+ }
1280
+ const pidContent = FileUtils.readFile(pidFilePath).trim();
1281
+ const [pidStr, startTimeStr, mode] = pidContent.split("|");
1282
+ const pid = Number.parseInt(pidStr);
1283
+ const startTime = Number.parseInt(startTimeStr);
1284
+ if (Number.isNaN(pid) || Number.isNaN(startTime)) {
1285
+ this.cleanupPidFile();
1286
+ return null;
1287
+ }
1288
+ return {
1289
+ pid,
1290
+ startTime,
1291
+ mode: mode || "foreground"
1292
+ };
1293
+ } catch (error) {
1294
+ this.cleanupPidFile();
1295
+ return null;
1296
+ }
1297
+ }
1298
+ /**
1299
+ * 写入 PID 文件信息
1300
+ */
1301
+ writePidFile(pid, mode) {
1302
+ try {
1303
+ const pidInfo = `${pid}|${Date.now()}|${mode}`;
1304
+ const pidFilePath = this.getPidFilePath();
1305
+ FileUtils.writeFile(pidFilePath, pidInfo, { overwrite: true });
1306
+ } catch (error) {
1307
+ throw new FileError("\u65E0\u6CD5\u5199\u5165 PID \u6587\u4EF6", this.getPidFilePath());
1308
+ }
1309
+ }
1310
+ /**
1311
+ * 检查是否为 xiaozhi 进程
1312
+ */
1313
+ isXiaozhiProcess(pid) {
1314
+ return PlatformUtils.isXiaozhiProcess(pid);
1315
+ }
1316
+ /**
1317
+ * 获取服务状态
1318
+ */
1319
+ getServiceStatus() {
1320
+ try {
1321
+ const pidInfo = this.readPidFile();
1322
+ if (!pidInfo) {
1323
+ return { running: false };
1324
+ }
1325
+ if (!this.isXiaozhiProcess(pidInfo.pid)) {
1326
+ this.cleanupPidFile();
1327
+ return { running: false };
1328
+ }
1329
+ const uptime = FormatUtils.formatUptime(Date.now() - pidInfo.startTime);
1330
+ return {
1331
+ running: true,
1332
+ pid: pidInfo.pid,
1333
+ uptime,
1334
+ mode: pidInfo.mode
1335
+ };
1336
+ } catch (error) {
1337
+ return { running: false };
1338
+ }
1339
+ }
1340
+ /**
1341
+ * 保存进程信息
1342
+ */
1343
+ savePidInfo(pid, mode) {
1344
+ this.writePidFile(pid, mode);
1345
+ }
1346
+ /**
1347
+ * 杀死进程
1348
+ */
1349
+ async killProcess(pid) {
1350
+ try {
1351
+ await PlatformUtils.killProcess(pid);
1352
+ } catch (error) {
1353
+ throw new ProcessError(
1354
+ `\u65E0\u6CD5\u7EC8\u6B62\u8FDB\u7A0B: ${error instanceof Error ? error.message : String(error)}`,
1355
+ pid
1356
+ );
1357
+ }
1358
+ }
1359
+ /**
1360
+ * 优雅停止进程
1361
+ */
1362
+ async gracefulKillProcess(pid) {
1363
+ try {
1364
+ process.kill(pid, "SIGTERM");
1365
+ let attempts = 0;
1366
+ const maxAttempts = 30;
1367
+ while (attempts < maxAttempts) {
1368
+ await new Promise((resolve) => setTimeout(resolve, 100));
1369
+ try {
1370
+ process.kill(pid, 0);
1371
+ attempts++;
1372
+ } catch {
1373
+ return;
1374
+ }
1375
+ }
1376
+ try {
1377
+ process.kill(pid, 0);
1378
+ process.kill(pid, "SIGKILL");
1379
+ await new Promise((resolve) => setTimeout(resolve, 500));
1380
+ } catch {
1381
+ }
1382
+ } catch (error) {
1383
+ throw new ProcessError(
1384
+ `\u65E0\u6CD5\u505C\u6B62\u8FDB\u7A0B: ${error instanceof Error ? error.message : String(error)}`,
1385
+ pid
1386
+ );
1387
+ }
1388
+ }
1389
+ /**
1390
+ * 清理 PID 文件
1391
+ */
1392
+ cleanupPidFile() {
1393
+ try {
1394
+ const pidFilePath = this.getPidFilePath();
1395
+ if (FileUtils.exists(pidFilePath)) {
1396
+ FileUtils.deleteFile(pidFilePath);
1397
+ }
1398
+ } catch (error) {
1399
+ console.warn("\u6E05\u7406 PID \u6587\u4EF6\u5931\u8D25:", error);
1400
+ }
1401
+ }
1402
+ /**
1403
+ * 检查进程是否存在
1404
+ */
1405
+ processExists(pid) {
1406
+ return PlatformUtils.processExists(pid);
1407
+ }
1408
+ /**
1409
+ * 清理容器环境的旧状态
1410
+ */
1411
+ cleanupContainerState() {
1412
+ if (PlatformUtils.isContainerEnvironment()) {
1413
+ try {
1414
+ this.cleanupPidFile();
1415
+ } catch (error) {
1416
+ }
1417
+ }
1418
+ }
1419
+ /**
1420
+ * 获取进程信息
1421
+ */
1422
+ getProcessInfo(pid) {
1423
+ const exists = this.processExists(pid);
1424
+ const isXiaozhi = exists ? this.isXiaozhiProcess(pid) : false;
1425
+ return { exists, isXiaozhi };
1426
+ }
1427
+ /**
1428
+ * 验证 PID 文件完整性
1429
+ */
1430
+ validatePidFile() {
1431
+ try {
1432
+ const pidInfo = this.readPidFile();
1433
+ return pidInfo !== null;
1434
+ } catch {
1435
+ return false;
1436
+ }
1437
+ }
1438
+ };
1439
+ }
1440
+ });
1441
+
1442
+ // src/services/DaemonManager.ts
1443
+ var DaemonManager_exports = {};
1444
+ __export(DaemonManager_exports, {
1445
+ DaemonManagerImpl: () => DaemonManagerImpl
1446
+ });
1447
+ import { spawn } from "child_process";
1448
+ import fs3 from "fs";
1449
+ var DaemonManagerImpl;
1450
+ var init_DaemonManager = __esm({
1451
+ "src/services/DaemonManager.ts"() {
1452
+ init_errors();
1453
+ init_PathUtils();
1454
+ init_PlatformUtils();
1455
+ DaemonManagerImpl = class {
1456
+ constructor(processManager, logger3) {
1457
+ this.processManager = processManager;
1458
+ this.logger = logger3;
1459
+ }
1460
+ static {
1461
+ __name(this, "DaemonManagerImpl");
1462
+ }
1463
+ currentDaemon = null;
1464
+ /**
1465
+ * 启动守护进程
1466
+ */
1467
+ async startDaemon(serverFactory, options = {}) {
1468
+ try {
1469
+ const status = this.processManager.getServiceStatus();
1470
+ if (status.running) {
1471
+ throw ServiceError.alreadyRunning(status.pid);
1472
+ }
1473
+ const child = await this.spawnDaemonProcess(serverFactory, options);
1474
+ this.currentDaemon = child;
1475
+ this.processManager.savePidInfo(child.pid, "daemon");
1476
+ await this.setupLogging(child, options.logFileName || "xiaozhi.log");
1477
+ this.setupEventHandlers(child);
1478
+ child.unref();
1479
+ this.logger.info(`\u5B88\u62A4\u8FDB\u7A0B\u5DF2\u542F\u52A8 (PID: ${child.pid})`);
1480
+ } catch (error) {
1481
+ throw new ServiceError(
1482
+ `\u542F\u52A8\u5B88\u62A4\u8FDB\u7A0B\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
1483
+ );
1484
+ }
1485
+ }
1486
+ /**
1487
+ * 停止守护进程
1488
+ */
1489
+ async stopDaemon() {
1490
+ try {
1491
+ const status = this.processManager.getServiceStatus();
1492
+ if (!status.running) {
1493
+ throw ServiceError.notRunning();
1494
+ }
1495
+ await this.processManager.gracefulKillProcess(status.pid);
1496
+ this.processManager.cleanupPidFile();
1497
+ this.currentDaemon = null;
1498
+ this.logger.info("\u5B88\u62A4\u8FDB\u7A0B\u5DF2\u505C\u6B62");
1499
+ } catch (error) {
1500
+ throw new ServiceError(
1501
+ `\u505C\u6B62\u5B88\u62A4\u8FDB\u7A0B\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
1502
+ );
1503
+ }
1504
+ }
1505
+ /**
1506
+ * 重启守护进程
1507
+ */
1508
+ async restartDaemon(serverFactory, options = {}) {
1509
+ try {
1510
+ const status = this.processManager.getServiceStatus();
1511
+ if (status.running) {
1512
+ await this.stopDaemon();
1513
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
1514
+ }
1515
+ await this.startDaemon(serverFactory, options);
1516
+ } catch (error) {
1517
+ throw new ServiceError(
1518
+ `\u91CD\u542F\u5B88\u62A4\u8FDB\u7A0B\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
1519
+ );
1520
+ }
1521
+ }
1522
+ /**
1523
+ * 获取守护进程状态
1524
+ */
1525
+ getDaemonStatus() {
1526
+ return this.processManager.getServiceStatus();
1527
+ }
1528
+ /**
1529
+ * 连接到守护进程日志
1530
+ */
1531
+ async attachToLogs(logFileName = "xiaozhi.log") {
1532
+ try {
1533
+ const logFilePath = PathUtils.getLogFile();
1534
+ if (!fs3.existsSync(logFilePath)) {
1535
+ throw new ServiceError("\u65E5\u5FD7\u6587\u4EF6\u4E0D\u5B58\u5728");
1536
+ }
1537
+ const { command, args } = PlatformUtils.getTailCommand(logFilePath);
1538
+ const tail = spawn(command, args, { stdio: "inherit" });
1539
+ process.on("SIGINT", () => {
1540
+ console.log("\n\u65AD\u5F00\u8FDE\u63A5\uFF0C\u670D\u52A1\u7EE7\u7EED\u5728\u540E\u53F0\u8FD0\u884C");
1541
+ tail.kill();
1542
+ process.exit(0);
1543
+ });
1544
+ tail.on("exit", () => {
1545
+ process.exit(0);
1546
+ });
1547
+ tail.on("error", (error) => {
1548
+ throw new ServiceError(`\u8FDE\u63A5\u65E5\u5FD7\u5931\u8D25: ${error.message}`);
1549
+ });
1550
+ } catch (error) {
1551
+ throw new ServiceError(
1552
+ `\u8FDE\u63A5\u65E5\u5FD7\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
1553
+ );
1554
+ }
1555
+ }
1556
+ /**
1557
+ * 生成守护进程
1558
+ */
1559
+ async spawnDaemonProcess(serverFactory, options) {
1560
+ const scriptPath2 = PathUtils.getWebServerLauncherPath();
1561
+ const args = [scriptPath2];
1562
+ if (options.openBrowser) {
1563
+ args.push("--open-browser");
1564
+ }
1565
+ const env = {
1566
+ ...process.env,
1567
+ XIAOZHI_CONFIG_DIR: PathUtils.getConfigDir(),
1568
+ XIAOZHI_DAEMON: "true",
1569
+ ...options.env
1570
+ };
1571
+ const child = spawn("node", args, {
1572
+ detached: true,
1573
+ stdio: ["ignore", "pipe", "pipe"],
1574
+ env,
1575
+ cwd: options.cwd || process.cwd()
1576
+ });
1577
+ if (!child.pid) {
1578
+ throw new ProcessError("\u65E0\u6CD5\u542F\u52A8\u5B88\u62A4\u8FDB\u7A0B", 0);
1579
+ }
1580
+ return child;
1581
+ }
1582
+ /**
1583
+ * 设置日志重定向
1584
+ */
1585
+ async setupLogging(child, logFileName) {
1586
+ try {
1587
+ const logFilePath = PathUtils.getLogFile();
1588
+ const path7 = await import("path");
1589
+ const logDir = path7.dirname(logFilePath);
1590
+ if (!fs3.existsSync(logDir)) {
1591
+ fs3.mkdirSync(logDir, { recursive: true });
1592
+ }
1593
+ const logStream = fs3.createWriteStream(logFilePath, { flags: "a" });
1594
+ child.stdout?.pipe(logStream);
1595
+ child.stderr?.pipe(logStream);
1596
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
1597
+ logStream.write(`
1598
+ [${timestamp}] \u5B88\u62A4\u8FDB\u7A0B\u542F\u52A8 (PID: ${child.pid})
1599
+ `);
1600
+ } catch (error) {
1601
+ this.logger.warn(
1602
+ `\u8BBE\u7F6E\u65E5\u5FD7\u91CD\u5B9A\u5411\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
1603
+ );
1604
+ }
1605
+ }
1606
+ /**
1607
+ * 设置事件处理器
1608
+ */
1609
+ setupEventHandlers(child) {
1610
+ child.on("exit", (code, signal) => {
1611
+ if (code !== 0 && code !== null) {
1612
+ this.logger.error(`\u5B88\u62A4\u8FDB\u7A0B\u5F02\u5E38\u9000\u51FA (\u4EE3\u7801: ${code}, \u4FE1\u53F7: ${signal})`);
1613
+ } else {
1614
+ this.logger.info("\u5B88\u62A4\u8FDB\u7A0B\u6B63\u5E38\u9000\u51FA");
1615
+ }
1616
+ this.processManager.cleanupPidFile();
1617
+ this.currentDaemon = null;
1618
+ });
1619
+ child.on("error", (error) => {
1620
+ this.logger.error(`\u5B88\u62A4\u8FDB\u7A0B\u9519\u8BEF: ${error.message}`);
1621
+ this.processManager.cleanupPidFile();
1622
+ this.currentDaemon = null;
1623
+ });
1624
+ child.on("disconnect", () => {
1625
+ this.logger.info("\u5B88\u62A4\u8FDB\u7A0B\u65AD\u5F00\u8FDE\u63A5");
1626
+ });
1627
+ }
1628
+ /**
1629
+ * 检查守护进程健康状态
1630
+ */
1631
+ async checkHealth() {
1632
+ try {
1633
+ const status = this.getDaemonStatus();
1634
+ if (!status.running || !status.pid) {
1635
+ return false;
1636
+ }
1637
+ const processInfo = this.processManager.getProcessInfo(status.pid);
1638
+ return processInfo.exists && processInfo.isXiaozhi;
1639
+ } catch {
1640
+ return false;
1641
+ }
1642
+ }
1643
+ /**
1644
+ * 获取当前守护进程引用
1645
+ */
1646
+ getCurrentDaemon() {
1647
+ return this.currentDaemon;
1648
+ }
1649
+ /**
1650
+ * 清理守护进程资源
1651
+ */
1652
+ cleanup() {
1653
+ if (this.currentDaemon) {
1654
+ try {
1655
+ this.currentDaemon.kill("SIGTERM");
1656
+ } catch (error) {
1657
+ this.logger.warn(
1658
+ `\u6E05\u7406\u5B88\u62A4\u8FDB\u7A0B\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
1659
+ );
1660
+ }
1661
+ this.currentDaemon = null;
1662
+ }
1663
+ }
1664
+ };
1665
+ }
1666
+ });
1667
+
1668
+ // src/services/ServiceManager.ts
1669
+ var ServiceManager_exports = {};
1670
+ __export(ServiceManager_exports, {
1671
+ ServiceManagerImpl: () => ServiceManagerImpl
1672
+ });
1673
+ var ServiceManagerImpl;
1674
+ var init_ServiceManager = __esm({
1675
+ "src/services/ServiceManager.ts"() {
1676
+ init_errors();
1677
+ init_PathUtils();
1678
+ init_Validation();
1679
+ ServiceManagerImpl = class {
1680
+ constructor(processManager, configManager3) {
1681
+ this.processManager = processManager;
1682
+ this.configManager = configManager3;
1683
+ }
1684
+ static {
1685
+ __name(this, "ServiceManagerImpl");
1686
+ }
1687
+ /**
1688
+ * 启动服务
1689
+ */
1690
+ async start(options) {
1691
+ try {
1692
+ this.validateStartOptions(options);
1693
+ this.processManager.cleanupContainerState();
1694
+ const status = this.getStatus();
1695
+ if (status.running) {
1696
+ console.log(`\u68C0\u6D4B\u5230\u670D\u52A1\u5DF2\u5728\u8FD0\u884C (PID: ${status.pid})\uFF0C\u6B63\u5728\u81EA\u52A8\u91CD\u542F...`);
1697
+ try {
1698
+ await this.processManager.gracefulKillProcess(status.pid || 0);
1699
+ this.processManager.cleanupPidFile();
1700
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
1701
+ console.log("\u73B0\u6709\u670D\u52A1\u5DF2\u505C\u6B62\uFF0C\u6B63\u5728\u542F\u52A8\u65B0\u670D\u52A1...");
1702
+ } catch (stopError) {
1703
+ console.warn(
1704
+ `\u505C\u6B62\u73B0\u6709\u670D\u52A1\u65F6\u51FA\u73B0\u8B66\u544A: ${stopError instanceof Error ? stopError.message : String(stopError)}`
1705
+ );
1706
+ }
1707
+ }
1708
+ this.checkEnvironment();
1709
+ switch (options.mode) {
1710
+ case "mcp-server":
1711
+ await this.startMcpServerMode(options);
1712
+ break;
1713
+ case "stdio":
1714
+ await this.startNormalMode(options);
1715
+ break;
1716
+ case "normal":
1717
+ await this.startNormalMode(options);
1718
+ break;
1719
+ default:
1720
+ await this.startNormalMode(options);
1721
+ break;
1722
+ }
1723
+ } catch (error) {
1724
+ if (error instanceof ServiceError) {
1725
+ throw error;
1726
+ }
1727
+ throw ServiceError.startFailed(
1728
+ error instanceof Error ? error.message : String(error)
1729
+ );
1730
+ }
1731
+ }
1732
+ /**
1733
+ * 停止服务
1734
+ */
1735
+ async stop() {
1736
+ try {
1737
+ const status = this.getStatus();
1738
+ if (!status.running) {
1739
+ throw ServiceError.notRunning();
1740
+ }
1741
+ await this.processManager.gracefulKillProcess(status.pid || 0);
1742
+ this.processManager.cleanupPidFile();
1743
+ } catch (error) {
1744
+ if (error instanceof ServiceError) {
1745
+ throw error;
1746
+ }
1747
+ throw new ServiceError(
1748
+ `\u505C\u6B62\u670D\u52A1\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
1749
+ );
1750
+ }
1751
+ }
1752
+ /**
1753
+ * 重启服务
1754
+ */
1755
+ async restart(options) {
1756
+ try {
1757
+ const status = this.getStatus();
1758
+ if (status.running) {
1759
+ await this.stop();
1760
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
1761
+ }
1762
+ await this.start(options);
1763
+ } catch (error) {
1764
+ throw new ServiceError(
1765
+ `\u91CD\u542F\u670D\u52A1\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
1766
+ );
1767
+ }
1768
+ }
1769
+ /**
1770
+ * 获取服务状态
1771
+ */
1772
+ getStatus() {
1773
+ return this.processManager.getServiceStatus();
1774
+ }
1775
+ /**
1776
+ * 验证启动选项
1777
+ */
1778
+ validateStartOptions(options) {
1779
+ if (options.port !== void 0) {
1780
+ Validation.validatePort(options.port);
1781
+ }
1782
+ if (options.mode && !["normal", "mcp-server", "stdio"].includes(options.mode)) {
1783
+ throw new ServiceError(`\u65E0\u6548\u7684\u8FD0\u884C\u6A21\u5F0F: ${options.mode}`);
1784
+ }
1785
+ }
1786
+ /**
1787
+ * 检查环境配置
1788
+ */
1789
+ checkEnvironment() {
1790
+ if (!this.configManager.configExists()) {
1791
+ throw ConfigError.configNotFound();
1792
+ }
1793
+ try {
1794
+ const config = this.configManager.getConfig();
1795
+ if (!config) {
1796
+ throw new ConfigError("\u914D\u7F6E\u6587\u4EF6\u65E0\u6548");
1797
+ }
1798
+ } catch (error) {
1799
+ if (error instanceof ConfigError) {
1800
+ throw error;
1801
+ }
1802
+ throw new ConfigError(
1803
+ `\u914D\u7F6E\u6587\u4EF6\u9519\u8BEF: ${error instanceof Error ? error.message : String(error)}`
1804
+ );
1805
+ }
1806
+ }
1807
+ /**
1808
+ * 启动普通模式
1809
+ */
1810
+ async startNormalMode(options) {
1811
+ if (options.daemon) {
1812
+ await this.startWebServerInDaemon();
1813
+ } else {
1814
+ await this.startWebServerInForeground();
1815
+ }
1816
+ }
1817
+ /**
1818
+ * 启动 MCP Server 模式
1819
+ */
1820
+ async startMcpServerMode(options) {
1821
+ const port = options.port || 3e3;
1822
+ const { spawn: spawn2 } = await import("child_process");
1823
+ if (options.daemon) {
1824
+ const scriptPath2 = PathUtils.getExecutablePath("cli");
1825
+ const child = spawn2(
1826
+ "node",
1827
+ [scriptPath2, "start", "--server", port.toString()],
1828
+ {
1829
+ detached: true,
1830
+ stdio: ["ignore", "ignore", "ignore"],
1831
+ // 完全忽略所有 stdio,避免阻塞
1832
+ env: {
1833
+ ...process.env,
1834
+ XIAOZHI_CONFIG_DIR: PathUtils.getConfigDir(),
1835
+ XIAOZHI_DAEMON: "true",
1836
+ MCP_SERVER_MODE: "true"
1837
+ }
1838
+ }
1839
+ );
1840
+ this.processManager.savePidInfo(child.pid || 0, "daemon");
1841
+ child.unref();
1842
+ console.log(
1843
+ `\u2705 MCP Server \u5DF2\u5728\u540E\u53F0\u542F\u52A8 (PID: ${child.pid}, Port: ${port})`
1844
+ );
1845
+ console.log(`\u{1F4A1} \u4F7F\u7528 'xiaozhi status' \u67E5\u770B\u72B6\u6001`);
1846
+ process.exit(0);
1847
+ } else {
1848
+ const { WebServer } = await import("../backend/WebServer.js");
1849
+ const server = new WebServer(port);
1850
+ const cleanup = /* @__PURE__ */ __name(async () => {
1851
+ await server.stop();
1852
+ process.exit(0);
1853
+ }, "cleanup");
1854
+ process.on("SIGINT", cleanup);
1855
+ process.on("SIGTERM", cleanup);
1856
+ await server.start();
1857
+ }
1858
+ }
1859
+ /**
1860
+ * 后台模式启动 WebServer
1861
+ */
1862
+ async startWebServerInDaemon() {
1863
+ const { spawn: spawn2 } = await import("child_process");
1864
+ const webServerPath = PathUtils.getWebServerLauncherPath();
1865
+ const fs5 = await import("fs");
1866
+ if (!fs5.default.existsSync(webServerPath)) {
1867
+ throw new ServiceError(`WebServer \u6587\u4EF6\u4E0D\u5B58\u5728: ${webServerPath}`);
1868
+ }
1869
+ const args = [webServerPath];
1870
+ const child = spawn2("node", args, {
1871
+ detached: true,
1872
+ stdio: ["ignore", "ignore", "ignore"],
1873
+ // 完全忽略所有 stdio,避免阻塞
1874
+ env: {
1875
+ ...process.env,
1876
+ XIAOZHI_CONFIG_DIR: PathUtils.getConfigDir(),
1877
+ XIAOZHI_DAEMON: "true"
1878
+ }
1879
+ });
1880
+ this.processManager.savePidInfo(child.pid || 0, "daemon");
1881
+ child.unref();
1882
+ console.log(`\u2705 \u540E\u53F0\u670D\u52A1\u5DF2\u542F\u52A8 (PID: ${child.pid})`);
1883
+ console.log(`\u{1F4A1} \u4F7F\u7528 'xiaozhi status' \u67E5\u770B\u72B6\u6001`);
1884
+ console.log(`\u{1F4A1} \u4F7F\u7528 'xiaozhi attach' \u67E5\u770B\u65E5\u5FD7`);
1885
+ process.exit(0);
1886
+ }
1887
+ /**
1888
+ * 前台模式启动 WebServer
1889
+ */
1890
+ async startWebServerInForeground() {
1891
+ const { WebServer } = await import("../backend/WebServer.js");
1892
+ const server = new WebServer();
1893
+ const cleanup = /* @__PURE__ */ __name(async () => {
1894
+ await server.stop();
1895
+ this.processManager.cleanupPidFile();
1896
+ process.exit(0);
1897
+ }, "cleanup");
1898
+ process.on("SIGINT", cleanup);
1899
+ process.on("SIGTERM", cleanup);
1900
+ this.processManager.savePidInfo(process.pid, "foreground");
1901
+ await server.start();
1902
+ }
1903
+ };
1904
+ }
1905
+ });
1906
+
1907
+ // src/services/TemplateManager.ts
1908
+ var TemplateManager_exports = {};
1909
+ __export(TemplateManager_exports, {
1910
+ TemplateManagerImpl: () => TemplateManagerImpl
1911
+ });
1912
+ import fs4 from "fs";
1913
+ import path4 from "path";
1914
+ var TemplateManagerImpl;
1915
+ var init_TemplateManager = __esm({
1916
+ "src/services/TemplateManager.ts"() {
1917
+ init_errors();
1918
+ init_FileUtils();
1919
+ init_PathUtils();
1920
+ init_Validation();
1921
+ TemplateManagerImpl = class {
1922
+ static {
1923
+ __name(this, "TemplateManagerImpl");
1924
+ }
1925
+ templateCache = /* @__PURE__ */ new Map();
1926
+ /**
1927
+ * 获取可用模板列表
1928
+ */
1929
+ async getAvailableTemplates() {
1930
+ try {
1931
+ const templatesDir = PathUtils.findTemplatesDir();
1932
+ if (!templatesDir) {
1933
+ return [];
1934
+ }
1935
+ const templates = [];
1936
+ const templateDirs = fs4.readdirSync(templatesDir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
1937
+ for (const templateName of templateDirs) {
1938
+ try {
1939
+ const templateInfo = await this.getTemplateInfo(templateName);
1940
+ if (templateInfo) {
1941
+ templates.push(templateInfo);
1942
+ }
1943
+ } catch (error) {
1944
+ console.warn(`\u8DF3\u8FC7\u65E0\u6548\u6A21\u677F: ${templateName}`);
1945
+ }
1946
+ }
1947
+ return templates;
1948
+ } catch (error) {
1949
+ throw new FileError(
1950
+ "\u65E0\u6CD5\u8BFB\u53D6\u6A21\u677F\u76EE\u5F55",
1951
+ PathUtils.findTemplatesDir() || ""
1952
+ );
1953
+ }
1954
+ }
1955
+ /**
1956
+ * 获取模板信息
1957
+ */
1958
+ async getTemplateInfo(templateName) {
1959
+ try {
1960
+ Validation.validateTemplateName(templateName);
1961
+ if (this.templateCache.has(templateName)) {
1962
+ return this.templateCache.get(templateName);
1963
+ }
1964
+ const templatePath = PathUtils.getTemplatePath(templateName);
1965
+ if (!templatePath) {
1966
+ return null;
1967
+ }
1968
+ const configPath = path4.join(templatePath, "template.json");
1969
+ let config = {};
1970
+ if (FileUtils.exists(configPath)) {
1971
+ try {
1972
+ const configContent = FileUtils.readFile(configPath);
1973
+ config = JSON.parse(configContent);
1974
+ } catch (error) {
1975
+ console.warn(`\u6A21\u677F\u914D\u7F6E\u6587\u4EF6\u89E3\u6790\u5931\u8D25: ${templateName}`);
1976
+ }
1977
+ }
1978
+ const files = this.getTemplateFiles(templatePath);
1979
+ const templateInfo = {
1980
+ name: templateName,
1981
+ path: templatePath,
1982
+ description: config.description || `${templateName} \u6A21\u677F`,
1983
+ version: config.version || "1.0.0",
1984
+ author: config.author,
1985
+ files
1986
+ };
1987
+ this.templateCache.set(templateName, templateInfo);
1988
+ return templateInfo;
1989
+ } catch (error) {
1990
+ if (error instanceof ValidationError) {
1991
+ throw error;
1992
+ }
1993
+ throw new FileError(`\u65E0\u6CD5\u83B7\u53D6\u6A21\u677F\u4FE1\u606F: ${templateName}`, "");
1994
+ }
1995
+ }
1996
+ /**
1997
+ * 复制模板到目标目录
1998
+ */
1999
+ async copyTemplate(templateName, targetPath) {
2000
+ await this.createProject({
2001
+ templateName,
2002
+ targetPath,
2003
+ projectName: path4.basename(targetPath)
2004
+ });
2005
+ }
2006
+ /**
2007
+ * 创建项目
2008
+ */
2009
+ async createProject(options) {
2010
+ try {
2011
+ this.validateCreateOptions(options);
2012
+ const templateName = options.templateName || "default";
2013
+ const templateInfo = await this.getTemplateInfo(templateName);
2014
+ if (!templateInfo) {
2015
+ throw new FileError(`\u6A21\u677F\u4E0D\u5B58\u5728: ${templateName}`, "");
2016
+ }
2017
+ const targetPath = path4.resolve(options.targetPath);
2018
+ if (FileUtils.exists(targetPath)) {
2019
+ throw FileError.alreadyExists(targetPath);
2020
+ }
2021
+ FileUtils.ensureDir(targetPath);
2022
+ await this.copyTemplateFiles(templateInfo, targetPath, options);
2023
+ await this.processTemplateVariables(targetPath, options);
2024
+ console.log(`\u2705 \u9879\u76EE\u521B\u5EFA\u6210\u529F: ${targetPath}`);
2025
+ } catch (error) {
2026
+ if (error instanceof FileError || error instanceof ValidationError) {
2027
+ throw error;
2028
+ }
2029
+ throw new FileError(
2030
+ `\u521B\u5EFA\u9879\u76EE\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`,
2031
+ options.targetPath
2032
+ );
2033
+ }
2034
+ }
2035
+ /**
2036
+ * 验证模板
2037
+ */
2038
+ async validateTemplate(templateName) {
2039
+ try {
2040
+ const templateInfo = await this.getTemplateInfo(templateName);
2041
+ if (!templateInfo) {
2042
+ return false;
2043
+ }
2044
+ const requiredFiles = ["package.json"];
2045
+ for (const requiredFile of requiredFiles) {
2046
+ const filePath = path4.join(templateInfo.path, requiredFile);
2047
+ if (!FileUtils.exists(filePath)) {
2048
+ console.warn(`\u6A21\u677F\u7F3A\u5C11\u5FC5\u8981\u6587\u4EF6: ${requiredFile}`);
2049
+ return false;
2050
+ }
2051
+ }
2052
+ return true;
2053
+ } catch {
2054
+ return false;
2055
+ }
2056
+ }
2057
+ /**
2058
+ * 清除模板缓存
2059
+ */
2060
+ clearCache() {
2061
+ this.templateCache.clear();
2062
+ }
2063
+ /**
2064
+ * 获取模板文件列表
2065
+ */
2066
+ getTemplateFiles(templatePath) {
2067
+ try {
2068
+ const files = FileUtils.listDirectory(templatePath, {
2069
+ recursive: true,
2070
+ includeHidden: false
2071
+ });
2072
+ return files.filter((file) => {
2073
+ const relativePath = path4.relative(templatePath, file);
2074
+ return !relativePath.startsWith(".") && relativePath !== "template.json" && !relativePath.includes("node_modules");
2075
+ });
2076
+ } catch {
2077
+ return [];
2078
+ }
2079
+ }
2080
+ /**
2081
+ * 验证创建选项
2082
+ */
2083
+ validateCreateOptions(options) {
2084
+ Validation.validateRequired(options.targetPath, "targetPath");
2085
+ Validation.validateRequired(options.projectName, "projectName");
2086
+ Validation.validateProjectName(options.projectName);
2087
+ if (options.templateName) {
2088
+ Validation.validateTemplateName(options.templateName);
2089
+ }
2090
+ }
2091
+ /**
2092
+ * 复制模板文件
2093
+ */
2094
+ async copyTemplateFiles(templateInfo, targetPath, options) {
2095
+ try {
2096
+ FileUtils.copyDirectory(templateInfo.path, targetPath, {
2097
+ exclude: ["template.json", ".git", "node_modules"],
2098
+ overwrite: false,
2099
+ recursive: true
2100
+ });
2101
+ } catch (error) {
2102
+ throw new FileError(
2103
+ `\u590D\u5236\u6A21\u677F\u6587\u4EF6\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`,
2104
+ templateInfo.path
2105
+ );
2106
+ }
2107
+ }
2108
+ /**
2109
+ * 处理模板变量替换
2110
+ */
2111
+ async processTemplateVariables(targetPath, options) {
2112
+ try {
2113
+ const variables = {
2114
+ PROJECT_NAME: options.projectName,
2115
+ PROJECT_NAME_LOWER: options.projectName.toLowerCase(),
2116
+ PROJECT_NAME_UPPER: options.projectName.toUpperCase(),
2117
+ ...options.variables
2118
+ };
2119
+ const filesToProcess = [
2120
+ "package.json",
2121
+ "README.md",
2122
+ "src/**/*.ts",
2123
+ "src/**/*.js",
2124
+ "src/**/*.json"
2125
+ ];
2126
+ for (const pattern of filesToProcess) {
2127
+ const files = this.findFilesByPattern(targetPath, pattern);
2128
+ for (const filePath of files) {
2129
+ await this.replaceVariablesInFile(filePath, variables);
2130
+ }
2131
+ }
2132
+ } catch (error) {
2133
+ console.warn(
2134
+ `\u5904\u7406\u6A21\u677F\u53D8\u91CF\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
2135
+ );
2136
+ }
2137
+ }
2138
+ /**
2139
+ * 根据模式查找文件
2140
+ */
2141
+ findFilesByPattern(basePath, pattern) {
2142
+ try {
2143
+ if (!pattern.includes("*")) {
2144
+ const filePath = path4.join(basePath, pattern);
2145
+ return FileUtils.exists(filePath) ? [filePath] : [];
2146
+ }
2147
+ const files = FileUtils.listDirectory(basePath, { recursive: true });
2148
+ const regex = new RegExp(
2149
+ pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*")
2150
+ );
2151
+ return files.filter((file) => {
2152
+ const relativePath = path4.relative(basePath, file).split(path4.sep).join("/");
2153
+ return regex.test(relativePath);
2154
+ });
2155
+ } catch {
2156
+ return [];
2157
+ }
2158
+ }
2159
+ /**
2160
+ * 替换文件中的变量
2161
+ */
2162
+ async replaceVariablesInFile(filePath, variables) {
2163
+ try {
2164
+ let content = FileUtils.readFile(filePath);
2165
+ let hasChanges = false;
2166
+ for (const [key, value] of Object.entries(variables)) {
2167
+ const regex = new RegExp(`{{\\s*${key}\\s*}}`, "g");
2168
+ if (regex.test(content)) {
2169
+ content = content.replace(regex, value);
2170
+ hasChanges = true;
2171
+ }
2172
+ }
2173
+ if (hasChanges) {
2174
+ FileUtils.writeFile(filePath, content, { overwrite: true });
2175
+ }
2176
+ } catch (error) {
2177
+ console.warn(
2178
+ `\u66FF\u6362\u6587\u4EF6\u53D8\u91CF\u5931\u8D25 ${filePath}: ${error instanceof Error ? error.message : String(error)}`
2179
+ );
2180
+ }
2181
+ }
2182
+ };
2183
+ }
2184
+ });
2185
+
2186
+ // src/interfaces/Command.ts
2187
+ var BaseCommandHandler;
2188
+ var init_Command = __esm({
2189
+ "src/interfaces/Command.ts"() {
2190
+ BaseCommandHandler = class {
2191
+ constructor(container) {
2192
+ this.container = container;
2193
+ }
2194
+ static {
2195
+ __name(this, "BaseCommandHandler");
2196
+ }
2197
+ options;
2198
+ subcommands;
2199
+ /**
2200
+ * 获取服务实例
2201
+ */
2202
+ getService(serviceName) {
2203
+ return this.container.get(serviceName);
2204
+ }
2205
+ /**
2206
+ * 处理错误
2207
+ */
2208
+ handleError(error) {
2209
+ const errorHandler = this.getService("errorHandler");
2210
+ const handler = errorHandler;
2211
+ handler.handle(error);
2212
+ }
2213
+ /**
2214
+ * 验证参数
2215
+ */
2216
+ validateArgs(args, expectedCount) {
2217
+ if (args.length < expectedCount) {
2218
+ throw new Error(
2219
+ `\u547D\u4EE4\u9700\u8981\u81F3\u5C11 ${expectedCount} \u4E2A\u53C2\u6570\uFF0C\u4F46\u53EA\u63D0\u4F9B\u4E86 ${args.length} \u4E2A`
2220
+ );
2221
+ }
2222
+ }
2223
+ };
2224
+ }
2225
+ });
2226
+
2227
+ // src/commands/ServiceCommandHandler.ts
2228
+ var ServiceCommandHandler_exports = {};
2229
+ __export(ServiceCommandHandler_exports, {
2230
+ ServiceCommandHandler: () => ServiceCommandHandler
2231
+ });
2232
+ import { setGlobalLogLevel } from "../backend/Logger.js";
2233
+ var ServiceCommandHandler;
2234
+ var init_ServiceCommandHandler = __esm({
2235
+ "src/commands/ServiceCommandHandler.ts"() {
2236
+ init_Command();
2237
+ ServiceCommandHandler = class extends BaseCommandHandler {
2238
+ static {
2239
+ __name(this, "ServiceCommandHandler");
2240
+ }
2241
+ name = "service";
2242
+ description = "\u670D\u52A1\u7BA1\u7406\u547D\u4EE4";
2243
+ subcommands = [
2244
+ {
2245
+ name: "start",
2246
+ description: "\u542F\u52A8\u670D\u52A1",
2247
+ options: [
2248
+ { flags: "-d, --daemon", description: "\u5728\u540E\u53F0\u8FD0\u884C\u670D\u52A1" },
2249
+ { flags: "--debug", description: "\u542F\u7528\u8C03\u8BD5\u6A21\u5F0F (\u8F93\u51FADEBUG\u7EA7\u522B\u65E5\u5FD7)" },
2250
+ {
2251
+ flags: "--stdio",
2252
+ description: "\u4EE5 stdio \u6A21\u5F0F\u8FD0\u884C MCP Server (\u7528\u4E8E Cursor \u7B49\u5BA2\u6237\u7AEF)"
2253
+ }
2254
+ ],
2255
+ execute: /* @__PURE__ */ __name(async (args, options) => {
2256
+ await this.handleStart(options);
2257
+ }, "execute")
2258
+ },
2259
+ {
2260
+ name: "stop",
2261
+ description: "\u505C\u6B62\u670D\u52A1",
2262
+ execute: /* @__PURE__ */ __name(async (args, options) => {
2263
+ await this.handleStop();
2264
+ }, "execute")
2265
+ },
2266
+ {
2267
+ name: "status",
2268
+ description: "\u68C0\u67E5\u670D\u52A1\u72B6\u6001",
2269
+ execute: /* @__PURE__ */ __name(async (args, options) => {
2270
+ await this.handleStatus();
2271
+ }, "execute")
2272
+ },
2273
+ {
2274
+ name: "restart",
2275
+ description: "\u91CD\u542F\u670D\u52A1",
2276
+ options: [{ flags: "-d, --daemon", description: "\u5728\u540E\u53F0\u8FD0\u884C\u670D\u52A1" }],
2277
+ execute: /* @__PURE__ */ __name(async (args, options) => {
2278
+ await this.handleRestart(options);
2279
+ }, "execute")
2280
+ },
2281
+ {
2282
+ name: "attach",
2283
+ description: "\u8FDE\u63A5\u5230\u540E\u53F0\u670D\u52A1\u67E5\u770B\u65E5\u5FD7",
2284
+ execute: /* @__PURE__ */ __name(async (args, options) => {
2285
+ await this.handleAttach();
2286
+ }, "execute")
2287
+ }
2288
+ ];
2289
+ constructor(container) {
2290
+ super(container);
2291
+ }
2292
+ /**
2293
+ * 主命令执行(显示帮助)
2294
+ */
2295
+ async execute(args, options) {
2296
+ console.log("\u670D\u52A1\u7BA1\u7406\u547D\u4EE4\u3002\u4F7F\u7528 --help \u67E5\u770B\u53EF\u7528\u7684\u5B50\u547D\u4EE4\u3002");
2297
+ }
2298
+ /**
2299
+ * 处理启动命令
2300
+ */
2301
+ async handleStart(options) {
2302
+ try {
2303
+ if (options.debug) {
2304
+ setGlobalLogLevel("debug");
2305
+ }
2306
+ const serviceManager = this.getService("serviceManager");
2307
+ if (options.stdio) {
2308
+ this.showStdioMigrationGuide();
2309
+ return;
2310
+ }
2311
+ await serviceManager.start({
2312
+ daemon: options.daemon || false
2313
+ });
2314
+ } catch (error) {
2315
+ this.handleError(error);
2316
+ }
2317
+ }
2318
+ /**
2319
+ * 处理停止命令
2320
+ */
2321
+ async handleStop() {
2322
+ try {
2323
+ const serviceManager = this.getService("serviceManager");
2324
+ await serviceManager.stop();
2325
+ } catch (error) {
2326
+ this.handleError(error);
2327
+ }
2328
+ }
2329
+ /**
2330
+ * 处理状态检查命令
2331
+ */
2332
+ async handleStatus() {
2333
+ try {
2334
+ const serviceManager = this.getService("serviceManager");
2335
+ const status = await serviceManager.getStatus();
2336
+ if (status.running) {
2337
+ console.log(`\u2705 \u670D\u52A1\u6B63\u5728\u8FD0\u884C (PID: ${status.pid})`);
2338
+ if (status.uptime) {
2339
+ console.log(`\u23F1\uFE0F \u8FD0\u884C\u65F6\u95F4: ${status.uptime}`);
2340
+ }
2341
+ if (status.mode) {
2342
+ console.log(`\u{1F527} \u8FD0\u884C\u6A21\u5F0F: ${status.mode}`);
2343
+ }
2344
+ } else {
2345
+ console.log("\u274C \u670D\u52A1\u672A\u8FD0\u884C");
2346
+ }
2347
+ } catch (error) {
2348
+ this.handleError(error);
2349
+ }
2350
+ }
2351
+ /**
2352
+ * 处理重启命令
2353
+ */
2354
+ async handleRestart(options) {
2355
+ try {
2356
+ const serviceManager = this.getService("serviceManager");
2357
+ await serviceManager.restart({
2358
+ daemon: options.daemon || false
2359
+ });
2360
+ } catch (error) {
2361
+ this.handleError(error);
2362
+ }
2363
+ }
2364
+ /**
2365
+ * 处理附加命令
2366
+ */
2367
+ async handleAttach() {
2368
+ try {
2369
+ const daemonManager = this.getService("daemonManager");
2370
+ await daemonManager.attachToLogs();
2371
+ } catch (error) {
2372
+ this.handleError(error);
2373
+ }
2374
+ }
2375
+ /**
2376
+ * 显示 stdio 模式迁移指南
2377
+ */
2378
+ showStdioMigrationGuide() {
2379
+ console.log("\n\u274C stdio \u6A21\u5F0F\u5DF2\u5E9F\u5F03\n");
2380
+ console.log("\u5C0F\u667A\u5BA2\u6237\u7AEF\u5DF2\u8FC1\u79FB\u5230\u7EAF HTTP \u67B6\u6784\uFF0C\u8BF7\u4F7F\u7528\u4EE5\u4E0B\u65B9\u5F0F\uFF1A\n");
2381
+ console.log("1. \u542F\u52A8 Web \u670D\u52A1\uFF1A");
2382
+ console.log(" xiaozhi start\n");
2383
+ console.log("2. \u5728 Cursor \u4E2D\u914D\u7F6E HTTP \u7AEF\u70B9\uFF1A");
2384
+ console.log(' "mcpServers": {');
2385
+ console.log(' "xiaozhi-client": {');
2386
+ console.log(' "type": "streamableHttp",');
2387
+ console.log(' "url": "http://localhost:9999/mcp"');
2388
+ console.log(" }");
2389
+ console.log(" }\n");
2390
+ }
2391
+ };
2392
+ }
2393
+ });
2394
+
2395
+ // src/commands/ConfigCommandHandler.ts
2396
+ var ConfigCommandHandler_exports = {};
2397
+ __export(ConfigCommandHandler_exports, {
2398
+ ConfigCommandHandler: () => ConfigCommandHandler
2399
+ });
2400
+ import path5 from "path";
2401
+ import chalk2 from "chalk";
2402
+ import ora from "ora";
2403
+ var ConfigCommandHandler;
2404
+ var init_ConfigCommandHandler = __esm({
2405
+ "src/commands/ConfigCommandHandler.ts"() {
2406
+ init_Command();
2407
+ ConfigCommandHandler = class extends BaseCommandHandler {
2408
+ static {
2409
+ __name(this, "ConfigCommandHandler");
2410
+ }
2411
+ name = "config";
2412
+ description = "\u914D\u7F6E\u7BA1\u7406\u547D\u4EE4";
2413
+ subcommands = [
2414
+ {
2415
+ name: "init",
2416
+ description: "\u521D\u59CB\u5316\u914D\u7F6E\u6587\u4EF6",
2417
+ options: [
2418
+ {
2419
+ flags: "-f, --format <format>",
2420
+ description: "\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F (json, json5, jsonc)",
2421
+ defaultValue: "json"
2422
+ }
2423
+ ],
2424
+ execute: /* @__PURE__ */ __name(async (args, options) => {
2425
+ await this.handleInit(options);
2426
+ }, "execute")
2427
+ },
2428
+ {
2429
+ name: "get",
2430
+ description: "\u67E5\u770B\u914D\u7F6E\u503C",
2431
+ execute: /* @__PURE__ */ __name(async (args, options) => {
2432
+ this.validateArgs(args, 1);
2433
+ await this.handleGet(args[0]);
2434
+ }, "execute")
2435
+ },
2436
+ {
2437
+ name: "set",
2438
+ description: "\u8BBE\u7F6E\u914D\u7F6E\u503C",
2439
+ execute: /* @__PURE__ */ __name(async (args, options) => {
2440
+ this.validateArgs(args, 2);
2441
+ await this.handleSet(args[0], args[1]);
2442
+ }, "execute")
2443
+ }
2444
+ ];
2445
+ constructor(container) {
2446
+ super(container);
2447
+ }
2448
+ /**
2449
+ * 主命令执行(显示帮助)
2450
+ */
2451
+ async execute(args, options) {
2452
+ console.log("\u914D\u7F6E\u7BA1\u7406\u547D\u4EE4\u3002\u4F7F\u7528 --help \u67E5\u770B\u53EF\u7528\u7684\u5B50\u547D\u4EE4\u3002");
2453
+ }
2454
+ /**
2455
+ * 处理初始化命令
2456
+ */
2457
+ async handleInit(options) {
2458
+ const spinner = ora("\u521D\u59CB\u5316\u914D\u7F6E...").start();
2459
+ try {
2460
+ const format = options.format;
2461
+ if (format !== "json" && format !== "json5" && format !== "jsonc") {
2462
+ throw new Error("\u683C\u5F0F\u5FC5\u987B\u662F json, json5 \u6216 jsonc");
2463
+ }
2464
+ const configManager3 = this.getService("configManager");
2465
+ if (configManager3.configExists()) {
2466
+ spinner.warn("\u914D\u7F6E\u6587\u4EF6\u5DF2\u5B58\u5728");
2467
+ console.log(chalk2.yellow("\u5982\u9700\u91CD\u65B0\u521D\u59CB\u5316\uFF0C\u8BF7\u5148\u5220\u9664\u73B0\u6709\u7684\u914D\u7F6E\u6587\u4EF6"));
2468
+ return;
2469
+ }
2470
+ configManager3.initConfig(format);
2471
+ spinner.succeed("\u914D\u7F6E\u6587\u4EF6\u521D\u59CB\u5316\u6210\u529F");
2472
+ const configDir = process.env.XIAOZHI_CONFIG_DIR || process.cwd();
2473
+ const configFileName = `xiaozhi.config.${format}`;
2474
+ const configPath = path5.join(configDir, configFileName);
2475
+ console.log(chalk2.green(`\u2705 \u914D\u7F6E\u6587\u4EF6\u5DF2\u521B\u5EFA: ${configFileName}`));
2476
+ console.log(chalk2.yellow("\u{1F4DD} \u8BF7\u7F16\u8F91\u914D\u7F6E\u6587\u4EF6\u8BBE\u7F6E\u4F60\u7684 MCP \u7AEF\u70B9:"));
2477
+ console.log(chalk2.gray(` \u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84: ${configPath}`));
2478
+ console.log(chalk2.yellow("\u{1F4A1} \u6216\u8005\u4F7F\u7528\u547D\u4EE4\u8BBE\u7F6E:"));
2479
+ console.log(
2480
+ chalk2.gray(" xiaozhi config set mcpEndpoint <your-endpoint-url>")
2481
+ );
2482
+ } catch (error) {
2483
+ spinner.fail(
2484
+ `\u521D\u59CB\u5316\u914D\u7F6E\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
2485
+ );
2486
+ this.handleError(error);
2487
+ }
2488
+ }
2489
+ /**
2490
+ * 处理获取配置命令
2491
+ */
2492
+ async handleGet(key) {
2493
+ const spinner = ora("\u8BFB\u53D6\u914D\u7F6E...").start();
2494
+ try {
2495
+ const configManager3 = this.getService("configManager");
2496
+ if (!configManager3.configExists()) {
2497
+ spinner.fail("\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728");
2498
+ console.log(
2499
+ chalk2.yellow('\u{1F4A1} \u63D0\u793A: \u8BF7\u5148\u8FD0\u884C "xiaozhi config init" \u521D\u59CB\u5316\u914D\u7F6E')
2500
+ );
2501
+ return;
2502
+ }
2503
+ const config = configManager3.getConfig();
2504
+ switch (key) {
2505
+ case "mcpEndpoint": {
2506
+ spinner.succeed("\u914D\u7F6E\u4FE1\u606F");
2507
+ const endpoints = configManager3.getMcpEndpoints();
2508
+ if (endpoints.length === 0) {
2509
+ console.log(chalk2.yellow("\u672A\u914D\u7F6E\u4EFB\u4F55 MCP \u7AEF\u70B9"));
2510
+ } else if (endpoints.length === 1) {
2511
+ console.log(chalk2.green(`MCP \u7AEF\u70B9: ${endpoints[0]}`));
2512
+ } else {
2513
+ console.log(chalk2.green(`MCP \u7AEF\u70B9 (${endpoints.length} \u4E2A):`));
2514
+ endpoints.forEach((ep, index) => {
2515
+ console.log(chalk2.gray(` ${index + 1}. ${ep}`));
2516
+ });
2517
+ }
2518
+ break;
2519
+ }
2520
+ case "mcpServers":
2521
+ spinner.succeed("\u914D\u7F6E\u4FE1\u606F");
2522
+ console.log(chalk2.green("MCP \u670D\u52A1:"));
2523
+ for (const [name, serverConfig] of Object.entries(
2524
+ config.mcpServers
2525
+ )) {
2526
+ const server = serverConfig;
2527
+ if ("type" in server && server.type === "sse") {
2528
+ console.log(chalk2.gray(` ${name}: [SSE] ${server.url}`));
2529
+ } else {
2530
+ console.log(
2531
+ chalk2.gray(
2532
+ ` ${name}: ${server.command} ${server.args.join(" ")}`
2533
+ )
2534
+ );
2535
+ }
2536
+ }
2537
+ break;
2538
+ case "connection": {
2539
+ spinner.succeed("\u914D\u7F6E\u4FE1\u606F");
2540
+ const connectionConfig = configManager3.getConnectionConfig();
2541
+ console.log(chalk2.green("\u8FDE\u63A5\u914D\u7F6E:"));
2542
+ console.log(
2543
+ chalk2.gray(
2544
+ ` \u5FC3\u8DF3\u68C0\u6D4B\u95F4\u9694: ${connectionConfig.heartbeatInterval}ms`
2545
+ )
2546
+ );
2547
+ console.log(
2548
+ chalk2.gray(` \u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4: ${connectionConfig.heartbeatTimeout}ms`)
2549
+ );
2550
+ console.log(
2551
+ chalk2.gray(` \u91CD\u8FDE\u95F4\u9694: ${connectionConfig.reconnectInterval}ms`)
2552
+ );
2553
+ break;
2554
+ }
2555
+ case "heartbeatInterval":
2556
+ spinner.succeed("\u914D\u7F6E\u4FE1\u606F");
2557
+ console.log(
2558
+ chalk2.green(
2559
+ `\u5FC3\u8DF3\u68C0\u6D4B\u95F4\u9694: ${configManager3.getHeartbeatInterval()}ms`
2560
+ )
2561
+ );
2562
+ break;
2563
+ case "heartbeatTimeout":
2564
+ spinner.succeed("\u914D\u7F6E\u4FE1\u606F");
2565
+ console.log(
2566
+ chalk2.green(
2567
+ `\u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4: ${configManager3.getHeartbeatTimeout()}ms`
2568
+ )
2569
+ );
2570
+ break;
2571
+ case "reconnectInterval":
2572
+ spinner.succeed("\u914D\u7F6E\u4FE1\u606F");
2573
+ console.log(
2574
+ chalk2.green(`\u91CD\u8FDE\u95F4\u9694: ${configManager3.getReconnectInterval()}ms`)
2575
+ );
2576
+ break;
2577
+ default:
2578
+ spinner.fail(`\u672A\u77E5\u7684\u914D\u7F6E\u9879: ${key}`);
2579
+ console.log(
2580
+ chalk2.yellow(
2581
+ "\u652F\u6301\u7684\u914D\u7F6E\u9879: mcpEndpoint, mcpServers, connection, heartbeatInterval, heartbeatTimeout, reconnectInterval"
2582
+ )
2583
+ );
2584
+ }
2585
+ } catch (error) {
2586
+ spinner.fail(
2587
+ `\u8BFB\u53D6\u914D\u7F6E\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
2588
+ );
2589
+ this.handleError(error);
2590
+ }
2591
+ }
2592
+ /**
2593
+ * 处理设置配置命令
2594
+ */
2595
+ async handleSet(key, value) {
2596
+ const spinner = ora("\u66F4\u65B0\u914D\u7F6E...").start();
2597
+ try {
2598
+ const configManager3 = this.getService("configManager");
2599
+ if (!configManager3.configExists()) {
2600
+ spinner.fail("\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728");
2601
+ console.log(
2602
+ chalk2.yellow('\u{1F4A1} \u63D0\u793A: \u8BF7\u5148\u8FD0\u884C "xiaozhi config init" \u521D\u59CB\u5316\u914D\u7F6E')
2603
+ );
2604
+ return;
2605
+ }
2606
+ switch (key) {
2607
+ case "mcpEndpoint":
2608
+ configManager3.updateMcpEndpoint(value);
2609
+ spinner.succeed(`MCP \u7AEF\u70B9\u5DF2\u8BBE\u7F6E\u4E3A: ${value}`);
2610
+ break;
2611
+ case "heartbeatInterval": {
2612
+ const interval = Number.parseInt(value);
2613
+ if (Number.isNaN(interval) || interval <= 0) {
2614
+ throw new Error("\u5FC3\u8DF3\u68C0\u6D4B\u95F4\u9694\u5FC5\u987B\u662F\u6B63\u6574\u6570");
2615
+ }
2616
+ configManager3.updateHeartbeatInterval(interval);
2617
+ spinner.succeed(`\u5FC3\u8DF3\u68C0\u6D4B\u95F4\u9694\u5DF2\u8BBE\u7F6E\u4E3A: ${interval}ms`);
2618
+ break;
2619
+ }
2620
+ case "heartbeatTimeout": {
2621
+ const timeout = Number.parseInt(value);
2622
+ if (Number.isNaN(timeout) || timeout <= 0) {
2623
+ throw new Error("\u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4\u5FC5\u987B\u662F\u6B63\u6574\u6570");
2624
+ }
2625
+ configManager3.updateHeartbeatTimeout(timeout);
2626
+ spinner.succeed(`\u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4\u5DF2\u8BBE\u7F6E\u4E3A: ${timeout}ms`);
2627
+ break;
2628
+ }
2629
+ case "reconnectInterval": {
2630
+ const interval = Number.parseInt(value);
2631
+ if (Number.isNaN(interval) || interval <= 0) {
2632
+ throw new Error("\u91CD\u8FDE\u95F4\u9694\u5FC5\u987B\u662F\u6B63\u6574\u6570");
2633
+ }
2634
+ configManager3.updateReconnectInterval(interval);
2635
+ spinner.succeed(`\u91CD\u8FDE\u95F4\u9694\u5DF2\u8BBE\u7F6E\u4E3A: ${interval}ms`);
2636
+ break;
2637
+ }
2638
+ default:
2639
+ spinner.fail(`\u4E0D\u652F\u6301\u8BBE\u7F6E\u7684\u914D\u7F6E\u9879: ${key}`);
2640
+ console.log(
2641
+ chalk2.yellow(
2642
+ "\u652F\u6301\u8BBE\u7F6E\u7684\u914D\u7F6E\u9879: mcpEndpoint, heartbeatInterval, heartbeatTimeout, reconnectInterval"
2643
+ )
2644
+ );
2645
+ }
2646
+ } catch (error) {
2647
+ spinner.fail(
2648
+ `\u8BBE\u7F6E\u914D\u7F6E\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
2649
+ );
2650
+ this.handleError(error);
2651
+ }
2652
+ }
2653
+ };
2654
+ }
2655
+ });
2656
+
2657
+ // src/commands/ProjectCommandHandler.ts
2658
+ var ProjectCommandHandler_exports = {};
2659
+ __export(ProjectCommandHandler_exports, {
2660
+ ProjectCommandHandler: () => ProjectCommandHandler
2661
+ });
2662
+ import path6 from "path";
2663
+ import chalk3 from "chalk";
2664
+ import ora2 from "ora";
2665
+ var ProjectCommandHandler;
2666
+ var init_ProjectCommandHandler = __esm({
2667
+ "src/commands/ProjectCommandHandler.ts"() {
2668
+ init_Command();
2669
+ ProjectCommandHandler = class extends BaseCommandHandler {
2670
+ static {
2671
+ __name(this, "ProjectCommandHandler");
2672
+ }
2673
+ name = "create";
2674
+ description = "\u521B\u5EFA\u9879\u76EE";
2675
+ options = [
2676
+ {
2677
+ flags: "-t, --template <templateName>",
2678
+ description: "\u4F7F\u7528\u6307\u5B9A\u6A21\u677F\u521B\u5EFA\u9879\u76EE"
2679
+ }
2680
+ ];
2681
+ constructor(container) {
2682
+ super(container);
2683
+ }
2684
+ /**
2685
+ * 执行创建项目命令
2686
+ */
2687
+ async execute(args, options) {
2688
+ this.validateArgs(args, 1);
2689
+ const projectName = args[0];
2690
+ await this.handleCreate(projectName, options);
2691
+ }
2692
+ /**
2693
+ * 处理创建项目命令
2694
+ */
2695
+ async handleCreate(projectName, options) {
2696
+ const spinner = ora2("\u521D\u59CB\u5316\u9879\u76EE...").start();
2697
+ try {
2698
+ const templateManager = this.getService("templateManager");
2699
+ const fileUtils = this.getService("fileUtils");
2700
+ const targetPath = path6.join(process.cwd(), projectName);
2701
+ if (await fileUtils.exists(targetPath)) {
2702
+ spinner.fail(`\u76EE\u5F55 "${projectName}" \u5DF2\u5B58\u5728`);
2703
+ console.log(
2704
+ chalk3.yellow("\u{1F4A1} \u63D0\u793A: \u8BF7\u9009\u62E9\u4E0D\u540C\u7684\u9879\u76EE\u540D\u79F0\u6216\u5220\u9664\u73B0\u6709\u76EE\u5F55")
2705
+ );
2706
+ return;
2707
+ }
2708
+ if (options.template) {
2709
+ await this.createFromTemplate(
2710
+ projectName,
2711
+ options.template,
2712
+ targetPath,
2713
+ spinner,
2714
+ templateManager
2715
+ );
2716
+ } else {
2717
+ await this.createBasicProject(
2718
+ projectName,
2719
+ targetPath,
2720
+ spinner,
2721
+ templateManager
2722
+ );
2723
+ }
2724
+ } catch (error) {
2725
+ spinner.fail(
2726
+ `\u521B\u5EFA\u9879\u76EE\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
2727
+ );
2728
+ this.handleError(error);
2729
+ }
2730
+ }
2731
+ /**
2732
+ * 从模板创建项目
2733
+ */
2734
+ async createFromTemplate(projectName, templateName, targetPath, spinner, templateManager) {
2735
+ spinner.text = "\u68C0\u67E5\u6A21\u677F...";
2736
+ const availableTemplates = await templateManager.getAvailableTemplates();
2737
+ if (availableTemplates.length === 0) {
2738
+ spinner.fail("\u627E\u4E0D\u5230\u53EF\u7528\u6A21\u677F");
2739
+ console.log(chalk3.yellow("\u{1F4A1} \u63D0\u793A: \u8BF7\u786E\u4FDD xiaozhi-client \u6B63\u786E\u5B89\u88C5"));
2740
+ return;
2741
+ }
2742
+ let actualTemplateName = templateName;
2743
+ const isValidTemplate = await templateManager.validateTemplate(actualTemplateName);
2744
+ if (!isValidTemplate) {
2745
+ spinner.fail(`\u6A21\u677F "${actualTemplateName}" \u4E0D\u5B58\u5728`);
2746
+ const similarTemplate = this.findSimilarTemplate(
2747
+ actualTemplateName,
2748
+ availableTemplates
2749
+ );
2750
+ if (similarTemplate) {
2751
+ console.log(
2752
+ chalk3.yellow(`\u{1F4A1} \u4F60\u662F\u60F3\u4F7F\u7528\u6A21\u677F "${similarTemplate}" \u5417\uFF1F`)
2753
+ );
2754
+ const confirmed = await this.askUserConfirmation(
2755
+ chalk3.cyan("\u786E\u8BA4\u4F7F\u7528\u6B64\u6A21\u677F\uFF1F(y/n): ")
2756
+ );
2757
+ if (confirmed) {
2758
+ actualTemplateName = similarTemplate;
2759
+ } else {
2760
+ this.showAvailableTemplates(availableTemplates);
2761
+ return;
2762
+ }
2763
+ } else {
2764
+ this.showAvailableTemplates(availableTemplates);
2765
+ return;
2766
+ }
2767
+ }
2768
+ spinner.text = `\u4ECE\u6A21\u677F "${actualTemplateName}" \u521B\u5EFA\u9879\u76EE "${projectName}"...`;
2769
+ await templateManager.createProject({
2770
+ templateName: actualTemplateName,
2771
+ targetPath,
2772
+ projectName
2773
+ });
2774
+ spinner.succeed(`\u9879\u76EE "${projectName}" \u521B\u5EFA\u6210\u529F`);
2775
+ console.log(chalk3.green("\u2705 \u9879\u76EE\u521B\u5EFA\u5B8C\u6210!"));
2776
+ console.log(chalk3.yellow("\u{1F4DD} \u63A5\u4E0B\u6765\u7684\u6B65\u9AA4:"));
2777
+ console.log(chalk3.gray(` cd ${projectName}`));
2778
+ console.log(chalk3.gray(" pnpm install # \u5B89\u88C5\u4F9D\u8D56"));
2779
+ console.log(chalk3.gray(" # \u7F16\u8F91 xiaozhi.config.json \u8BBE\u7F6E\u4F60\u7684 MCP \u7AEF\u70B9"));
2780
+ console.log(chalk3.gray(" xiaozhi start # \u542F\u52A8\u670D\u52A1"));
2781
+ }
2782
+ /**
2783
+ * 创建基本项目
2784
+ */
2785
+ async createBasicProject(projectName, targetPath, spinner, templateManager) {
2786
+ spinner.text = `\u521B\u5EFA\u57FA\u672C\u9879\u76EE "${projectName}"...`;
2787
+ await templateManager.createProject({
2788
+ templateName: null,
2789
+ // 表示创建基本项目
2790
+ targetPath,
2791
+ projectName
2792
+ });
2793
+ spinner.succeed(`\u9879\u76EE "${projectName}" \u521B\u5EFA\u6210\u529F`);
2794
+ console.log(chalk3.green("\u2705 \u57FA\u672C\u9879\u76EE\u521B\u5EFA\u5B8C\u6210!"));
2795
+ console.log(chalk3.yellow("\u{1F4DD} \u63A5\u4E0B\u6765\u7684\u6B65\u9AA4:"));
2796
+ console.log(chalk3.gray(` cd ${projectName}`));
2797
+ console.log(
2798
+ chalk3.gray(" # \u7F16\u8F91 xiaozhi.config.json \u8BBE\u7F6E\u4F60\u7684 MCP \u7AEF\u70B9\u548C\u670D\u52A1")
2799
+ );
2800
+ console.log(chalk3.gray(" xiaozhi start # \u542F\u52A8\u670D\u52A1"));
2801
+ console.log(
2802
+ chalk3.yellow("\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 --template \u9009\u9879\u53EF\u4EE5\u4ECE\u6A21\u677F\u521B\u5EFA\u9879\u76EE")
2803
+ );
2804
+ }
2805
+ /**
2806
+ * 显示可用模板
2807
+ */
2808
+ showAvailableTemplates(templates) {
2809
+ console.log(chalk3.yellow("\u53EF\u7528\u7684\u6A21\u677F:"));
2810
+ for (const template of templates) {
2811
+ console.log(chalk3.gray(` - ${template}`));
2812
+ }
2813
+ }
2814
+ /**
2815
+ * 查找相似模板
2816
+ */
2817
+ findSimilarTemplate(input, templates) {
2818
+ const formatUtils = this.getService("formatUtils");
2819
+ let bestMatch = null;
2820
+ let bestSimilarity = 0;
2821
+ for (const template of templates) {
2822
+ const similarity = formatUtils.calculateSimilarity(
2823
+ input.toLowerCase(),
2824
+ template.toLowerCase()
2825
+ );
2826
+ if (similarity > bestSimilarity && similarity > 0.6) {
2827
+ bestSimilarity = similarity;
2828
+ bestMatch = template;
2829
+ }
2830
+ }
2831
+ return bestMatch;
2832
+ }
2833
+ /**
2834
+ * 询问用户确认
2835
+ */
2836
+ async askUserConfirmation(prompt) {
2837
+ const readline = await import("readline");
2838
+ const rl = readline.createInterface({
2839
+ input: process.stdin,
2840
+ output: process.stdout
2841
+ });
2842
+ return new Promise((resolve) => {
2843
+ rl.question(prompt, (answer) => {
2844
+ rl.close();
2845
+ resolve(
2846
+ answer.toLowerCase().trim() === "y" || answer.toLowerCase().trim() === "yes"
2847
+ );
2848
+ });
2849
+ });
2850
+ }
2851
+ };
2852
+ }
2853
+ });
2854
+
2855
+ // src/interfaces/CommandTypes.ts
2856
+ function isLocalMCPServerConfig(obj) {
2857
+ const config = obj;
2858
+ return typeof config === "object" && config !== null && "command" in config && "args" in config && typeof config.command === "string" && Array.isArray(config.args) && config.args.every((arg) => typeof arg === "string");
2859
+ }
2860
+ var init_CommandTypes = __esm({
2861
+ "src/interfaces/CommandTypes.ts"() {
2862
+ __name(isLocalMCPServerConfig, "isLocalMCPServerConfig");
2863
+ }
2864
+ });
2865
+
2866
+ // src/commands/McpCommandHandler.ts
2867
+ var McpCommandHandler_exports = {};
2868
+ __export(McpCommandHandler_exports, {
2869
+ McpCommandHandler: () => McpCommandHandler
2870
+ });
2871
+ import { configManager as configManager2 } from "../backend/lib/config/manager.js";
2872
+ import { logger as logger2 } from "../backend/Logger.js";
2873
+ import chalk4 from "chalk";
2874
+ import Table from "cli-table3";
2875
+ import ora3 from "ora";
2876
+ var McpCommandHandler;
2877
+ var init_McpCommandHandler = __esm({
2878
+ "src/commands/McpCommandHandler.ts"() {
2879
+ init_Command();
2880
+ init_CommandTypes();
2881
+ init_ProcessManager();
2882
+ McpCommandHandler = class _McpCommandHandler extends BaseCommandHandler {
2883
+ static {
2884
+ __name(this, "McpCommandHandler");
2885
+ }
2886
+ logger;
2887
+ processManager;
2888
+ baseUrl;
2889
+ constructor(...args) {
2890
+ super(...args);
2891
+ this.logger = logger2;
2892
+ this.processManager = new ProcessManagerImpl();
2893
+ try {
2894
+ const webPort = configManager2.getWebUIPort() ?? 9999;
2895
+ this.baseUrl = `http://localhost:${webPort}`;
2896
+ } catch {
2897
+ this.baseUrl = "http://localhost:9999";
2898
+ }
2899
+ }
2900
+ /**
2901
+ * 中文字符正则表达式
2902
+ *
2903
+ * Unicode 范围说明:
2904
+ * - \u4e00-\u9fff: CJK 统一汉字(基本汉字)
2905
+ * - \u3400-\u4dbf: CJK 扩展 A(扩展汉字)
2906
+ * - \uff00-\uffef: 全角字符和半角片假名(包括中文标点符号)
2907
+ *
2908
+ * 注意:此范围可能不完全覆盖所有中日韩字符(如 CJK 扩展 B-F 等),
2909
+ * 但已覆盖绝大多数常用中文场景。
2910
+ */
2911
+ static CHINESE_CHAR_REGEX = /[\u4e00-\u9fff\u3400-\u4dbf\uff00-\uffef]/;
2912
+ /**
2913
+ * 计算字符串的显示宽度(中文字符占2个宽度,英文字符占1个宽度)
2914
+ */
2915
+ static getDisplayWidth(str) {
2916
+ let width = 0;
2917
+ for (const char of str) {
2918
+ if (_McpCommandHandler.CHINESE_CHAR_REGEX.test(char)) {
2919
+ width += 2;
2920
+ } else {
2921
+ width += 1;
2922
+ }
2923
+ }
2924
+ return width;
2925
+ }
2926
+ /**
2927
+ * 截断字符串到指定的显示宽度
2928
+ */
2929
+ static truncateToWidth(str, maxWidth) {
2930
+ if (_McpCommandHandler.getDisplayWidth(str) <= maxWidth) {
2931
+ return str;
2932
+ }
2933
+ if (maxWidth <= 3) {
2934
+ return "";
2935
+ }
2936
+ let result = "";
2937
+ let currentWidth = 0;
2938
+ let hasAddedChar = false;
2939
+ for (const char of str) {
2940
+ const charWidth = _McpCommandHandler.CHINESE_CHAR_REGEX.test(char) ? 2 : 1;
2941
+ if (currentWidth + charWidth > maxWidth - 3) {
2942
+ if (!hasAddedChar) {
2943
+ return "";
2944
+ }
2945
+ result += "...";
2946
+ break;
2947
+ }
2948
+ result += char;
2949
+ currentWidth += charWidth;
2950
+ hasAddedChar = true;
2951
+ }
2952
+ return result;
2953
+ }
2954
+ /**
2955
+ * 解析 JSON 参数
2956
+ * @param argsString JSON 字符串
2957
+ * @returns 解析后的参数对象
2958
+ */
2959
+ static parseJsonArgs(argsString) {
2960
+ try {
2961
+ return JSON.parse(argsString);
2962
+ } catch (error) {
2963
+ throw new Error(
2964
+ `\u53C2\u6570\u683C\u5F0F\u9519\u8BEF\uFF0C\u8BF7\u4F7F\u7528\u6709\u6548\u7684 JSON \u683C\u5F0F\u3002\u9519\u8BEF\u8BE6\u60C5: ${error instanceof Error ? error.message : String(error)}`
2965
+ );
2966
+ }
2967
+ }
2968
+ /**
2969
+ * 格式化工具调用结果输出
2970
+ * @param result 工具调用结果
2971
+ * @returns 格式化后的字符串
2972
+ */
2973
+ static formatToolCallResult(result) {
2974
+ return JSON.stringify(result);
2975
+ }
2976
+ name = "mcp";
2977
+ description = "MCP \u670D\u52A1\u548C\u5DE5\u5177\u7BA1\u7406";
2978
+ subcommands = [
2979
+ {
2980
+ name: "list",
2981
+ description: "\u5217\u51FA MCP \u670D\u52A1",
2982
+ options: [{ flags: "--tools", description: "\u663E\u793A\u6240\u6709\u670D\u52A1\u7684\u5DE5\u5177\u5217\u8868" }],
2983
+ execute: /* @__PURE__ */ __name(async (args, options) => {
2984
+ await this.handleList(options);
2985
+ }, "execute")
2986
+ },
2987
+ {
2988
+ name: "server",
2989
+ description: "\u7BA1\u7406\u6307\u5B9A\u7684 MCP \u670D\u52A1",
2990
+ execute: /* @__PURE__ */ __name(async (args, options) => {
2991
+ this.validateArgs(args, 1);
2992
+ await this.handleServer(args[0]);
2993
+ }, "execute")
2994
+ },
2995
+ {
2996
+ name: "tool",
2997
+ description: "\u542F\u7528\u6216\u7981\u7528\u6307\u5B9A\u670D\u52A1\u7684\u5DE5\u5177",
2998
+ execute: /* @__PURE__ */ __name(async (args, options) => {
2999
+ this.validateArgs(args, 3);
3000
+ const [serverName, toolName, action] = args;
3001
+ if (action !== "enable" && action !== "disable") {
3002
+ console.error(chalk4.red("\u9519\u8BEF: \u64CD\u4F5C\u5FC5\u987B\u662F 'enable' \u6216 'disable'"));
3003
+ process.exit(1);
3004
+ }
3005
+ const enabled = action === "enable";
3006
+ await this.handleTool(serverName, toolName, enabled);
3007
+ }, "execute")
3008
+ },
3009
+ {
3010
+ name: "call",
3011
+ description: "\u8C03\u7528\u6307\u5B9A\u670D\u52A1\u7684\u5DE5\u5177",
3012
+ options: [
3013
+ {
3014
+ flags: "--args <json>",
3015
+ description: "\u5DE5\u5177\u53C2\u6570 (JSON \u683C\u5F0F)",
3016
+ defaultValue: "{}"
3017
+ }
3018
+ ],
3019
+ execute: /* @__PURE__ */ __name(async (args, options) => {
3020
+ this.validateArgs(args, 2);
3021
+ const [serviceName, toolName] = args;
3022
+ await this.handleCall(
3023
+ serviceName,
3024
+ toolName,
3025
+ options.args ?? "{}"
3026
+ );
3027
+ }, "execute")
3028
+ }
3029
+ ];
3030
+ /**
3031
+ * 主命令执行(显示帮助)
3032
+ */
3033
+ async execute(args, options) {
3034
+ console.log("MCP \u670D\u52A1\u548C\u5DE5\u5177\u7BA1\u7406\u547D\u4EE4\u3002\u4F7F\u7528 --help \u67E5\u770B\u53EF\u7528\u7684\u5B50\u547D\u4EE4\u3002");
3035
+ }
3036
+ /**
3037
+ * 处理列出服务命令
3038
+ */
3039
+ async handleList(options) {
3040
+ try {
3041
+ await this.handleListInternal(options);
3042
+ } catch (error) {
3043
+ this.handleError(error);
3044
+ }
3045
+ }
3046
+ /**
3047
+ * 处理服务管理命令
3048
+ */
3049
+ async handleServer(serverName) {
3050
+ try {
3051
+ await this.handleServerInternal(serverName);
3052
+ } catch (error) {
3053
+ this.handleError(error);
3054
+ }
3055
+ }
3056
+ /**
3057
+ * 处理工具管理命令
3058
+ */
3059
+ async handleTool(serverName, toolName, enabled) {
3060
+ try {
3061
+ await this.handleToolInternal(serverName, toolName, enabled);
3062
+ } catch (error) {
3063
+ this.handleError(error);
3064
+ }
3065
+ }
3066
+ /**
3067
+ * 验证服务状态
3068
+ * @private
3069
+ */
3070
+ async validateServiceStatus() {
3071
+ const processStatus = this.processManager.getServiceStatus();
3072
+ if (!processStatus.running) {
3073
+ throw new Error(
3074
+ "xiaozhi \u670D\u52A1\u672A\u542F\u52A8\u3002\u8BF7\u5148\u8FD0\u884C 'xiaozhi start' \u6216 'xiaozhi start -d' \u542F\u52A8\u670D\u52A1\u3002"
3075
+ );
3076
+ }
3077
+ try {
3078
+ const response = await fetch(`${this.baseUrl}/api/status`, {
3079
+ method: "GET",
3080
+ signal: AbortSignal.timeout(5e3)
3081
+ // 5秒超时
3082
+ });
3083
+ if (!response.ok) {
3084
+ throw new Error(`Web \u670D\u52A1\u5668\u54CD\u5E94\u9519\u8BEF: ${response.status}`);
3085
+ }
3086
+ } catch (error) {
3087
+ if (error instanceof Error && error.name === "AbortError") {
3088
+ throw new Error("\u8FDE\u63A5 xiaozhi \u670D\u52A1\u8D85\u65F6\u3002\u8BF7\u68C0\u67E5\u670D\u52A1\u662F\u5426\u6B63\u5E38\u8FD0\u884C\u3002");
3089
+ }
3090
+ if (error instanceof Error) {
3091
+ const isNetworkError = error instanceof TypeError && /fetch|network|failed/i.test(error.message);
3092
+ if (isNetworkError) {
3093
+ throw new Error(
3094
+ `\u65E0\u6CD5\u8FDE\u63A5\u5230 xiaozhi \u670D\u52A1\uFF08\u7F51\u7EDC\u8BF7\u6C42\u5931\u8D25\uFF09\u3002\u8BF7\u68C0\u67E5\u7F51\u7EDC\u8FDE\u63A5\u6216\u670D\u52A1\u5730\u5740\u662F\u5426\u6B63\u786E\u3002\u539F\u59CB\u9519\u8BEF: ${error.message}`
3095
+ );
3096
+ }
3097
+ throw new Error(
3098
+ `\u65E0\u6CD5\u8FDE\u63A5\u5230 xiaozhi \u670D\u52A1\u3002\u8BF7\u68C0\u67E5\u670D\u52A1\u72B6\u6001\u3002\u539F\u59CB\u9519\u8BEF: ${error.message}`
3099
+ );
3100
+ }
3101
+ let detail;
3102
+ try {
3103
+ detail = JSON.stringify(error);
3104
+ } catch {
3105
+ detail = String(error);
3106
+ }
3107
+ throw new Error(
3108
+ `\u65E0\u6CD5\u8FDE\u63A5\u5230 xiaozhi \u670D\u52A1\u3002\u8BF7\u68C0\u67E5\u670D\u52A1\u72B6\u6001\u3002\u9519\u8BEF\u8BE6\u60C5: ${detail}`
3109
+ );
3110
+ }
3111
+ }
3112
+ /**
3113
+ * 调用 MCP 工具的内部实现
3114
+ * @param serviceName 服务名称
3115
+ * @param toolName 工具名称
3116
+ * @param args 工具参数
3117
+ * @returns 工具调用结果
3118
+ */
3119
+ async callToolInternal(serviceName, toolName, args) {
3120
+ await this.validateServiceStatus();
3121
+ try {
3122
+ const response = await fetch(`${this.baseUrl}/api/tools/call`, {
3123
+ method: "POST",
3124
+ headers: {
3125
+ "Content-Type": "application/json"
3126
+ },
3127
+ body: JSON.stringify({
3128
+ serviceName,
3129
+ toolName,
3130
+ args
3131
+ })
3132
+ });
3133
+ if (!response.ok) {
3134
+ let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
3135
+ try {
3136
+ const errorData = await response.json();
3137
+ const detailedMessage = errorData?.error?.message ?? errorData?.message;
3138
+ if (typeof detailedMessage === "string" && detailedMessage.trim()) {
3139
+ errorMessage = detailedMessage;
3140
+ }
3141
+ } catch {
3142
+ }
3143
+ throw new Error(errorMessage);
3144
+ }
3145
+ const responseData = await response.json();
3146
+ if (!responseData.success) {
3147
+ throw new Error(responseData.error?.message || "\u5DE5\u5177\u8C03\u7528\u5931\u8D25");
3148
+ }
3149
+ return responseData.data;
3150
+ } catch (error) {
3151
+ this.logger.error(
3152
+ `\u5DE5\u5177\u8C03\u7528\u5931\u8D25: ${serviceName}/${toolName}`,
3153
+ error instanceof Error ? error.message : String(error)
3154
+ );
3155
+ throw error;
3156
+ }
3157
+ }
3158
+ /**
3159
+ * 处理工具调用命令
3160
+ */
3161
+ async handleCall(serviceName, toolName, argsString) {
3162
+ try {
3163
+ const args = _McpCommandHandler.parseJsonArgs(argsString);
3164
+ const result = await this.callToolInternal(serviceName, toolName, args);
3165
+ console.log(_McpCommandHandler.formatToolCallResult(result));
3166
+ } catch (error) {
3167
+ console.log(`\u5DE5\u5177\u8C03\u7528\u5931\u8D25: ${serviceName}/${toolName}`);
3168
+ console.error(chalk4.red("\u9519\u8BEF:"), error.message);
3169
+ const errorMessage = error.message;
3170
+ if (errorMessage.includes("\u670D\u52A1\u672A\u542F\u52A8")) {
3171
+ console.log();
3172
+ console.log(chalk4.yellow("\u{1F4A1} \u8BF7\u5148\u542F\u52A8\u670D\u52A1:"));
3173
+ console.log(chalk4.gray(" xiaozhi start # \u524D\u53F0\u542F\u52A8"));
3174
+ console.log(chalk4.gray(" xiaozhi start -d # \u540E\u53F0\u542F\u52A8"));
3175
+ } else if (errorMessage.includes("\u53C2\u6570\u683C\u5F0F\u9519\u8BEF")) {
3176
+ console.log();
3177
+ console.log(chalk4.yellow("\u{1F4A1} \u6B63\u786E\u683C\u5F0F\u793A\u4F8B:"));
3178
+ console.log(
3179
+ chalk4.gray(
3180
+ ` xiaozhi mcp call ${serviceName} ${toolName} --args '{"param": "value"}'`
3181
+ )
3182
+ );
3183
+ }
3184
+ if (process.env.NODE_ENV === "test") {
3185
+ throw new Error("process.exit called");
3186
+ }
3187
+ process.exit(1);
3188
+ }
3189
+ }
3190
+ /**
3191
+ * 列出所有 MCP 服务
3192
+ */
3193
+ async handleListInternal(options = {}) {
3194
+ const spinner = ora3("\u83B7\u53D6 MCP \u670D\u52A1\u5217\u8868...").start();
3195
+ try {
3196
+ const mcpServers = configManager2.getMcpServers();
3197
+ const serverNames = Object.keys(mcpServers);
3198
+ const customMCPTools = configManager2.getCustomMCPTools();
3199
+ const hasCustomMCP = customMCPTools.length > 0;
3200
+ const totalServices = serverNames.length + (hasCustomMCP ? 1 : 0);
3201
+ if (totalServices === 0) {
3202
+ spinner.warn("\u672A\u914D\u7F6E\u4EFB\u4F55 MCP \u670D\u52A1\u6216 customMCP \u5DE5\u5177");
3203
+ console.log(
3204
+ chalk4.yellow(
3205
+ "\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 'xiaozhi config' \u547D\u4EE4\u914D\u7F6E MCP \u670D\u52A1\u6216\u5728 xiaozhi.config.json \u4E2D\u914D\u7F6E customMCP \u5DE5\u5177"
3206
+ )
3207
+ );
3208
+ return;
3209
+ }
3210
+ spinner.succeed(
3211
+ `\u627E\u5230 ${totalServices} \u4E2A MCP \u670D\u52A1${hasCustomMCP ? " (\u5305\u62EC customMCP)" : ""}`
3212
+ );
3213
+ if (options.tools) {
3214
+ console.log();
3215
+ console.log(chalk4.bold("MCP \u670D\u52A1\u5DE5\u5177\u5217\u8868:"));
3216
+ console.log();
3217
+ let maxToolNameWidth = 8;
3218
+ const allToolNames = [];
3219
+ for (const serverName of serverNames) {
3220
+ const toolsConfig = configManager2.getServerToolsConfig(serverName);
3221
+ const toolNames = Object.keys(toolsConfig);
3222
+ allToolNames.push(...toolNames);
3223
+ }
3224
+ if (hasCustomMCP) {
3225
+ const customToolNames = customMCPTools.map((tool) => tool.name);
3226
+ allToolNames.push(...customToolNames);
3227
+ }
3228
+ for (const toolName of allToolNames) {
3229
+ const width = _McpCommandHandler.getDisplayWidth(toolName);
3230
+ if (width > maxToolNameWidth) {
3231
+ maxToolNameWidth = width;
3232
+ }
3233
+ }
3234
+ maxToolNameWidth = Math.max(10, Math.min(maxToolNameWidth + 2, 30));
3235
+ const table = new Table({
3236
+ head: [
3237
+ chalk4.bold("MCP"),
3238
+ chalk4.bold("\u5DE5\u5177\u540D\u79F0"),
3239
+ chalk4.bold("\u72B6\u6001"),
3240
+ chalk4.bold("\u63CF\u8FF0")
3241
+ ],
3242
+ colWidths: [15, maxToolNameWidth, 8, 40],
3243
+ // MCP | 工具名称 | 状态 | 描述
3244
+ wordWrap: true,
3245
+ style: {
3246
+ head: [],
3247
+ border: []
3248
+ }
3249
+ });
3250
+ if (hasCustomMCP) {
3251
+ for (const customTool of customMCPTools) {
3252
+ const description = _McpCommandHandler.truncateToWidth(
3253
+ customTool.description || "",
3254
+ 32
3255
+ );
3256
+ table.push([
3257
+ "customMCP",
3258
+ customTool.name,
3259
+ chalk4.green("\u542F\u7528"),
3260
+ // customMCP 工具默认启用
3261
+ description
3262
+ ]);
3263
+ }
3264
+ }
3265
+ for (const serverName of serverNames) {
3266
+ const toolsConfig = configManager2.getServerToolsConfig(serverName);
3267
+ const toolNames = Object.keys(toolsConfig);
3268
+ if (toolNames.length === 0) {
3269
+ table.push([
3270
+ chalk4.gray(serverName),
3271
+ chalk4.gray("-"),
3272
+ chalk4.gray("-"),
3273
+ chalk4.gray("\u6682\u672A\u8BC6\u522B\u5230\u76F8\u5173\u5DE5\u5177")
3274
+ ]);
3275
+ } else {
3276
+ if (table.length > 0) {
3277
+ table.push([{ colSpan: 4, content: "" }]);
3278
+ }
3279
+ for (const toolName of toolNames) {
3280
+ const toolConfig = toolsConfig[toolName];
3281
+ const status = toolConfig.enable ? chalk4.green("\u542F\u7528") : chalk4.red("\u7981\u7528");
3282
+ const description = _McpCommandHandler.truncateToWidth(
3283
+ toolConfig.description || "",
3284
+ 32
3285
+ );
3286
+ table.push([serverName, toolName, status, description]);
3287
+ }
3288
+ }
3289
+ }
3290
+ console.log(table.toString());
3291
+ } else {
3292
+ console.log();
3293
+ console.log(chalk4.bold("MCP \u670D\u52A1\u5217\u8868:"));
3294
+ console.log();
3295
+ if (hasCustomMCP) {
3296
+ console.log(`${chalk4.cyan("\u2022")} ${chalk4.bold("customMCP")}`);
3297
+ console.log(` \u7C7B\u578B: ${chalk4.gray("\u81EA\u5B9A\u4E49 MCP \u5DE5\u5177")}`);
3298
+ console.log(` \u914D\u7F6E: ${chalk4.gray("xiaozhi.config.json")}`);
3299
+ console.log(
3300
+ ` \u5DE5\u5177: ${chalk4.green(customMCPTools.length)} \u542F\u7528 / ${chalk4.yellow(
3301
+ customMCPTools.length
3302
+ )} \u603B\u8BA1`
3303
+ );
3304
+ console.log();
3305
+ }
3306
+ for (const serverName of serverNames) {
3307
+ const serverConfig = mcpServers[serverName];
3308
+ const toolsConfig = configManager2.getServerToolsConfig(serverName);
3309
+ const toolCount = Object.keys(toolsConfig).length;
3310
+ const enabledCount = Object.values(toolsConfig).filter(
3311
+ (t) => t.enable !== false
3312
+ ).length;
3313
+ console.log(`${chalk4.cyan("\u2022")} ${chalk4.bold(serverName)}`);
3314
+ if ("url" in serverConfig) {
3315
+ if ("type" in serverConfig && serverConfig.type === "sse") {
3316
+ console.log(` \u7C7B\u578B: ${chalk4.gray("SSE")}`);
3317
+ } else {
3318
+ console.log(` \u7C7B\u578B: ${chalk4.gray("Streamable HTTP")}`);
3319
+ }
3320
+ console.log(` URL: ${chalk4.gray(serverConfig.url)}`);
3321
+ } else if (isLocalMCPServerConfig(serverConfig)) {
3322
+ console.log(
3323
+ ` \u547D\u4EE4: ${chalk4.gray(serverConfig.command)} ${chalk4.gray(
3324
+ serverConfig.args.join(" ")
3325
+ )}`
3326
+ );
3327
+ }
3328
+ if (toolCount > 0) {
3329
+ console.log(
3330
+ ` \u5DE5\u5177: ${chalk4.green(enabledCount)} \u542F\u7528 / ${chalk4.yellow(
3331
+ toolCount
3332
+ )} \u603B\u8BA1`
3333
+ );
3334
+ } else {
3335
+ console.log(` \u5DE5\u5177: ${chalk4.gray("\u672A\u626B\u63CF (\u8BF7\u5148\u542F\u52A8\u670D\u52A1)")}`);
3336
+ }
3337
+ console.log();
3338
+ }
3339
+ }
3340
+ console.log(chalk4.gray("\u{1F4A1} \u63D0\u793A:"));
3341
+ console.log(
3342
+ chalk4.gray(" - \u4F7F\u7528 'xiaozhi mcp list --tools' \u67E5\u770B\u6240\u6709\u5DE5\u5177")
3343
+ );
3344
+ console.log(
3345
+ chalk4.gray(" - \u4F7F\u7528 'xiaozhi mcp <\u670D\u52A1\u540D> list' \u67E5\u770B\u6307\u5B9A\u670D\u52A1\u7684\u5DE5\u5177")
3346
+ );
3347
+ console.log(
3348
+ chalk4.gray(
3349
+ " - \u4F7F\u7528 'xiaozhi mcp <\u670D\u52A1\u540D> <\u5DE5\u5177\u540D> enable/disable' \u542F\u7528/\u7981\u7528\u5DE5\u5177"
3350
+ )
3351
+ );
3352
+ } catch (error) {
3353
+ spinner.fail("\u83B7\u53D6 MCP \u670D\u52A1\u5217\u8868\u5931\u8D25");
3354
+ console.error(
3355
+ chalk4.red(
3356
+ `\u9519\u8BEF: ${error instanceof Error ? error.message : String(error)}`
3357
+ )
3358
+ );
3359
+ process.exit(1);
3360
+ }
3361
+ }
3362
+ /**
3363
+ * 列出指定服务的工具
3364
+ */
3365
+ async handleServerInternal(serverName) {
3366
+ const spinner = ora3(`\u83B7\u53D6 ${serverName} \u670D\u52A1\u7684\u5DE5\u5177\u5217\u8868...`).start();
3367
+ try {
3368
+ const mcpServers = configManager2.getMcpServers();
3369
+ if (!mcpServers[serverName]) {
3370
+ spinner.fail(`\u670D\u52A1 '${serverName}' \u4E0D\u5B58\u5728`);
3371
+ console.log(
3372
+ chalk4.yellow("\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 'xiaozhi mcp list' \u67E5\u770B\u6240\u6709\u53EF\u7528\u670D\u52A1")
3373
+ );
3374
+ return;
3375
+ }
3376
+ const toolsConfig = configManager2.getServerToolsConfig(serverName);
3377
+ const toolNames = Object.keys(toolsConfig);
3378
+ if (toolNames.length === 0) {
3379
+ spinner.warn(`\u670D\u52A1 '${serverName}' \u6682\u65E0\u5DE5\u5177\u4FE1\u606F`);
3380
+ console.log(chalk4.yellow("\u{1F4A1} \u63D0\u793A: \u8BF7\u5148\u542F\u52A8\u670D\u52A1\u4EE5\u626B\u63CF\u5DE5\u5177\u5217\u8868"));
3381
+ return;
3382
+ }
3383
+ spinner.succeed(`\u670D\u52A1 '${serverName}' \u5171\u6709 ${toolNames.length} \u4E2A\u5DE5\u5177`);
3384
+ console.log();
3385
+ console.log(chalk4.bold(`${serverName} \u670D\u52A1\u5DE5\u5177\u5217\u8868:`));
3386
+ console.log();
3387
+ const table = new Table({
3388
+ head: [chalk4.bold("\u5DE5\u5177\u540D\u79F0"), chalk4.bold("\u72B6\u6001"), chalk4.bold("\u63CF\u8FF0")],
3389
+ colWidths: [30, 8, 50],
3390
+ // 工具名称 | 状态 | 描述
3391
+ wordWrap: true,
3392
+ style: {
3393
+ head: [],
3394
+ border: []
3395
+ }
3396
+ });
3397
+ for (const toolName of toolNames) {
3398
+ const toolConfig = toolsConfig[toolName];
3399
+ const status = toolConfig.enable ? chalk4.green("\u542F\u7528") : chalk4.red("\u7981\u7528");
3400
+ const description = _McpCommandHandler.truncateToWidth(
3401
+ toolConfig.description || "",
3402
+ 40
3403
+ );
3404
+ table.push([toolName, status, description]);
3405
+ }
3406
+ console.log(table.toString());
3407
+ console.log();
3408
+ console.log(chalk4.gray("\u{1F4A1} \u63D0\u793A:"));
3409
+ console.log(
3410
+ chalk4.gray(
3411
+ ` - \u4F7F\u7528 'xiaozhi mcp ${serverName} <\u5DE5\u5177\u540D> enable' \u542F\u7528\u5DE5\u5177`
3412
+ )
3413
+ );
3414
+ console.log(
3415
+ chalk4.gray(
3416
+ ` - \u4F7F\u7528 'xiaozhi mcp ${serverName} <\u5DE5\u5177\u540D> disable' \u7981\u7528\u5DE5\u5177`
3417
+ )
3418
+ );
3419
+ } catch (error) {
3420
+ spinner.fail("\u83B7\u53D6\u5DE5\u5177\u5217\u8868\u5931\u8D25");
3421
+ console.error(
3422
+ chalk4.red(
3423
+ `\u9519\u8BEF: ${error instanceof Error ? error.message : String(error)}`
3424
+ )
3425
+ );
3426
+ process.exit(1);
3427
+ }
3428
+ }
3429
+ /**
3430
+ * 启用或禁用工具
3431
+ */
3432
+ async handleToolInternal(serverName, toolName, enabled) {
3433
+ const action = enabled ? "\u542F\u7528" : "\u7981\u7528";
3434
+ const spinner = ora3(`${action}\u5DE5\u5177 ${serverName}/${toolName}...`).start();
3435
+ try {
3436
+ const mcpServers = configManager2.getMcpServers();
3437
+ if (!mcpServers[serverName]) {
3438
+ spinner.fail(`\u670D\u52A1 '${serverName}' \u4E0D\u5B58\u5728`);
3439
+ console.log(
3440
+ chalk4.yellow("\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 'xiaozhi mcp list' \u67E5\u770B\u6240\u6709\u53EF\u7528\u670D\u52A1")
3441
+ );
3442
+ return;
3443
+ }
3444
+ const toolsConfig = configManager2.getServerToolsConfig(serverName);
3445
+ if (!toolsConfig[toolName]) {
3446
+ spinner.fail(`\u5DE5\u5177 '${toolName}' \u5728\u670D\u52A1 '${serverName}' \u4E2D\u4E0D\u5B58\u5728`);
3447
+ console.log(
3448
+ chalk4.yellow(
3449
+ `\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 'xiaozhi mcp ${serverName} list' \u67E5\u770B\u8BE5\u670D\u52A1\u7684\u6240\u6709\u5DE5\u5177`
3450
+ )
3451
+ );
3452
+ return;
3453
+ }
3454
+ configManager2.setToolEnabled(
3455
+ serverName,
3456
+ toolName,
3457
+ enabled,
3458
+ toolsConfig[toolName].description
3459
+ );
3460
+ spinner.succeed(
3461
+ `\u6210\u529F${action}\u5DE5\u5177 ${chalk4.cyan(serverName)}/${chalk4.cyan(toolName)}`
3462
+ );
3463
+ console.log();
3464
+ console.log(chalk4.gray("\u{1F4A1} \u63D0\u793A: \u5DE5\u5177\u72B6\u6001\u66F4\u6539\u5C06\u5728\u4E0B\u6B21\u542F\u52A8\u670D\u52A1\u65F6\u751F\u6548"));
3465
+ } catch (error) {
3466
+ spinner.fail(`${action}\u5DE5\u5177\u5931\u8D25`);
3467
+ console.error(
3468
+ chalk4.red(
3469
+ `\u9519\u8BEF: ${error instanceof Error ? error.message : String(error)}`
3470
+ )
3471
+ );
3472
+ process.exit(1);
3473
+ }
3474
+ }
3475
+ };
3476
+ }
3477
+ });
3478
+
3479
+ // src/commands/EndpointCommandHandler.ts
3480
+ var EndpointCommandHandler_exports = {};
3481
+ __export(EndpointCommandHandler_exports, {
3482
+ EndpointCommandHandler: () => EndpointCommandHandler
3483
+ });
3484
+ import chalk5 from "chalk";
3485
+ import ora4 from "ora";
3486
+ var EndpointCommandHandler;
3487
+ var init_EndpointCommandHandler = __esm({
3488
+ "src/commands/EndpointCommandHandler.ts"() {
3489
+ init_Command();
3490
+ EndpointCommandHandler = class extends BaseCommandHandler {
3491
+ static {
3492
+ __name(this, "EndpointCommandHandler");
3493
+ }
3494
+ name = "endpoint";
3495
+ description = "\u7BA1\u7406 MCP \u7AEF\u70B9";
3496
+ subcommands = [
3497
+ {
3498
+ name: "list",
3499
+ description: "\u5217\u51FA\u6240\u6709 MCP \u7AEF\u70B9",
3500
+ execute: /* @__PURE__ */ __name(async (args, options) => {
3501
+ await this.handleList();
3502
+ }, "execute")
3503
+ },
3504
+ {
3505
+ name: "add",
3506
+ description: "\u6DFB\u52A0\u65B0\u7684 MCP \u7AEF\u70B9",
3507
+ execute: /* @__PURE__ */ __name(async (args, options) => {
3508
+ this.validateArgs(args, 1);
3509
+ await this.handleAdd(args[0]);
3510
+ }, "execute")
3511
+ },
3512
+ {
3513
+ name: "remove",
3514
+ description: "\u79FB\u9664\u6307\u5B9A\u7684 MCP \u7AEF\u70B9",
3515
+ execute: /* @__PURE__ */ __name(async (args, options) => {
3516
+ this.validateArgs(args, 1);
3517
+ await this.handleRemove(args[0]);
3518
+ }, "execute")
3519
+ },
3520
+ {
3521
+ name: "set",
3522
+ description: "\u8BBE\u7F6E MCP \u7AEF\u70B9\uFF08\u53EF\u4EE5\u662F\u5355\u4E2A\u6216\u591A\u4E2A\uFF09",
3523
+ execute: /* @__PURE__ */ __name(async (args, options) => {
3524
+ this.validateArgs(args, 1);
3525
+ await this.handleSet(args);
3526
+ }, "execute")
3527
+ }
3528
+ ];
3529
+ constructor(container) {
3530
+ super(container);
3531
+ }
3532
+ /**
3533
+ * 主命令执行(显示帮助)
3534
+ */
3535
+ async execute(args, options) {
3536
+ console.log("MCP \u7AEF\u70B9\u7BA1\u7406\u547D\u4EE4\u3002\u4F7F\u7528 --help \u67E5\u770B\u53EF\u7528\u7684\u5B50\u547D\u4EE4\u3002");
3537
+ }
3538
+ /**
3539
+ * 处理列出端点命令
3540
+ */
3541
+ async handleList() {
3542
+ const spinner = ora4("\u8BFB\u53D6\u7AEF\u70B9\u914D\u7F6E...").start();
3543
+ try {
3544
+ const configManager3 = this.getService("configManager");
3545
+ const endpoints = configManager3.getMcpEndpoints();
3546
+ spinner.succeed("\u7AEF\u70B9\u5217\u8868");
3547
+ if (endpoints.length === 0) {
3548
+ console.log(chalk5.yellow("\u672A\u914D\u7F6E\u4EFB\u4F55 MCP \u7AEF\u70B9"));
3549
+ } else {
3550
+ console.log(chalk5.green(`\u5171 ${endpoints.length} \u4E2A\u7AEF\u70B9:`));
3551
+ endpoints.forEach((ep, index) => {
3552
+ console.log(chalk5.gray(` ${index + 1}. ${ep}`));
3553
+ });
3554
+ }
3555
+ } catch (error) {
3556
+ spinner.fail(
3557
+ `\u8BFB\u53D6\u7AEF\u70B9\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
3558
+ );
3559
+ this.handleError(error);
3560
+ }
3561
+ }
3562
+ /**
3563
+ * 处理添加端点命令
3564
+ */
3565
+ async handleAdd(url) {
3566
+ const spinner = ora4("\u6DFB\u52A0\u7AEF\u70B9...").start();
3567
+ try {
3568
+ const configManager3 = this.getService("configManager");
3569
+ configManager3.addMcpEndpoint(url);
3570
+ spinner.succeed(`\u6210\u529F\u6DFB\u52A0\u7AEF\u70B9: ${url}`);
3571
+ const endpoints = configManager3.getMcpEndpoints();
3572
+ console.log(chalk5.gray(`\u5F53\u524D\u5171 ${endpoints.length} \u4E2A\u7AEF\u70B9`));
3573
+ } catch (error) {
3574
+ spinner.fail(
3575
+ `\u6DFB\u52A0\u7AEF\u70B9\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
3576
+ );
3577
+ this.handleError(error);
3578
+ }
3579
+ }
3580
+ /**
3581
+ * 处理移除端点命令
3582
+ */
3583
+ async handleRemove(url) {
3584
+ const spinner = ora4("\u79FB\u9664\u7AEF\u70B9...").start();
3585
+ try {
3586
+ const configManager3 = this.getService("configManager");
3587
+ configManager3.removeMcpEndpoint(url);
3588
+ spinner.succeed(`\u6210\u529F\u79FB\u9664\u7AEF\u70B9: ${url}`);
3589
+ const endpoints = configManager3.getMcpEndpoints();
3590
+ console.log(chalk5.gray(`\u5F53\u524D\u5269\u4F59 ${endpoints.length} \u4E2A\u7AEF\u70B9`));
3591
+ } catch (error) {
3592
+ spinner.fail(
3593
+ `\u79FB\u9664\u7AEF\u70B9\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
3594
+ );
3595
+ this.handleError(error);
3596
+ }
3597
+ }
3598
+ /**
3599
+ * 处理设置端点命令
3600
+ */
3601
+ async handleSet(urls) {
3602
+ const spinner = ora4("\u8BBE\u7F6E\u7AEF\u70B9...").start();
3603
+ try {
3604
+ const configManager3 = this.getService("configManager");
3605
+ if (urls.length === 1) {
3606
+ configManager3.updateMcpEndpoint(urls[0]);
3607
+ spinner.succeed(`\u6210\u529F\u8BBE\u7F6E\u7AEF\u70B9: ${urls[0]}`);
3608
+ } else {
3609
+ configManager3.updateMcpEndpoint(urls);
3610
+ spinner.succeed(`\u6210\u529F\u8BBE\u7F6E ${urls.length} \u4E2A\u7AEF\u70B9`);
3611
+ for (const [index, url] of urls.entries()) {
3612
+ console.log(chalk5.gray(` ${index + 1}. ${url}`));
3613
+ }
3614
+ }
3615
+ } catch (error) {
3616
+ spinner.fail(
3617
+ `\u8BBE\u7F6E\u7AEF\u70B9\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
3618
+ );
3619
+ this.handleError(error);
3620
+ }
3621
+ }
3622
+ };
3623
+ }
3624
+ });
3625
+
3626
+ // src/index.ts
3627
+ import { Command } from "commander";
3628
+
3629
+ // src/Container.ts
3630
+ import { configManager } from "../backend/lib/config/manager.js";
3631
+ import { logger } from "../backend/Logger.js";
3632
+
3633
+ // src/errors/ErrorHandlers.ts
3634
+ import chalk from "chalk";
3635
+
3636
+ // src/errors/ErrorMessages.ts
3637
+ init_Constants();
3638
+ var ERROR_HELP_MESSAGES = {
3639
+ [ERROR_CODES.CONFIG_ERROR]: '\u8FD0\u884C "xiaozhi --help" \u67E5\u770B\u914D\u7F6E\u76F8\u5173\u547D\u4EE4',
3640
+ [ERROR_CODES.SERVICE_ERROR]: '\u8FD0\u884C "xiaozhi status" \u68C0\u67E5\u670D\u52A1\u72B6\u6001',
3641
+ [ERROR_CODES.VALIDATION_ERROR]: "\u68C0\u67E5\u8F93\u5165\u53C2\u6570\u662F\u5426\u6B63\u786E",
3642
+ [ERROR_CODES.FILE_ERROR]: "\u68C0\u67E5\u6587\u4EF6\u8DEF\u5F84\u548C\u6743\u9650",
3643
+ [ERROR_CODES.PROCESS_ERROR]: "\u68C0\u67E5\u8FDB\u7A0B\u72B6\u6001\u548C\u6743\u9650",
3644
+ [ERROR_CODES.NETWORK_ERROR]: "\u68C0\u67E5\u7F51\u7EDC\u8FDE\u63A5\u548C\u9632\u706B\u5899\u8BBE\u7F6E",
3645
+ [ERROR_CODES.PERMISSION_ERROR]: "\u5C1D\u8BD5\u4F7F\u7528\u7BA1\u7406\u5458\u6743\u9650\u8FD0\u884C"
3646
+ };
3647
+ var COMMON_SOLUTIONS = {
3648
+ config_not_found: [
3649
+ '\u8FD0\u884C "xiaozhi init" \u521D\u59CB\u5316\u914D\u7F6E\u6587\u4EF6',
3650
+ "\u68C0\u67E5\u5F53\u524D\u76EE\u5F55\u662F\u5426\u4E3A\u9879\u76EE\u6839\u76EE\u5F55",
3651
+ "\u8BBE\u7F6E XIAOZHI_CONFIG_DIR \u73AF\u5883\u53D8\u91CF\u6307\u5B9A\u914D\u7F6E\u76EE\u5F55"
3652
+ ],
3653
+ service_port_occupied: [
3654
+ "\u68C0\u67E5\u7AEF\u53E3\u662F\u5426\u88AB\u5176\u4ED6\u7A0B\u5E8F\u5360\u7528",
3655
+ '\u4F7F\u7528 "lsof -i :\u7AEF\u53E3\u53F7" \u67E5\u770B\u7AEF\u53E3\u4F7F\u7528\u60C5\u51B5',
3656
+ "\u66F4\u6539\u914D\u7F6E\u6587\u4EF6\u4E2D\u7684\u7AEF\u53E3\u8BBE\u7F6E"
3657
+ ],
3658
+ permission_denied: [
3659
+ "\u68C0\u67E5\u6587\u4EF6\u548C\u76EE\u5F55\u6743\u9650",
3660
+ "\u4F7F\u7528 sudo \u6216\u7BA1\u7406\u5458\u6743\u9650\u8FD0\u884C",
3661
+ "\u786E\u4FDD\u5F53\u524D\u7528\u6237\u6709\u8DB3\u591F\u7684\u6743\u9650"
3662
+ ],
3663
+ service_start_failed: [
3664
+ "\u68C0\u67E5\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u662F\u5426\u6B63\u786E",
3665
+ "\u67E5\u770B\u65E5\u5FD7\u6587\u4EF6\u83B7\u53D6\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F",
3666
+ "\u786E\u4FDD\u6240\u6709\u4F9D\u8D56\u670D\u52A1\u6B63\u5E38\u8FD0\u884C"
3667
+ ]
3668
+ };
3669
+ var ERROR_MESSAGES = class {
3670
+ static {
3671
+ __name(this, "ERROR_MESSAGES");
3672
+ }
3673
+ /**
3674
+ * 获取错误码对应的帮助信息
3675
+ */
3676
+ static getHelpMessage(errorCode) {
3677
+ return ERROR_HELP_MESSAGES[errorCode];
3678
+ }
3679
+ /**
3680
+ * 获取常见问题的解决方案
3681
+ */
3682
+ static getSolutions(problemKey) {
3683
+ return COMMON_SOLUTIONS[problemKey] || [];
3684
+ }
3685
+ /**
3686
+ * 格式化错误消息
3687
+ */
3688
+ static formatError(error, context) {
3689
+ const contextPrefix = context ? `[${context}] ` : "";
3690
+ return `${contextPrefix}${error.message}`;
3691
+ }
3692
+ /**
3693
+ * 获取友好的错误描述
3694
+ */
3695
+ static getFriendlyMessage(errorCode) {
3696
+ const friendlyMessages = {
3697
+ [ERROR_CODES.CONFIG_ERROR]: "\u914D\u7F6E\u6587\u4EF6\u76F8\u5173\u9519\u8BEF",
3698
+ [ERROR_CODES.SERVICE_ERROR]: "\u670D\u52A1\u8FD0\u884C\u76F8\u5173\u9519\u8BEF",
3699
+ [ERROR_CODES.VALIDATION_ERROR]: "\u8F93\u5165\u9A8C\u8BC1\u9519\u8BEF",
3700
+ [ERROR_CODES.FILE_ERROR]: "\u6587\u4EF6\u64CD\u4F5C\u9519\u8BEF",
3701
+ [ERROR_CODES.PROCESS_ERROR]: "\u8FDB\u7A0B\u7BA1\u7406\u9519\u8BEF",
3702
+ [ERROR_CODES.NETWORK_ERROR]: "\u7F51\u7EDC\u8FDE\u63A5\u9519\u8BEF",
3703
+ [ERROR_CODES.PERMISSION_ERROR]: "\u6743\u9650\u4E0D\u8DB3\u9519\u8BEF"
3704
+ };
3705
+ return friendlyMessages[errorCode] || "\u672A\u77E5\u9519\u8BEF";
3706
+ }
3707
+ /**
3708
+ * 检查是否为可恢复错误
3709
+ */
3710
+ static isRecoverable(errorCode) {
3711
+ const recoverableErrors = [
3712
+ ERROR_CODES.NETWORK_ERROR,
3713
+ ERROR_CODES.FILE_ERROR,
3714
+ ERROR_CODES.SERVICE_ERROR
3715
+ ];
3716
+ return recoverableErrors.includes(errorCode);
3717
+ }
3718
+ /**
3719
+ * 获取错误的严重程度
3720
+ */
3721
+ static getSeverity(errorCode) {
3722
+ const severityMap = {
3723
+ [ERROR_CODES.VALIDATION_ERROR]: "low",
3724
+ [ERROR_CODES.FILE_ERROR]: "medium",
3725
+ [ERROR_CODES.CONFIG_ERROR]: "medium",
3726
+ [ERROR_CODES.NETWORK_ERROR]: "medium",
3727
+ [ERROR_CODES.SERVICE_ERROR]: "high",
3728
+ [ERROR_CODES.PROCESS_ERROR]: "high",
3729
+ [ERROR_CODES.PERMISSION_ERROR]: "critical"
3730
+ };
3731
+ return severityMap[errorCode] || "medium";
3732
+ }
3733
+ };
3734
+
3735
+ // src/errors/ErrorHandlers.ts
3736
+ init_errors();
3737
+ var ErrorHandler = class _ErrorHandler {
3738
+ static {
3739
+ __name(this, "ErrorHandler");
3740
+ }
3741
+ /**
3742
+ * 处理错误并退出程序
3743
+ */
3744
+ static handle(error) {
3745
+ if (error instanceof CLIError) {
3746
+ _ErrorHandler.handleCLIError(error);
3747
+ } else {
3748
+ _ErrorHandler.handleUnknownError(error);
3749
+ }
3750
+ process.exit(1);
3751
+ }
3752
+ /**
3753
+ * 处理 CLI 错误
3754
+ */
3755
+ static handleCLIError(error) {
3756
+ console.error(chalk.red(`\u274C \u9519\u8BEF: ${error.message}`));
3757
+ if (process.env.DEBUG) {
3758
+ console.error(chalk.gray(`\u9519\u8BEF\u7801: ${error.code}`));
3759
+ }
3760
+ if (error.suggestions && error.suggestions.length > 0) {
3761
+ console.log(chalk.yellow("\u{1F4A1} \u5EFA\u8BAE:"));
3762
+ for (const suggestion of error.suggestions) {
3763
+ console.log(chalk.gray(` ${suggestion}`));
3764
+ }
3765
+ }
3766
+ const helpMessage = ERROR_MESSAGES.getHelpMessage(error.code);
3767
+ if (helpMessage) {
3768
+ console.log(chalk.blue(`\u2139\uFE0F ${helpMessage}`));
3769
+ }
3770
+ }
3771
+ /**
3772
+ * 处理未知错误
3773
+ */
3774
+ static handleUnknownError(error) {
3775
+ console.error(chalk.red(`\u274C \u672A\u77E5\u9519\u8BEF: ${error.message}`));
3776
+ if (process.env.DEBUG || process.env.NODE_ENV === "development") {
3777
+ console.error(chalk.gray("\u5806\u6808\u4FE1\u606F:"));
3778
+ console.error(chalk.gray(error.stack));
3779
+ } else {
3780
+ console.log(
3781
+ chalk.yellow("\u{1F4A1} \u63D0\u793A: \u8BBE\u7F6E DEBUG=1 \u73AF\u5883\u53D8\u91CF\u67E5\u770B\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F")
3782
+ );
3783
+ }
3784
+ }
3785
+ /**
3786
+ * 异步操作错误处理包装器
3787
+ */
3788
+ static async handleAsync(operation, context) {
3789
+ try {
3790
+ return await operation();
3791
+ } catch (error) {
3792
+ if (error instanceof CLIError) {
3793
+ throw error;
3794
+ }
3795
+ if (error instanceof Error) {
3796
+ throw new CLIError(
3797
+ `${context}\u5931\u8D25: ${error.message}`,
3798
+ "OPERATION_FAILED",
3799
+ 1
3800
+ );
3801
+ }
3802
+ throw new CLIError(`${context}\u5931\u8D25: \u672A\u77E5\u9519\u8BEF`, "OPERATION_FAILED", 1);
3803
+ }
3804
+ }
3805
+ /**
3806
+ * 同步操作错误处理包装器
3807
+ */
3808
+ static handleSync(operation, context) {
3809
+ try {
3810
+ return operation();
3811
+ } catch (error) {
3812
+ if (error instanceof CLIError) {
3813
+ throw error;
3814
+ }
3815
+ if (error instanceof Error) {
3816
+ throw new CLIError(
3817
+ `${context}\u5931\u8D25: ${error.message}`,
3818
+ "OPERATION_FAILED",
3819
+ 1
3820
+ );
3821
+ }
3822
+ throw new CLIError(`${context}\u5931\u8D25: \u672A\u77E5\u9519\u8BEF`, "OPERATION_FAILED", 1);
3823
+ }
3824
+ }
3825
+ /**
3826
+ * 警告处理
3827
+ */
3828
+ static warn(message, suggestions) {
3829
+ console.warn(chalk.yellow(`\u26A0\uFE0F \u8B66\u544A: ${message}`));
3830
+ if (suggestions && suggestions.length > 0) {
3831
+ console.log(chalk.yellow("\u{1F4A1} \u5EFA\u8BAE:"));
3832
+ for (const suggestion of suggestions) {
3833
+ console.log(chalk.gray(` ${suggestion}`));
3834
+ }
3835
+ }
3836
+ }
3837
+ /**
3838
+ * 信息提示
3839
+ */
3840
+ static info(message) {
3841
+ console.log(chalk.blue(`\u2139\uFE0F ${message}`));
3842
+ }
3843
+ /**
3844
+ * 成功提示
3845
+ */
3846
+ static success(message) {
3847
+ console.log(chalk.green(`\u2705 ${message}`));
3848
+ }
3849
+ };
3850
+
3851
+ // src/Container.ts
3852
+ init_FileUtils();
3853
+ init_FormatUtils();
3854
+ init_PathUtils();
3855
+ init_PlatformUtils();
3856
+ init_Validation();
3857
+
3858
+ // src/utils/VersionUtils.ts
3859
+ init_errors();
3860
+ import fs2 from "fs";
3861
+ import path3 from "path";
3862
+ import { fileURLToPath as fileURLToPath2 } from "url";
3863
+ var VersionUtils = class _VersionUtils {
3864
+ static {
3865
+ __name(this, "VersionUtils");
3866
+ }
3867
+ static cachedVersion = null;
3868
+ /**
3869
+ * 获取版本号
3870
+ */
3871
+ static getVersion() {
3872
+ if (_VersionUtils.cachedVersion) {
3873
+ return _VersionUtils.cachedVersion;
3874
+ }
3875
+ try {
3876
+ const __filename = fileURLToPath2(import.meta.url);
3877
+ const currentDir = path3.dirname(__filename);
3878
+ const possiblePaths = [
3879
+ // 构建后环境:dist/cli.js -> dist/package.json (优先)
3880
+ path3.join(currentDir, "package.json"),
3881
+ // 构建后环境:dist/cli.js -> package.json
3882
+ path3.join(currentDir, "..", "package.json"),
3883
+ // 开发环境:src/cli/utils/VersionUtils.ts -> package.json
3884
+ path3.join(currentDir, "..", "..", "..", "package.json"),
3885
+ // 全局安装环境
3886
+ path3.join(currentDir, "..", "..", "..", "..", "package.json")
3887
+ ];
3888
+ for (const packagePath of possiblePaths) {
3889
+ if (fs2.existsSync(packagePath)) {
3890
+ const packageJson = JSON.parse(fs2.readFileSync(packagePath, "utf8"));
3891
+ if (packageJson.version) {
3892
+ _VersionUtils.cachedVersion = packageJson.version;
3893
+ return packageJson.version;
3894
+ }
3895
+ }
3896
+ }
3897
+ _VersionUtils.cachedVersion = "unknown";
3898
+ return "unknown";
3899
+ } catch (error) {
3900
+ console.warn("\u65E0\u6CD5\u4ECE package.json \u8BFB\u53D6\u7248\u672C\u4FE1\u606F:", error);
3901
+ _VersionUtils.cachedVersion = "unknown";
3902
+ return "unknown";
3903
+ }
3904
+ }
3905
+ /**
3906
+ * 获取完整版本信息
3907
+ */
3908
+ static getVersionInfo() {
3909
+ try {
3910
+ const __filename = fileURLToPath2(import.meta.url);
3911
+ const currentDir = path3.dirname(__filename);
3912
+ const possiblePaths = [
3913
+ // 构建后环境:dist/cli.js -> dist/package.json (优先)
3914
+ path3.join(currentDir, "package.json"),
3915
+ // 构建后环境:dist/cli.js -> package.json
3916
+ path3.join(currentDir, "..", "package.json"),
3917
+ // 开发环境:src/cli/utils/VersionUtils.ts -> package.json
3918
+ path3.join(currentDir, "..", "..", "..", "package.json"),
3919
+ // 全局安装环境
3920
+ path3.join(currentDir, "..", "..", "..", "..", "package.json")
3921
+ ];
3922
+ for (const packagePath of possiblePaths) {
3923
+ if (fs2.existsSync(packagePath)) {
3924
+ const packageJson = JSON.parse(fs2.readFileSync(packagePath, "utf8"));
3925
+ return {
3926
+ version: packageJson.version || "unknown",
3927
+ name: packageJson.name,
3928
+ description: packageJson.description,
3929
+ author: packageJson.author
3930
+ };
3931
+ }
3932
+ }
3933
+ return { version: "unknown" };
3934
+ } catch (error) {
3935
+ throw new FileError("\u65E0\u6CD5\u8BFB\u53D6\u7248\u672C\u4FE1\u606F", "package.json");
3936
+ }
3937
+ }
3938
+ /**
3939
+ * 比较版本号
3940
+ */
3941
+ static compareVersions(version1, version2) {
3942
+ const v1Parts = version1.split(".").map(Number);
3943
+ const v2Parts = version2.split(".").map(Number);
3944
+ const maxLength = Math.max(v1Parts.length, v2Parts.length);
3945
+ for (let i = 0; i < maxLength; i++) {
3946
+ const v1Part = v1Parts[i] || 0;
3947
+ const v2Part = v2Parts[i] || 0;
3948
+ if (v1Part > v2Part) return 1;
3949
+ if (v1Part < v2Part) return -1;
3950
+ }
3951
+ return 0;
3952
+ }
3953
+ /**
3954
+ * 检查版本是否有效
3955
+ */
3956
+ static isValidVersion(version) {
3957
+ const versionRegex = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?$/;
3958
+ return versionRegex.test(version);
3959
+ }
3960
+ /**
3961
+ * 清除版本缓存
3962
+ */
3963
+ static clearCache() {
3964
+ _VersionUtils.cachedVersion = null;
3965
+ }
3966
+ };
3967
+
3968
+ // src/Container.ts
3969
+ var DIContainer = class _DIContainer {
3970
+ static {
3971
+ __name(this, "DIContainer");
3972
+ }
3973
+ instances = /* @__PURE__ */ new Map();
3974
+ factories = /* @__PURE__ */ new Map();
3975
+ asyncFactories = /* @__PURE__ */ new Map();
3976
+ singletons = /* @__PURE__ */ new Set();
3977
+ /**
3978
+ * 注册服务工厂
3979
+ */
3980
+ register(key, factory, singleton = false) {
3981
+ this.factories.set(key, factory);
3982
+ if (singleton) {
3983
+ this.singletons.add(key);
3984
+ }
3985
+ }
3986
+ /**
3987
+ * 注册单例服务
3988
+ */
3989
+ registerSingleton(key, factory) {
3990
+ this.register(key, factory, true);
3991
+ }
3992
+ /**
3993
+ * 注册实例
3994
+ */
3995
+ registerInstance(key, instance) {
3996
+ this.instances.set(key, instance);
3997
+ this.singletons.add(key);
3998
+ }
3999
+ /**
4000
+ * 获取服务实例
4001
+ */
4002
+ get(key) {
4003
+ if (this.singletons.has(key) && this.instances.has(key)) {
4004
+ return this.instances.get(key);
4005
+ }
4006
+ const factory = this.factories.get(key);
4007
+ if (!factory) {
4008
+ throw new Error(`Service ${key} not registered`);
4009
+ }
4010
+ const instance = factory();
4011
+ if (this.singletons.has(key)) {
4012
+ this.instances.set(key, instance);
4013
+ }
4014
+ return instance;
4015
+ }
4016
+ /**
4017
+ * 检查服务是否已注册
4018
+ */
4019
+ has(key) {
4020
+ return this.factories.has(key) || this.instances.has(key);
4021
+ }
4022
+ /**
4023
+ * 清除所有注册的服务
4024
+ */
4025
+ clear() {
4026
+ this.instances.clear();
4027
+ this.factories.clear();
4028
+ this.singletons.clear();
4029
+ }
4030
+ /**
4031
+ * 获取所有已注册的服务键
4032
+ */
4033
+ getRegisteredKeys() {
4034
+ const factoryKeys = Array.from(this.factories.keys());
4035
+ const instanceKeys = Array.from(this.instances.keys());
4036
+ return [.../* @__PURE__ */ new Set([...factoryKeys, ...instanceKeys])];
4037
+ }
4038
+ /**
4039
+ * 创建默认容器实例
4040
+ */
4041
+ static create() {
4042
+ const container = new _DIContainer();
4043
+ container.registerSingleton("versionUtils", () => {
4044
+ return VersionUtils;
4045
+ });
4046
+ container.registerSingleton("platformUtils", () => {
4047
+ return PlatformUtils;
4048
+ });
4049
+ container.registerSingleton("formatUtils", () => {
4050
+ return FormatUtils;
4051
+ });
4052
+ container.registerSingleton("fileUtils", () => {
4053
+ return FileUtils;
4054
+ });
4055
+ container.registerSingleton("pathUtils", () => {
4056
+ return PathUtils;
4057
+ });
4058
+ container.registerSingleton("validation", () => {
4059
+ return Validation;
4060
+ });
4061
+ container.registerSingleton("configManager", () => {
4062
+ return configManager;
4063
+ });
4064
+ container.registerSingleton("logger", () => {
4065
+ return logger;
4066
+ });
4067
+ container.registerSingleton("errorHandler", () => {
4068
+ return ErrorHandler;
4069
+ });
4070
+ container.registerSingleton("processManager", () => {
4071
+ const ProcessManagerModule = (init_ProcessManager(), __toCommonJS(ProcessManager_exports));
4072
+ return new ProcessManagerModule.ProcessManagerImpl();
4073
+ });
4074
+ container.registerSingleton("daemonManager", () => {
4075
+ const DaemonManagerModule = (init_DaemonManager(), __toCommonJS(DaemonManager_exports));
4076
+ const processManager = container.get("processManager");
4077
+ const logger3 = container.get("logger");
4078
+ return new DaemonManagerModule.DaemonManagerImpl(processManager, logger3);
4079
+ });
4080
+ container.registerSingleton("serviceManager", () => {
4081
+ const ServiceManagerModule = (init_ServiceManager(), __toCommonJS(ServiceManager_exports));
4082
+ const processManager = container.get("processManager");
4083
+ const configManager3 = container.get("configManager");
4084
+ const logger3 = container.get("logger");
4085
+ return new ServiceManagerModule.ServiceManagerImpl(
4086
+ processManager,
4087
+ configManager3,
4088
+ logger3
4089
+ );
4090
+ });
4091
+ container.registerSingleton("templateManager", () => {
4092
+ const TemplateManagerModule = (init_TemplateManager(), __toCommonJS(TemplateManager_exports));
4093
+ return new TemplateManagerModule.TemplateManagerImpl();
4094
+ });
4095
+ return container;
4096
+ }
4097
+ };
4098
+
4099
+ // src/commands/CommandHandlerFactory.ts
4100
+ var CommandHandlerFactory = class {
4101
+ constructor(container) {
4102
+ this.container = container;
4103
+ }
4104
+ static {
4105
+ __name(this, "CommandHandlerFactory");
4106
+ }
4107
+ /**
4108
+ * 创建所有命令处理器
4109
+ */
4110
+ createHandlers() {
4111
+ return [
4112
+ this.createHandler("service"),
4113
+ this.createHandler("config"),
4114
+ this.createHandler("project"),
4115
+ this.createHandler("mcp"),
4116
+ this.createHandler("endpoint")
4117
+ ];
4118
+ }
4119
+ /**
4120
+ * 创建指定类型的命令处理器
4121
+ */
4122
+ createHandler(type) {
4123
+ switch (type) {
4124
+ case "service":
4125
+ return this.createServiceCommandHandler();
4126
+ case "config":
4127
+ return this.createConfigCommandHandler();
4128
+ case "project":
4129
+ return this.createProjectCommandHandler();
4130
+ case "mcp":
4131
+ return this.createMcpCommandHandler();
4132
+ case "endpoint":
4133
+ return this.createEndpointCommandHandler();
4134
+ default:
4135
+ throw new Error(`\u672A\u77E5\u7684\u547D\u4EE4\u5904\u7406\u5668\u7C7B\u578B: ${type}`);
4136
+ }
4137
+ }
4138
+ /**
4139
+ * 创建服务命令处理器
4140
+ */
4141
+ createServiceCommandHandler() {
4142
+ const {
4143
+ ServiceCommandHandler: ServiceCommandHandler2
4144
+ } = (init_ServiceCommandHandler(), __toCommonJS(ServiceCommandHandler_exports));
4145
+ return new ServiceCommandHandler2(this.container);
4146
+ }
4147
+ /**
4148
+ * 创建配置命令处理器
4149
+ */
4150
+ createConfigCommandHandler() {
4151
+ const {
4152
+ ConfigCommandHandler: ConfigCommandHandler2
4153
+ } = (init_ConfigCommandHandler(), __toCommonJS(ConfigCommandHandler_exports));
4154
+ return new ConfigCommandHandler2(this.container);
4155
+ }
4156
+ /**
4157
+ * 创建项目命令处理器
4158
+ */
4159
+ createProjectCommandHandler() {
4160
+ const {
4161
+ ProjectCommandHandler: ProjectCommandHandler2
4162
+ } = (init_ProjectCommandHandler(), __toCommonJS(ProjectCommandHandler_exports));
4163
+ return new ProjectCommandHandler2(this.container);
4164
+ }
4165
+ /**
4166
+ * 创建MCP命令处理器
4167
+ */
4168
+ createMcpCommandHandler() {
4169
+ const { McpCommandHandler: McpCommandHandler2 } = (init_McpCommandHandler(), __toCommonJS(McpCommandHandler_exports));
4170
+ return new McpCommandHandler2(this.container);
4171
+ }
4172
+ /**
4173
+ * 创建端点命令处理器
4174
+ */
4175
+ createEndpointCommandHandler() {
4176
+ const {
4177
+ EndpointCommandHandler: EndpointCommandHandler2
4178
+ } = (init_EndpointCommandHandler(), __toCommonJS(EndpointCommandHandler_exports));
4179
+ return new EndpointCommandHandler2(this.container);
4180
+ }
4181
+ };
4182
+
4183
+ // src/commands/index.ts
4184
+ var CommandRegistry = class {
4185
+ constructor(container) {
4186
+ this.container = container;
4187
+ this.handlerFactory = new CommandHandlerFactory(container);
4188
+ }
4189
+ static {
4190
+ __name(this, "CommandRegistry");
4191
+ }
4192
+ handlers = [];
4193
+ handlerFactory;
4194
+ /**
4195
+ * 注册所有命令到 Commander 程序
4196
+ */
4197
+ async registerCommands(program2) {
4198
+ try {
4199
+ this.registerVersionCommand(program2);
4200
+ this.registerHelpCommand(program2);
4201
+ const handlers = this.handlerFactory.createHandlers();
4202
+ for (const handler of handlers) {
4203
+ this.registerHandler(handler);
4204
+ this.registerCommand(program2, handler);
4205
+ }
4206
+ this.registerLegacyServiceCommands(program2, handlers);
4207
+ } catch (error) {
4208
+ ErrorHandler.handle(error);
4209
+ }
4210
+ }
4211
+ /**
4212
+ * 注册命令处理器
4213
+ */
4214
+ registerHandler(handler) {
4215
+ this.handlers.push(handler);
4216
+ }
4217
+ /**
4218
+ * 注册单个命令
4219
+ */
4220
+ registerCommand(program2, handler) {
4221
+ if (handler.subcommands && handler.subcommands.length > 0) {
4222
+ const commandGroup = program2.command(handler.name).description(handler.description);
4223
+ for (const subcommand of handler.subcommands) {
4224
+ let subcommandName = subcommand.name;
4225
+ if (subcommand.name === "get") {
4226
+ subcommandName = "get <key>";
4227
+ } else if (subcommand.name === "set") {
4228
+ subcommandName = "set <key> <value>";
4229
+ } else if (subcommand.name === "call") {
4230
+ subcommandName = "call <serviceName> <toolName>";
4231
+ }
4232
+ const cmd = commandGroup.command(subcommandName).description(subcommand.description);
4233
+ if (subcommand.options) {
4234
+ for (const option of subcommand.options) {
4235
+ cmd.option(option.flags, option.description, option.defaultValue);
4236
+ }
4237
+ }
4238
+ cmd.action(async (...args) => {
4239
+ try {
4240
+ const command = args[args.length - 1];
4241
+ const options = command.opts();
4242
+ await subcommand.execute(args.slice(0, -1), options);
4243
+ } catch (error) {
4244
+ ErrorHandler.handle(error);
4245
+ }
4246
+ });
4247
+ }
4248
+ commandGroup.action(async (...args) => {
4249
+ try {
4250
+ const command = args[args.length - 1];
4251
+ const options = command.opts();
4252
+ await handler.execute(args.slice(0, -1), options);
4253
+ } catch (error) {
4254
+ ErrorHandler.handle(error);
4255
+ }
4256
+ });
4257
+ } else {
4258
+ let commandName = handler.name;
4259
+ if (handler.name === "create") {
4260
+ commandName = "create <projectName>";
4261
+ }
4262
+ const command = program2.command(commandName).description(handler.description);
4263
+ if (handler.options) {
4264
+ for (const option of handler.options) {
4265
+ command.option(option.flags, option.description, option.defaultValue);
4266
+ }
4267
+ }
4268
+ command.action(async (...args) => {
4269
+ try {
4270
+ const command2 = args[args.length - 1];
4271
+ const options = command2.opts();
4272
+ await handler.execute(args.slice(0, -1), options);
4273
+ } catch (error) {
4274
+ ErrorHandler.handle(error);
4275
+ }
4276
+ });
4277
+ }
4278
+ }
4279
+ /**
4280
+ * 注册版本命令
4281
+ */
4282
+ registerVersionCommand(program2) {
4283
+ const versionUtils = this.container.get("versionUtils");
4284
+ program2.version(versionUtils.getVersion(), "-v, --version", "\u663E\u793A\u7248\u672C\u4FE1\u606F");
4285
+ program2.option("--info", "\u663E\u793A\u8BE6\u7EC6\u4FE1\u606F");
4286
+ program2.option("--version-info", "\u663E\u793A\u8BE6\u7EC6\u7248\u672C\u4FE1\u606F");
4287
+ }
4288
+ /**
4289
+ * 注册帮助命令
4290
+ */
4291
+ registerHelpCommand(program2) {
4292
+ program2.helpOption("-h, --help", "\u663E\u793A\u5E2E\u52A9\u4FE1\u606F").addHelpText(
4293
+ "after",
4294
+ `
4295
+ \u793A\u4F8B:
4296
+ xiaozhi init # \u521D\u59CB\u5316\u914D\u7F6E\u6587\u4EF6
4297
+ xiaozhi start # \u542F\u52A8\u670D\u52A1\uFF08\u5305\u542B Web UI\uFF09
4298
+ xiaozhi start -d # \u540E\u53F0\u542F\u52A8\u670D\u52A1
4299
+ xiaozhi start -s 3000 # \u4EE5 MCP Server \u6A21\u5F0F\u542F\u52A8
4300
+ xiaozhi stop # \u505C\u6B62\u670D\u52A1
4301
+ xiaozhi status # \u68C0\u67E5\u670D\u52A1\u72B6\u6001
4302
+ xiaozhi restart -d # \u91CD\u542F\u670D\u52A1\uFF08\u540E\u53F0\u6A21\u5F0F\uFF09
4303
+ xiaozhi config mcpEndpoint <url> # \u8BBE\u7F6E MCP \u7AEF\u70B9
4304
+ xiaozhi create my-project # \u521B\u5EFA\u9879\u76EE
4305
+ xiaozhi mcp list # \u5217\u51FA MCP \u670D\u52A1
4306
+
4307
+ \u66F4\u591A\u4FE1\u606F\u8BF7\u8BBF\u95EE: https://github.com/your-org/xiaozhi-client
4308
+ `
4309
+ );
4310
+ }
4311
+ /**
4312
+ * 注册向后兼容的顶级服务命令
4313
+ */
4314
+ registerLegacyServiceCommands(program2, handlers) {
4315
+ const serviceHandler = handlers.find((h) => h.name === "service");
4316
+ if (!serviceHandler || !serviceHandler.subcommands) {
4317
+ return;
4318
+ }
4319
+ for (const subcommand of serviceHandler.subcommands) {
4320
+ const command = program2.command(subcommand.name).description(subcommand.description);
4321
+ if (subcommand.options) {
4322
+ for (const option of subcommand.options) {
4323
+ command.option(option.flags, option.description, option.defaultValue);
4324
+ }
4325
+ }
4326
+ command.action(async (...args) => {
4327
+ try {
4328
+ const command2 = args[args.length - 1];
4329
+ const options = command2.opts();
4330
+ await subcommand.execute(args.slice(0, -1), options);
4331
+ } catch (error) {
4332
+ ErrorHandler.handle(error);
4333
+ }
4334
+ });
4335
+ }
4336
+ }
4337
+ /**
4338
+ * 注册服务管理命令(稍后实现)
4339
+ */
4340
+ // private async registerServiceCommands(program: Command): Promise<void> {
4341
+ // const serviceCommand = this.container.get('serviceCommand');
4342
+ // // start 命令
4343
+ // program
4344
+ // .command('start')
4345
+ // .description('启动服务')
4346
+ // .option('-d, --daemon', '在后台运行服务')
4347
+ // .option('-u, --ui', '同时启动 Web UI 服务')
4348
+ // .option('-s, --server [port]', '以 MCP Server 模式启动 (可选指定端口,默认 3000)')
4349
+ // .option('--stdio', '以 stdio 模式运行 MCP Server (用于 Cursor 等客户端)')
4350
+ // .action(async (options) => {
4351
+ // await serviceCommand.start(options);
4352
+ // });
4353
+ // // stop 命令
4354
+ // program
4355
+ // .command('stop')
4356
+ // .description('停止服务')
4357
+ // .action(async () => {
4358
+ // await serviceCommand.stop();
4359
+ // });
4360
+ // // status 命令
4361
+ // program
4362
+ // .command('status')
4363
+ // .description('检查服务状态')
4364
+ // .action(async () => {
4365
+ // await serviceCommand.status();
4366
+ // });
4367
+ // // restart 命令
4368
+ // program
4369
+ // .command('restart')
4370
+ // .description('重启服务')
4371
+ // .option('-d, --daemon', '在后台运行服务')
4372
+ // .option('-u, --ui', '同时启动 Web UI 服务')
4373
+ // .action(async (options) => {
4374
+ // await serviceCommand.restart(options);
4375
+ // });
4376
+ // // attach 命令
4377
+ // program
4378
+ // .command('attach')
4379
+ // .description('连接到后台服务查看日志')
4380
+ // .action(async () => {
4381
+ // await serviceCommand.attach();
4382
+ // });
4383
+ // }
4384
+ /**
4385
+ * 注册配置管理命令(稍后实现)
4386
+ */
4387
+ // private async registerConfigCommands(program: Command): Promise<void> {
4388
+ // const configCommand = this.container.get('configCommand');
4389
+ // // init 命令
4390
+ // program
4391
+ // .command('init')
4392
+ // .description('初始化配置文件')
4393
+ // .option('-f, --format <format>', '配置文件格式 (json, json5, jsonc)', 'json')
4394
+ // .action(async (options) => {
4395
+ // await configCommand.init(options);
4396
+ // });
4397
+ // // config 命令
4398
+ // program
4399
+ // .command('config <key> [value]')
4400
+ // .description('查看或设置配置')
4401
+ // .action(async (key, value) => {
4402
+ // await configCommand.manage(key, value);
4403
+ // });
4404
+ // }
4405
+ /**
4406
+ * 注册项目管理命令(稍后实现)
4407
+ */
4408
+ // private async registerProjectCommands(program: Command): Promise<void> {
4409
+ // const projectCommand = this.container.get('projectCommand');
4410
+ // // create 命令
4411
+ // program
4412
+ // .command('create <projectName>')
4413
+ // .description('创建项目')
4414
+ // .option('-t, --template <templateName>', '使用指定模板创建项目')
4415
+ // .action(async (projectName, options) => {
4416
+ // await projectCommand.create(projectName, options);
4417
+ // });
4418
+ // }
4419
+ /**
4420
+ * 注册 MCP 管理命令(稍后实现)
4421
+ */
4422
+ // private async registerMcpCommands(program: Command): Promise<void> {
4423
+ // const mcpCommand = this.container.get('mcpCommand');
4424
+ // // mcp 命令组
4425
+ // const mcpGroup = program.command('mcp').description('MCP 服务和工具管理');
4426
+ // // mcp list 命令
4427
+ // mcpGroup
4428
+ // .command('list')
4429
+ // .description('列出 MCP 服务')
4430
+ // .option('--tools', '显示所有服务的工具列表')
4431
+ // .action(async (options) => {
4432
+ // await mcpCommand.list(options);
4433
+ // });
4434
+ // // mcp server 命令
4435
+ // mcpGroup
4436
+ // .command('server <serverName>')
4437
+ // .description('管理指定的 MCP 服务')
4438
+ // .action(async (serverName) => {
4439
+ // await mcpCommand.server(serverName);
4440
+ // });
4441
+ // // mcp tool 命令
4442
+ // mcpGroup
4443
+ // .command('tool <serverName> <toolName> <action>')
4444
+ // .description('管理 MCP 工具 (enable/disable)')
4445
+ // .action(async (serverName, toolName, action) => {
4446
+ // await mcpCommand.tool(serverName, toolName, action);
4447
+ // });
4448
+ // }
4449
+ };
4450
+
4451
+ // src/index.ts
4452
+ var program = new Command();
4453
+ async function initializeCLI() {
4454
+ try {
4455
+ const container = DIContainer.create();
4456
+ const commandRegistry = new CommandRegistry(container);
4457
+ await commandRegistry.registerCommands(program);
4458
+ program.name("xiaozhi").description("\u5C0F\u667A MCP \u5BA2\u6237\u7AEF").helpOption("-h, --help", "\u663E\u793A\u5E2E\u52A9\u4FE1\u606F");
4459
+ await program.parseAsync(process.argv);
4460
+ } catch (error) {
4461
+ ErrorHandler.handle(error);
4462
+ }
4463
+ }
4464
+ __name(initializeCLI, "initializeCLI");
4465
+ var scriptPath = process.argv[1].replace(/\\/g, "/");
4466
+ var isMainModule = import.meta.url === `file:///${scriptPath}` || import.meta.url === `file://${scriptPath}`;
4467
+ if (isMainModule) {
4468
+ initializeCLI();
4469
+ }
4470
+ export {
4471
+ initializeCLI
4472
+ };
4473
+ //# sourceMappingURL=index.js.map