aico-cli 0.4.6 → 0.4.8

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.
@@ -13,7 +13,7 @@ import { join as join$1 } from 'node:path';
13
13
  import { join, dirname, basename } from 'pathe';
14
14
  import { fileURLToPath } from 'node:url';
15
15
 
16
- const version = "0.4.6";
16
+ const version = "0.4.8";
17
17
 
18
18
  function displayBanner(subtitle) {
19
19
  const defaultSubtitle = "\u4E00\u952E\u914D\u7F6E\u4F60\u7684\u5F00\u53D1\u73AF\u5883";
package/dist/cli.mjs CHANGED
@@ -47,32 +47,57 @@ class ProcessManager extends EventEmitter {
47
47
  this.cleanup();
48
48
  });
49
49
  }
50
+ /**
51
+ * 获取适用于当前平台的命令路径
52
+ */
53
+ getPlatformCommand(command) {
54
+ const isWindows = process.platform === "win32";
55
+ if (isWindows) {
56
+ const commandMappings = {
57
+ "npm": "npm.cmd",
58
+ "npx": "npx.cmd",
59
+ "node": "node.exe",
60
+ "claude": "claude.cmd"
61
+ };
62
+ return commandMappings[command] || command;
63
+ }
64
+ return command;
65
+ }
66
+ /**
67
+ * 检查命令是否存在
68
+ */
69
+ async checkCommandExists(command) {
70
+ try {
71
+ const { execSync } = await import('node:child_process');
72
+ const isWindows = process.platform === "win32";
73
+ if (isWindows) {
74
+ execSync(`where ${command}`, { stdio: "ignore" });
75
+ } else {
76
+ execSync(`which ${command}`, { stdio: "ignore" });
77
+ }
78
+ return true;
79
+ } catch {
80
+ return false;
81
+ }
82
+ }
50
83
  /**
51
84
  * 启动进程
52
85
  */
53
86
  async spawn(command, args = [], options = {}) {
54
87
  const startTime = Date.now();
55
88
  const processName = options.name || command;
56
- const isWindows = process.platform === "win32";
57
89
  this.log(`\u542F\u52A8\u8FDB\u7A0B: ${processName}`, "info");
58
90
  try {
59
- let finalCommand = command;
60
- let finalArgs = args;
61
- if (isWindows) {
62
- if (command === "npx") {
63
- finalCommand = "npx.cmd";
64
- } else if (command === "claude") {
65
- finalCommand = "claude.cmd";
66
- }
91
+ const platformCommand = this.getPlatformCommand(command);
92
+ const commandExists = await this.checkCommandExists(platformCommand);
93
+ if (!commandExists) {
94
+ throw new Error(`\u547D\u4EE4 '${platformCommand}' \u4E0D\u5B58\u5728\uFF0C\u8BF7\u786E\u4FDD\u5DF2\u5B89\u88C5\u5E76\u6DFB\u52A0\u5230PATH\u73AF\u5883\u53D8\u91CF`);
67
95
  }
68
- const child = spawn(finalCommand, finalArgs, {
96
+ const child = spawn(platformCommand, args, {
69
97
  cwd: options.cwd || process.cwd(),
70
98
  env: { ...process.env, ...options.env },
71
99
  stdio: options.stdio || "pipe",
72
- shell: false,
73
- // 禁用shell模式,避免进程泄漏
74
- windowsHide: isWindows
75
- // Windows上隐藏子进程窗口
100
+ shell: options.shell || process.platform === "win32"
76
101
  });
77
102
  this.registerProcess(child, processName);
78
103
  let timeoutId;
@@ -104,13 +129,25 @@ class ProcessManager extends EventEmitter {
104
129
  const exitCode = await new Promise((resolve, reject) => {
105
130
  child.on("close", (code) => {
106
131
  if (timeoutId) clearTimeout(timeoutId);
107
- this.unregisterProcess(child.pid);
132
+ if (child.pid) {
133
+ this.unregisterProcess(child.pid);
134
+ }
108
135
  resolve(code ?? 0);
109
136
  });
110
137
  child.on("error", (error) => {
111
138
  if (timeoutId) clearTimeout(timeoutId);
112
- this.unregisterProcess(child.pid);
113
- reject(error);
139
+ if (child.pid) {
140
+ this.unregisterProcess(child.pid);
141
+ }
142
+ let enhancedError = error;
143
+ if (error.message?.includes("ENOENT")) {
144
+ const isWindows = process.platform === "win32";
145
+ const commandInfo = isWindows ? `\u547D\u4EE4 '${platformCommand}' \u4E0D\u5B58\u5728\u3002\u5728Windows\u4E0A\uFF0C\u8BF7\u786E\u4FDD\u5DF2\u5B89\u88C5Node.js\u5E76\u6DFB\u52A0\u5230PATH\u73AF\u5883\u53D8\u91CF\u3002\u5C1D\u8BD5\u4F7F\u7528\u5B8C\u6574\u8DEF\u5F84\u6216\u68C0\u67E5\u547D\u4EE4\u662F\u5426\u6B63\u786E\u3002` : `\u547D\u4EE4 '${platformCommand}' \u4E0D\u5B58\u5728\u3002\u8BF7\u786E\u4FDD\u5DF2\u5B89\u88C5\u5E76\u6DFB\u52A0\u5230PATH\u73AF\u5883\u53D8\u91CF\u3002`;
146
+ enhancedError = new Error(`${error.message}
147
+ ${commandInfo}`);
148
+ enhancedError.stack = error.stack;
149
+ }
150
+ reject(enhancedError);
114
151
  });
115
152
  });
116
153
  const duration = Date.now() - startTime;
@@ -143,6 +180,12 @@ class ProcessManager extends EventEmitter {
143
180
  registerProcess(child, name) {
144
181
  if (!child.pid) {
145
182
  this.log(`\u65E0\u6CD5\u6CE8\u518C\u8FDB\u7A0B ${name}: \u6CA1\u6709PID`, "error");
183
+ child.on("close", (code) => {
184
+ this.log(`\u8FDB\u7A0B ${name} (\u65E0PID) \u5DF2\u5173\u95ED\uFF0C\u9000\u51FA\u7801: ${code}`, "info");
185
+ });
186
+ child.on("error", (error) => {
187
+ this.log(`\u8FDB\u7A0B ${name} (\u65E0PID) \u53D1\u751F\u9519\u8BEF: ${error}`, "error");
188
+ });
146
189
  return;
147
190
  }
148
191
  if (this.processes.has(child.pid)) {
@@ -186,6 +229,10 @@ class ProcessManager extends EventEmitter {
186
229
  * 注销进程
187
230
  */
188
231
  unregisterProcess(pid) {
232
+ if (!pid) {
233
+ this.log(`\u5C1D\u8BD5\u6CE8\u9500\u65E0\u6548\u7684\u8FDB\u7A0B (PID: undefined)`, "warning");
234
+ return;
235
+ }
189
236
  const info = this.processes.get(pid);
190
237
  if (info) {
191
238
  this.log(`\u6CE8\u9500\u8FDB\u7A0B: ${info.name} (PID: ${pid})`, "info");
@@ -443,15 +490,8 @@ class ProcessManager extends EventEmitter {
443
490
  try {
444
491
  this.cleanup();
445
492
  const { spawn: spawn2 } = await import('node:child_process');
446
- let finalCommand = command;
447
- let finalArgs = args;
448
- if (isWindows) {
449
- if (command === "npx") {
450
- finalCommand = "npx.cmd";
451
- } else if (command === "claude") {
452
- finalCommand = "claude.cmd";
453
- }
454
- }
493
+ const finalCommand = this.getPlatformCommand(command);
494
+ const finalArgs = args;
455
495
  const child = spawn2(finalCommand, finalArgs, {
456
496
  stdio: "inherit",
457
497
  // 继承所有 stdio
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aico-cli",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
4
4
  "packageManager": "pnpm@9.15.9",
5
5
  "description": "AI CLI",
6
6
  "repository": {