coding-tool-x 3.5.13 → 3.6.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/CHANGELOG.md +15 -0
- package/dist/web/assets/{Analytics-B9Shk-uq.js → Analytics-DvWZOZ13.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-Dlkn-_zW.js → ConfigTemplates-DiuKu3LA.js} +1 -1
- package/dist/web/assets/{Home-AGf32LvJ.js → Home-DeGTc9uf.js} +1 -1
- package/dist/web/assets/{PluginManager-BnAjHN4u.js → PluginManager-CljkN0Rb.js} +1 -1
- package/dist/web/assets/{ProjectList-7YV_zk9L.js → ProjectList-CFQJaSQ8.js} +1 -1
- package/dist/web/assets/{SessionList-D0un-UKS.js → SessionList-Fq4uQSQp.js} +1 -1
- package/dist/web/assets/{SkillManager-DhYBmbtd.js → SkillManager-DLhg11LF.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-JOZIPAGo.js → WorkspaceManager-DXB4kE9p.js} +1 -1
- package/dist/web/assets/index-CLra7uzu.js +2 -0
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/src/commands/daemon.js +306 -98
- package/src/config/channel-preset-websites.js +1 -0
- package/src/index.js +18 -11
- package/src/server/api/channels.js +62 -17
- package/src/server/api/codex-channels.js +12 -1
- package/src/server/api/pm2-autostart.js +59 -24
- package/src/server/proxy-server.js +97 -5
- package/src/server/services/channels.js +62 -4
- package/src/server/services/claude-openai-gateway.js +1259 -0
- package/src/server/services/claude-token-recovery.js +386 -0
- package/src/server/services/codex-channels.js +8 -7
- package/src/server/services/proxy-log-helper.js +9 -4
- package/dist/web/assets/index-DM3yF1Oo.js +0 -2
package/dist/web/index.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<link rel="icon" href="/favicon.ico">
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
7
|
<title>coding-tool-x · AI 编程工作台</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-CLra7uzu.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/markdown-BNJfEgCA.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/vue-vendor-B5RqjEjQ.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/naive-ui-DM897mIP.js">
|
package/package.json
CHANGED
package/src/commands/daemon.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
1
2
|
const pm2 = require('pm2');
|
|
2
3
|
const path = require('path');
|
|
3
4
|
const chalk = require('chalk');
|
|
@@ -12,6 +13,16 @@ const {
|
|
|
12
13
|
} = require('../utils/port-helper');
|
|
13
14
|
|
|
14
15
|
const PM2_APP_NAME = 'cc-tool';
|
|
16
|
+
const STARTUP_LOG_FILE = 'cc-tool-out.log';
|
|
17
|
+
const CURRENT_PM2_FORK_PATH = resolveCurrentPm2ForkPath();
|
|
18
|
+
|
|
19
|
+
function resolveCurrentPm2ForkPath() {
|
|
20
|
+
try {
|
|
21
|
+
return require.resolve('pm2/lib/ProcessContainerFork');
|
|
22
|
+
} catch {
|
|
23
|
+
return '';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
15
26
|
|
|
16
27
|
/**
|
|
17
28
|
* 连接到 PM2
|
|
@@ -74,6 +85,18 @@ function deletePM2Process(name) {
|
|
|
74
85
|
});
|
|
75
86
|
}
|
|
76
87
|
|
|
88
|
+
function restartPM2Process(name) {
|
|
89
|
+
return new Promise((resolve, reject) => {
|
|
90
|
+
pm2.restart(name, (err) => {
|
|
91
|
+
if (err) {
|
|
92
|
+
reject(err);
|
|
93
|
+
} else {
|
|
94
|
+
resolve();
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
77
100
|
/**
|
|
78
101
|
* 获取 Coding-Tool 进程
|
|
79
102
|
*/
|
|
@@ -86,6 +109,113 @@ function sleep(ms) {
|
|
|
86
109
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
87
110
|
}
|
|
88
111
|
|
|
112
|
+
function normalizePathForComparison(filePath) {
|
|
113
|
+
return path.normalize(String(filePath || ''));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function getFileSize(filePath) {
|
|
117
|
+
try {
|
|
118
|
+
return fs.statSync(filePath).size;
|
|
119
|
+
} catch {
|
|
120
|
+
return 0;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function readLogChunkSince(filePath, startOffset = 0, maxBytes = 32768) {
|
|
125
|
+
try {
|
|
126
|
+
const stat = fs.statSync(filePath);
|
|
127
|
+
if (stat.size <= startOffset) {
|
|
128
|
+
return '';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const safeOffset = Math.max(0, startOffset);
|
|
132
|
+
const readStart = Math.max(safeOffset, stat.size - maxBytes);
|
|
133
|
+
const length = stat.size - readStart;
|
|
134
|
+
if (length <= 0) {
|
|
135
|
+
return '';
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const fd = fs.openSync(filePath, 'r');
|
|
139
|
+
try {
|
|
140
|
+
const buffer = Buffer.alloc(length);
|
|
141
|
+
fs.readSync(fd, buffer, 0, length, readStart);
|
|
142
|
+
return buffer.toString('utf8');
|
|
143
|
+
} finally {
|
|
144
|
+
fs.closeSync(fd);
|
|
145
|
+
}
|
|
146
|
+
} catch {
|
|
147
|
+
return '';
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function extractMissingPm2ForkScriptPath(logText = '') {
|
|
152
|
+
const match = String(logText).match(/Cannot find module '([^']*ProcessContainerFork\.js)'/);
|
|
153
|
+
return match ? match[1] : '';
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function detectStalePm2RuntimeIssue(logText = '', currentForkPath = CURRENT_PM2_FORK_PATH) {
|
|
157
|
+
const missingPath = extractMissingPm2ForkScriptPath(logText);
|
|
158
|
+
if (!missingPath || !currentForkPath) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (normalizePathForComparison(missingPath) === normalizePathForComparison(currentForkPath)) {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
missingPath,
|
|
168
|
+
currentPath: currentForkPath
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function updatePM2Daemon() {
|
|
173
|
+
return new Promise((resolve, reject) => {
|
|
174
|
+
pm2.update((err, result) => {
|
|
175
|
+
if (err) {
|
|
176
|
+
reject(err);
|
|
177
|
+
} else {
|
|
178
|
+
resolve(result);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function dumpPM2State(timeoutMs = 2000) {
|
|
185
|
+
return new Promise((resolve) => {
|
|
186
|
+
let settled = false;
|
|
187
|
+
const timer = setTimeout(() => {
|
|
188
|
+
if (settled) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
settled = true;
|
|
192
|
+
resolve({ timedOut: true, err: null });
|
|
193
|
+
}, timeoutMs);
|
|
194
|
+
|
|
195
|
+
pm2.dump((err) => {
|
|
196
|
+
if (settled) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
settled = true;
|
|
200
|
+
clearTimeout(timer);
|
|
201
|
+
resolve({ timedOut: false, err: err || null });
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async function finalizePM2Session(options = {}) {
|
|
207
|
+
const shouldPersist = options.persist === true;
|
|
208
|
+
if (shouldPersist) {
|
|
209
|
+
const result = await dumpPM2State(options.timeoutMs);
|
|
210
|
+
if (result.err) {
|
|
211
|
+
console.log(chalk.yellow(`[WARN] 保存 PM2 状态失败: ${result.err.message}`));
|
|
212
|
+
} else if (result.timedOut) {
|
|
213
|
+
console.log(chalk.yellow('[WARN] 保存 PM2 状态超时,继续关闭 CLI 连接'));
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
disconnectPM2();
|
|
217
|
+
}
|
|
218
|
+
|
|
89
219
|
function isPortOwnedByPid(port, pid) {
|
|
90
220
|
if (!pid || pid <= 0) {
|
|
91
221
|
return false;
|
|
@@ -257,104 +387,193 @@ async function waitForServiceReady(port, timeoutMs = 15000, intervalMs = 500) {
|
|
|
257
387
|
};
|
|
258
388
|
}
|
|
259
389
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
390
|
+
function buildStartOptions(port, enableHost, enableHttps) {
|
|
391
|
+
const pmArgs = ['ui', '--daemon'];
|
|
392
|
+
if (enableHost) {
|
|
393
|
+
pmArgs.push('--host');
|
|
394
|
+
}
|
|
395
|
+
if (enableHttps) {
|
|
396
|
+
pmArgs.push('--https');
|
|
397
|
+
}
|
|
266
398
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
399
|
+
return {
|
|
400
|
+
name: PM2_APP_NAME,
|
|
401
|
+
script: path.join(__dirname, '../index.js'),
|
|
402
|
+
args: pmArgs,
|
|
403
|
+
interpreter: 'node',
|
|
404
|
+
autorestart: true,
|
|
405
|
+
max_memory_restart: '500M',
|
|
406
|
+
env: {
|
|
407
|
+
NODE_ENV: 'production',
|
|
408
|
+
CC_TOOL_PORT: port
|
|
409
|
+
},
|
|
410
|
+
output: path.join(PATHS.logs, STARTUP_LOG_FILE),
|
|
411
|
+
error: path.join(PATHS.logs, STARTUP_LOG_FILE),
|
|
412
|
+
merge_logs: true,
|
|
413
|
+
log_date_format: 'YYYY-MM-DD HH:mm:ss'
|
|
414
|
+
};
|
|
415
|
+
}
|
|
278
416
|
|
|
279
|
-
|
|
280
|
-
|
|
417
|
+
async function attemptStartService(startOptions, port, options = {}) {
|
|
418
|
+
const allowPm2Refresh = options.allowPm2Refresh !== false;
|
|
419
|
+
const logPath = path.join(PATHS.logs, STARTUP_LOG_FILE);
|
|
420
|
+
const logOffset = getFileSize(logPath);
|
|
281
421
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
const enableHttps = process.argv.includes('--https');
|
|
285
|
-
const pmArgs = ['ui', '--daemon'];
|
|
286
|
-
if (enableHost) {
|
|
287
|
-
pmArgs.push('--host');
|
|
288
|
-
}
|
|
289
|
-
if (enableHttps) {
|
|
290
|
-
pmArgs.push('--https');
|
|
291
|
-
}
|
|
292
|
-
require('fs').mkdirSync(PATHS.logs, { recursive: true });
|
|
293
|
-
|
|
294
|
-
// 启动 PM2 进程
|
|
295
|
-
pm2.start({
|
|
296
|
-
name: PM2_APP_NAME,
|
|
297
|
-
script: path.join(__dirname, '../index.js'),
|
|
298
|
-
args: pmArgs,
|
|
299
|
-
interpreter: 'node',
|
|
300
|
-
autorestart: true,
|
|
301
|
-
max_memory_restart: '500M',
|
|
302
|
-
env: {
|
|
303
|
-
NODE_ENV: 'production',
|
|
304
|
-
CC_TOOL_PORT: port
|
|
305
|
-
},
|
|
306
|
-
output: path.join(PATHS.logs, 'cc-tool-out.log'),
|
|
307
|
-
error: path.join(PATHS.logs, 'cc-tool-out.log'),
|
|
308
|
-
merge_logs: true,
|
|
309
|
-
log_date_format: 'YYYY-MM-DD HH:mm:ss'
|
|
310
|
-
}, async (err) => {
|
|
422
|
+
return new Promise((resolve, reject) => {
|
|
423
|
+
pm2.start(startOptions, async (err) => {
|
|
311
424
|
if (err) {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
process.exit(1);
|
|
425
|
+
reject(err);
|
|
426
|
+
return;
|
|
315
427
|
}
|
|
316
428
|
|
|
317
|
-
let readyState = null;
|
|
318
429
|
try {
|
|
319
|
-
readyState = await waitForServiceReady(port);
|
|
320
|
-
if (
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
430
|
+
const readyState = await waitForServiceReady(port);
|
|
431
|
+
if (readyState.ready) {
|
|
432
|
+
resolve({ ok: true, readyState });
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const recentLogText = readLogChunkSince(logPath, logOffset);
|
|
437
|
+
const staleRuntimeIssue = allowPm2Refresh ? detectStalePm2RuntimeIssue(recentLogText) : null;
|
|
438
|
+
|
|
439
|
+
if (staleRuntimeIssue) {
|
|
440
|
+
console.log(chalk.yellow('\n[WARN] 检测到 PM2 守护进程仍引用旧的 pm2 运行时,正在自动刷新...'));
|
|
441
|
+
console.log(chalk.gray(`旧路径: ${staleRuntimeIssue.missingPath}`));
|
|
442
|
+
console.log(chalk.gray(`当前路径: ${staleRuntimeIssue.currentPath}`));
|
|
443
|
+
|
|
444
|
+
await updatePM2Daemon();
|
|
445
|
+
|
|
446
|
+
const recoveredState = await waitForServiceReady(port, 5000, 500);
|
|
447
|
+
if (recoveredState.ready) {
|
|
448
|
+
resolve({
|
|
449
|
+
ok: true,
|
|
450
|
+
readyState: recoveredState,
|
|
451
|
+
recoveredFromStalePm2: staleRuntimeIssue
|
|
331
452
|
});
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
try {
|
|
457
|
+
await deletePM2Process(PM2_APP_NAME);
|
|
458
|
+
} catch {
|
|
459
|
+
// ignore stale process cleanup errors; the next retry will surface real failures
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
resolve({
|
|
463
|
+
retry: true,
|
|
464
|
+
recoveredFromStalePm2: staleRuntimeIssue
|
|
332
465
|
});
|
|
333
466
|
return;
|
|
334
467
|
}
|
|
468
|
+
|
|
469
|
+
resolve({
|
|
470
|
+
ok: false,
|
|
471
|
+
readyState
|
|
472
|
+
});
|
|
335
473
|
} catch (checkError) {
|
|
336
|
-
|
|
474
|
+
reject(checkError);
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function cleanupFailedStart() {
|
|
481
|
+
return new Promise((resolve) => {
|
|
482
|
+
pm2.delete(PM2_APP_NAME, async () => {
|
|
483
|
+
await finalizePM2Session({ persist: true });
|
|
484
|
+
resolve();
|
|
485
|
+
});
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* 启动服务(后台)
|
|
491
|
+
*/
|
|
492
|
+
async function handleStart() {
|
|
493
|
+
try {
|
|
494
|
+
const config = loadConfig();
|
|
495
|
+
const port = config.ports?.webUI || 19999;
|
|
496
|
+
const enableHost = process.argv.includes('--host');
|
|
497
|
+
const enableHttps = process.argv.includes('--https');
|
|
498
|
+
fs.mkdirSync(PATHS.logs, { recursive: true });
|
|
499
|
+
|
|
500
|
+
let readyState = null;
|
|
501
|
+
let recoveredFromStalePm2 = null;
|
|
502
|
+
|
|
503
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
504
|
+
await connectPM2();
|
|
505
|
+
|
|
506
|
+
const existing = await getCCToolProcess();
|
|
507
|
+
if (existing && existing.pm2_env.status === 'online') {
|
|
508
|
+
if (recoveredFromStalePm2) {
|
|
509
|
+
readyState = {
|
|
510
|
+
ready: true,
|
|
511
|
+
process: existing,
|
|
512
|
+
degradedPortCheckIssue: getPortToolIssue()
|
|
513
|
+
};
|
|
514
|
+
break;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
console.log(chalk.yellow('\n[WARN] 服务已在运行中\n'));
|
|
518
|
+
console.log(chalk.gray(`进程 ID: ${existing.pid}`));
|
|
519
|
+
console.log(chalk.gray(`运行时长: ${formatUptime(existing.pm2_env.pm_uptime)}`));
|
|
520
|
+
console.log(chalk.gray('\n使用 ') + chalk.cyan('ctx status') + chalk.gray(' 查看详细状态'));
|
|
521
|
+
console.log(chalk.gray('使用 ') + chalk.cyan('ctx restart') + chalk.gray(' 重启服务\n'));
|
|
337
522
|
disconnectPM2();
|
|
338
|
-
|
|
523
|
+
return;
|
|
339
524
|
}
|
|
340
525
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
526
|
+
const startResult = await attemptStartService(
|
|
527
|
+
buildStartOptions(port, enableHost, enableHttps),
|
|
528
|
+
port,
|
|
529
|
+
{ allowPm2Refresh: attempt === 0 }
|
|
530
|
+
);
|
|
531
|
+
|
|
532
|
+
if (startResult.retry) {
|
|
533
|
+
recoveredFromStalePm2 = startResult.recoveredFromStalePm2 || recoveredFromStalePm2;
|
|
534
|
+
disconnectPM2();
|
|
535
|
+
continue;
|
|
346
536
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
537
|
+
|
|
538
|
+
if (!startResult.ok) {
|
|
539
|
+
readyState = startResult.readyState;
|
|
540
|
+
const statusText = readyState.process?.pm2_env?.status || 'unknown';
|
|
541
|
+
console.error(chalk.red('\n[ERROR] Coding-Tool 服务启动失败,进程未就绪\n'));
|
|
542
|
+
console.error(chalk.gray(`PM2 状态: ${statusText}`));
|
|
543
|
+
printPortToolIssue(readyState.degradedPortCheckIssue);
|
|
544
|
+
console.error(chalk.yellow('[TIP] 请使用 ctx logs ui 查看详细日志\n'));
|
|
545
|
+
await cleanupFailedStart();
|
|
355
546
|
disconnectPM2();
|
|
356
|
-
|
|
357
|
-
|
|
547
|
+
process.exit(1);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
readyState = startResult.readyState;
|
|
551
|
+
recoveredFromStalePm2 = startResult.recoveredFromStalePm2 || recoveredFromStalePm2;
|
|
552
|
+
break;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (!readyState || !readyState.ready) {
|
|
556
|
+
console.error(chalk.red('\n[ERROR] Coding-Tool 服务启动失败,PM2 守护进程刷新后仍未恢复\n'));
|
|
557
|
+
disconnectPM2();
|
|
558
|
+
process.exit(1);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
console.log(chalk.green('\n[OK] Coding-Tool 服务已启动(后台运行)\n'));
|
|
562
|
+
console.log(chalk.gray(`Web UI: ${enableHttps ? 'https' : 'http'}://localhost:${port}`));
|
|
563
|
+
if (recoveredFromStalePm2) {
|
|
564
|
+
console.log(chalk.green('[OK] 已自动修复旧 PM2 运行时路径残留'));
|
|
565
|
+
}
|
|
566
|
+
printPortToolIssue(readyState.degradedPortCheckIssue);
|
|
567
|
+
if (enableHost) {
|
|
568
|
+
console.log(chalk.yellow(`[WARN] LAN 访问已启用 (${enableHttps ? 'https' : 'http'}://<your-ip>:${port})`));
|
|
569
|
+
}
|
|
570
|
+
console.log(chalk.gray('\n可以安全关闭此终端窗口'));
|
|
571
|
+
console.log(chalk.gray('\n常用命令:'));
|
|
572
|
+
console.log(chalk.gray(' ') + chalk.cyan('ctx status') + chalk.gray(' - 查看服务状态'));
|
|
573
|
+
console.log(chalk.gray(' ') + chalk.cyan('ctx logs') + chalk.gray(' - 查看实时日志'));
|
|
574
|
+
console.log(chalk.gray(' ') + chalk.cyan('ctx stop') + chalk.gray(' - 停止服务\n'));
|
|
575
|
+
|
|
576
|
+
await finalizePM2Session({ persist: true });
|
|
358
577
|
} catch (error) {
|
|
359
578
|
console.error(chalk.red('启动失败:'), error.message);
|
|
360
579
|
disconnectPM2();
|
|
@@ -374,9 +593,7 @@ async function handleStop() {
|
|
|
374
593
|
const stopResult = await stopAllManagedInstances(existing, config);
|
|
375
594
|
printStopResult(stopResult);
|
|
376
595
|
|
|
377
|
-
|
|
378
|
-
disconnectPM2();
|
|
379
|
-
});
|
|
596
|
+
await finalizePM2Session({ persist: true });
|
|
380
597
|
} catch (error) {
|
|
381
598
|
console.error(chalk.red('停止失败:'), error.message);
|
|
382
599
|
disconnectPM2();
|
|
@@ -398,19 +615,9 @@ async function handleRestart() {
|
|
|
398
615
|
return;
|
|
399
616
|
}
|
|
400
617
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
disconnectPM2();
|
|
405
|
-
process.exit(1);
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
console.log(chalk.green('\n[OK] Coding-Tool 服务已重启\n'));
|
|
409
|
-
|
|
410
|
-
pm2.dump((err) => {
|
|
411
|
-
disconnectPM2();
|
|
412
|
-
});
|
|
413
|
-
});
|
|
618
|
+
await restartPM2Process(PM2_APP_NAME);
|
|
619
|
+
console.log(chalk.green('\n[OK] Coding-Tool 服务已重启\n'));
|
|
620
|
+
await finalizePM2Session({ persist: true });
|
|
414
621
|
} catch (error) {
|
|
415
622
|
console.error(chalk.red('重启失败:'), error.message);
|
|
416
623
|
disconnectPM2();
|
|
@@ -446,7 +653,6 @@ async function handleStatus() {
|
|
|
446
653
|
}
|
|
447
654
|
|
|
448
655
|
// 代理服务状态(从运行时文件检测)
|
|
449
|
-
const fs = require('fs');
|
|
450
656
|
ensureStorageDirMigrated();
|
|
451
657
|
const claudeActive = fs.existsSync(PATHS.activeChannel.claude);
|
|
452
658
|
const codexActive = fs.existsSync(PATHS.activeChannel.codex);
|
|
@@ -516,6 +722,8 @@ module.exports = {
|
|
|
516
722
|
handleRestart,
|
|
517
723
|
handleStatus,
|
|
518
724
|
_test: {
|
|
725
|
+
detectStalePm2RuntimeIssue,
|
|
726
|
+
extractMissingPm2ForkScriptPath,
|
|
519
727
|
shouldTreatPortOwnershipAsReady,
|
|
520
728
|
getManagedPorts,
|
|
521
729
|
shouldStopPM2Process
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const PRESET_WEBSITE_SOURCES = {
|
|
2
2
|
claude: [
|
|
3
3
|
{ presetId: 'official', baseUrl: 'https://api.anthropic.com', websiteUrl: 'https://www.anthropic.com' },
|
|
4
|
+
{ presetId: 'openai', baseUrl: 'https://api.openai.com/v1', websiteUrl: 'https://platform.openai.com' },
|
|
4
5
|
{ presetId: 'deepseek', baseUrl: 'https://api.deepseek.com/anthropic', websiteUrl: 'https://platform.deepseek.com' },
|
|
5
6
|
{ presetId: 'zhipu', baseUrl: 'https://open.bigmodel.cn/api/anthropic', websiteUrl: 'https://open.bigmodel.cn' },
|
|
6
7
|
{ presetId: 'kimi', baseUrl: 'https://api.moonshot.cn/anthropic', websiteUrl: 'https://platform.moonshot.cn' },
|
package/src/index.js
CHANGED
|
@@ -31,6 +31,12 @@ function getVersion() {
|
|
|
31
31
|
return packageJson.version;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
function finishCli(code = 0) {
|
|
35
|
+
eventBus.emitSync('cli:shutdown', {});
|
|
36
|
+
PluginManager.shutdownPlugins();
|
|
37
|
+
process.exit(code);
|
|
38
|
+
}
|
|
39
|
+
|
|
34
40
|
// 显示帮助信息
|
|
35
41
|
function showHelp() {
|
|
36
42
|
const version = getVersion();
|
|
@@ -156,19 +162,19 @@ async function main() {
|
|
|
156
162
|
|
|
157
163
|
if (subCommand === 'start') {
|
|
158
164
|
await handleStart();
|
|
159
|
-
return;
|
|
165
|
+
return finishCli(0);
|
|
160
166
|
}
|
|
161
167
|
if (subCommand === 'stop') {
|
|
162
168
|
await handleStop();
|
|
163
|
-
return;
|
|
169
|
+
return finishCli(0);
|
|
164
170
|
}
|
|
165
171
|
if (subCommand === 'restart') {
|
|
166
172
|
await handleRestart();
|
|
167
|
-
return;
|
|
173
|
+
return finishCli(0);
|
|
168
174
|
}
|
|
169
175
|
if (subCommand === 'status') {
|
|
170
176
|
await handleStatus();
|
|
171
|
-
return;
|
|
177
|
+
return finishCli(0);
|
|
172
178
|
}
|
|
173
179
|
if (subCommand === 'logs') {
|
|
174
180
|
const options = {};
|
|
@@ -213,25 +219,25 @@ async function main() {
|
|
|
213
219
|
// start 命令 - 启动服务(后台)
|
|
214
220
|
if (args[0] === 'start') {
|
|
215
221
|
await handleStart();
|
|
216
|
-
return;
|
|
222
|
+
return finishCli(0);
|
|
217
223
|
}
|
|
218
224
|
|
|
219
225
|
// stop 命令 - 停止服务
|
|
220
226
|
if (args[0] === 'stop') {
|
|
221
227
|
await handleStop();
|
|
222
|
-
return;
|
|
228
|
+
return finishCli(0);
|
|
223
229
|
}
|
|
224
230
|
|
|
225
231
|
// restart 命令 - 重启服务
|
|
226
232
|
if (args[0] === 'restart') {
|
|
227
233
|
await handleRestart();
|
|
228
|
-
return;
|
|
234
|
+
return finishCli(0);
|
|
229
235
|
}
|
|
230
236
|
|
|
231
237
|
// status 命令 - 查看服务状态
|
|
232
238
|
if (args[0] === 'status') {
|
|
233
239
|
await handleStatus();
|
|
234
|
-
return;
|
|
240
|
+
return finishCli(0);
|
|
235
241
|
}
|
|
236
242
|
|
|
237
243
|
// ui 命令 - Web UI 管理
|
|
@@ -239,10 +245,13 @@ async function main() {
|
|
|
239
245
|
const subCommand = args[1];
|
|
240
246
|
if (subCommand === 'start') {
|
|
241
247
|
await handleStart(); // UI start 实际上就是启动整个服务
|
|
248
|
+
return finishCli(0);
|
|
242
249
|
} else if (subCommand === 'stop') {
|
|
243
250
|
await handleStop();
|
|
251
|
+
return finishCli(0);
|
|
244
252
|
} else if (subCommand === 'restart') {
|
|
245
253
|
await handleRestart();
|
|
254
|
+
return finishCli(0);
|
|
246
255
|
} else {
|
|
247
256
|
// 默认前台运行
|
|
248
257
|
const { handleUI } = require('./commands/ui');
|
|
@@ -670,9 +679,7 @@ async function main() {
|
|
|
670
679
|
|
|
671
680
|
case 'exit':
|
|
672
681
|
console.log('\n[BYE] 再见!\n');
|
|
673
|
-
|
|
674
|
-
PluginManager.shutdownPlugins();
|
|
675
|
-
process.exit(0);
|
|
682
|
+
finishCli(0);
|
|
676
683
|
break;
|
|
677
684
|
|
|
678
685
|
default:
|