@xiaozhi-client/cli 1.10.8-beta.9 → 2.0.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -42,15 +42,15 @@ packages/cli/
42
42
 
43
43
  - **入口点**:`src/index.ts` → `../../dist/cli/index.js`
44
44
  - **格式**:ESM (ES Modules)
45
- - **目标**:Node.js 18+
45
+ - **目标**:Node.js 20+
46
46
  - **打包工具**:tsup (esbuild)
47
47
 
48
48
  ## 外部依赖
49
49
 
50
- CLI 包通过 external 配置引用 backend 模块:
50
+ CLI 包通过 external 配置引用 backend 模块和 workspace 包:
51
51
 
52
52
  - `@/WebServer` → `dist/backend/WebServer.js` (通过 WebServerLauncher)
53
- - `@/lib/config/manager` → `dist/backend/lib/config/manager.js`
53
+ - `@xiaozhi-client/config` → `dist/config/index.js` (配置管理)
54
54
 
55
55
  ## 导入方式
56
56
 
@@ -62,11 +62,14 @@ import { DIContainer } from "./Container";
62
62
  import { CommandRegistry } from "./commands/index";
63
63
  ```
64
64
 
65
- ### 外部依赖(使用路径别名)
65
+ ### 外部依赖(使用 workspace 包)
66
66
 
67
67
  ```typescript
68
- // 引用 backend 模块
69
- import { configManager } from "@/lib/config/manager";
68
+ // 引用配置管理(从 workspace 包导入)
69
+ import { configManager } from "@xiaozhi-client/config";
70
+
71
+ // 引用 WebServer(使用路径别名,运行时解析为 backend 模块)
72
+ import { WebServer } from "@/WebServer";
70
73
  ```
71
74
 
72
75
  ## 开发命令
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiaozhi-client/cli",
3
- "version": "1.10.8-beta.9",
3
+ "version": "2.0.0-beta.4",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -22,10 +22,11 @@
22
22
  "commander": "^14.0.0",
23
23
  "consola": "^3.4.2",
24
24
  "ora": "^8.2.0",
25
- "@xiaozhi-client/config": "1.10.8-beta.9"
25
+ "@xiaozhi-client/config": "2.0.0-beta.4"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^24.3.0",
29
+ "tsup": "^8.5.0",
29
30
  "typescript": "^5.9.2"
30
31
  },
31
32
  "scripts": {
package/src/Container.ts CHANGED
@@ -166,40 +166,6 @@ export class DIContainer implements IDIContainer {
166
166
  return new TemplateManagerModule.TemplateManagerImpl();
167
167
  });
168
168
 
169
- // 注册命令层(稍后在命令层实现时添加)
170
- // container.register('serviceCommand', () => {
171
- // const { ServiceCommand } = require('./commands/ServiceCommand.js');
172
- // const serviceManager = container.get('serviceManager');
173
- // const processManager = container.get('processManager');
174
- // return new ServiceCommand(serviceManager, processManager);
175
- // });
176
-
177
- // container.register('configCommand', () => {
178
- // const { ConfigCommand } = require('./commands/ConfigCommand.js');
179
- // const configManager = container.get('configManager');
180
- // const validation = container.get('validation');
181
- // return new ConfigCommand(configManager, validation);
182
- // });
183
-
184
- // container.register('projectCommand', () => {
185
- // const { ProjectCommand } = require('./commands/ProjectCommand.js');
186
- // const templateManager = container.get('templateManager');
187
- // const fileUtils = container.get('fileUtils');
188
- // return new ProjectCommand(templateManager, fileUtils);
189
- // });
190
-
191
- // container.register('mcpCommand', () => {
192
- // const { McpCommand } = require('./commands/McpCommand.js');
193
- // return new McpCommand();
194
- // });
195
-
196
- // container.register('infoCommand', () => {
197
- // const { InfoCommand } = require('./commands/InfoCommand.js');
198
- // const versionUtils = container.get('versionUtils');
199
- // const platformUtils = container.get('platformUtils');
200
- // return new InfoCommand(versionUtils, platformUtils);
201
- // });
202
-
203
169
  return container;
204
170
  }
205
171
  }
@@ -7,6 +7,10 @@ import chalk from "chalk";
7
7
  import ora from "ora";
8
8
  import type { SubCommand } from "../interfaces/Command";
9
9
  import { BaseCommandHandler } from "../interfaces/Command";
10
+ import type {
11
+ CommandArguments,
12
+ CommandOptions,
13
+ } from "../interfaces/CommandTypes";
10
14
  import type { IDIContainer } from "../interfaces/Config";
11
15
 
12
16
  /**
@@ -27,14 +31,14 @@ export class ConfigCommandHandler extends BaseCommandHandler {
27
31
  defaultValue: "json",
28
32
  },
29
33
  ],
30
- execute: async (args: any[], options: any) => {
34
+ execute: async (args: CommandArguments, options: CommandOptions) => {
31
35
  await this.handleInit(options);
32
36
  },
33
37
  },
34
38
  {
35
39
  name: "get",
36
40
  description: "查看配置值",
37
- execute: async (args: any[], options: any) => {
41
+ execute: async (args: CommandArguments, options: CommandOptions) => {
38
42
  this.validateArgs(args, 1);
39
43
  await this.handleGet(args[0]);
40
44
  },
@@ -42,7 +46,7 @@ export class ConfigCommandHandler extends BaseCommandHandler {
42
46
  {
43
47
  name: "set",
44
48
  description: "设置配置值",
45
- execute: async (args: any[], options: any) => {
49
+ execute: async (args: CommandArguments, options: CommandOptions) => {
46
50
  this.validateArgs(args, 2);
47
51
  await this.handleSet(args[0], args[1]);
48
52
  },
@@ -56,14 +60,17 @@ export class ConfigCommandHandler extends BaseCommandHandler {
56
60
  /**
57
61
  * 主命令执行(显示帮助)
58
62
  */
59
- async execute(args: any[], options: any): Promise<void> {
63
+ override async execute(
64
+ args: CommandArguments,
65
+ options: CommandOptions
66
+ ): Promise<void> {
60
67
  console.log("配置管理命令。使用 --help 查看可用的子命令。");
61
68
  }
62
69
 
63
70
  /**
64
71
  * 处理初始化命令
65
72
  */
66
- private async handleInit(options: any): Promise<void> {
73
+ private async handleInit(options: CommandOptions): Promise<void> {
67
74
  const spinner = ora("初始化配置...").start();
68
75
 
69
76
  try {
@@ -6,6 +6,10 @@ import chalk from "chalk";
6
6
  import ora from "ora";
7
7
  import type { SubCommand } from "../interfaces/Command";
8
8
  import { BaseCommandHandler } from "../interfaces/Command";
9
+ import type {
10
+ CommandArguments,
11
+ CommandOptions,
12
+ } from "../interfaces/CommandTypes";
9
13
  import type { IDIContainer } from "../interfaces/Config";
10
14
 
11
15
  /**
@@ -19,14 +23,14 @@ export class EndpointCommandHandler extends BaseCommandHandler {
19
23
  {
20
24
  name: "list",
21
25
  description: "列出所有 MCP 端点",
22
- execute: async (args: any[], options: any) => {
26
+ execute: async (args: CommandArguments, options: CommandOptions) => {
23
27
  await this.handleList();
24
28
  },
25
29
  },
26
30
  {
27
31
  name: "add",
28
32
  description: "添加新的 MCP 端点",
29
- execute: async (args: any[], options: any) => {
33
+ execute: async (args: CommandArguments, options: CommandOptions) => {
30
34
  this.validateArgs(args, 1);
31
35
  await this.handleAdd(args[0]);
32
36
  },
@@ -34,7 +38,7 @@ export class EndpointCommandHandler extends BaseCommandHandler {
34
38
  {
35
39
  name: "remove",
36
40
  description: "移除指定的 MCP 端点",
37
- execute: async (args: any[], options: any) => {
41
+ execute: async (args: CommandArguments, options: CommandOptions) => {
38
42
  this.validateArgs(args, 1);
39
43
  await this.handleRemove(args[0]);
40
44
  },
@@ -42,7 +46,7 @@ export class EndpointCommandHandler extends BaseCommandHandler {
42
46
  {
43
47
  name: "set",
44
48
  description: "设置 MCP 端点(可以是单个或多个)",
45
- execute: async (args: any[], options: any) => {
49
+ execute: async (args: CommandArguments, options: CommandOptions) => {
46
50
  this.validateArgs(args, 1);
47
51
  await this.handleSet(args);
48
52
  },
@@ -56,7 +60,10 @@ export class EndpointCommandHandler extends BaseCommandHandler {
56
60
  /**
57
61
  * 主命令执行(显示帮助)
58
62
  */
59
- async execute(args: any[], options: any): Promise<void> {
63
+ override async execute(
64
+ args: CommandArguments,
65
+ options: CommandOptions
66
+ ): Promise<void> {
60
67
  console.log("MCP 端点管理命令。使用 --help 查看可用的子命令。");
61
68
  }
62
69
 
@@ -7,6 +7,10 @@ import chalk from "chalk";
7
7
  import ora from "ora";
8
8
  import type { CommandOption } from "../interfaces/Command";
9
9
  import { BaseCommandHandler } from "../interfaces/Command";
10
+ import type {
11
+ CommandArguments,
12
+ CommandOptions,
13
+ } from "../interfaces/CommandTypes";
10
14
  import type { IDIContainer } from "../interfaces/Config";
11
15
 
12
16
  /**
@@ -30,7 +34,10 @@ export class ProjectCommandHandler extends BaseCommandHandler {
30
34
  /**
31
35
  * 执行创建项目命令
32
36
  */
33
- async execute(args: any[], options: any): Promise<void> {
37
+ override async execute(
38
+ args: CommandArguments,
39
+ options: CommandOptions
40
+ ): Promise<void> {
34
41
  this.validateArgs(args, 1);
35
42
  const projectName = args[0];
36
43
 
@@ -42,7 +49,7 @@ export class ProjectCommandHandler extends BaseCommandHandler {
42
49
  */
43
50
  protected async handleCreate(
44
51
  projectName: string,
45
- options: any
52
+ options: CommandOptions
46
53
  ): Promise<void> {
47
54
  const spinner = ora("初始化项目...").start();
48
55
 
@@ -5,6 +5,10 @@
5
5
  import consola from "consola";
6
6
  import type { SubCommand } from "../interfaces/Command";
7
7
  import { BaseCommandHandler } from "../interfaces/Command";
8
+ import type {
9
+ CommandArguments,
10
+ CommandOptions,
11
+ } from "../interfaces/CommandTypes";
8
12
  import type { IDIContainer } from "../interfaces/Config";
9
13
 
10
14
  /**
@@ -26,21 +30,21 @@ export class ServiceCommandHandler extends BaseCommandHandler {
26
30
  description: "以 stdio 模式运行 MCP Server (用于 Cursor 等客户端)",
27
31
  },
28
32
  ],
29
- execute: async (args: any[], options: any) => {
33
+ execute: async (args: CommandArguments, options: CommandOptions) => {
30
34
  await this.handleStart(options);
31
35
  },
32
36
  },
33
37
  {
34
38
  name: "stop",
35
39
  description: "停止服务",
36
- execute: async (args: any[], options: any) => {
40
+ execute: async (args: CommandArguments, options: CommandOptions) => {
37
41
  await this.handleStop();
38
42
  },
39
43
  },
40
44
  {
41
45
  name: "status",
42
46
  description: "检查服务状态",
43
- execute: async (args: any[], options: any) => {
47
+ execute: async (args: CommandArguments, options: CommandOptions) => {
44
48
  await this.handleStatus();
45
49
  },
46
50
  },
@@ -48,14 +52,14 @@ export class ServiceCommandHandler extends BaseCommandHandler {
48
52
  name: "restart",
49
53
  description: "重启服务",
50
54
  options: [{ flags: "-d, --daemon", description: "在后台运行服务" }],
51
- execute: async (args: any[], options: any) => {
55
+ execute: async (args: CommandArguments, options: CommandOptions) => {
52
56
  await this.handleRestart(options);
53
57
  },
54
58
  },
55
59
  {
56
60
  name: "attach",
57
61
  description: "连接到后台服务查看日志",
58
- execute: async (args: any[], options: any) => {
62
+ execute: async (args: CommandArguments, options: CommandOptions) => {
59
63
  await this.handleAttach();
60
64
  },
61
65
  },
@@ -68,14 +72,17 @@ export class ServiceCommandHandler extends BaseCommandHandler {
68
72
  /**
69
73
  * 主命令执行(显示帮助)
70
74
  */
71
- async execute(args: any[], options: any): Promise<void> {
75
+ override async execute(
76
+ args: CommandArguments,
77
+ options: CommandOptions
78
+ ): Promise<void> {
72
79
  console.log("服务管理命令。使用 --help 查看可用的子命令。");
73
80
  }
74
81
 
75
82
  /**
76
83
  * 处理启动命令
77
84
  */
78
- private async handleStart(options: any): Promise<void> {
85
+ private async handleStart(options: CommandOptions): Promise<void> {
79
86
  try {
80
87
  // 处理--debug参数
81
88
  if (options.debug) {
@@ -138,7 +145,7 @@ export class ServiceCommandHandler extends BaseCommandHandler {
138
145
  /**
139
146
  * 处理重启命令
140
147
  */
141
- private async handleRestart(options: any): Promise<void> {
148
+ private async handleRestart(options: CommandOptions): Promise<void> {
142
149
  try {
143
150
  const serviceManager = this.getService<any>("serviceManager");
144
151
  await serviceManager.restart({
@@ -23,6 +23,28 @@ export class CommandRegistry implements ICommandRegistry {
23
23
  this.handlerFactory = new CommandHandlerFactory(container);
24
24
  }
25
25
 
26
+ /**
27
+ * 包装命令处理函数,统一处理错误
28
+ * @param handler 命令处理函数
29
+ * @returns 包装后的处理函数
30
+ */
31
+ private wrapCommandHandler(
32
+ handler: (args: string[], options: Record<string, unknown>) => Promise<void>
33
+ ) {
34
+ return async (...args: unknown[]) => {
35
+ try {
36
+ // Commander.js 传递的最后一个参数是 Command 对象,包含选项值
37
+ const command = args[args.length - 1] as {
38
+ opts: () => Record<string, unknown>;
39
+ };
40
+ const options = command.opts();
41
+ await handler(args.slice(0, -1) as string[], options);
42
+ } catch (error) {
43
+ ErrorHandler.handle(error as Error);
44
+ }
45
+ };
46
+ }
47
+
26
48
  /**
27
49
  * 注册所有命令到 Commander 程序
28
50
  */
@@ -87,28 +109,19 @@ export class CommandRegistry implements ICommandRegistry {
87
109
  }
88
110
 
89
111
  // 设置子命令处理函数
90
- cmd.action(async (...args) => {
91
- try {
92
- // Commander.js 传递的最后一个参数是 Command 对象,包含选项值
93
- const command = args[args.length - 1];
94
- const options = command.opts(); // 获取解析后的选项
95
- await subcommand.execute(args.slice(0, -1), options);
96
- } catch (error) {
97
- ErrorHandler.handle(error as Error);
98
- }
99
- });
112
+ cmd.action(
113
+ this.wrapCommandHandler(async (args, options) => {
114
+ await subcommand.execute(args, options);
115
+ })
116
+ );
100
117
  }
101
118
 
102
119
  // 设置主命令的默认行为
103
- commandGroup.action(async (...args) => {
104
- try {
105
- const command = args[args.length - 1];
106
- const options = command.opts(); // 获取解析后的选项
107
- await handler.execute(args.slice(0, -1), options);
108
- } catch (error) {
109
- ErrorHandler.handle(error as Error);
110
- }
111
- });
120
+ commandGroup.action(
121
+ this.wrapCommandHandler(async (args, options) => {
122
+ await handler.execute(args, options);
123
+ })
124
+ );
112
125
  } else {
113
126
  // 没有子命令,注册为普通命令
114
127
  let commandName = handler.name;
@@ -130,15 +143,11 @@ export class CommandRegistry implements ICommandRegistry {
130
143
  }
131
144
 
132
145
  // 设置主命令处理函数
133
- command.action(async (...args) => {
134
- try {
135
- const command = args[args.length - 1];
136
- const options = command.opts(); // 获取解析后的选项
137
- await handler.execute(args.slice(0, -1), options);
138
- } catch (error) {
139
- ErrorHandler.handle(error as Error);
140
- }
141
- });
146
+ command.action(
147
+ this.wrapCommandHandler(async (args, options) => {
148
+ await handler.execute(args, options);
149
+ })
150
+ );
142
151
  }
143
152
  }
144
153
 
@@ -208,16 +217,11 @@ export class CommandRegistry implements ICommandRegistry {
208
217
  }
209
218
 
210
219
  // 设置命令处理函数
211
- command.action(async (...args) => {
212
- try {
213
- // Commander.js 传递的最后一个参数是 Command 对象,包含选项值
214
- const command = args[args.length - 1];
215
- const options = command.opts(); // 获取解析后的选项
216
- await subcommand.execute(args.slice(0, -1), options);
217
- } catch (error) {
218
- ErrorHandler.handle(error as Error);
219
- }
220
- });
220
+ command.action(
221
+ this.wrapCommandHandler(async (args, options) => {
222
+ await subcommand.execute(args, options);
223
+ })
224
+ );
221
225
  }
222
226
  }
223
227
 
@@ -66,6 +66,23 @@ export class ErrorHandler {
66
66
  }
67
67
  }
68
68
 
69
+ /**
70
+ * 错误处理包装器
71
+ */
72
+ private static wrapError(error: unknown, context: string): CLIError {
73
+ if (error instanceof CLIError) {
74
+ return error;
75
+ }
76
+ if (error instanceof Error) {
77
+ return new CLIError(
78
+ `${context}失败: ${error.message}`,
79
+ "OPERATION_FAILED",
80
+ 1
81
+ );
82
+ }
83
+ return new CLIError(`${context}失败: 未知错误`, "OPERATION_FAILED", 1);
84
+ }
85
+
69
86
  /**
70
87
  * 异步操作错误处理包装器
71
88
  */
@@ -76,17 +93,7 @@ export class ErrorHandler {
76
93
  try {
77
94
  return await operation();
78
95
  } catch (error) {
79
- if (error instanceof CLIError) {
80
- throw error;
81
- }
82
- if (error instanceof Error) {
83
- throw new CLIError(
84
- `${context}失败: ${error.message}`,
85
- "OPERATION_FAILED",
86
- 1
87
- );
88
- }
89
- throw new CLIError(`${context}失败: 未知错误`, "OPERATION_FAILED", 1);
96
+ throw ErrorHandler.wrapError(error, context);
90
97
  }
91
98
  }
92
99
 
@@ -97,17 +104,7 @@ export class ErrorHandler {
97
104
  try {
98
105
  return operation();
99
106
  } catch (error) {
100
- if (error instanceof CLIError) {
101
- throw error;
102
- }
103
- if (error instanceof Error) {
104
- throw new CLIError(
105
- `${context}失败: ${error.message}`,
106
- "OPERATION_FAILED",
107
- 1
108
- );
109
- }
110
- throw new CLIError(`${context}失败: 未知错误`, "OPERATION_FAILED", 1);
107
+ throw ErrorHandler.wrapError(error, context);
111
108
  }
112
109
  }
113
110
 
package/src/global.d.ts CHANGED
@@ -1,3 +1,11 @@
1
+ /**
2
+ * CLI 全局类型声明
3
+ *
4
+ * 扩展 Node.js 全局类型,包含以下内容:
5
+ * - Express 相关类型
6
+ * - WebSocket 相关类型
7
+ * - 环境变量类型扩展
8
+ */
1
9
  /// <reference types="node" />
2
10
  /// <reference types="express" />
3
11
  /// <reference types="ws" />
@@ -80,20 +80,42 @@ export interface DaemonManager {
80
80
  stopDaemon(): Promise<void>;
81
81
  }
82
82
 
83
+ /**
84
+ * 模板信息接口
85
+ */
86
+ export interface TemplateInfo {
87
+ name: string;
88
+ path: string;
89
+ description?: string;
90
+ version?: string;
91
+ author?: string;
92
+ files: string[];
93
+ }
94
+
95
+ /**
96
+ * 模板创建选项
97
+ */
98
+ export interface TemplateCreateOptions {
99
+ templateName?: string;
100
+ targetPath: string;
101
+ projectName: string;
102
+ variables?: Record<string, string>;
103
+ }
104
+
83
105
  /**
84
106
  * 模板管理器接口
85
107
  */
86
108
  export interface TemplateManager {
87
109
  /** 获取可用模板列表 */
88
- getAvailableTemplates(): Promise<any[]>;
110
+ getAvailableTemplates(): Promise<TemplateInfo[]>;
89
111
  /** 复制模板到目标目录 */
90
112
  copyTemplate(templateName: string, targetPath: string): Promise<void>;
91
113
  /** 验证模板是否存在 */
92
114
  validateTemplate(templateName: string): Promise<boolean>;
93
115
  /** 获取模板信息 */
94
- getTemplateInfo(templateName: string): Promise<any | null>;
116
+ getTemplateInfo(templateName: string): Promise<TemplateInfo | null>;
95
117
  /** 创建项目 */
96
- createProject(options: any): Promise<void>;
118
+ createProject(options: TemplateCreateOptions): Promise<void>;
97
119
  /** 清除模板缓存 */
98
120
  clearCache(): void;
99
121
  }
@@ -7,6 +7,7 @@ import { spawn } from "node:child_process";
7
7
  import fs from "node:fs";
8
8
  import type { WebServer } from "@/WebServer";
9
9
  import consola from "consola";
10
+ import { RETRY_CONSTANTS } from "../Constants";
10
11
  import { ProcessError, ServiceError } from "../errors/index";
11
12
  import type {
12
13
  DaemonManager as IDaemonManager,
@@ -116,7 +117,9 @@ export class DaemonManagerImpl implements IDaemonManager {
116
117
  if (status.running) {
117
118
  await this.stopDaemon();
118
119
  // 等待一下确保完全停止
119
- await new Promise((resolve) => setTimeout(resolve, 1000));
120
+ await new Promise((resolve) =>
121
+ setTimeout(resolve, RETRY_CONSTANTS.DEFAULT_INTERVAL)
122
+ );
120
123
  }
121
124
 
122
125
  // 重新启动守护进程
@@ -151,7 +154,7 @@ export class DaemonManagerImpl implements IDaemonManager {
151
154
  const tail = spawn(command, args, { stdio: "inherit" });
152
155
 
153
156
  // 处理中断信号
154
- process.on("SIGINT", () => {
157
+ process.once("SIGINT", () => {
155
158
  console.log("\n断开连接,服务继续在后台运行");
156
159
  tail.kill();
157
160
  process.exit(0);
@@ -153,33 +153,7 @@ export class ProcessManagerImpl implements IProcessManager {
153
153
  */
154
154
  async gracefulKillProcess(pid: number): Promise<void> {
155
155
  try {
156
- // 尝试优雅停止
157
- process.kill(pid, "SIGTERM");
158
-
159
- // 等待进程停止
160
- let attempts = 0;
161
- const maxAttempts = 30; // 3秒超时
162
-
163
- while (attempts < maxAttempts) {
164
- await new Promise((resolve) => setTimeout(resolve, 100));
165
-
166
- try {
167
- process.kill(pid, 0);
168
- attempts++;
169
- } catch {
170
- // 进程已停止
171
- return;
172
- }
173
- }
174
-
175
- // 如果还在运行,强制停止
176
- try {
177
- process.kill(pid, 0);
178
- process.kill(pid, "SIGKILL");
179
- await new Promise((resolve) => setTimeout(resolve, 500));
180
- } catch {
181
- // 进程已停止
182
- }
156
+ await PlatformUtils.killProcess(pid, "SIGTERM");
183
157
  } catch (error) {
184
158
  throw new ProcessError(
185
159
  `无法停止进程: ${error instanceof Error ? error.message : String(error)}`,
@@ -5,6 +5,7 @@
5
5
  import type { ConfigManager } from "@xiaozhi-client/config";
6
6
  import { ConfigInitializer } from "@xiaozhi-client/config";
7
7
  import consola from "consola";
8
+ import { RETRY_CONSTANTS } from "../Constants";
8
9
  import { ConfigError, ServiceError } from "../errors/index";
9
10
  import type {
10
11
  ServiceManager as IServiceManager,
@@ -51,7 +52,9 @@ export class ServiceManagerImpl implements IServiceManager {
51
52
  this.processManager.cleanupPidFile();
52
53
 
53
54
  // 等待一下确保完全停止
54
- await new Promise((resolve) => setTimeout(resolve, 1000));
55
+ await new Promise((resolve) =>
56
+ setTimeout(resolve, RETRY_CONSTANTS.DEFAULT_INTERVAL)
57
+ );
55
58
 
56
59
  consola.success("现有服务已停止,正在启动新服务...");
57
60
  } catch (stopError) {
@@ -127,7 +130,9 @@ export class ServiceManagerImpl implements IServiceManager {
127
130
  if (status.running) {
128
131
  await this.stop();
129
132
  // 等待一下确保完全停止
130
- await new Promise((resolve) => setTimeout(resolve, 1000));
133
+ await new Promise((resolve) =>
134
+ setTimeout(resolve, RETRY_CONSTANTS.DEFAULT_INTERVAL)
135
+ );
131
136
  }
132
137
 
133
138
  // 重新启动服务
@@ -273,8 +278,8 @@ export class ServiceManagerImpl implements IServiceManager {
273
278
  process.exit(0);
274
279
  };
275
280
 
276
- process.on("SIGINT", cleanup);
277
- process.on("SIGTERM", cleanup);
281
+ process.once("SIGINT", cleanup);
282
+ process.once("SIGTERM", cleanup);
278
283
 
279
284
  await server.start();
280
285
  }
@@ -333,8 +338,8 @@ export class ServiceManagerImpl implements IServiceManager {
333
338
  process.exit(0);
334
339
  };
335
340
 
336
- process.on("SIGINT", cleanup);
337
- process.on("SIGTERM", cleanup);
341
+ process.once("SIGINT", cleanup);
342
+ process.once("SIGTERM", cleanup);
338
343
 
339
344
  // 保存 PID 信息
340
345
  this.processManager.savePidInfo(process.pid, "foreground");
@@ -5,32 +5,17 @@
5
5
  import fs from "node:fs";
6
6
  import path from "node:path";
7
7
  import { FileError, ValidationError } from "../errors/index";
8
- import type { TemplateManager as ITemplateManager } from "../interfaces/Service";
8
+ import type {
9
+ TemplateManager as ITemplateManager,
10
+ TemplateCreateOptions,
11
+ TemplateInfo,
12
+ } from "../interfaces/Service";
9
13
  import { FileUtils } from "../utils/FileUtils";
10
14
  import { PathUtils } from "../utils/PathUtils";
11
15
  import { Validation } from "../utils/Validation";
12
16
 
13
- /**
14
- * 模板信息接口
15
- */
16
- export interface TemplateInfo {
17
- name: string;
18
- path: string;
19
- description?: string;
20
- version?: string;
21
- author?: string;
22
- files: string[];
23
- }
24
-
25
- /**
26
- * 模板创建选项
27
- */
28
- export interface TemplateCreateOptions {
29
- templateName?: string;
30
- targetPath: string;
31
- projectName: string;
32
- variables?: Record<string, string>;
33
- }
17
+ // 重新导出类型以保持向后兼容
18
+ export type { TemplateInfo, TemplateCreateOptions };
34
19
 
35
20
  /**
36
21
  * 模板管理器实现
@@ -139,43 +139,23 @@ describe("ProcessManagerImpl", () => {
139
139
  });
140
140
 
141
141
  describe("gracefulKillProcess", () => {
142
- let originalKill: typeof process.kill;
143
-
144
- beforeEach(() => {
145
- originalKill = process.kill;
146
- process.kill = vi.fn();
147
- });
148
-
149
- afterEach(() => {
150
- process.kill = originalKill;
151
- });
152
-
153
- it("should gracefully kill process", async () => {
154
- let killCallCount = 0;
155
- (process.kill as any).mockImplementation((pid: number, signal: any) => {
156
- killCallCount++;
157
- if (killCallCount > 1) {
158
- throw new Error("ESRCH"); // Process stopped
159
- }
160
- });
142
+ it("should delegate to PlatformUtils with SIGTERM signal", async () => {
143
+ mockPlatformUtils.killProcess.mockResolvedValue();
161
144
 
162
145
  await processManager.gracefulKillProcess(1234);
163
146
 
164
- expect(process.kill).toHaveBeenCalledWith(1234, "SIGTERM");
147
+ expect(mockPlatformUtils.killProcess).toHaveBeenCalledWith(
148
+ 1234,
149
+ "SIGTERM"
150
+ );
165
151
  });
166
152
 
167
- it("should force kill if graceful kill fails", async () => {
168
- (process.kill as any).mockImplementation((pid: number, signal: any) => {
169
- if (signal === "SIGKILL") {
170
- throw new Error("ESRCH"); // Process stopped
171
- }
172
- // Process still running for SIGTERM and signal 0
173
- });
174
-
175
- await processManager.gracefulKillProcess(1234);
153
+ it("should throw ProcessError when kill fails", async () => {
154
+ mockPlatformUtils.killProcess.mockRejectedValue(new Error("Kill failed"));
176
155
 
177
- expect(process.kill).toHaveBeenCalledWith(1234, "SIGTERM");
178
- expect(process.kill).toHaveBeenCalledWith(1234, "SIGKILL");
156
+ await expect(processManager.gracefulKillProcess(1234)).rejects.toThrow(
157
+ "无法停止进程"
158
+ );
179
159
  });
180
160
  });
181
161
 
@@ -74,28 +74,6 @@ vi.mock("../../utils/PathUtils.js", () => ({
74
74
  },
75
75
  }));
76
76
 
77
- // Mock ConfigManager for WebServer
78
- vi.mock("@/lib/config/manager.js", () => {
79
- const mockConfig = {
80
- mcpEndpoint: "ws://localhost:3000",
81
- mcpServers: {},
82
- webServer: { port: 9999 },
83
- };
84
- const mockConfigManager = {
85
- configExists: vi.fn().mockReturnValue(true),
86
- getConfig: vi.fn().mockReturnValue(mockConfig),
87
- loadConfig: vi.fn().mockResolvedValue(mockConfig),
88
- getToolCallLogConfig: vi.fn().mockReturnValue({ enabled: false }),
89
- getMcpServers: vi.fn().mockReturnValue({}),
90
- getMcpEndpoint: vi.fn().mockReturnValue("ws://localhost:3000"),
91
- getConfigDir: vi.fn().mockReturnValue("/mock/config"),
92
- };
93
- return {
94
- configManager: mockConfigManager,
95
- ConfigManager: vi.fn().mockImplementation(() => mockConfigManager),
96
- };
97
- });
98
-
99
77
  // Mock fs
100
78
  vi.mock("node:fs", () => {
101
79
  const mockExistsSync = vi.fn().mockReturnValue(true);
@@ -1,48 +1,38 @@
1
1
  /**
2
2
  * Backend 模块类型声明
3
- * 使用 any 类型避免递归解析 backend 代码
3
+ *
4
+ * 这些声明用于 CLI 包中引用 Backend 模块的类型
5
+ * 避免递归解析 backend 代码,同时提供类型安全
4
6
  */
5
7
 
6
- declare module "@/lib/config/manager" {
7
- export interface LocalMCPServerConfig {
8
- command: string;
9
- args: string[];
10
- env?: Record<string, string>;
11
- }
12
-
13
- export interface MCPServerConfig {
14
- type?: string;
15
- url?: string;
16
- command?: string;
17
- args?: string[];
18
- headers?: Record<string, string>;
19
- }
20
-
21
- export const configManager: any;
22
- }
23
-
24
- declare module "@/lib/config/manager.js" {
25
- export interface LocalMCPServerConfig {
26
- command: string;
27
- args: string[];
28
- env?: Record<string, string>;
29
- }
30
-
31
- export interface MCPServerConfig {
32
- type?: string;
33
- url?: string;
34
- command?: string;
35
- args?: string[];
36
- headers?: Record<string, string>;
37
- }
38
-
39
- export const configManager: any;
40
- }
41
-
42
- declare module "@/WebServer" {
43
- export class WebServer {}
44
- }
45
-
8
+ /**
9
+ * WebServer 类型声明
10
+ * 对应 apps/backend/WebServer.ts
11
+ */
46
12
  declare module "@/WebServer.js" {
47
- export class WebServer {}
13
+ /**
14
+ * WebServer - Web 服务器主控制器
15
+ */
16
+ export class WebServer {
17
+ /**
18
+ * 创建 WebServer 实例
19
+ * @param port - 可选的端口号,不指定则使用配置文件中的端口
20
+ */
21
+ constructor(port?: number);
22
+
23
+ /**
24
+ * 启动 Web 服务器
25
+ */
26
+ start(): Promise<void>;
27
+
28
+ /**
29
+ * 停止 Web 服务器
30
+ */
31
+ stop(): Promise<void>;
32
+
33
+ /**
34
+ * 销毁 WebServer 实例,清理所有资源
35
+ */
36
+ destroy(): void;
37
+ }
48
38
  }
@@ -37,7 +37,10 @@ export class Validation {
37
37
  /**
38
38
  * 验证必填字段
39
39
  */
40
- static validateRequired(value: any, fieldName: string): void {
40
+ static validateRequired(
41
+ value: string | number | boolean | object | null | undefined,
42
+ fieldName: string
43
+ ): void {
41
44
  if (value === undefined || value === null || value === "") {
42
45
  throw ValidationError.requiredField(fieldName);
43
46
  }
@@ -169,7 +172,7 @@ export class Validation {
169
172
  /**
170
173
  * 验证 JSON 格式
171
174
  */
172
- static validateJson(jsonString: string, fieldName = "json"): any {
175
+ static validateJson(jsonString: string, fieldName = "json"): unknown {
173
176
  try {
174
177
  return JSON.parse(jsonString);
175
178
  } catch (error) {
@@ -206,8 +209,8 @@ export class Validation {
206
209
  /**
207
210
  * 验证数组长度
208
211
  */
209
- static validateArrayLength(
210
- array: any[],
212
+ static validateArrayLength<T>(
213
+ array: T[],
211
214
  fieldName: string,
212
215
  options: { min?: number; max?: number } = {}
213
216
  ): void {
@@ -230,7 +233,7 @@ export class Validation {
230
233
  * 验证对象属性
231
234
  */
232
235
  static validateObjectProperties(
233
- obj: Record<string, any>,
236
+ obj: Record<string, unknown>,
234
237
  requiredProps: string[],
235
238
  fieldName = "object"
236
239
  ): void {
package/tsup.config.ts CHANGED
@@ -11,7 +11,7 @@ export default defineConfig({
11
11
  index: "src/index.ts",
12
12
  },
13
13
  format: ["esm"],
14
- target: "node18",
14
+ target: "node20",
15
15
  outDir: "../../dist/cli",
16
16
  clean: true,
17
17
  sourcemap: true,