aico-cli 0.4.8 → 0.4.9
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/dist/chunks/simple-config.mjs +1 -1
- package/dist/cli.mjs +10 -154
- package/package.json +1 -1
|
@@ -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.
|
|
16
|
+
const version = "0.4.9";
|
|
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,39 +47,6 @@ 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
|
-
}
|
|
83
50
|
/**
|
|
84
51
|
* 启动进程
|
|
85
52
|
*/
|
|
@@ -88,12 +55,7 @@ class ProcessManager extends EventEmitter {
|
|
|
88
55
|
const processName = options.name || command;
|
|
89
56
|
this.log(`\u542F\u52A8\u8FDB\u7A0B: ${processName}`, "info");
|
|
90
57
|
try {
|
|
91
|
-
const
|
|
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`);
|
|
95
|
-
}
|
|
96
|
-
const child = spawn(platformCommand, args, {
|
|
58
|
+
const child = spawn(command, args, {
|
|
97
59
|
cwd: options.cwd || process.cwd(),
|
|
98
60
|
env: { ...process.env, ...options.env },
|
|
99
61
|
stdio: options.stdio || "pipe",
|
|
@@ -129,25 +91,13 @@ class ProcessManager extends EventEmitter {
|
|
|
129
91
|
const exitCode = await new Promise((resolve, reject) => {
|
|
130
92
|
child.on("close", (code) => {
|
|
131
93
|
if (timeoutId) clearTimeout(timeoutId);
|
|
132
|
-
|
|
133
|
-
this.unregisterProcess(child.pid);
|
|
134
|
-
}
|
|
94
|
+
this.unregisterProcess(child.pid);
|
|
135
95
|
resolve(code ?? 0);
|
|
136
96
|
});
|
|
137
97
|
child.on("error", (error) => {
|
|
138
98
|
if (timeoutId) clearTimeout(timeoutId);
|
|
139
|
-
|
|
140
|
-
|
|
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);
|
|
99
|
+
this.unregisterProcess(child.pid);
|
|
100
|
+
reject(error);
|
|
151
101
|
});
|
|
152
102
|
});
|
|
153
103
|
const duration = Date.now() - startTime;
|
|
@@ -180,12 +130,6 @@ ${commandInfo}`);
|
|
|
180
130
|
registerProcess(child, name) {
|
|
181
131
|
if (!child.pid) {
|
|
182
132
|
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
|
-
});
|
|
189
133
|
return;
|
|
190
134
|
}
|
|
191
135
|
if (this.processes.has(child.pid)) {
|
|
@@ -229,10 +173,6 @@ ${commandInfo}`);
|
|
|
229
173
|
* 注销进程
|
|
230
174
|
*/
|
|
231
175
|
unregisterProcess(pid) {
|
|
232
|
-
if (!pid) {
|
|
233
|
-
this.log(`\u5C1D\u8BD5\u6CE8\u9500\u65E0\u6548\u7684\u8FDB\u7A0B (PID: undefined)`, "warning");
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
176
|
const info = this.processes.get(pid);
|
|
237
177
|
if (info) {
|
|
238
178
|
this.log(`\u6CE8\u9500\u8FDB\u7A0B: ${info.name} (PID: ${pid})`, "info");
|
|
@@ -306,20 +246,14 @@ ${commandInfo}`);
|
|
|
306
246
|
* 清理所有进程
|
|
307
247
|
*/
|
|
308
248
|
cleanup() {
|
|
309
|
-
const isWindows = process.platform === "win32";
|
|
310
|
-
if (isWindows) {
|
|
311
|
-
this.cleanupWindowsProcesses();
|
|
312
|
-
}
|
|
313
249
|
for (const [pid, info] of this.processes) {
|
|
314
250
|
if (info.status === "running") {
|
|
315
251
|
try {
|
|
316
|
-
|
|
317
|
-
info.process.kill(signal);
|
|
252
|
+
info.process.kill("SIGKILL");
|
|
318
253
|
} catch {
|
|
319
254
|
}
|
|
320
255
|
}
|
|
321
256
|
}
|
|
322
|
-
this.cleanupZombieProcesses();
|
|
323
257
|
this.processes.clear();
|
|
324
258
|
}
|
|
325
259
|
/**
|
|
@@ -401,18 +335,9 @@ ${commandInfo}`);
|
|
|
401
335
|
cleanupZombieProcesses() {
|
|
402
336
|
let cleanedCount = 0;
|
|
403
337
|
const runningProcesses = this.getRunningProcessList();
|
|
404
|
-
const isWindows = process.platform === "win32";
|
|
405
|
-
this.log(`\u5F00\u59CB\u6E05\u7406\u50F5\u5C38\u8FDB\u7A0B\uFF0C\u5F53\u524D\u8FD0\u884C\u4E2D\u8FDB\u7A0B\u6570: ${runningProcesses.length}`, "info");
|
|
406
338
|
for (const process2 of runningProcesses) {
|
|
407
339
|
try {
|
|
408
340
|
process2.process.kill(0);
|
|
409
|
-
if (isWindows) {
|
|
410
|
-
if (process2.process.exitCode !== null) {
|
|
411
|
-
this.log(`\u6E05\u7406Windows\u50F5\u5C38\u8FDB\u7A0B: ${process2.name} (PID: ${process2.pid})`, "warning");
|
|
412
|
-
this.unregisterProcess(process2.pid);
|
|
413
|
-
cleanedCount++;
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
341
|
} catch (error) {
|
|
417
342
|
this.log(`\u6E05\u7406\u50F5\u5C38\u8FDB\u7A0B: ${process2.name} (PID: ${process2.pid})`, "warning");
|
|
418
343
|
this.unregisterProcess(process2.pid);
|
|
@@ -424,56 +349,6 @@ ${commandInfo}`);
|
|
|
424
349
|
}
|
|
425
350
|
return cleanedCount;
|
|
426
351
|
}
|
|
427
|
-
/**
|
|
428
|
-
* 深度清理进程,包括可能的残留进程
|
|
429
|
-
*/
|
|
430
|
-
deepCleanup() {
|
|
431
|
-
const isWindows = process.platform === "win32";
|
|
432
|
-
let totalCleaned = 0;
|
|
433
|
-
this.log("\u5F00\u59CB\u6DF1\u5EA6\u6E05\u7406\u8FDB\u7A0B...", "info");
|
|
434
|
-
totalCleaned += this.cleanupZombieProcesses();
|
|
435
|
-
if (isWindows) {
|
|
436
|
-
totalCleaned += this.cleanupWindowsProcesses();
|
|
437
|
-
}
|
|
438
|
-
const remainingProcesses = this.getProcessCount();
|
|
439
|
-
if (remainingProcesses > 0) {
|
|
440
|
-
this.log(`\u5F3A\u5236\u6E05\u7406\u5269\u4F59\u7684 ${remainingProcesses} \u4E2A\u8FDB\u7A0B`, "warning");
|
|
441
|
-
this.cleanup();
|
|
442
|
-
totalCleaned += remainingProcesses;
|
|
443
|
-
}
|
|
444
|
-
this.log(`\u6DF1\u5EA6\u6E05\u7406\u5B8C\u6210\uFF0C\u603B\u5171\u6E05\u7406\u4E86 ${totalCleaned} \u4E2A\u8FDB\u7A0B`, "info");
|
|
445
|
-
return totalCleaned;
|
|
446
|
-
}
|
|
447
|
-
/**
|
|
448
|
-
* Windows平台特定的进程清理
|
|
449
|
-
* 针对Windows的进程管理特点进行优化
|
|
450
|
-
*/
|
|
451
|
-
cleanupWindowsProcesses() {
|
|
452
|
-
const isWindows = process.platform === "win32";
|
|
453
|
-
if (!isWindows) {
|
|
454
|
-
return 0;
|
|
455
|
-
}
|
|
456
|
-
let cleanedCount = 0;
|
|
457
|
-
const runningProcesses = this.getRunningProcessList();
|
|
458
|
-
this.log("\u6267\u884CWindows\u5E73\u53F0\u7279\u5B9A\u8FDB\u7A0B\u6E05\u7406...", "info");
|
|
459
|
-
for (const process2 of runningProcesses) {
|
|
460
|
-
try {
|
|
461
|
-
if (process2.process.exitCode !== null) {
|
|
462
|
-
this.log(`\u6E05\u7406Windows\u8FDB\u7A0B: ${process2.name} (PID: ${process2.pid})`, "warning");
|
|
463
|
-
this.unregisterProcess(process2.pid);
|
|
464
|
-
cleanedCount++;
|
|
465
|
-
}
|
|
466
|
-
} catch (error) {
|
|
467
|
-
this.log(`\u5F3A\u5236\u6E05\u7406Windows\u8FDB\u7A0B: ${process2.name} (PID: ${process2.pid})`, "error");
|
|
468
|
-
this.unregisterProcess(process2.pid);
|
|
469
|
-
cleanedCount++;
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
if (cleanedCount > 0) {
|
|
473
|
-
this.log(`Windows\u5E73\u53F0\u6E05\u7406\u4E86 ${cleanedCount} \u4E2A\u8FDB\u7A0B`, "info");
|
|
474
|
-
}
|
|
475
|
-
return cleanedCount;
|
|
476
|
-
}
|
|
477
352
|
/**
|
|
478
353
|
* 添加进程到列表(用于测试)
|
|
479
354
|
*/
|
|
@@ -486,52 +361,33 @@ ${commandInfo}`);
|
|
|
486
361
|
*/
|
|
487
362
|
async replaceProcess(command, args = [], options = {}) {
|
|
488
363
|
const processName = options.name || command;
|
|
489
|
-
const isWindows = process.platform === "win32";
|
|
490
364
|
try {
|
|
365
|
+
this.log(`\u6E05\u7406 ${this.processes.size} \u4E2A\u7BA1\u7406\u8FDB\u7A0B...`, "info");
|
|
491
366
|
this.cleanup();
|
|
492
367
|
const { spawn: spawn2 } = await import('node:child_process');
|
|
493
|
-
const
|
|
494
|
-
const finalArgs = args;
|
|
495
|
-
const child = spawn2(finalCommand, finalArgs, {
|
|
368
|
+
const child = spawn2(command, args, {
|
|
496
369
|
stdio: "inherit",
|
|
497
370
|
// 继承所有 stdio
|
|
498
371
|
cwd: options.cwd || process.cwd(),
|
|
499
372
|
env: { ...process.env, ...options.env },
|
|
500
|
-
shell:
|
|
501
|
-
//
|
|
502
|
-
//
|
|
503
|
-
windowsHide: isWindows
|
|
504
|
-
// Windows上隐藏子进程窗口
|
|
373
|
+
shell: options.shell || process.platform === "win32"
|
|
374
|
+
// 移除 detached: true,避免子进程脱离管理
|
|
375
|
+
// detached: false 是默认值,子进程会随着父进程退出而退出
|
|
505
376
|
});
|
|
506
377
|
child.on("error", (error) => {
|
|
507
378
|
this.log(`\u8FDB\u7A0B\u66FF\u6362\u5931\u8D25: ${error}`, "error");
|
|
508
|
-
this.cleanup();
|
|
509
379
|
process.exit(1);
|
|
510
380
|
});
|
|
511
381
|
child.on("exit", (code) => {
|
|
512
382
|
this.log(`\u66FF\u6362\u8FDB\u7A0B\u5DF2\u9000\u51FA\uFF0C\u9000\u51FA\u7801: ${code}`, "info");
|
|
513
|
-
this.cleanup();
|
|
514
383
|
process.exit(code || 0);
|
|
515
384
|
});
|
|
516
385
|
this.registerProcess(child, processName);
|
|
517
|
-
if (isWindows) {
|
|
518
|
-
const checkInterval = setInterval(() => {
|
|
519
|
-
if (child.exitCode !== null) {
|
|
520
|
-
clearInterval(checkInterval);
|
|
521
|
-
this.cleanup();
|
|
522
|
-
process.exit(child.exitCode || 0);
|
|
523
|
-
}
|
|
524
|
-
}, 1e3);
|
|
525
|
-
child.on("exit", () => {
|
|
526
|
-
clearInterval(checkInterval);
|
|
527
|
-
});
|
|
528
|
-
}
|
|
529
386
|
await new Promise(() => {
|
|
530
387
|
});
|
|
531
388
|
process.exit(0);
|
|
532
389
|
} catch (error) {
|
|
533
390
|
this.log(`\u8FDB\u7A0B\u66FF\u6362\u5931\u8D25: ${error}`, "error");
|
|
534
|
-
this.cleanup();
|
|
535
391
|
process.exit(1);
|
|
536
392
|
}
|
|
537
393
|
}
|