coding-tool-x 3.4.3 → 3.4.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/dist/web/assets/{Analytics-CbGxotgz.js → Analytics-_Byi9M6y.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-oP6nrFEb.js → ConfigTemplates-DIwosdtG.js} +1 -1
- package/dist/web/assets/{Home-DMntmEvh.js → Home-DdNMuQ9c.js} +1 -1
- package/dist/web/assets/{PluginManager-BUC_c7nH.js → PluginManager-iuY24cnW.js} +1 -1
- package/dist/web/assets/{ProjectList-oJIyIRkP.css → ProjectList-DL4JK6ci.css} +1 -1
- package/dist/web/assets/{ProjectList-CW8J49n7.js → ProjectList-DSkMulzL.js} +1 -1
- package/dist/web/assets/{SessionList-7lYnF92v.js → SessionList-B6pGquIr.js} +1 -1
- package/dist/web/assets/{SkillManager-Cs08216i.js → SkillManager-CHtQX5r8.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-CY-oGtyB.js → WorkspaceManager-gNPs-VaI.js} +1 -1
- package/dist/web/assets/index-DGjGCo37.js +2 -0
- package/dist/web/assets/{index-5qy5NMIP.css → index-pMqqe9ei.css} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/src/commands/channels.js +13 -13
- package/src/commands/cli-type.js +5 -5
- package/src/commands/daemon.js +31 -31
- package/src/commands/doctor.js +14 -14
- package/src/commands/export-config.js +23 -23
- package/src/commands/list.js +4 -4
- package/src/commands/logs.js +19 -19
- package/src/commands/plugin.js +62 -62
- package/src/commands/port-config.js +4 -4
- package/src/commands/proxy-control.js +35 -35
- package/src/commands/proxy.js +28 -28
- package/src/commands/resume.js +4 -4
- package/src/commands/search.js +9 -9
- package/src/commands/security.js +5 -5
- package/src/commands/stats.js +18 -18
- package/src/commands/switch.js +1 -1
- package/src/commands/toggle-proxy.js +18 -18
- package/src/commands/ui.js +11 -11
- package/src/commands/update.js +9 -9
- package/src/commands/workspace.js +11 -11
- package/src/index.js +24 -24
- package/src/plugins/plugin-installer.js +1 -1
- package/src/reset-config.js +9 -9
- package/src/server/api/channels.js +1 -1
- package/src/server/api/claude-hooks.js +2 -2
- package/src/server/api/plugins.js +4 -0
- package/src/server/api/pm2-autostart.js +2 -2
- package/src/server/api/proxy.js +6 -6
- package/src/server/api/skills.js +4 -0
- package/src/server/dev-server.js +2 -2
- package/src/server/index.js +37 -37
- package/src/server/proxy-server.js +4 -4
- package/src/server/services/config-export-service.js +1 -1
- package/src/server/services/mcp-service.js +2 -1
- package/src/server/services/model-detector.js +2 -2
- package/src/server/services/native-keychain.js +1 -0
- package/src/server/services/plugins-service.js +7 -27
- package/src/server/services/settings-manager.js +3 -3
- package/src/server/services/skill-service.js +4 -12
- package/src/server/websocket-server.js +8 -8
- package/src/ui/menu.js +2 -2
- package/src/ui/prompts.js +5 -5
- package/dist/web/assets/index-ClCqKpvX.js +0 -2
package/src/server/dev-server.js
CHANGED
|
@@ -13,12 +13,12 @@ const chalk = require('chalk');
|
|
|
13
13
|
const config = loadConfig();
|
|
14
14
|
const port = config.ports?.webUI || 19999;
|
|
15
15
|
|
|
16
|
-
console.log(chalk.cyan('\n
|
|
16
|
+
console.log(chalk.cyan('\n[FIX] 开发模式:启动后端 API 服务器...\n'));
|
|
17
17
|
|
|
18
18
|
(async () => {
|
|
19
19
|
await startServer(port);
|
|
20
20
|
|
|
21
|
-
console.log(chalk.yellow('
|
|
21
|
+
console.log(chalk.yellow('[TIP] 开发提示:'));
|
|
22
22
|
console.log(chalk.gray(` - 后端 API: http://localhost:${port}/api`));
|
|
23
23
|
console.log(chalk.gray(' - 前端开发服务器: http://localhost:5000'));
|
|
24
24
|
console.log(chalk.gray(' - 修改后端代码会自动重启 (nodemon)'));
|
package/src/server/index.js
CHANGED
|
@@ -36,7 +36,7 @@ function isInteractivePortConflictMode(options = {}) {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
function printPortConflictHelp(port) {
|
|
39
|
-
console.log(chalk.yellow('\n
|
|
39
|
+
console.log(chalk.yellow('\n[TIP] 解决方案:'));
|
|
40
40
|
console.log(chalk.gray(' 1. 运行 ctx 命令,选择"配置端口"修改端口'));
|
|
41
41
|
console.log(chalk.gray(` 2. 或手动关闭占用端口 ${port} 的程序\n`));
|
|
42
42
|
}
|
|
@@ -47,7 +47,7 @@ function printPortToolIssue(issue = getPortToolIssue()) {
|
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
console.error(chalk.yellow(`\n
|
|
50
|
+
console.error(chalk.yellow(`\n[TIP] ${lines[0]}`));
|
|
51
51
|
lines.slice(1).forEach((line) => {
|
|
52
52
|
console.error(chalk.gray(` ${line}`));
|
|
53
53
|
});
|
|
@@ -64,7 +64,7 @@ async function startServer(port, host = '127.0.0.1', options = {}) {
|
|
|
64
64
|
// 检查端口是否被占用
|
|
65
65
|
const portInUse = await isPortInUse(port, host);
|
|
66
66
|
if (portInUse) {
|
|
67
|
-
console.log(chalk.yellow(`\n
|
|
67
|
+
console.log(chalk.yellow(`\n[WARN] 端口 ${port} 已被占用\n`));
|
|
68
68
|
|
|
69
69
|
const interactiveMode = isInteractivePortConflictMode(options);
|
|
70
70
|
let shouldKill = false;
|
|
@@ -87,7 +87,7 @@ async function startServer(port, host = '127.0.0.1', options = {}) {
|
|
|
87
87
|
]);
|
|
88
88
|
shouldKill = answer.shouldKill;
|
|
89
89
|
} else {
|
|
90
|
-
console.error(chalk.red('
|
|
90
|
+
console.error(chalk.red('[ERROR] 当前为非交互模式,无法确认端口清理操作,已取消启动。'));
|
|
91
91
|
printPortConflictHelp(port);
|
|
92
92
|
process.exit(1);
|
|
93
93
|
}
|
|
@@ -107,9 +107,9 @@ async function startServer(port, host = '127.0.0.1', options = {}) {
|
|
|
107
107
|
if (toolIssue) {
|
|
108
108
|
printPortToolIssue(toolIssue);
|
|
109
109
|
} else {
|
|
110
|
-
console.error(chalk.red('\n
|
|
110
|
+
console.error(chalk.red('\n[ERROR] 无法关闭占用端口的进程'));
|
|
111
111
|
}
|
|
112
|
-
console.error(chalk.yellow('\n
|
|
112
|
+
console.error(chalk.yellow('\n[TIP] 请手动关闭占用端口的程序,或使用其他端口\n'));
|
|
113
113
|
process.exit(1);
|
|
114
114
|
}
|
|
115
115
|
|
|
@@ -118,12 +118,12 @@ async function startServer(port, host = '127.0.0.1', options = {}) {
|
|
|
118
118
|
const released = await waitForPortRelease(port, 3000, host);
|
|
119
119
|
|
|
120
120
|
if (!released) {
|
|
121
|
-
console.error(chalk.red('\n
|
|
122
|
-
console.error(chalk.yellow('\n
|
|
121
|
+
console.error(chalk.red('\n[ERROR] 端口释放超时'));
|
|
122
|
+
console.error(chalk.yellow('\n[TIP] 请稍后重试,或手动检查端口占用情况\n'));
|
|
123
123
|
process.exit(1);
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
console.log(chalk.green('
|
|
126
|
+
console.log(chalk.green('[v] 端口已释放\n'));
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
const app = express();
|
|
@@ -249,12 +249,12 @@ async function startServer(port, host = '127.0.0.1', options = {}) {
|
|
|
249
249
|
const onError = (err) => {
|
|
250
250
|
server.off('listening', onListening);
|
|
251
251
|
if (err.code === 'EADDRINUSE') {
|
|
252
|
-
console.error(chalk.red(`\n
|
|
253
|
-
console.error(chalk.yellow('\n
|
|
252
|
+
console.error(chalk.red(`\n[ERROR] 端口 ${port} 已被占用`));
|
|
253
|
+
console.error(chalk.yellow('\n[TIP] 解决方案:'));
|
|
254
254
|
console.error(chalk.gray(' 1. 运行 ctx 命令,选择"配置端口"修改端口'));
|
|
255
255
|
console.error(chalk.gray(` 2. 或关闭占用端口 ${port} 的程序\n`));
|
|
256
256
|
} else {
|
|
257
|
-
console.error(chalk.red(`\n
|
|
257
|
+
console.error(chalk.red(`\n[ERROR] 启动服务器失败: ${err.message}\n`));
|
|
258
258
|
}
|
|
259
259
|
process.exit(1);
|
|
260
260
|
};
|
|
@@ -263,9 +263,9 @@ async function startServer(port, host = '127.0.0.1', options = {}) {
|
|
|
263
263
|
server.once('error', onError);
|
|
264
264
|
});
|
|
265
265
|
|
|
266
|
-
console.log(`\n
|
|
266
|
+
console.log(`\n[START] Coding-Tool Web UI running at:`);
|
|
267
267
|
if (host === '0.0.0.0') {
|
|
268
|
-
console.log(chalk.yellow(`
|
|
268
|
+
console.log(chalk.yellow(` [WARN] 警告: 服务正在监听所有网络接口 (LAN 可访问)`));
|
|
269
269
|
console.log(` http://localhost:${port}`);
|
|
270
270
|
console.log(chalk.gray(` http://<your-ip>:${port} (LAN 访问)`));
|
|
271
271
|
} else {
|
|
@@ -277,7 +277,7 @@ async function startServer(port, host = '127.0.0.1', options = {}) {
|
|
|
277
277
|
console.log(` ws://localhost:${port}/ws\n`);
|
|
278
278
|
|
|
279
279
|
if (host === '0.0.0.0' && !allowRemoteMutation) {
|
|
280
|
-
console.log(chalk.yellow('
|
|
280
|
+
console.log(chalk.yellow(' [LOCK] 已启用 LAN 安全保护:远程写操作默认禁用'));
|
|
281
281
|
}
|
|
282
282
|
// 自动恢复代理状态
|
|
283
283
|
autoRestoreProxies();
|
|
@@ -296,26 +296,26 @@ function autoRestoreProxies() {
|
|
|
296
296
|
// 检查 Claude 代理状态文件
|
|
297
297
|
const claudeActiveFile = PATHS.activeChannel.claude;
|
|
298
298
|
if (fs.existsSync(claudeActiveFile)) {
|
|
299
|
-
console.log(chalk.cyan('\n
|
|
299
|
+
console.log(chalk.cyan('\n[SYNC] 检测到 Claude 代理状态文件,正在自动启动...'));
|
|
300
300
|
const proxyPort = config.ports?.proxy || 20088;
|
|
301
301
|
startProxyServer(proxyPort)
|
|
302
302
|
.then(() => {
|
|
303
|
-
console.log(chalk.green(
|
|
303
|
+
console.log(chalk.green(`[OK] Claude 代理已自动启动,端口: ${proxyPort}`));
|
|
304
304
|
})
|
|
305
305
|
.catch((err) => {
|
|
306
|
-
console.error(chalk.red(
|
|
306
|
+
console.error(chalk.red(`[ERROR] Claude 代理启动失败: ${err.message}`));
|
|
307
307
|
});
|
|
308
308
|
}
|
|
309
309
|
|
|
310
310
|
// 检查 Codex 代理状态文件
|
|
311
311
|
const codexActiveFile = PATHS.activeChannel.codex;
|
|
312
312
|
if (fs.existsSync(codexActiveFile)) {
|
|
313
|
-
console.log(chalk.cyan('\n
|
|
313
|
+
console.log(chalk.cyan('\n[SYNC] 检测到 Codex 代理状态文件,正在自动启动...'));
|
|
314
314
|
const codexProxyPort = config.ports?.codexProxy || 20089;
|
|
315
315
|
startCodexProxyServer(codexProxyPort)
|
|
316
316
|
.then((result) => {
|
|
317
317
|
const port = result?.port || codexProxyPort;
|
|
318
|
-
console.log(chalk.green(
|
|
318
|
+
console.log(chalk.green(`[OK] Codex 代理已自动启动,端口: ${port}`));
|
|
319
319
|
|
|
320
320
|
// 重启后重新写入 cc-proxy 配置与环境变量,避免缺少 provider/env 导致报错
|
|
321
321
|
try {
|
|
@@ -324,43 +324,43 @@ function autoRestoreProxies() {
|
|
|
324
324
|
console.log(chalk.gray(' 已同步 codex config.toml 与 CC_PROXY_KEY'));
|
|
325
325
|
}
|
|
326
326
|
} catch (err) {
|
|
327
|
-
console.error(chalk.red(
|
|
327
|
+
console.error(chalk.red(`[ERROR] Codex 代理配置同步失败: ${err.message}`));
|
|
328
328
|
}
|
|
329
329
|
})
|
|
330
330
|
.catch((err) => {
|
|
331
|
-
console.error(chalk.red(
|
|
331
|
+
console.error(chalk.red(`[ERROR] Codex 代理启动失败: ${err.message}`));
|
|
332
332
|
});
|
|
333
333
|
}
|
|
334
334
|
|
|
335
335
|
// 检查 Gemini 代理状态文件
|
|
336
336
|
const geminiActiveFile = PATHS.activeChannel.gemini;
|
|
337
337
|
if (fs.existsSync(geminiActiveFile)) {
|
|
338
|
-
console.log(chalk.cyan('\n
|
|
338
|
+
console.log(chalk.cyan('\n[SYNC] 检测到 Gemini 代理状态文件,正在自动启动...'));
|
|
339
339
|
const geminiProxyPort = config.ports?.geminiProxy || 20090;
|
|
340
340
|
startGeminiProxyServer(geminiProxyPort)
|
|
341
341
|
.then((result) => {
|
|
342
342
|
if (result.success) {
|
|
343
|
-
console.log(chalk.green(
|
|
343
|
+
console.log(chalk.green(`[OK] Gemini 代理已自动启动,端口: ${result.port}`));
|
|
344
344
|
} else {
|
|
345
|
-
console.error(chalk.red(
|
|
345
|
+
console.error(chalk.red(`[ERROR] Gemini 代理启动失败: ${result.error || 'Unknown error'}`));
|
|
346
346
|
}
|
|
347
347
|
})
|
|
348
348
|
.catch((err) => {
|
|
349
|
-
console.error(chalk.red(
|
|
349
|
+
console.error(chalk.red(`[ERROR] Gemini 代理启动失败: ${err.message}`));
|
|
350
350
|
});
|
|
351
351
|
} else {
|
|
352
|
-
console.log(chalk.gray('\n
|
|
352
|
+
console.log(chalk.gray('\n[TIP] 提示: 如需使用 Gemini 代理,请在前端界面激活 Gemini 渠道'));
|
|
353
353
|
}
|
|
354
354
|
|
|
355
355
|
// 检查 OpenCode 代理状态文件
|
|
356
356
|
const opencodeActiveFile = PATHS.activeChannel.opencode;
|
|
357
357
|
if (fs.existsSync(opencodeActiveFile)) {
|
|
358
|
-
console.log(chalk.cyan('\n
|
|
358
|
+
console.log(chalk.cyan('\n[SYNC] 检测到 OpenCode 代理状态文件,正在自动启动...'));
|
|
359
359
|
const opencodeProxyPort = config.ports?.opencodeProxy || 20091;
|
|
360
360
|
startOpenCodeProxyServer(opencodeProxyPort)
|
|
361
361
|
.then(async (result) => {
|
|
362
362
|
if (result.success) {
|
|
363
|
-
console.log(chalk.green(
|
|
363
|
+
console.log(chalk.green(`[OK] OpenCode 代理已自动启动,端口: ${result.port}`));
|
|
364
364
|
try {
|
|
365
365
|
const { getEnabledChannels: getEnabledOpenCodeChannels } = require('./services/opencode-channels');
|
|
366
366
|
const enabledChs = getEnabledOpenCodeChannels();
|
|
@@ -390,14 +390,14 @@ function autoRestoreProxies() {
|
|
|
390
390
|
console.log(chalk.gray(' 已同步 OpenCode 配置文件'));
|
|
391
391
|
}
|
|
392
392
|
} catch (err) {
|
|
393
|
-
console.error(chalk.red(
|
|
393
|
+
console.error(chalk.red(`[ERROR] OpenCode 代理配置同步失败: ${err.message}`));
|
|
394
394
|
}
|
|
395
395
|
} else {
|
|
396
|
-
console.error(chalk.red(
|
|
396
|
+
console.error(chalk.red(`[ERROR] OpenCode 代理启动失败: ${result.error || 'Unknown error'}`));
|
|
397
397
|
}
|
|
398
398
|
})
|
|
399
399
|
.catch((err) => {
|
|
400
|
-
console.error(chalk.red(
|
|
400
|
+
console.error(chalk.red(`[ERROR] OpenCode 代理启动失败: ${err.message}`));
|
|
401
401
|
});
|
|
402
402
|
}
|
|
403
403
|
}
|
|
@@ -408,7 +408,7 @@ async function performStartupHealthCheck() {
|
|
|
408
408
|
const { getProjects } = require('./services/sessions');
|
|
409
409
|
|
|
410
410
|
try {
|
|
411
|
-
console.log(chalk.cyan('\n
|
|
411
|
+
console.log(chalk.cyan('\n[SEARCH] 正在进行启动健康检查...'));
|
|
412
412
|
|
|
413
413
|
// 获取所有项目
|
|
414
414
|
const config = loadConfig();
|
|
@@ -423,20 +423,20 @@ async function performStartupHealthCheck() {
|
|
|
423
423
|
const healthResult = healthCheckAllProjects(projects);
|
|
424
424
|
|
|
425
425
|
if (healthResult.summary.created > 0) {
|
|
426
|
-
console.log(chalk.green(`
|
|
426
|
+
console.log(chalk.green(` [v] 已为 ${healthResult.summary.created} 个项目创建 .claude/sessions 目录`));
|
|
427
427
|
}
|
|
428
428
|
|
|
429
429
|
if (healthResult.summary.errors > 0) {
|
|
430
|
-
console.log(chalk.yellow(`
|
|
430
|
+
console.log(chalk.yellow(` [!] ${healthResult.summary.errors} 个项目检查失败`));
|
|
431
431
|
}
|
|
432
432
|
|
|
433
433
|
if (healthResult.summary.created === 0 && healthResult.summary.errors === 0) {
|
|
434
|
-
console.log(chalk.green(`
|
|
434
|
+
console.log(chalk.green(` [v] 所有 ${healthResult.summary.healthy} 个项目状态正常`));
|
|
435
435
|
}
|
|
436
436
|
|
|
437
437
|
console.log('');
|
|
438
438
|
} catch (err) {
|
|
439
|
-
console.error(chalk.red('
|
|
439
|
+
console.error(chalk.red(' [x] 健康检查失败:'), err.message);
|
|
440
440
|
}
|
|
441
441
|
}
|
|
442
442
|
|
|
@@ -544,7 +544,7 @@ async function startProxyServer(options = {}) {
|
|
|
544
544
|
|
|
545
545
|
return new Promise((resolve, reject) => {
|
|
546
546
|
proxyServer.listen(port, '127.0.0.1', () => {
|
|
547
|
-
console.log(
|
|
547
|
+
console.log(`[OK] Proxy server started on http://127.0.0.1:${port}`);
|
|
548
548
|
saveProxyStartTime('claude', preserveStartTime);
|
|
549
549
|
eventBus.emitSync('proxy:start', { channel: 'claude', port });
|
|
550
550
|
resolve({ success: true, port });
|
|
@@ -552,8 +552,8 @@ async function startProxyServer(options = {}) {
|
|
|
552
552
|
|
|
553
553
|
proxyServer.on('error', (err) => {
|
|
554
554
|
if (err.code === 'EADDRINUSE') {
|
|
555
|
-
console.error(chalk.red(`\n
|
|
556
|
-
console.error(chalk.yellow('\n
|
|
555
|
+
console.error(chalk.red(`\n[ERROR] 代理服务端口 ${port} 已被占用`));
|
|
556
|
+
console.error(chalk.yellow('\n[TIP] 解决方案:'));
|
|
557
557
|
console.error(chalk.gray(' 1. 运行 ctx 命令,选择"配置端口"修改端口'));
|
|
558
558
|
console.error(chalk.gray(` 2. 或关闭占用端口 ${port} 的程序\n`));
|
|
559
559
|
} else {
|
|
@@ -582,7 +582,7 @@ async function stopProxyServer(options = {}) {
|
|
|
582
582
|
|
|
583
583
|
return new Promise((resolve) => {
|
|
584
584
|
proxyServer.close(() => {
|
|
585
|
-
console.log('
|
|
585
|
+
console.log('[OK] Proxy server stopped');
|
|
586
586
|
if (clearStartTime) {
|
|
587
587
|
clearProxyStartTime('claude');
|
|
588
588
|
}
|
|
@@ -890,12 +890,12 @@ async function probeModelAvailability(channel, channelType, options = {}) {
|
|
|
890
890
|
|
|
891
891
|
if (isAvailable) {
|
|
892
892
|
availableModels.push(model);
|
|
893
|
-
console.log(`[ModelDetector]
|
|
893
|
+
console.log(`[ModelDetector] [v] ${model} available`);
|
|
894
894
|
if (stopOnFirstAvailable) {
|
|
895
895
|
break;
|
|
896
896
|
}
|
|
897
897
|
} else {
|
|
898
|
-
console.log(`[ModelDetector]
|
|
898
|
+
console.log(`[ModelDetector] [x] ${model} not available${formatProbeFailureDetail(probeResult.failureDetail)}`);
|
|
899
899
|
}
|
|
900
900
|
}
|
|
901
901
|
|
|
@@ -18,32 +18,7 @@ const CLAUDE_MARKETPLACES_FILE = path.join(CLAUDE_PLUGINS_DIR, 'known_marketplac
|
|
|
18
18
|
const OPENCODE_CONFIG_DIR = NATIVE_PATHS.opencode.config;
|
|
19
19
|
const DEFAULT_REPOS_BY_PLATFORM = {
|
|
20
20
|
claude: [],
|
|
21
|
-
opencode: [
|
|
22
|
-
{
|
|
23
|
-
owner: 'Tommertom',
|
|
24
|
-
name: 'opencode-plugin-marketplace',
|
|
25
|
-
url: 'https://github.com/Tommertom/opencode-plugin-marketplace',
|
|
26
|
-
branch: 'main',
|
|
27
|
-
enabled: true,
|
|
28
|
-
source: 'opencode-default'
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
owner: 'avifenesh',
|
|
32
|
-
name: 'awesome-slash',
|
|
33
|
-
url: 'https://github.com/avifenesh/awesome-slash',
|
|
34
|
-
branch: 'main',
|
|
35
|
-
enabled: true,
|
|
36
|
-
source: 'opencode-default'
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
owner: 'NeoLabHQ',
|
|
40
|
-
name: 'context-engineering-kit',
|
|
41
|
-
url: 'https://github.com/NeoLabHQ/context-engineering-kit',
|
|
42
|
-
branch: 'master',
|
|
43
|
-
enabled: true,
|
|
44
|
-
source: 'opencode-default'
|
|
45
|
-
}
|
|
46
|
-
]
|
|
21
|
+
opencode: []
|
|
47
22
|
};
|
|
48
23
|
|
|
49
24
|
function cloneRepos(repos = []) {
|
|
@@ -1249,6 +1224,8 @@ class PluginsService {
|
|
|
1249
1224
|
let repoFailureCount = 0;
|
|
1250
1225
|
|
|
1251
1226
|
for (const repo of repos) {
|
|
1227
|
+
const repoLabel = repo.owner ? `${repo.owner}/${repo.name}` : repo.url;
|
|
1228
|
+
const pluginsBefore = marketPlugins.length;
|
|
1252
1229
|
try {
|
|
1253
1230
|
const branch = repo.branch || 'main';
|
|
1254
1231
|
|
|
@@ -1340,8 +1317,11 @@ class PluginsService {
|
|
|
1340
1317
|
}
|
|
1341
1318
|
} catch (err) {
|
|
1342
1319
|
repoFailureCount++;
|
|
1343
|
-
console.error(`[PluginsService] Failed to fetch plugins from ${
|
|
1320
|
+
console.error(`[PluginsService] Failed to fetch plugins from ${repoLabel}:`, err.message);
|
|
1321
|
+
continue;
|
|
1344
1322
|
}
|
|
1323
|
+
const added = marketPlugins.length - pluginsBefore;
|
|
1324
|
+
console.log(`[PluginsService] ${repoLabel}: ${added} plugins loaded`);
|
|
1345
1325
|
}
|
|
1346
1326
|
|
|
1347
1327
|
const preparedPlugins = this.prepareMarketPlugins(marketPlugins);
|
|
@@ -57,7 +57,7 @@ function backupSettings() {
|
|
|
57
57
|
const content = fs.readFileSync(getSettingsPath(), 'utf8');
|
|
58
58
|
fs.writeFileSync(getBackupPath(), content, 'utf8');
|
|
59
59
|
|
|
60
|
-
console.log('
|
|
60
|
+
console.log('[OK] Settings backed up to:', getBackupPath());
|
|
61
61
|
return { success: true, alreadyExists: false };
|
|
62
62
|
} catch (err) {
|
|
63
63
|
throw new Error('Failed to backup settings: ' + err.message);
|
|
@@ -77,7 +77,7 @@ function restoreSettings() {
|
|
|
77
77
|
// 删除备份文件
|
|
78
78
|
fs.unlinkSync(getBackupPath());
|
|
79
79
|
|
|
80
|
-
console.log('
|
|
80
|
+
console.log('[OK] Settings restored from backup');
|
|
81
81
|
return { success: true };
|
|
82
82
|
} catch (err) {
|
|
83
83
|
throw new Error('Failed to restore settings: ' + err.message);
|
|
@@ -118,7 +118,7 @@ function setProxyConfig(proxyPort) {
|
|
|
118
118
|
// 写入
|
|
119
119
|
writeSettings(settings);
|
|
120
120
|
|
|
121
|
-
console.log(
|
|
121
|
+
console.log(`[OK] Settings updated to use proxy on port ${proxyPort}`);
|
|
122
122
|
return { success: true, port: proxyPort };
|
|
123
123
|
} catch (err) {
|
|
124
124
|
throw new Error('Failed to set proxy config: ' + err.message);
|
|
@@ -153,18 +153,10 @@ function isRootSkillFile(filePath = '') {
|
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
const DEFAULT_REPOS_BY_PLATFORM = {
|
|
156
|
-
claude: [
|
|
157
|
-
|
|
158
|
-
],
|
|
159
|
-
|
|
160
|
-
{ owner: 'openai', name: 'skills', branch: 'main', directory: 'skills/.curated', enabled: true }
|
|
161
|
-
],
|
|
162
|
-
gemini: [
|
|
163
|
-
{ owner: 'google-gemini', name: 'gemini-cli', branch: 'main', directory: '.gemini/skills', enabled: true }
|
|
164
|
-
],
|
|
165
|
-
opencode: [
|
|
166
|
-
{ owner: 'Shakudo-io', name: 'opencode-skills', branch: 'main', directory: '', enabled: true }
|
|
167
|
-
]
|
|
156
|
+
claude: [],
|
|
157
|
+
codex: [],
|
|
158
|
+
gemini: [],
|
|
159
|
+
opencode: []
|
|
168
160
|
};
|
|
169
161
|
|
|
170
162
|
const PLATFORM_CONFIG = {
|
|
@@ -291,7 +291,7 @@ function startWebSocketServer(httpServer, options = {}) {
|
|
|
291
291
|
acc[source] = (acc[source] || 0) + 1;
|
|
292
292
|
return acc;
|
|
293
293
|
}, {});
|
|
294
|
-
console.log(
|
|
294
|
+
console.log(`[NOTE] Loaded ${logsCache.length} persisted logs today ->`, counts);
|
|
295
295
|
|
|
296
296
|
try {
|
|
297
297
|
// 如果传入的是 HTTP server,则附加到该服务器;否则创建独立的 WebSocket 服务器
|
|
@@ -301,7 +301,7 @@ function startWebSocketServer(httpServer, options = {}) {
|
|
|
301
301
|
path: '/ws' // 指定 WebSocket 路径
|
|
302
302
|
});
|
|
303
303
|
installOriginGuard(wss);
|
|
304
|
-
console.log(
|
|
304
|
+
console.log(`[OK] WebSocket server attached to HTTP server at /ws`);
|
|
305
305
|
} else {
|
|
306
306
|
// 创建独立的 WebSocket 服务器,使用配置的 webUI 端口
|
|
307
307
|
const config = loadConfig();
|
|
@@ -311,7 +311,7 @@ function startWebSocketServer(httpServer, options = {}) {
|
|
|
311
311
|
path: '/ws'
|
|
312
312
|
});
|
|
313
313
|
installOriginGuard(wss);
|
|
314
|
-
console.log(
|
|
314
|
+
console.log(`[OK] WebSocket server started on ws://127.0.0.1:${port}/ws`);
|
|
315
315
|
}
|
|
316
316
|
|
|
317
317
|
wss.on('connection', (ws, req) => {
|
|
@@ -352,7 +352,7 @@ function startWebSocketServer(httpServer, options = {}) {
|
|
|
352
352
|
wsClients.forEach(ws => {
|
|
353
353
|
if (ws.isAlive === false) {
|
|
354
354
|
// 客户端没有响应 pong,断开连接
|
|
355
|
-
console.log('
|
|
355
|
+
console.log('[ERROR] WebSocket client timeout, terminating');
|
|
356
356
|
wsClients.delete(ws);
|
|
357
357
|
return ws.terminate();
|
|
358
358
|
}
|
|
@@ -369,8 +369,8 @@ function startWebSocketServer(httpServer, options = {}) {
|
|
|
369
369
|
wss.on('error', (error) => {
|
|
370
370
|
console.error('WebSocket server error:', error);
|
|
371
371
|
if (error.code === 'EADDRINUSE') {
|
|
372
|
-
console.error(chalk.red('\n
|
|
373
|
-
console.error(chalk.yellow('\n
|
|
372
|
+
console.error(chalk.red('\n[ERROR] WebSocket 端口已被占用'));
|
|
373
|
+
console.error(chalk.yellow('\n[TIP] 请检查端口配置\n'));
|
|
374
374
|
wss = null;
|
|
375
375
|
}
|
|
376
376
|
});
|
|
@@ -400,7 +400,7 @@ function stopWebSocketServer() {
|
|
|
400
400
|
|
|
401
401
|
// 关闭服务器
|
|
402
402
|
wss.close(() => {
|
|
403
|
-
console.log('
|
|
403
|
+
console.log('[OK] WebSocket server stopped');
|
|
404
404
|
});
|
|
405
405
|
|
|
406
406
|
wss = null;
|
|
@@ -443,7 +443,7 @@ function broadcastLog(logData) {
|
|
|
443
443
|
function clearAllLogs() {
|
|
444
444
|
logsCache = [];
|
|
445
445
|
saveLogsToFile([]);
|
|
446
|
-
console.log('
|
|
446
|
+
console.log('[OK] All logs cleared');
|
|
447
447
|
}
|
|
448
448
|
|
|
449
449
|
// 去掉敏感字段
|
package/src/ui/menu.js
CHANGED
|
@@ -50,9 +50,9 @@ function getChannelAndProxyStatus(cliType) {
|
|
|
50
50
|
* 显示主菜单
|
|
51
51
|
*/
|
|
52
52
|
async function showMainMenu(config) {
|
|
53
|
-
console.log(chalk.bold.cyan('\n
|
|
53
|
+
console.log(chalk.bold.cyan('\n╔===============================================╗'));
|
|
54
54
|
console.log(chalk.bold.cyan(`║ Claude Code 会话管理工具 v${packageInfo.version} ║`));
|
|
55
|
-
console.log(chalk.bold.cyan('
|
|
55
|
+
console.log(chalk.bold.cyan('╚===============================================╝\n'));
|
|
56
56
|
|
|
57
57
|
// 显示当前CLI类型
|
|
58
58
|
const cliTypes = {
|
package/src/ui/prompts.js
CHANGED
|
@@ -31,15 +31,15 @@ async function promptForkConfirm() {
|
|
|
31
31
|
default: 'continue',
|
|
32
32
|
choices: [
|
|
33
33
|
{
|
|
34
|
-
name: chalk.green('
|
|
34
|
+
name: chalk.green('[NOTE] 继续原会话 (推荐) - 在原会话上继续对话,所有内容会追加到原文件'),
|
|
35
35
|
value: 'continue',
|
|
36
36
|
},
|
|
37
37
|
{
|
|
38
|
-
name: chalk.yellow('
|
|
38
|
+
name: chalk.yellow('[FORK] 创建新分支 (Fork) - 基于原会话创建新会话,保留原会话不变'),
|
|
39
39
|
value: 'fork',
|
|
40
40
|
},
|
|
41
41
|
new inquirer.Separator(chalk.gray('─'.repeat(14))),
|
|
42
|
-
{ name: chalk.blue('
|
|
42
|
+
{ name: chalk.blue('[<-] 返回重新选择'), value: 'back' },
|
|
43
43
|
],
|
|
44
44
|
},
|
|
45
45
|
]);
|
|
@@ -55,7 +55,7 @@ async function promptSearchKeyword() {
|
|
|
55
55
|
{
|
|
56
56
|
type: 'input',
|
|
57
57
|
name: 'keyword',
|
|
58
|
-
message: chalk.cyan('
|
|
58
|
+
message: chalk.cyan('[SEARCH] 输入搜索关键词:'),
|
|
59
59
|
validate: (input) => {
|
|
60
60
|
if (!input.trim()) {
|
|
61
61
|
return '请输入搜索关键词';
|
|
@@ -76,7 +76,7 @@ async function promptSelectProject(projects) {
|
|
|
76
76
|
const choices = [
|
|
77
77
|
...projects,
|
|
78
78
|
new inquirer.Separator(chalk.gray('─'.repeat(14))),
|
|
79
|
-
{ name: chalk.gray('
|
|
79
|
+
{ name: chalk.gray('[<-] 取消切换'), value: null }
|
|
80
80
|
];
|
|
81
81
|
|
|
82
82
|
const { project } = await inquirer.prompt([
|