@xiaozhi-client/cli 1.9.4-beta.10

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 (55) hide show
  1. package/README.md +98 -0
  2. package/package.json +31 -0
  3. package/project.json +75 -0
  4. package/src/Constants.ts +105 -0
  5. package/src/Container.ts +212 -0
  6. package/src/Types.ts +79 -0
  7. package/src/commands/CommandHandlerFactory.ts +98 -0
  8. package/src/commands/ConfigCommandHandler.ts +279 -0
  9. package/src/commands/EndpointCommandHandler.ts +158 -0
  10. package/src/commands/McpCommandHandler.ts +778 -0
  11. package/src/commands/ProjectCommandHandler.ts +254 -0
  12. package/src/commands/ServiceCommandHandler.ts +182 -0
  13. package/src/commands/__tests__/CommandHandlerFactory.test.ts +323 -0
  14. package/src/commands/__tests__/CommandRegistry.test.ts +287 -0
  15. package/src/commands/__tests__/ConfigCommandHandler.test.ts +844 -0
  16. package/src/commands/__tests__/EndpointCommandHandler.test.ts +426 -0
  17. package/src/commands/__tests__/McpCommandHandler.test.ts +753 -0
  18. package/src/commands/__tests__/ProjectCommandHandler.test.ts +230 -0
  19. package/src/commands/__tests__/ServiceCommands.integration.test.ts +408 -0
  20. package/src/commands/index.ts +351 -0
  21. package/src/errors/ErrorHandlers.ts +141 -0
  22. package/src/errors/ErrorMessages.ts +121 -0
  23. package/src/errors/__tests__/index.test.ts +186 -0
  24. package/src/errors/index.ts +163 -0
  25. package/src/global.d.ts +19 -0
  26. package/src/index.ts +53 -0
  27. package/src/interfaces/Command.ts +128 -0
  28. package/src/interfaces/CommandTypes.ts +95 -0
  29. package/src/interfaces/Config.ts +25 -0
  30. package/src/interfaces/Service.ts +99 -0
  31. package/src/services/DaemonManager.ts +318 -0
  32. package/src/services/ProcessManager.ts +235 -0
  33. package/src/services/ServiceManager.ts +319 -0
  34. package/src/services/TemplateManager.ts +382 -0
  35. package/src/services/__tests__/DaemonManager.test.ts +378 -0
  36. package/src/services/__tests__/DaemonMode.integration.test.ts +321 -0
  37. package/src/services/__tests__/ProcessManager.test.ts +296 -0
  38. package/src/services/__tests__/ServiceManager.test.ts +774 -0
  39. package/src/services/__tests__/TemplateManager.test.ts +337 -0
  40. package/src/types/backend.d.ts +48 -0
  41. package/src/utils/FileUtils.ts +320 -0
  42. package/src/utils/FormatUtils.ts +198 -0
  43. package/src/utils/PathUtils.ts +255 -0
  44. package/src/utils/PlatformUtils.ts +217 -0
  45. package/src/utils/Validation.ts +274 -0
  46. package/src/utils/VersionUtils.ts +141 -0
  47. package/src/utils/__tests__/FileUtils.test.ts +728 -0
  48. package/src/utils/__tests__/FormatUtils.test.ts +243 -0
  49. package/src/utils/__tests__/PathUtils.test.ts +1165 -0
  50. package/src/utils/__tests__/PlatformUtils.test.ts +723 -0
  51. package/src/utils/__tests__/Validation.test.ts +560 -0
  52. package/src/utils/__tests__/VersionUtils.test.ts +410 -0
  53. package/tsconfig.json +32 -0
  54. package/tsup.config.ts +100 -0
  55. package/vitest.config.ts +97 -0
@@ -0,0 +1,186 @@
1
+ /**
2
+ * 错误处理系统单元测试
3
+ */
4
+
5
+ import { describe, expect, it } from "vitest";
6
+ import { ERROR_CODES } from "../../Constants";
7
+ import {
8
+ CLIError,
9
+ ConfigError,
10
+ FileError,
11
+ ProcessError,
12
+ ServiceError,
13
+ ValidationError,
14
+ } from "../index";
15
+
16
+ describe("CLIError", () => {
17
+ it("should create basic CLI error", () => {
18
+ const error = new CLIError("Test error", "TEST_ERROR");
19
+
20
+ expect(error.message).toBe("Test error");
21
+ expect(error.code).toBe("TEST_ERROR");
22
+ expect(error.exitCode).toBe(1);
23
+ expect(error.name).toBe("CLIError");
24
+ });
25
+
26
+ it("should create CLI error with suggestions", () => {
27
+ const suggestions = ["Try this", "Or that"];
28
+ const error = CLIError.withSuggestions(
29
+ "Test error",
30
+ "TEST_ERROR",
31
+ suggestions
32
+ );
33
+
34
+ expect(error.suggestions).toEqual(suggestions);
35
+ });
36
+
37
+ it("should create CLI error with custom exit code", () => {
38
+ const error = new CLIError("Test error", "TEST_ERROR", 2);
39
+
40
+ expect(error.exitCode).toBe(2);
41
+ });
42
+ });
43
+
44
+ describe("ConfigError", () => {
45
+ it("should create config error", () => {
46
+ const error = new ConfigError("Config not found");
47
+
48
+ expect(error.message).toBe("Config not found");
49
+ expect(error.code).toBe(ERROR_CODES.CONFIG_ERROR);
50
+ expect(error.name).toBe("ConfigError");
51
+ });
52
+
53
+ it("should create config not found error", () => {
54
+ const error = ConfigError.configNotFound();
55
+
56
+ expect(error.message).toBe("配置文件不存在");
57
+ expect(error.suggestions).toContain('请运行 "xiaozhi init" 初始化配置文件');
58
+ });
59
+
60
+ it("should create invalid format error", () => {
61
+ const error = ConfigError.invalidFormat("xml");
62
+
63
+ expect(error.message).toBe("无效的配置文件格式: xml");
64
+ expect(error.suggestions).toContain("支持的格式: json, json5, jsonc");
65
+ });
66
+ });
67
+
68
+ describe("ServiceError", () => {
69
+ it("should create service error", () => {
70
+ const error = new ServiceError("Service failed");
71
+
72
+ expect(error.message).toBe("Service failed");
73
+ expect(error.code).toBe(ERROR_CODES.SERVICE_ERROR);
74
+ expect(error.name).toBe("ServiceError");
75
+ });
76
+
77
+ it("should create already running error", () => {
78
+ const error = ServiceError.alreadyRunning(1234);
79
+
80
+ expect(error.message).toBe("服务已经在运行 (PID: 1234)");
81
+ expect(error.suggestions).toContain('请先运行 "xiaozhi stop" 停止现有服务');
82
+ expect(error.suggestions).toContain('或者使用 "xiaozhi restart" 重启服务');
83
+ });
84
+
85
+ it("should create auto restarting error", () => {
86
+ const error = ServiceError.autoRestarting(1234);
87
+
88
+ expect(error.message).toBe(
89
+ "检测到服务已在运行 (PID: 1234),正在自动重启..."
90
+ );
91
+ expect(error.suggestions).toContain(
92
+ "如果不希望自动重启,请使用 xiaozhi stop 手动停止服务"
93
+ );
94
+ });
95
+
96
+ it("should create not running error", () => {
97
+ const error = ServiceError.notRunning();
98
+
99
+ expect(error.message).toBe("服务未运行");
100
+ expect(error.suggestions).toContain('请运行 "xiaozhi start" 启动服务');
101
+ });
102
+
103
+ it("should create start failed error", () => {
104
+ const error = ServiceError.startFailed("Port occupied");
105
+
106
+ expect(error.message).toBe("服务启动失败: Port occupied");
107
+ expect(error.suggestions).toContain("检查配置文件是否正确");
108
+ });
109
+ });
110
+
111
+ describe("ValidationError", () => {
112
+ it("should create validation error", () => {
113
+ const error = new ValidationError("Invalid value", "port");
114
+
115
+ expect(error.message).toBe("验证失败: port - Invalid value");
116
+ expect(error.code).toBe(ERROR_CODES.VALIDATION_ERROR);
117
+ expect(error.name).toBe("ValidationError");
118
+ });
119
+
120
+ it("should create invalid port error", () => {
121
+ const error = ValidationError.invalidPort(99999);
122
+
123
+ expect(error.message).toContain("端口号必须在 1-65535 范围内");
124
+ expect(error.message).toContain("99999");
125
+ });
126
+
127
+ it("should create required field error", () => {
128
+ const error = ValidationError.requiredField("name");
129
+
130
+ expect(error.message).toBe("验证失败: name - 必填字段不能为空");
131
+ });
132
+ });
133
+
134
+ describe("FileError", () => {
135
+ it("should create file error", () => {
136
+ const error = new FileError("File operation failed", "/path/to/file");
137
+
138
+ expect(error.message).toBe("File operation failed: /path/to/file");
139
+ expect(error.code).toBe(ERROR_CODES.FILE_ERROR);
140
+ expect(error.name).toBe("FileError");
141
+ });
142
+
143
+ it("should create file not found error", () => {
144
+ const error = FileError.notFound("/missing/file");
145
+
146
+ expect(error.message).toBe("文件不存在: /missing/file");
147
+ expect(error.suggestions).toContain("检查文件路径是否正确");
148
+ });
149
+
150
+ it("should create permission denied error", () => {
151
+ const error = FileError.permissionDenied("/protected/file");
152
+
153
+ expect(error.message).toBe("权限不足: /protected/file");
154
+ expect(error.suggestions).toContain("检查文件权限或使用管理员权限运行");
155
+ });
156
+
157
+ it("should create already exists error", () => {
158
+ const error = FileError.alreadyExists("/existing/file");
159
+
160
+ expect(error.message).toBe("文件已存在: /existing/file");
161
+ expect(error.suggestions).toContain("使用不同的文件名或删除现有文件");
162
+ });
163
+ });
164
+
165
+ describe("ProcessError", () => {
166
+ it("should create process error", () => {
167
+ const error = new ProcessError("Process failed", 1234);
168
+
169
+ expect(error.message).toBe("Process failed (PID: 1234)");
170
+ expect(error.code).toBe(ERROR_CODES.PROCESS_ERROR);
171
+ expect(error.name).toBe("ProcessError");
172
+ });
173
+
174
+ it("should create kill failed error", () => {
175
+ const error = ProcessError.killFailed(1234);
176
+
177
+ expect(error.message).toBe("无法终止进程 (PID: 1234)");
178
+ expect(error.suggestions).toContain("进程可能已经停止或权限不足");
179
+ });
180
+
181
+ it("should create not found error", () => {
182
+ const error = ProcessError.notFound(1234);
183
+
184
+ expect(error.message).toBe("进程不存在 (PID: 1234)");
185
+ });
186
+ });
@@ -0,0 +1,163 @@
1
+ /**
2
+ * 统一错误处理系统
3
+ */
4
+
5
+ import { ERROR_CODES } from "../Constants";
6
+
7
+ /**
8
+ * CLI 基础错误类
9
+ */
10
+ export class CLIError extends Error {
11
+ constructor(
12
+ message: string,
13
+ public code: string,
14
+ public exitCode = 1,
15
+ public suggestions?: string[]
16
+ ) {
17
+ super(message);
18
+ this.name = "CLIError";
19
+
20
+ // 确保错误堆栈正确显示
21
+ if (Error.captureStackTrace) {
22
+ Error.captureStackTrace(this, CLIError);
23
+ }
24
+ }
25
+
26
+ /**
27
+ * 创建带建议的错误
28
+ */
29
+ static withSuggestions(
30
+ message: string,
31
+ code: string,
32
+ suggestions: string[]
33
+ ): CLIError {
34
+ return new CLIError(message, code, 1, suggestions);
35
+ }
36
+ }
37
+
38
+ /**
39
+ * 配置错误
40
+ */
41
+ export class ConfigError extends CLIError {
42
+ constructor(message: string, suggestions?: string[]) {
43
+ super(message, ERROR_CODES.CONFIG_ERROR, 1, suggestions);
44
+ this.name = "ConfigError";
45
+ }
46
+
47
+ static configNotFound(): ConfigError {
48
+ return new ConfigError("配置文件不存在", [
49
+ '请运行 "xiaozhi init" 初始化配置文件',
50
+ ]);
51
+ }
52
+
53
+ static invalidFormat(format: string): ConfigError {
54
+ return new ConfigError(`无效的配置文件格式: ${format}`, [
55
+ "支持的格式: json, json5, jsonc",
56
+ ]);
57
+ }
58
+ }
59
+
60
+ /**
61
+ * 服务错误
62
+ */
63
+ export class ServiceError extends CLIError {
64
+ constructor(message: string, suggestions?: string[]) {
65
+ super(message, ERROR_CODES.SERVICE_ERROR, 1, suggestions);
66
+ this.name = "ServiceError";
67
+ }
68
+
69
+ static alreadyRunning(pid: number): ServiceError {
70
+ return new ServiceError(`服务已经在运行 (PID: ${pid})`, [
71
+ '请先运行 "xiaozhi stop" 停止现有服务',
72
+ '或者使用 "xiaozhi restart" 重启服务',
73
+ ]);
74
+ }
75
+
76
+ static autoRestarting(pid: number): ServiceError {
77
+ return new ServiceError(
78
+ `检测到服务已在运行 (PID: ${pid}),正在自动重启...`,
79
+ ["如果不希望自动重启,请使用 xiaozhi stop 手动停止服务"]
80
+ );
81
+ }
82
+
83
+ static notRunning(): ServiceError {
84
+ return new ServiceError("服务未运行", ['请运行 "xiaozhi start" 启动服务']);
85
+ }
86
+
87
+ static startFailed(reason: string): ServiceError {
88
+ return new ServiceError(`服务启动失败: ${reason}`, [
89
+ "检查配置文件是否正确",
90
+ "确保端口未被占用",
91
+ "查看日志文件获取详细信息",
92
+ ]);
93
+ }
94
+ }
95
+
96
+ /**
97
+ * 验证错误
98
+ */
99
+ export class ValidationError extends CLIError {
100
+ constructor(message: string, field: string) {
101
+ super(`验证失败: ${field} - ${message}`, ERROR_CODES.VALIDATION_ERROR, 1);
102
+ this.name = "ValidationError";
103
+ }
104
+
105
+ static invalidPort(port: number): ValidationError {
106
+ return new ValidationError(
107
+ `端口号必须在 1-65535 范围内,当前值: ${port}`,
108
+ "port"
109
+ );
110
+ }
111
+
112
+ static requiredField(field: string): ValidationError {
113
+ return new ValidationError("必填字段不能为空", field);
114
+ }
115
+ }
116
+
117
+ /**
118
+ * 文件操作错误
119
+ */
120
+ export class FileError extends CLIError {
121
+ constructor(message: string, filePath?: string, suggestions?: string[]) {
122
+ const fullMessage = filePath ? `${message}: ${filePath}` : message;
123
+ super(fullMessage, ERROR_CODES.FILE_ERROR, 1, suggestions);
124
+ this.name = "FileError";
125
+ }
126
+
127
+ static notFound(filePath: string): FileError {
128
+ return new FileError("文件不存在", filePath, ["检查文件路径是否正确"]);
129
+ }
130
+
131
+ static permissionDenied(filePath: string): FileError {
132
+ return new FileError("权限不足", filePath, [
133
+ "检查文件权限或使用管理员权限运行",
134
+ ]);
135
+ }
136
+
137
+ static alreadyExists(filePath: string): FileError {
138
+ return new FileError("文件已存在", filePath, [
139
+ "使用不同的文件名或删除现有文件",
140
+ ]);
141
+ }
142
+ }
143
+
144
+ /**
145
+ * 进程错误
146
+ */
147
+ export class ProcessError extends CLIError {
148
+ constructor(message: string, pid?: number, suggestions?: string[]) {
149
+ const fullMessage = pid ? `${message} (PID: ${pid})` : message;
150
+ super(fullMessage, ERROR_CODES.PROCESS_ERROR, 1, suggestions);
151
+ this.name = "ProcessError";
152
+ }
153
+
154
+ static killFailed(pid: number): ProcessError {
155
+ return new ProcessError("无法终止进程", pid, [
156
+ "进程可能已经停止或权限不足",
157
+ ]);
158
+ }
159
+
160
+ static notFound(pid: number): ProcessError {
161
+ return new ProcessError("进程不存在", pid);
162
+ }
163
+ }
@@ -0,0 +1,19 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="express" />
3
+ /// <reference types="ws" />
4
+ /// <reference types="semver" />
5
+ /// <reference types="node-fetch" />
6
+ /// <reference types="supertest" />
7
+
8
+ declare global {
9
+ namespace NodeJS {
10
+ interface ProcessEnv {
11
+ readonly NODE_ENV: string;
12
+ readonly XIAOZHI_CONFIG_DIR?: string;
13
+ readonly TMPDIR?: string;
14
+ readonly TEMP?: string;
15
+ }
16
+ }
17
+ }
18
+
19
+ export {};
package/src/index.ts ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * CLI 入口文件
5
+ * 负责初始化依赖注入容器和启动命令处理器
6
+ */
7
+
8
+ import { Command } from "commander";
9
+ import { DIContainer } from "./Container";
10
+ import { CommandRegistry } from "./commands/index";
11
+ import { ErrorHandler } from "./errors/ErrorHandlers";
12
+
13
+ const program = new Command();
14
+
15
+ /**
16
+ * 初始化 CLI 应用
17
+ */
18
+ async function initializeCLI(): Promise<void> {
19
+ try {
20
+ // 创建依赖注入容器
21
+ const container = DIContainer.create();
22
+
23
+ // 创建命令注册器
24
+ const commandRegistry = new CommandRegistry(container);
25
+
26
+ // 注册所有命令
27
+ await commandRegistry.registerCommands(program);
28
+
29
+ // 配置程序基本信息
30
+ program
31
+ .name("xiaozhi")
32
+ .description("小智 MCP 客户端")
33
+ .helpOption("-h, --help", "显示帮助信息");
34
+
35
+ // 解析命令行参数
36
+ await program.parseAsync(process.argv);
37
+ } catch (error) {
38
+ ErrorHandler.handle(error as Error);
39
+ }
40
+ }
41
+
42
+ // 启动 CLI 应用
43
+ // 使用更可靠的检测方法,兼容 Windows 路径
44
+ // 将路径转换为 URL 格式进行比较
45
+ const scriptPath = process.argv[1].replace(/\\/g, "/");
46
+ const isMainModule =
47
+ import.meta.url === `file:///${scriptPath}` ||
48
+ import.meta.url === `file://${scriptPath}`;
49
+ if (isMainModule) {
50
+ initializeCLI();
51
+ }
52
+
53
+ export { initializeCLI };
@@ -0,0 +1,128 @@
1
+ /**
2
+ * 命令接口定义
3
+ */
4
+
5
+ import type { Command } from "commander";
6
+ import type { CommandArguments, CommandOptions } from "./CommandTypes";
7
+ import type { IDIContainer } from "./Config";
8
+
9
+ /**
10
+ * 命令处理器接口
11
+ */
12
+ export interface CommandHandler {
13
+ /** 命令名称 */
14
+ name: string;
15
+ /** 命令描述 */
16
+ description: string;
17
+ /** 命令选项 */
18
+ options?: CommandOption[];
19
+ /** 子命令 */
20
+ subcommands?: SubCommand[];
21
+ /** 执行命令 */
22
+ execute(args: CommandArguments, options: CommandOptions): Promise<void>;
23
+ }
24
+
25
+ /**
26
+ * 命令选项接口
27
+ */
28
+ export interface CommandOption {
29
+ /** 选项标志 */
30
+ flags: string;
31
+ /** 选项描述 */
32
+ description: string;
33
+ /** 默认值(限制为 Commander.js 支持的类型) */
34
+ defaultValue?: string | boolean | string[];
35
+ }
36
+
37
+ /**
38
+ * 子命令接口
39
+ */
40
+ export interface SubCommand {
41
+ /** 子命令名称 */
42
+ name: string;
43
+ /** 子命令描述 */
44
+ description: string;
45
+ /** 子命令选项 */
46
+ options?: CommandOption[];
47
+ /** 执行子命令 */
48
+ execute(args: CommandArguments, options: CommandOptions): Promise<void>;
49
+ }
50
+
51
+ /**
52
+ * 命令执行上下文
53
+ */
54
+ export interface CommandContext {
55
+ /** DI 容器 */
56
+ container: IDIContainer;
57
+ /** 命令行参数 */
58
+ args: CommandArguments;
59
+ /** 命令选项 */
60
+ options: CommandOptions;
61
+ }
62
+
63
+ /**
64
+ * 基础命令处理器抽象类
65
+ */
66
+ export abstract class BaseCommandHandler implements CommandHandler {
67
+ abstract name: string;
68
+ abstract description: string;
69
+ options?: CommandOption[];
70
+ subcommands?: SubCommand[];
71
+
72
+ constructor(protected container: IDIContainer) {}
73
+
74
+ abstract execute(
75
+ args: CommandArguments,
76
+ options: CommandOptions
77
+ ): Promise<void>;
78
+
79
+ /**
80
+ * 获取服务实例
81
+ */
82
+ protected getService<T>(serviceName: string): T {
83
+ return this.container.get(serviceName) as T;
84
+ }
85
+
86
+ /**
87
+ * 处理错误
88
+ */
89
+ protected handleError(error: Error): void {
90
+ const errorHandler = this.getService<unknown>("errorHandler");
91
+ // 类型断言:errorHandler 应该有 handle 方法
92
+ const handler = errorHandler as { handle: (error: Error) => void };
93
+ handler.handle(error);
94
+ }
95
+
96
+ /**
97
+ * 验证参数
98
+ */
99
+ protected validateArgs(args: CommandArguments, expectedCount: number): void {
100
+ if (args.length < expectedCount) {
101
+ throw new Error(
102
+ `命令需要至少 ${expectedCount} 个参数,但只提供了 ${args.length} 个`
103
+ );
104
+ }
105
+ }
106
+ }
107
+
108
+ /**
109
+ * 命令注册器接口
110
+ */
111
+ export interface ICommandRegistry {
112
+ /** 注册所有命令到 Commander 程序 */
113
+ registerCommands(program: Command): Promise<void>;
114
+ /** 注册单个命令 */
115
+ registerCommand(program: Command, handler: CommandHandler): void;
116
+ /** 注册命令处理器 */
117
+ registerHandler(handler: CommandHandler): void;
118
+ }
119
+
120
+ /**
121
+ * 命令处理器工厂接口
122
+ */
123
+ export interface ICommandHandlerFactory {
124
+ /** 创建所有命令处理器 */
125
+ createHandlers(): CommandHandler[];
126
+ /** 创建指定类型的命令处理器 */
127
+ createHandler(type: string): CommandHandler;
128
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * CLI 命令相关类型定义
3
+ * 用于替代命令处理器中的 any 类型使用,提供类型安全保障
4
+ */
5
+
6
+ import type {
7
+ LocalMCPServerConfig,
8
+ MCPServerConfig,
9
+ } from "@xiaozhi-client/config";
10
+
11
+ // =========================
12
+ // 基础命令参数和选项类型
13
+ // =========================
14
+
15
+ /**
16
+ * 命令行参数类型
17
+ * 替代 any[] 类型
18
+ */
19
+ export type CommandArguments = string[];
20
+
21
+ /**
22
+ * 命令选项类型
23
+ * 替代 any 类型
24
+ */
25
+ export type CommandOptions = Record<string, unknown>;
26
+
27
+ // =========================
28
+ // 子命令具体选项类型
29
+ // =========================
30
+
31
+ /**
32
+ * list 子命令选项
33
+ */
34
+ export interface ListOptions {
35
+ /** 是否显示所有服务的工具列表 */
36
+ tools?: boolean;
37
+ }
38
+
39
+ /**
40
+ * call 子命令选项
41
+ */
42
+ export interface CallOptions {
43
+ /** 工具参数 (JSON 格式) */
44
+ args?: string;
45
+ }
46
+
47
+ // =========================
48
+ // 类型守卫函数
49
+ // =========================
50
+
51
+ /**
52
+ * 检查对象是否为本地 MCP 服务配置
53
+ * @param obj 待检查的对象
54
+ * @returns 是否为本地服务配置
55
+ */
56
+ export function isLocalMCPServerConfig(
57
+ obj: unknown
58
+ ): obj is LocalMCPServerConfig {
59
+ const config = obj as MCPServerConfig;
60
+ return (
61
+ typeof config === "object" &&
62
+ config !== null &&
63
+ "command" in config &&
64
+ "args" in config &&
65
+ typeof config.command === "string" &&
66
+ Array.isArray(config.args) &&
67
+ config.args.every((arg: unknown) => typeof arg === "string")
68
+ );
69
+ }
70
+
71
+ // =========================
72
+ // 类型化子命令接口
73
+ // =========================
74
+
75
+ /**
76
+ * 类型化子命令接口
77
+ * 提供类型安全的子命令定义
78
+ */
79
+ export interface TypedSubCommand<TOptions = CommandOptions> {
80
+ /** 子命令名称 */
81
+ name: string;
82
+ /** 子命令描述 */
83
+ description: string;
84
+ /** 子命令选项 */
85
+ options?: Array<{
86
+ /** 选项标志 */
87
+ flags: string;
88
+ /** 选项描述 */
89
+ description: string;
90
+ /** 默认值 */
91
+ defaultValue?: unknown;
92
+ }>;
93
+ /** 执行子命令 */
94
+ execute: (args: CommandArguments, options: TOptions) => Promise<void>;
95
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * 配置接口定义
3
+ */
4
+
5
+ /**
6
+ * 依赖注入容器接口
7
+ */
8
+ export interface IDIContainer {
9
+ /** 注册服务 */
10
+ register<T>(key: string, factory: () => T): void;
11
+ /** 获取服务实例 */
12
+ get<T>(key: string): T;
13
+ /** 检查服务是否已注册 */
14
+ has(key: string): boolean;
15
+ }
16
+
17
+ /**
18
+ * 配置验证结果
19
+ */
20
+ export interface ValidationResult {
21
+ /** 是否有效 */
22
+ valid: boolean;
23
+ /** 错误信息 */
24
+ error?: string;
25
+ }