@xiaozhi-client/cli 1.9.4-beta.9 → 1.9.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
@@ -76,7 +76,7 @@ import { configManager } from "@/lib/config/manager";
76
76
  pnpm build
77
77
 
78
78
  # 类型检查
79
- pnpm type-check
79
+ pnpm check:type
80
80
 
81
81
  # 运行测试
82
82
  pnpm test
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiaozhi-client/cli",
3
- "version": "1.9.4-beta.9",
3
+ "version": "1.9.4",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
package/src/Container.ts CHANGED
@@ -140,18 +140,18 @@ export class DIContainer implements IDIContainer {
140
140
 
141
141
  // 注册服务层
142
142
  container.registerSingleton("processManager", () => {
143
- const ProcessManagerModule = require("@cli/services/ProcessManager.js");
143
+ const ProcessManagerModule = require("./services/ProcessManager.js");
144
144
  return new ProcessManagerModule.ProcessManagerImpl();
145
145
  });
146
146
 
147
147
  container.registerSingleton("daemonManager", () => {
148
- const DaemonManagerModule = require("@cli/services/DaemonManager.js");
148
+ const DaemonManagerModule = require("./services/DaemonManager.js");
149
149
  const processManager = container.get("processManager") as any;
150
150
  return new DaemonManagerModule.DaemonManagerImpl(processManager);
151
151
  });
152
152
 
153
153
  container.registerSingleton("serviceManager", () => {
154
- const ServiceManagerModule = require("@cli/services/ServiceManager.js");
154
+ const ServiceManagerModule = require("./services/ServiceManager.js");
155
155
  const processManager = container.get("processManager") as any;
156
156
  const configManager = container.get("configManager") as any;
157
157
  return new ServiceManagerModule.ServiceManagerImpl(
@@ -162,7 +162,7 @@ export class DIContainer implements IDIContainer {
162
162
 
163
163
  container.registerSingleton("templateManager", () => {
164
164
  // 使用动态导入的同步版本
165
- const TemplateManagerModule = require("@cli/services/TemplateManager.js");
165
+ const TemplateManagerModule = require("./services/TemplateManager.js");
166
166
  return new TemplateManagerModule.TemplateManagerImpl();
167
167
  });
168
168
 
@@ -52,9 +52,7 @@ export class CommandHandlerFactory implements ICommandHandlerFactory {
52
52
  */
53
53
  private createServiceCommandHandler(): CommandHandler {
54
54
  // 动态导入以避免循环依赖
55
- const {
56
- ServiceCommandHandler,
57
- } = require("@cli/commands/ServiceCommandHandler.js");
55
+ const { ServiceCommandHandler } = require("./ServiceCommandHandler.js");
58
56
  return new ServiceCommandHandler(this.container);
59
57
  }
60
58
 
@@ -62,9 +60,7 @@ export class CommandHandlerFactory implements ICommandHandlerFactory {
62
60
  * 创建配置命令处理器
63
61
  */
64
62
  private createConfigCommandHandler(): CommandHandler {
65
- const {
66
- ConfigCommandHandler,
67
- } = require("@cli/commands/ConfigCommandHandler.js");
63
+ const { ConfigCommandHandler } = require("./ConfigCommandHandler.js");
68
64
  return new ConfigCommandHandler(this.container);
69
65
  }
70
66
 
@@ -72,9 +68,7 @@ export class CommandHandlerFactory implements ICommandHandlerFactory {
72
68
  * 创建项目命令处理器
73
69
  */
74
70
  private createProjectCommandHandler(): CommandHandler {
75
- const {
76
- ProjectCommandHandler,
77
- } = require("@cli/commands/ProjectCommandHandler.js");
71
+ const { ProjectCommandHandler } = require("./ProjectCommandHandler.js");
78
72
  return new ProjectCommandHandler(this.container);
79
73
  }
80
74
 
@@ -82,7 +76,7 @@ export class CommandHandlerFactory implements ICommandHandlerFactory {
82
76
  * 创建MCP命令处理器
83
77
  */
84
78
  private createMcpCommandHandler(): CommandHandler {
85
- const { McpCommandHandler } = require("@cli/commands/McpCommandHandler.js");
79
+ const { McpCommandHandler } = require("./McpCommandHandler.js");
86
80
  return new McpCommandHandler(this.container);
87
81
  }
88
82
 
@@ -90,9 +84,7 @@ export class CommandHandlerFactory implements ICommandHandlerFactory {
90
84
  * 创建端点命令处理器
91
85
  */
92
86
  private createEndpointCommandHandler(): CommandHandler {
93
- const {
94
- EndpointCommandHandler,
95
- } = require("@cli/commands/EndpointCommandHandler.js");
87
+ const { EndpointCommandHandler } = require("./EndpointCommandHandler.js");
96
88
  return new EndpointCommandHandler(this.container);
97
89
  }
98
90
  }
@@ -79,7 +79,7 @@ export class ServiceCommandHandler extends BaseCommandHandler {
79
79
  try {
80
80
  // 处理--debug参数
81
81
  if (options.debug) {
82
- consola.level = "debug";
82
+ consola.level = 5; // debug 级别
83
83
  }
84
84
 
85
85
  const serviceManager = this.getService<any>("serviceManager");
@@ -1,4 +1,3 @@
1
- import type { IDIContainer } from "@cli/interfaces/Config.js";
2
1
  import { configManager } from "@xiaozhi-client/config";
3
2
  import type {
4
3
  MCPServerConfig,
@@ -7,6 +6,7 @@ import type {
7
6
  } from "@xiaozhi-client/config";
8
7
  import ora from "ora";
9
8
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
9
+ import type { IDIContainer } from "../../interfaces/Config.js";
10
10
  import { McpCommandHandler } from "../McpCommandHandler.js";
11
11
 
12
12
  // 测试专用类型定义
@@ -143,7 +143,7 @@ const mockGetServiceStatus = vi
143
143
  .fn()
144
144
  .mockReturnValue({ running: false, pid: null });
145
145
 
146
- vi.mock("@cli/services/ProcessManager.js", () => ({
146
+ vi.mock("../../services/ProcessManager.js", () => ({
147
147
  ProcessManagerImpl: vi.fn().mockImplementation(() => ({
148
148
  getServiceStatus: mockGetServiceStatus,
149
149
  })),
package/src/global.d.ts CHANGED
@@ -16,4 +16,8 @@ declare global {
16
16
  }
17
17
  }
18
18
 
19
+ // 构建时注入的版本号常量
20
+ declare const __VERSION__: string;
21
+ declare const __APP_NAME__: string;
22
+
19
23
  export {};
package/src/index.ts CHANGED
@@ -39,15 +39,6 @@ async function initializeCLI(): Promise<void> {
39
39
  }
40
40
  }
41
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
- }
42
+ initializeCLI();
52
43
 
53
44
  export { initializeCLI };
@@ -2,10 +2,10 @@
2
2
  * 守护进程管理服务单元测试
3
3
  */
4
4
 
5
- import { ServiceError } from "@cli/errors/index.js";
6
- import type { ProcessManager } from "@cli/interfaces/Service.js";
7
5
  import consola from "consola";
8
6
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
7
+ import { ServiceError } from "../../errors/index.js";
8
+ import type { ProcessManager } from "../../interfaces/Service.js";
9
9
  import type { DaemonOptions } from "../DaemonManager";
10
10
  import { DaemonManagerImpl } from "../DaemonManager";
11
11
 
@@ -49,7 +49,7 @@ vi.mock("node:fs", () => ({
49
49
  }));
50
50
 
51
51
  // Mock utils
52
- vi.mock("@cli/utils/PathUtils.js", () => ({
52
+ vi.mock("../../utils/PathUtils.js", () => ({
53
53
  PathUtils: {
54
54
  getWebServerLauncherPath: vi.fn(() => "/path/to/webserver.js"),
55
55
  getConfigDir: vi.fn(() => "/config"),
@@ -57,7 +57,7 @@ vi.mock("@cli/utils/PathUtils.js", () => ({
57
57
  },
58
58
  }));
59
59
 
60
- vi.mock("@cli/utils/PlatformUtils.js", () => ({
60
+ vi.mock("../../utils/PlatformUtils.js", () => ({
61
61
  PlatformUtils: {
62
62
  getTailCommand: vi.fn(() => ({
63
63
  command: "tail",
@@ -1,6 +1,6 @@
1
- import type { ServiceStartOptions } from "@cli/interfaces/Service.js";
2
- import { PathUtils } from "@cli/utils/PathUtils.js";
3
1
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+ import type { ServiceStartOptions } from "../../interfaces/Service.js";
3
+ import { PathUtils } from "../../utils/PathUtils.js";
4
4
  import { ServiceManagerImpl } from "../ServiceManager";
5
5
 
6
6
  // Mock external dependencies
@@ -19,7 +19,7 @@ vi.mock("node:fs", () => ({
19
19
  mkdirSync: vi.fn(),
20
20
  }));
21
21
 
22
- vi.mock("@cli/utils/PathUtils.js", () => ({
22
+ vi.mock("../../utils/PathUtils.js", () => ({
23
23
  PathUtils: {
24
24
  getWebServerLauncherPath: vi.fn(),
25
25
  getExecutablePath: vi.fn(),
@@ -2,16 +2,16 @@
2
2
  * 进程管理服务单元测试
3
3
  */
4
4
 
5
- import { FileUtils } from "@cli/utils/FileUtils.js";
6
- import { PathUtils } from "@cli/utils/PathUtils.js";
7
- import { PlatformUtils } from "@cli/utils/PlatformUtils.js";
8
5
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
6
+ import { FileUtils } from "../../utils/FileUtils.js";
7
+ import { PathUtils } from "../../utils/PathUtils.js";
8
+ import { PlatformUtils } from "../../utils/PlatformUtils.js";
9
9
  import { ProcessManagerImpl } from "../ProcessManager";
10
10
 
11
11
  // Mock 依赖
12
- vi.mock("@cli/utils/FileUtils.js");
13
- vi.mock("@cli/utils/PathUtils.js");
14
- vi.mock("@cli/utils/PlatformUtils.js");
12
+ vi.mock("../../utils/FileUtils.js");
13
+ vi.mock("../../utils/PathUtils.js");
14
+ vi.mock("../../utils/PlatformUtils.js");
15
15
 
16
16
  const mockFileUtils = vi.mocked(FileUtils);
17
17
  const mockPathUtils = vi.mocked(PathUtils);
@@ -2,12 +2,12 @@
2
2
  * 服务管理服务单元测试
3
3
  */
4
4
 
5
- import { ConfigError, ServiceError } from "@cli/errors/index.js";
5
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
6
+ import { ConfigError, ServiceError } from "../../errors/index.js";
6
7
  import type {
7
8
  ProcessManager,
8
9
  ServiceStartOptions,
9
- } from "@cli/interfaces/Service.js";
10
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
10
+ } from "../../interfaces/Service.js";
11
11
  import { ServiceManagerImpl } from "../ServiceManager";
12
12
 
13
13
  // Mock 依赖
@@ -62,7 +62,7 @@ vi.mock("@services/MCPServer.js", () => ({
62
62
  }));
63
63
 
64
64
  // Mock PathUtils
65
- vi.mock("@cli/utils/PathUtils.js", () => ({
65
+ vi.mock("../../utils/PathUtils.js", () => ({
66
66
  PathUtils: {
67
67
  getWebServerLauncherPath: vi
68
68
  .fn()
@@ -158,7 +158,7 @@ describe("ServiceManagerImpl 服务管理器实现", () => {
158
158
  mockProcessExit.mockClear();
159
159
 
160
160
  // Reset PathUtils mocks
161
- const { PathUtils } = await import("@cli/utils/PathUtils.js");
161
+ const { PathUtils } = await import("../../utils/PathUtils.js");
162
162
  vi.mocked(PathUtils.getWebServerLauncherPath).mockReturnValue(
163
163
  "/mock/path/WebServerLauncher.js"
164
164
  );
@@ -2,20 +2,20 @@
2
2
  * 模板管理服务单元测试
3
3
  */
4
4
 
5
- import { FileError, ValidationError } from "@cli/errors/index.js";
6
5
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
6
+ import { FileError, ValidationError } from "../../errors/index.js";
7
7
  import type { TemplateCreateOptions } from "../TemplateManager";
8
8
  import { TemplateManagerImpl } from "../TemplateManager";
9
9
 
10
10
  // Mock 依赖
11
- vi.mock("@cli/utils/PathUtils.js", () => ({
11
+ vi.mock("../../utils/PathUtils.js", () => ({
12
12
  PathUtils: {
13
13
  findTemplatesDir: vi.fn(),
14
14
  getTemplatePath: vi.fn(),
15
15
  },
16
16
  }));
17
17
 
18
- vi.mock("@cli/utils/FileUtils.js", () => ({
18
+ vi.mock("../../utils/FileUtils.js", () => ({
19
19
  FileUtils: {
20
20
  exists: vi.fn(),
21
21
  readFile: vi.fn(),
@@ -26,7 +26,7 @@ vi.mock("@cli/utils/FileUtils.js", () => ({
26
26
  },
27
27
  }));
28
28
 
29
- vi.mock("@cli/utils/Validation.js", () => ({
29
+ vi.mock("../../utils/Validation.js", () => ({
30
30
  Validation: {
31
31
  validateTemplateName: vi.fn(),
32
32
  validateRequired: vi.fn(),
@@ -48,9 +48,9 @@ vi.mock("node:path", () => ({
48
48
  },
49
49
  }));
50
50
 
51
- const { PathUtils } = await import("@cli/utils/PathUtils.js");
52
- const { FileUtils } = await import("@cli/utils/FileUtils.js");
53
- const { Validation } = await import("@cli/utils/Validation.js");
51
+ const { PathUtils } = await import("../../utils/PathUtils.js");
52
+ const { FileUtils } = await import("../../utils/FileUtils.js");
53
+ const { Validation } = await import("../../utils/Validation.js");
54
54
  const fs = await import("node:fs");
55
55
 
56
56
  describe("TemplateManagerImpl", () => {
@@ -2,10 +2,7 @@
2
2
  * 版本管理工具
3
3
  */
4
4
 
5
- import fs from "node:fs";
6
- import path from "node:path";
7
- import { fileURLToPath } from "node:url";
8
- import { FileError } from "../errors/index";
5
+ import { APP_NAME, VERSION } from "../version";
9
6
 
10
7
  /**
11
8
  * 版本信息接口
@@ -21,88 +18,21 @@ export interface VersionInfo {
21
18
  * 版本工具类
22
19
  */
23
20
  export class VersionUtils {
24
- private static cachedVersion: string | null = null;
25
-
26
21
  /**
27
- * 获取版本号
22
+ * 获取版本号(构建时注入)
28
23
  */
29
24
  static getVersion(): string {
30
- if (VersionUtils.cachedVersion) {
31
- return VersionUtils.cachedVersion;
32
- }
33
-
34
- try {
35
- // 在 ES 模块环境中获取当前目录
36
- const __filename = fileURLToPath(import.meta.url);
37
- const currentDir = path.dirname(__filename);
38
-
39
- // 尝试多个可能的 package.json 路径
40
- const possiblePaths = [
41
- // 构建后环境:dist/cli.js -> dist/package.json (优先)
42
- path.join(currentDir, "package.json"),
43
- // 构建后环境:dist/cli.js -> package.json
44
- path.join(currentDir, "..", "package.json"),
45
- // 开发环境:src/cli/utils/VersionUtils.ts -> package.json
46
- path.join(currentDir, "..", "..", "..", "package.json"),
47
- // 全局安装环境
48
- path.join(currentDir, "..", "..", "..", "..", "package.json"),
49
- ];
50
-
51
- for (const packagePath of possiblePaths) {
52
- if (fs.existsSync(packagePath)) {
53
- const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf8"));
54
- if (packageJson.version) {
55
- VersionUtils.cachedVersion = packageJson.version;
56
- return packageJson.version;
57
- }
58
- }
59
- }
60
-
61
- // 如果都找不到,返回默认版本
62
- VersionUtils.cachedVersion = "unknown";
63
- return "unknown";
64
- } catch (error) {
65
- console.warn("无法从 package.json 读取版本信息:", error);
66
- VersionUtils.cachedVersion = "unknown";
67
- return "unknown";
68
- }
25
+ return VERSION;
69
26
  }
70
27
 
71
28
  /**
72
- * 获取完整版本信息
29
+ * 获取完整版本信息(构建时注入)
73
30
  */
74
31
  static getVersionInfo(): VersionInfo {
75
- try {
76
- const __filename = fileURLToPath(import.meta.url);
77
- const currentDir = path.dirname(__filename);
78
-
79
- const possiblePaths = [
80
- // 构建后环境:dist/cli.js -> dist/package.json (优先)
81
- path.join(currentDir, "package.json"),
82
- // 构建后环境:dist/cli.js -> package.json
83
- path.join(currentDir, "..", "package.json"),
84
- // 开发环境:src/cli/utils/VersionUtils.ts -> package.json
85
- path.join(currentDir, "..", "..", "..", "package.json"),
86
- // 全局安装环境
87
- path.join(currentDir, "..", "..", "..", "..", "package.json"),
88
- ];
89
-
90
- for (const packagePath of possiblePaths) {
91
- if (fs.existsSync(packagePath)) {
92
- const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf8"));
93
- return {
94
- version: packageJson.version || "unknown",
95
- name: packageJson.name,
96
- description: packageJson.description,
97
- author: packageJson.author,
98
- };
99
- }
100
- }
101
-
102
- return { version: "unknown" };
103
- } catch (error) {
104
- throw new FileError("无法读取版本信息", "package.json");
105
- }
32
+ return {
33
+ version: VERSION,
34
+ name: APP_NAME,
35
+ };
106
36
  }
107
37
 
108
38
  /**
@@ -131,11 +61,4 @@ export class VersionUtils {
131
61
  const versionRegex = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?$/;
132
62
  return versionRegex.test(version);
133
63
  }
134
-
135
- /**
136
- * 清除版本缓存
137
- */
138
- static clearCache(): void {
139
- VersionUtils.cachedVersion = null;
140
- }
141
64
  }
@@ -2,11 +2,11 @@ import { realpathSync } from "node:fs";
2
2
  import { tmpdir } from "node:os";
3
3
  import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
- import { PathUtils } from "@cli/utils/PathUtils.js";
6
5
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
6
+ import { PathUtils } from "../PathUtils.js";
7
7
 
8
8
  // Mock dependencies - 需要使用与源文件相同的导入方式
9
- vi.mock("@cli/utils/FileUtils.js", () => ({
9
+ vi.mock("../FileUtils.js", () => ({
10
10
  FileUtils: {
11
11
  exists: vi.fn(),
12
12
  },
@@ -38,7 +38,7 @@ describe("PathUtils 路径工具", () => {
38
38
  vi.clearAllMocks();
39
39
 
40
40
  // Setup mocks
41
- const { FileUtils } = await import("@cli/utils/FileUtils.js");
41
+ const { FileUtils } = await import("../FileUtils.js");
42
42
  mockFileExists = vi.mocked(FileUtils.exists);
43
43
  mockTmpdir = vi.mocked(tmpdir);
44
44
  mockFileURLToPath = vi.mocked(fileURLToPath);
@@ -2,261 +2,43 @@
2
2
  * 版本管理工具单元测试
3
3
  */
4
4
 
5
- import fs from "node:fs";
6
- import path from "node:path";
7
- import { fileURLToPath } from "node:url";
8
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
9
- import { FileError } from "../../errors/index";
5
+ import { describe, expect, it } from "vitest";
10
6
  import { VersionUtils } from "../VersionUtils";
11
7
 
12
- // Mock fileURLToPath
13
- vi.mock("node:url");
14
- const mockedFileURLToPath = vi.mocked(fileURLToPath);
15
-
16
- // Mock path module
17
- vi.mock("node:path");
18
- const mockedPath = vi.mocked(path);
19
-
20
- // Mock fs module
21
- vi.mock("node:fs");
22
- const mockedFs = vi.mocked(fs);
23
-
24
8
  describe("VersionUtils", () => {
25
- beforeEach(() => {
26
- vi.clearAllMocks();
27
- // Clear static cache
28
- VersionUtils.clearCache();
29
- });
30
-
31
- afterEach(() => {
32
- vi.restoreAllMocks();
33
- });
9
+ // 不再需要 beforeEach afterEach,因为不再使用缓存
10
+ // 也不再需要 mock fs、path、url 模块,因为不再读取文件
34
11
 
35
12
  describe("获取版本号", () => {
36
- it("缓存可用时应返回缓存的版本号", () => {
37
- // Set cache directly
38
- VersionUtils.clearCache();
39
- (VersionUtils as any).cachedVersion = "1.0.0";
40
-
41
- const result = VersionUtils.getVersion();
42
-
43
- expect(result).toBe("1.0.0");
44
- expect(mockedFs.existsSync).not.toHaveBeenCalled();
45
- });
46
-
47
- it("应从dist目录中的package.json读取版本号", () => {
48
- const mockPackageJson = { version: "1.2.3", name: "test-package" };
49
-
50
- // Mock fileURLToPath
51
- mockedFileURLToPath.mockReturnValue("/dist/cli/utils/VersionUtils.js");
52
-
53
- mockedPath.dirname.mockReturnValue("/dist/cli/utils");
54
- mockedPath.join
55
- .mockReturnValueOnce("/dist/cli/utils/package.json")
56
- .mockReturnValueOnce("/dist/cli/package.json")
57
- .mockReturnValueOnce("/package.json")
58
- .mockReturnValueOnce("/../package.json");
59
-
60
- mockedFs.existsSync.mockImplementation((filePath) => {
61
- return filePath === "/dist/cli/utils/package.json";
62
- });
63
-
64
- mockedFs.readFileSync.mockReturnValue(JSON.stringify(mockPackageJson));
65
-
66
- const result = VersionUtils.getVersion();
67
-
68
- expect(result).toBe("1.2.3");
69
- expect(mockedFs.existsSync).toHaveBeenCalledWith(
70
- "/dist/cli/utils/package.json"
71
- );
72
- expect(mockedFs.readFileSync).toHaveBeenCalledWith(
73
- "/dist/cli/utils/package.json",
74
- "utf8"
75
- );
76
- });
77
-
78
- it("当前目录中未找到时应从父级package.json读取版本号", () => {
79
- const mockPackageJson = { version: "2.0.0", name: "test-package" };
80
-
81
- mockedFileURLToPath.mockReturnValue("/dist/cli/utils/VersionUtils.js");
82
-
83
- mockedPath.dirname.mockReturnValue("/dist/cli/utils");
84
- mockedPath.join
85
- .mockReturnValueOnce("/dist/cli/utils/package.json")
86
- .mockReturnValueOnce("/dist/cli/package.json")
87
- .mockReturnValueOnce("/package.json")
88
- .mockReturnValueOnce("/../package.json");
89
-
90
- mockedFs.existsSync.mockImplementation((filePath) => {
91
- return filePath === "/package.json";
92
- });
93
-
94
- mockedFs.readFileSync.mockReturnValue(JSON.stringify(mockPackageJson));
95
-
13
+ it("应返回编译时注入的版本号", () => {
96
14
  const result = VersionUtils.getVersion();
97
-
98
- expect(result).toBe("2.0.0");
99
- expect(mockedFs.existsSync).toHaveBeenCalledTimes(3);
100
- expect(mockedFs.readFileSync).toHaveBeenCalledWith(
101
- "/package.json",
102
- "utf8"
103
- );
15
+ // 在测试环境中,版本号被定义为 "1.0.0-test"
16
+ expect(result).toBe("1.0.0-test");
104
17
  });
105
18
 
106
- it("未找到package.json时应返回 'unknown'", () => {
107
- mockedFileURLToPath.mockReturnValue("/dist/cli/utils/VersionUtils.js");
108
-
109
- mockedPath.dirname.mockReturnValue("/dist/cli/utils");
110
- mockedPath.join
111
- .mockReturnValueOnce("/dist/cli/utils/package.json")
112
- .mockReturnValueOnce("/dist/cli/package.json")
113
- .mockReturnValueOnce("/package.json")
114
- .mockReturnValueOnce("/../package.json");
115
-
116
- mockedFs.existsSync.mockReturnValue(false);
117
-
118
- const result = VersionUtils.getVersion();
119
-
120
- expect(result).toBe("unknown");
121
- });
122
-
123
- it("package.json没有版本字段时应返回 'unknown'", () => {
124
- const mockPackageJson = { name: "test-package" };
125
-
126
- mockedFileURLToPath.mockReturnValue("/dist/cli/utils/VersionUtils.js");
127
-
128
- mockedPath.dirname.mockReturnValue("/dist/cli/utils");
129
- mockedPath.join.mockReturnValue("/dist/cli/utils/package.json");
130
-
131
- mockedFs.existsSync.mockReturnValue(true);
132
- mockedFs.readFileSync.mockReturnValue(JSON.stringify(mockPackageJson));
133
-
134
- const result = VersionUtils.getVersion();
135
-
136
- expect(result).toBe("unknown");
137
- });
138
-
139
- it("读取package.json失败时应返回 'unknown'", () => {
140
- mockedFileURLToPath.mockReturnValue("/dist/cli/utils/VersionUtils.js");
141
-
142
- mockedPath.dirname.mockReturnValue("/dist/cli/utils");
143
- mockedPath.join.mockReturnValue("/dist/cli/utils/package.json");
144
-
145
- mockedFs.existsSync.mockReturnValue(true);
146
- mockedFs.readFileSync.mockImplementation(() => {
147
- throw new Error("Read error");
148
- });
149
-
150
- const result = VersionUtils.getVersion();
151
-
152
- expect(result).toBe("unknown");
153
- });
154
-
155
- it("首次成功读取后应缓存版本号", () => {
156
- const mockPackageJson = { version: "1.0.0", name: "test-package" };
157
-
158
- mockedFileURLToPath.mockReturnValue("/dist/cli/utils/VersionUtils.js");
159
-
160
- mockedPath.dirname.mockReturnValue("/dist/cli/utils");
161
- mockedPath.join.mockReturnValue("/dist/cli/utils/package.json");
162
-
163
- mockedFs.existsSync.mockReturnValue(true);
164
- mockedFs.readFileSync.mockReturnValue(JSON.stringify(mockPackageJson));
165
-
166
- // First call
19
+ it("多次调用应返回相同的版本号", () => {
167
20
  const result1 = VersionUtils.getVersion();
168
- expect(result1).toBe("1.0.0");
169
- expect(mockedFs.readFileSync).toHaveBeenCalledTimes(1);
170
-
171
- // Second call should use cache
172
21
  const result2 = VersionUtils.getVersion();
173
- expect(result2).toBe("1.0.0");
174
- expect(mockedFs.readFileSync).toHaveBeenCalledTimes(1); // No additional calls
22
+ expect(result1).toBe(result2);
23
+ expect(result1).toBe("1.0.0-test");
175
24
  });
176
25
  });
177
26
 
178
27
  describe("获取版本信息", () => {
179
- it("应从package.json返回完整的版本信息", () => {
180
- const mockPackageJson = {
181
- version: "1.2.3",
182
- name: "xiaozhi-client",
183
- description: "A test package",
184
- author: "Test Author",
185
- };
186
-
187
- mockedFileURLToPath.mockReturnValue("/dist/cli/utils/VersionUtils.js");
188
-
189
- mockedPath.dirname.mockReturnValue("/dist/cli/utils");
190
- mockedPath.join
191
- .mockReturnValueOnce("/dist/cli/utils/package.json")
192
- .mockReturnValueOnce("/dist/cli/package.json")
193
- .mockReturnValueOnce("/package.json")
194
- .mockReturnValueOnce("/../package.json");
195
-
196
- mockedFs.existsSync.mockReturnValue(true);
197
- mockedFs.readFileSync.mockReturnValue(JSON.stringify(mockPackageJson));
198
-
28
+ it("应返回完整的版本信息", () => {
199
29
  const result = VersionUtils.getVersionInfo();
200
-
201
30
  expect(result).toEqual({
202
- version: "1.2.3",
31
+ version: "1.0.0-test",
203
32
  name: "xiaozhi-client",
204
- description: "A test package",
205
- author: "Test Author",
206
33
  });
207
34
  });
208
35
 
209
- it("应处理信息最少的package.json", () => {
210
- const mockPackageJson = { version: "1.0.0" };
211
-
212
- mockedFileURLToPath.mockReturnValue("/dist/cli/utils/VersionUtils.js");
213
-
214
- mockedPath.dirname.mockReturnValue("/dist/cli/utils");
215
- mockedPath.join.mockReturnValue("/dist/cli/utils/package.json");
216
-
217
- mockedFs.existsSync.mockReturnValue(true);
218
- mockedFs.readFileSync.mockReturnValue(JSON.stringify(mockPackageJson));
219
-
36
+ it("应包含版本号和名称", () => {
220
37
  const result = VersionUtils.getVersionInfo();
221
-
222
- expect(result).toEqual({
223
- version: "1.0.0",
224
- name: undefined,
225
- description: undefined,
226
- author: undefined,
227
- });
228
- });
229
-
230
- it("未找到package.json时应返回默认版本信息", () => {
231
- mockedFileURLToPath.mockReturnValue("/dist/cli/utils/VersionUtils.js");
232
-
233
- mockedPath.dirname.mockReturnValue("/dist/cli/utils");
234
- mockedPath.join
235
- .mockReturnValueOnce("/dist/cli/utils/package.json")
236
- .mockReturnValueOnce("/dist/cli/package.json")
237
- .mockReturnValueOnce("/package.json")
238
- .mockReturnValueOnce("/../package.json");
239
-
240
- mockedFs.existsSync.mockReturnValue(false);
241
-
242
- const result = VersionUtils.getVersionInfo();
243
-
244
- expect(result).toEqual({ version: "unknown" });
245
- });
246
-
247
- it("读取失败时应抛出文件错误", () => {
248
- mockedFileURLToPath.mockReturnValue("/dist/cli/utils/VersionUtils.js");
249
-
250
- mockedPath.dirname.mockReturnValue("/dist/cli/utils");
251
- mockedPath.join.mockReturnValue("/dist/cli/utils/package.json");
252
-
253
- mockedFs.existsSync.mockReturnValue(true);
254
- mockedFs.readFileSync.mockImplementation(() => {
255
- throw new Error("Read error");
256
- });
257
-
258
- expect(() => VersionUtils.getVersionInfo()).toThrow(FileError);
259
- expect(() => VersionUtils.getVersionInfo()).toThrow("无法读取版本信息");
38
+ expect(result.version).toBeDefined();
39
+ expect(result.name).toBeDefined();
40
+ expect(typeof result.version).toBe("string");
41
+ expect(typeof result.name).toBe("string");
260
42
  });
261
43
  });
262
44
 
@@ -331,80 +113,4 @@ describe("VersionUtils", () => {
331
113
  expect(VersionUtils.isValidVersion("1.0.0.1")).toBe(false);
332
114
  });
333
115
  });
334
-
335
- describe("清除缓存", () => {
336
- it("应清除缓存的版本号", () => {
337
- // Set cache first
338
- (VersionUtils as any).cachedVersion = "1.0.0";
339
-
340
- VersionUtils.clearCache();
341
-
342
- expect((VersionUtils as any).cachedVersion).toBeNull();
343
- });
344
-
345
- it("应处理null缓存", () => {
346
- // Ensure cache is null
347
- VersionUtils.clearCache();
348
-
349
- // Should not throw
350
- VersionUtils.clearCache();
351
-
352
- expect((VersionUtils as any).cachedVersion).toBeNull();
353
- });
354
- });
355
-
356
- describe("集成测试", () => {
357
- it("清除缓存后应正常工作", () => {
358
- const mockPackageJson = { version: "1.0.0" };
359
-
360
- mockedFileURLToPath.mockReturnValue("/dist/cli/utils/VersionUtils.js");
361
-
362
- mockedPath.dirname.mockReturnValue("/dist/cli/utils");
363
- mockedPath.join.mockReturnValue("/dist/cli/utils/package.json");
364
-
365
- mockedFs.existsSync.mockReturnValue(true);
366
- mockedFs.readFileSync.mockReturnValue(JSON.stringify(mockPackageJson));
367
-
368
- // First call
369
- const result1 = VersionUtils.getVersion();
370
- expect(result1).toBe("1.0.0");
371
-
372
- // Clear cache
373
- VersionUtils.clearCache();
374
-
375
- // Should read file again
376
- const result2 = VersionUtils.getVersion();
377
- expect(result2).toBe("1.0.0");
378
- expect(mockedFs.readFileSync).toHaveBeenCalledTimes(2);
379
- });
380
-
381
- it("应正确处理多次路径尝试", () => {
382
- const mockPackageJson = { version: "2.0.0" };
383
-
384
- mockedFileURLToPath.mockReturnValue("/dist/cli/utils/VersionUtils.js");
385
-
386
- mockedPath.dirname.mockReturnValue("/dist/cli/utils");
387
- mockedPath.join
388
- .mockReturnValueOnce("/dist/cli/utils/package.json")
389
- .mockReturnValueOnce("/dist/cli/package.json")
390
- .mockReturnValueOnce("/package.json")
391
- .mockReturnValueOnce("/../package.json");
392
-
393
- // Simulate finding package.json in the third attempt
394
- mockedFs.existsSync.mockImplementation((filePath) => {
395
- return filePath === "/package.json";
396
- });
397
-
398
- mockedFs.readFileSync.mockReturnValue(JSON.stringify(mockPackageJson));
399
-
400
- const result = VersionUtils.getVersion();
401
-
402
- expect(result).toBe("2.0.0");
403
- expect(mockedFs.existsSync).toHaveBeenCalledTimes(3);
404
- expect(mockedFs.readFileSync).toHaveBeenCalledWith(
405
- "/package.json",
406
- "utf8"
407
- );
408
- });
409
- });
410
116
  });
package/src/version.ts ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * 版本号常量(构建时注入)
3
+ */
4
+ export const VERSION = __VERSION__;
5
+ export const APP_NAME = __APP_NAME__;
package/tsconfig.json CHANGED
@@ -1,32 +1,11 @@
1
1
  {
2
+ "extends": "../../tsconfig.json",
2
3
  "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "lib": ["ES2022"],
7
- "strict": false,
8
- "esModuleInterop": true,
9
- "skipLibCheck": true,
10
- "forceConsistentCasingInFileNames": true,
11
- "resolveJsonModule": true,
12
- "allowSyntheticDefaultImports": true,
13
- "noEmit": true,
14
- "baseUrl": ".",
15
- "moduleSuffixes": [".ts", ".js"],
16
- "paths": {
17
- "@/*": ["src/*"],
18
- "@cli/commands": ["src/commands"],
19
- "@cli/commands/*": ["src/commands/*"],
20
- "@cli/services": ["src/services"],
21
- "@cli/services/*": ["src/services/*"],
22
- "@cli/utils": ["src/utils"],
23
- "@cli/utils/*": ["src/utils/*"],
24
- "@cli/errors": ["src/errors"],
25
- "@cli/errors/*": ["src/errors/*"],
26
- "@cli/interfaces": ["src/interfaces"],
27
- "@cli/interfaces/*": ["src/interfaces/*"]
28
- }
4
+ "composite": true,
5
+ "outDir": "../../dist/cli",
6
+ "rootDir": "src"
29
7
  },
30
8
  "include": ["src/**/*.ts"],
31
- "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
9
+ "exclude": ["node_modules", "dist", "**/*.test.ts"],
10
+ "references": [{ "path": "../../apps/backend" }, { "path": "../config" }]
32
11
  }
package/tsup.config.ts CHANGED
@@ -2,6 +2,10 @@ import { readFileSync, writeFileSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
3
  import { defineConfig } from "tsup";
4
4
 
5
+ // 读取根目录 package.json 获取版本号
6
+ const rootPkgPath = resolve("../../package.json");
7
+ const pkg = JSON.parse(readFileSync(rootPkgPath, "utf-8"));
8
+
5
9
  export default defineConfig({
6
10
  entry: {
7
11
  index: "src/index.ts",
@@ -19,8 +23,13 @@ export default defineConfig({
19
23
  platform: "node",
20
24
  esbuildOptions: (options) => {
21
25
  options.resolveExtensions = [".ts", ".js", ".json"];
22
- // 使用 esbuild 的别名机制
23
- // 由于 esbuild 的 alias 不支持通配符,我们需要注入多个具体的别名
26
+
27
+ // 构建时注入版本号常量
28
+ options.define = {
29
+ ...options.define, // 保留已有的 define
30
+ __VERSION__: JSON.stringify(pkg.version),
31
+ __APP_NAME__: JSON.stringify(pkg.name),
32
+ };
24
33
  },
25
34
  external: [
26
35
  // Node.js 内置模块
package/vitest.config.ts CHANGED
@@ -13,6 +13,11 @@ export default defineConfig({
13
13
  // 添加 tsconfig 路径解析插件
14
14
  tsconfigPaths(),
15
15
  ],
16
+ // 定义构建时注入的全局变量,用于测试环境
17
+ define: {
18
+ __VERSION__: JSON.stringify("1.0.0-test"),
19
+ __APP_NAME__: JSON.stringify("xiaozhi-client"),
20
+ },
16
21
  test: {
17
22
  globals: true,
18
23
  environment: "node",
@@ -46,17 +51,6 @@ export default defineConfig({
46
51
  },
47
52
  resolve: {
48
53
  alias: {
49
- // CLI 内部路径别名(__dirname 是 packages/cli,所以使用相对路径)
50
- "@cli/commands": resolve(__dirname, "./src/commands"),
51
- "@cli/commands/*": resolve(__dirname, "./src/commands/*"),
52
- "@cli/services": resolve(__dirname, "./src/services"),
53
- "@cli/services/*": resolve(__dirname, "./src/services/*"),
54
- "@cli/utils": resolve(__dirname, "./src/utils"),
55
- "@cli/utils/*": resolve(__dirname, "./src/utils/*"),
56
- "@cli/errors": resolve(__dirname, "./src/errors"),
57
- "@cli/errors/*": resolve(__dirname, "./src/errors/*"),
58
- "@cli/interfaces": resolve(__dirname, "./src/interfaces"),
59
- "@cli/interfaces/*": resolve(__dirname, "./src/interfaces/*"),
60
54
  // Backend 路径别名(从 packages/cli 向上到项目根目录)
61
55
  "@handlers": resolve(__dirname, "../../apps/backend/handlers"),
62
56
  "@handlers/*": resolve(__dirname, "../../apps/backend/handlers/*"),
package/fix-imports.js DELETED
@@ -1,32 +0,0 @@
1
- /**
2
- * 构建后处理脚本
3
- * 将路径别名替换为相对路径
4
- */
5
-
6
- import { readFileSync, writeFileSync } from "node:fs";
7
- import { resolve } from "node:path";
8
-
9
- const filePath = resolve("./dist/cli/index.js");
10
- let content = readFileSync(filePath, "utf-8");
11
-
12
- // 替换 @root/* 为指向 dist/backend 的相对路径
13
- content = content
14
- .replace(
15
- /from "@\/lib\/config\/manager\.js"/g,
16
- 'from "../../dist/backend/lib/config/manager.js"'
17
- )
18
- .replace(
19
- /from "@\/lib\/config\/manager"/g,
20
- 'from "../../dist/backend/lib/config/manager.js"'
21
- )
22
- .replace(
23
- /from "@root\/WebServer\.js"/g,
24
- 'from "../../dist/backend/WebServer.js"'
25
- )
26
- .replace(
27
- /from "@root\/WebServer"/g,
28
- 'from "../../dist/backend/WebServer.js"'
29
- );
30
-
31
- writeFileSync(filePath, content);
32
- console.log("✅ 已修复 dist/cli/index.js 中的导入路径");