aico-cli 0.4.8 → 0.5.0
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 +47 -161
- 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.
|
|
16
|
+
const version = "0.5.0";
|
|
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
|
/**
|
|
@@ -396,81 +330,38 @@ ${commandInfo}`);
|
|
|
396
330
|
return report;
|
|
397
331
|
}
|
|
398
332
|
/**
|
|
399
|
-
*
|
|
400
|
-
*/
|
|
401
|
-
cleanupZombieProcesses() {
|
|
402
|
-
let cleanedCount = 0;
|
|
403
|
-
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
|
-
for (const process2 of runningProcesses) {
|
|
407
|
-
try {
|
|
408
|
-
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
|
-
} catch (error) {
|
|
417
|
-
this.log(`\u6E05\u7406\u50F5\u5C38\u8FDB\u7A0B: ${process2.name} (PID: ${process2.pid})`, "warning");
|
|
418
|
-
this.unregisterProcess(process2.pid);
|
|
419
|
-
cleanedCount++;
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
if (cleanedCount > 0) {
|
|
423
|
-
this.log(`\u6E05\u7406\u4E86 ${cleanedCount} \u4E2A\u50F5\u5C38\u8FDB\u7A0B`, "info");
|
|
424
|
-
}
|
|
425
|
-
return cleanedCount;
|
|
426
|
-
}
|
|
427
|
-
/**
|
|
428
|
-
* 深度清理进程,包括可能的残留进程
|
|
333
|
+
* 获取适用于当前平台的命令路径
|
|
429
334
|
*/
|
|
430
|
-
|
|
335
|
+
getPlatformCommand(command) {
|
|
431
336
|
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
337
|
if (isWindows) {
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
338
|
+
const commandMappings = {
|
|
339
|
+
"npm": "npm.cmd",
|
|
340
|
+
"npx": "npx.cmd",
|
|
341
|
+
"node": "node.exe",
|
|
342
|
+
"claude": "claude.cmd"
|
|
343
|
+
};
|
|
344
|
+
return commandMappings[command] || command;
|
|
443
345
|
}
|
|
444
|
-
|
|
445
|
-
return totalCleaned;
|
|
346
|
+
return command;
|
|
446
347
|
}
|
|
447
348
|
/**
|
|
448
|
-
*
|
|
449
|
-
* 针对Windows的进程管理特点进行优化
|
|
349
|
+
* 强制清理僵尸进程(状态为运行中但实际已退出的进程)
|
|
450
350
|
*/
|
|
451
|
-
|
|
452
|
-
const isWindows = process.platform === "win32";
|
|
453
|
-
if (!isWindows) {
|
|
454
|
-
return 0;
|
|
455
|
-
}
|
|
351
|
+
cleanupZombieProcesses() {
|
|
456
352
|
let cleanedCount = 0;
|
|
457
353
|
const runningProcesses = this.getRunningProcessList();
|
|
458
|
-
this.log("\u6267\u884CWindows\u5E73\u53F0\u7279\u5B9A\u8FDB\u7A0B\u6E05\u7406...", "info");
|
|
459
354
|
for (const process2 of runningProcesses) {
|
|
460
355
|
try {
|
|
461
|
-
|
|
462
|
-
this.log(`\u6E05\u7406Windows\u8FDB\u7A0B: ${process2.name} (PID: ${process2.pid})`, "warning");
|
|
463
|
-
this.unregisterProcess(process2.pid);
|
|
464
|
-
cleanedCount++;
|
|
465
|
-
}
|
|
356
|
+
process2.process.kill(0);
|
|
466
357
|
} catch (error) {
|
|
467
|
-
this.log(`\
|
|
358
|
+
this.log(`\u6E05\u7406\u50F5\u5C38\u8FDB\u7A0B: ${process2.name} (PID: ${process2.pid})`, "warning");
|
|
468
359
|
this.unregisterProcess(process2.pid);
|
|
469
360
|
cleanedCount++;
|
|
470
361
|
}
|
|
471
362
|
}
|
|
472
363
|
if (cleanedCount > 0) {
|
|
473
|
-
this.log(
|
|
364
|
+
this.log(`\u6E05\u7406\u4E86 ${cleanedCount} \u4E2A\u50F5\u5C38\u8FDB\u7A0B`, "info");
|
|
474
365
|
}
|
|
475
366
|
return cleanedCount;
|
|
476
367
|
}
|
|
@@ -486,52 +377,47 @@ ${commandInfo}`);
|
|
|
486
377
|
*/
|
|
487
378
|
async replaceProcess(command, args = [], options = {}) {
|
|
488
379
|
const processName = options.name || command;
|
|
489
|
-
const isWindows = process.platform === "win32";
|
|
490
380
|
try {
|
|
491
381
|
this.cleanup();
|
|
492
382
|
const { spawn: spawn2 } = await import('node:child_process');
|
|
493
|
-
const
|
|
494
|
-
|
|
495
|
-
|
|
383
|
+
const isWindows = process.platform === "win32";
|
|
384
|
+
let finalCommand = this.getPlatformCommand(command);
|
|
385
|
+
let finalArgs = args;
|
|
386
|
+
const spawnOptions = {
|
|
496
387
|
stdio: "inherit",
|
|
497
388
|
// 继承所有 stdio
|
|
498
389
|
cwd: options.cwd || process.cwd(),
|
|
499
390
|
env: { ...process.env, ...options.env },
|
|
500
|
-
shell: false,
|
|
501
|
-
// 禁用shell模式,避免进程泄漏
|
|
502
|
-
// Windows上使用更严格的进程控制
|
|
503
391
|
windowsHide: isWindows
|
|
504
392
|
// Windows上隐藏子进程窗口
|
|
505
|
-
}
|
|
393
|
+
};
|
|
394
|
+
if (isWindows) {
|
|
395
|
+
if (finalCommand.endsWith(".cmd")) {
|
|
396
|
+
spawnOptions.shell = true;
|
|
397
|
+
const fullCommand = `${finalCommand} ${finalArgs.join(" ")}`;
|
|
398
|
+
finalCommand = fullCommand;
|
|
399
|
+
finalArgs = [];
|
|
400
|
+
} else {
|
|
401
|
+
spawnOptions.shell = false;
|
|
402
|
+
}
|
|
403
|
+
} else {
|
|
404
|
+
spawnOptions.shell = false;
|
|
405
|
+
}
|
|
406
|
+
const child = spawn2(finalCommand, finalArgs, spawnOptions);
|
|
506
407
|
child.on("error", (error) => {
|
|
507
408
|
this.log(`\u8FDB\u7A0B\u66FF\u6362\u5931\u8D25: ${error}`, "error");
|
|
508
|
-
this.cleanup();
|
|
509
409
|
process.exit(1);
|
|
510
410
|
});
|
|
511
411
|
child.on("exit", (code) => {
|
|
512
412
|
this.log(`\u66FF\u6362\u8FDB\u7A0B\u5DF2\u9000\u51FA\uFF0C\u9000\u51FA\u7801: ${code}`, "info");
|
|
513
|
-
this.cleanup();
|
|
514
413
|
process.exit(code || 0);
|
|
515
414
|
});
|
|
516
415
|
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
416
|
await new Promise(() => {
|
|
530
417
|
});
|
|
531
418
|
process.exit(0);
|
|
532
419
|
} catch (error) {
|
|
533
420
|
this.log(`\u8FDB\u7A0B\u66FF\u6362\u5931\u8D25: ${error}`, "error");
|
|
534
|
-
this.cleanup();
|
|
535
421
|
process.exit(1);
|
|
536
422
|
}
|
|
537
423
|
}
|
|
@@ -646,15 +532,15 @@ promisify$1(exec$1);
|
|
|
646
532
|
promisify$1(exec$1);
|
|
647
533
|
|
|
648
534
|
async function tryStartClaude(command, args, options) {
|
|
649
|
-
|
|
650
|
-
|
|
535
|
+
process.platform === "win32";
|
|
536
|
+
const spawnOptions = {
|
|
651
537
|
name: "Claude Code",
|
|
652
|
-
cwd: options.cwd,
|
|
653
|
-
stdio: "inherit"
|
|
538
|
+
cwd: options.cwd || process.cwd(),
|
|
539
|
+
stdio: "inherit"
|
|
654
540
|
// 必须继承 stdio 才能正确交互
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
541
|
+
// shell选项由processManager内部处理
|
|
542
|
+
};
|
|
543
|
+
await processManager.replaceProcess(command, args, spawnOptions);
|
|
658
544
|
}
|
|
659
545
|
async function startClaudeCodeEditor(_lang) {
|
|
660
546
|
try {
|
|
@@ -670,7 +556,7 @@ async function startClaudeCodeEditor(_lang) {
|
|
|
670
556
|
};
|
|
671
557
|
try {
|
|
672
558
|
const command = isWindows ? "claude.cmd" : "claude";
|
|
673
|
-
const args = [
|
|
559
|
+
const args = [];
|
|
674
560
|
if (isWindows) {
|
|
675
561
|
await tryStartClaude(command, args, spawnOptions);
|
|
676
562
|
} else {
|
|
@@ -681,7 +567,7 @@ async function startClaudeCodeEditor(_lang) {
|
|
|
681
567
|
if (firstError.code === "ENOENT") {
|
|
682
568
|
console.log(ansis.yellow("\u26A0\uFE0F \u76F4\u63A5\u8C03\u7528 claude \u5931\u8D25\uFF0C\u5C1D\u8BD5\u4F7F\u7528 npx..."));
|
|
683
569
|
try {
|
|
684
|
-
const npxArgs = ["@anthropic-ai/claude-code"
|
|
570
|
+
const npxArgs = ["@anthropic-ai/claude-code"];
|
|
685
571
|
if (isWindows) {
|
|
686
572
|
await tryStartClaude("npx.cmd", npxArgs, spawnOptions);
|
|
687
573
|
} else {
|