coding-tool-x 3.6.13 → 3.7.1
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/README.md +45 -33
- package/dist/web/assets/{Analytics-CnA75GxO.js → Analytics-Dpje9L84.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-D8XEscQY.js → ConfigTemplates-Cz6kasek.js} +1 -1
- package/dist/web/assets/Home-BwTaYu3q.js +1 -0
- package/dist/web/assets/Home-Jom_ZcdY.css +1 -0
- package/dist/web/assets/{PluginManager-BzKZ8TYu.js → PluginManager-DsN-xNXx.js} +1 -1
- package/dist/web/assets/ProjectList-4BCObimU.js +1 -0
- package/dist/web/assets/{ProjectList-DL4JK6ci.css → ProjectList-D3a0oRum.css} +1 -1
- package/dist/web/assets/SessionList-D4c8FRzq.js +1 -0
- package/dist/web/assets/{SkillManager-fxOvFTYD.js → SkillManager-C-KYbXB5.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-Ch2mM8jm.js → WorkspaceManager-B-BlPJHX.js} +1 -1
- package/dist/web/assets/{icons-QFXzEgqM.js → icons-Dop5j7Bf.js} +1 -1
- package/dist/web/assets/index-Btfvh0GG.js +2 -0
- package/dist/web/assets/index-CLNa6DhH.css +1 -0
- package/dist/web/assets/{naive-ui-kcWDeJwl.js → naive-ui-CVyvdmg1.js} +1 -1
- package/dist/web/index.html +4 -4
- package/package.json +3 -1
- package/src/commands/channels.js +85 -24
- package/src/commands/daemon.js +7 -4
- package/src/commands/doctor.js +17 -8
- package/src/commands/logs.js +46 -6
- package/src/commands/port-config.js +83 -75
- package/src/commands/proxy-control.js +59 -26
- package/src/commands/stats.js +37 -19
- package/src/commands/switch.js +5 -1
- package/src/commands/toggle-proxy.js +131 -53
- package/src/commands/ui.js +3 -4
- package/src/commands/workspace.js +3 -35
- package/src/config/default.js +19 -2
- package/src/config/loader.js +5 -76
- package/src/config/paths.js +96 -11
- package/src/index.js +10 -16
- package/src/server/api/commands.js +1 -1
- package/src/server/api/config-export.js +4 -2
- package/src/server/api/config-registry.js +40 -34
- package/src/server/api/config.js +5 -1
- package/src/server/api/dashboard.js +22 -6
- package/src/server/api/mcp.js +5 -3
- package/src/server/api/omp-channels.js +349 -0
- package/src/server/api/omp-config.js +164 -0
- package/src/server/api/omp-projects.js +76 -0
- package/src/server/api/omp-proxy.js +154 -0
- package/src/server/api/omp-sessions.js +334 -0
- package/src/server/api/omp-statistics.js +40 -0
- package/src/server/api/plugins.js +1 -1
- package/src/server/api/prompts.js +3 -2
- package/src/server/api/settings.js +2 -0
- package/src/server/api/skills.js +1 -1
- package/src/server/api/workspaces.js +3 -41
- package/src/server/index.js +41 -0
- package/src/server/omp-proxy-server.js +66 -0
- package/src/server/services/agents-service.js +2 -1
- package/src/server/services/base/proxy-utils.js +1 -0
- package/src/server/services/channel-balance.js +151 -1
- package/src/server/services/channel-scheduler.js +7 -1
- package/src/server/services/commands-service.js +11 -2
- package/src/server/services/config-export-service.js +296 -39
- package/src/server/services/config-registry-service.js +20 -18
- package/src/server/services/config-sync-manager.js +115 -6
- package/src/server/services/config-sync-service.js +4 -2
- package/src/server/services/config-templates-service.js +183 -45
- package/src/server/services/mcp-service.js +217 -33
- package/src/server/services/notification-hooks.js +138 -6
- package/src/server/services/omp-auth-providers.js +294 -0
- package/src/server/services/omp-channels.js +94 -0
- package/src/server/services/omp-config.js +269 -0
- package/src/server/services/omp-sessions.js +490 -0
- package/src/server/services/omp-settings-manager.js +975 -0
- package/src/server/services/omp-statistics-service.js +317 -0
- package/src/server/services/plugins-service.js +673 -11
- package/src/server/services/prompts-service.js +112 -7
- package/src/server/services/proxy-log-helper.js +1 -0
- package/src/server/services/proxy-runtime.js +5 -1
- package/src/server/services/sessions.js +19 -35
- package/src/server/services/skill-service.js +13 -3
- package/src/server/services/speed-test.js +34 -28
- package/src/server/services/ui-config.js +50 -12
- package/src/server/services/workspace-service.js +117 -225
- package/src/server/websocket-server.js +7 -1
- package/src/shared/platforms.js +201 -0
- package/src/utils/session.js +4 -10
- package/dist/web/assets/Home-BiaKZMah.js +0 -1
- package/dist/web/assets/Home-CIua-b-o.css +0 -1
- package/dist/web/assets/ProjectList-BPfG_92X.js +0 -1
- package/dist/web/assets/SessionList-BIVtw00v.js +0 -1
- package/dist/web/assets/index-BpBFF3b2.js +0 -2
- package/dist/web/assets/index-Dsh4Q5Cg.css +0 -1
- package/src/utils/cli-flags.js +0 -7
package/dist/web/index.html
CHANGED
|
@@ -5,14 +5,14 @@
|
|
|
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-Btfvh0GG.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
|
-
<link rel="modulepreload" crossorigin href="/assets/naive-ui-
|
|
12
|
-
<link rel="modulepreload" crossorigin href="/assets/icons-
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/naive-ui-CVyvdmg1.js">
|
|
12
|
+
<link rel="modulepreload" crossorigin href="/assets/icons-Dop5j7Bf.js">
|
|
13
13
|
<link rel="modulepreload" crossorigin href="/assets/vendors-DQA4Hw9v.js">
|
|
14
14
|
<link rel="stylesheet" crossorigin href="/assets/markdown-BfC0goYb.css">
|
|
15
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
15
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CLNa6DhH.css">
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
|
18
18
|
<div id="app"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "coding-tool-x",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.1",
|
|
4
4
|
"description": "Vibe Coding 增强工作助手 - 智能会话管理、动态渠道切换、全局搜索、实时监控",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"src/config/",
|
|
46
46
|
"src/plugins/",
|
|
47
47
|
"src/server/",
|
|
48
|
+
"src/shared/",
|
|
48
49
|
"src/ui/",
|
|
49
50
|
"src/utils/",
|
|
50
51
|
"src/index.js",
|
|
@@ -64,6 +65,7 @@
|
|
|
64
65
|
"http-proxy": "^1.18.1",
|
|
65
66
|
"https-proxy-agent": "^7.0.6",
|
|
66
67
|
"inquirer": "^8.2.7",
|
|
68
|
+
"js-yaml": "^4.1.1",
|
|
67
69
|
"open": "^8.4.2",
|
|
68
70
|
"ora": "^5.4.1",
|
|
69
71
|
"pm2": "^6.0.14",
|
package/src/commands/channels.js
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
const chalk = require('chalk');
|
|
3
3
|
const inquirer = require('inquirer');
|
|
4
4
|
const { loadConfig } = require('../config/loader');
|
|
5
|
+
const { normalizePlatformKey } = require('../shared/platforms');
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* 获取当前类型的渠道服务
|
|
8
9
|
*/
|
|
9
10
|
function getChannelServices(cliType) {
|
|
10
|
-
|
|
11
|
+
const normalizedCliType = normalizePlatformKey(cliType || 'claude');
|
|
12
|
+
if (normalizedCliType === 'claude') {
|
|
11
13
|
const {
|
|
12
14
|
getAllChannels,
|
|
13
15
|
createChannel,
|
|
@@ -15,7 +17,7 @@ function getChannelServices(cliType) {
|
|
|
15
17
|
} = require('../server/services/channels');
|
|
16
18
|
const { getProxyStatus } = require('../server/proxy-server');
|
|
17
19
|
return { getAllChannels, createChannel, updateChannel, getProxyStatus };
|
|
18
|
-
} else if (
|
|
20
|
+
} else if (normalizedCliType === 'codex') {
|
|
19
21
|
const {
|
|
20
22
|
getChannels,
|
|
21
23
|
createChannel,
|
|
@@ -31,7 +33,7 @@ function getChannelServices(cliType) {
|
|
|
31
33
|
updateChannel,
|
|
32
34
|
getProxyStatus: getCodexProxyStatus
|
|
33
35
|
};
|
|
34
|
-
} else if (
|
|
36
|
+
} else if (normalizedCliType === 'gemini') {
|
|
35
37
|
const {
|
|
36
38
|
getChannels,
|
|
37
39
|
createChannel,
|
|
@@ -47,7 +49,7 @@ function getChannelServices(cliType) {
|
|
|
47
49
|
updateChannel,
|
|
48
50
|
getProxyStatus: getGeminiProxyStatus
|
|
49
51
|
};
|
|
50
|
-
} else if (
|
|
52
|
+
} else if (normalizedCliType === 'opencode') {
|
|
51
53
|
const {
|
|
52
54
|
getChannels,
|
|
53
55
|
createChannel,
|
|
@@ -63,7 +65,27 @@ function getChannelServices(cliType) {
|
|
|
63
65
|
updateChannel,
|
|
64
66
|
getProxyStatus: getOpenCodeProxyStatus
|
|
65
67
|
};
|
|
68
|
+
} else if (normalizedCliType === 'omp') {
|
|
69
|
+
const {
|
|
70
|
+
getChannels,
|
|
71
|
+
createChannel,
|
|
72
|
+
updateChannel
|
|
73
|
+
} = require('../server/services/omp-channels');
|
|
74
|
+
const { getOmpProxyStatus } = require('../server/omp-proxy-server');
|
|
75
|
+
return {
|
|
76
|
+
getAllChannels: () => {
|
|
77
|
+
const result = getChannels();
|
|
78
|
+
return Array.isArray(result?.channels) ? result.channels : [];
|
|
79
|
+
},
|
|
80
|
+
createChannel,
|
|
81
|
+
updateChannel,
|
|
82
|
+
getProxyStatus: getOmpProxyStatus,
|
|
83
|
+
supportsCliCreate: false,
|
|
84
|
+
managedProviderConfig: true
|
|
85
|
+
};
|
|
66
86
|
}
|
|
87
|
+
|
|
88
|
+
return null;
|
|
67
89
|
}
|
|
68
90
|
|
|
69
91
|
/**
|
|
@@ -76,13 +98,8 @@ async function handleChannelManagement() {
|
|
|
76
98
|
console.log(chalk.bold.cyan('╚=======================================╝\n'));
|
|
77
99
|
|
|
78
100
|
const config = loadConfig();
|
|
79
|
-
const cliType = config.currentCliType || 'claude';
|
|
101
|
+
const cliType = normalizePlatformKey(config.currentCliType || 'claude');
|
|
80
102
|
const services = getChannelServices(cliType);
|
|
81
|
-
if (!services || typeof services.createChannel !== 'function') {
|
|
82
|
-
console.log(chalk.red(`当前 CLI 类型 (${cliType}) 暂不支持添加渠道`));
|
|
83
|
-
await inquirer.prompt([{ type: 'input', name: 'continue', message: '按回车返回...' }]);
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
103
|
if (!services || typeof services.getAllChannels !== 'function') {
|
|
87
104
|
console.log(chalk.red(`当前 CLI 类型 (${cliType}) 暂不支持渠道管理`));
|
|
88
105
|
await inquirer.prompt([{ type: 'input', name: 'continue', message: '按回车返回...' }]);
|
|
@@ -93,7 +110,13 @@ async function handleChannelManagement() {
|
|
|
93
110
|
|
|
94
111
|
if (channels.length === 0) {
|
|
95
112
|
console.log(chalk.yellow('还没有添加任何渠道'));
|
|
96
|
-
|
|
113
|
+
if (cliType === 'omp') {
|
|
114
|
+
console.log(chalk.gray('提示: OMP 渠道请通过 Web UI 或 API 添加后,再在这里启停调度。\n'));
|
|
115
|
+
} else if (services.supportsCliCreate === false || typeof services.createChannel !== 'function') {
|
|
116
|
+
console.log(chalk.gray('提示: 当前平台不支持在 CLI 中添加渠道,请通过 Web UI 添加。\n'));
|
|
117
|
+
} else {
|
|
118
|
+
console.log(chalk.gray('提示: 使用主菜单的"添加渠道"功能来添加新渠道\n'));
|
|
119
|
+
}
|
|
97
120
|
|
|
98
121
|
const { action } = await inquirer.prompt([
|
|
99
122
|
{
|
|
@@ -129,9 +152,19 @@ async function handleAddChannel() {
|
|
|
129
152
|
console.log(chalk.bold.cyan('╚=======================================╝\n'));
|
|
130
153
|
|
|
131
154
|
const config = loadConfig();
|
|
132
|
-
const cliType = config.currentCliType || 'claude';
|
|
155
|
+
const cliType = normalizePlatformKey(config.currentCliType || 'claude');
|
|
133
156
|
const services = getChannelServices(cliType);
|
|
134
157
|
|
|
158
|
+
if (!services || services.supportsCliCreate === false || typeof services.createChannel !== 'function') {
|
|
159
|
+
if (cliType === 'omp') {
|
|
160
|
+
console.log(chalk.yellow('OMP 渠道创建/编辑需要使用 Web UI 或 API,以便配置 providerKey、providerApi、模型白名单等 OMP 字段。\n'));
|
|
161
|
+
} else {
|
|
162
|
+
console.log(chalk.red(`当前 CLI 类型 (${cliType}) 暂不支持添加渠道\n`));
|
|
163
|
+
}
|
|
164
|
+
await inquirer.prompt([{ type: 'input', name: 'continue', message: '按回车返回...' }]);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
135
168
|
const answers = await inquirer.prompt([
|
|
136
169
|
{
|
|
137
170
|
type: 'input',
|
|
@@ -280,12 +313,7 @@ async function handleChannelStatus() {
|
|
|
280
313
|
|
|
281
314
|
try {
|
|
282
315
|
const { getSchedulerState } = require('../server/services/channel-scheduler');
|
|
283
|
-
const sources =
|
|
284
|
-
{ key: 'claude', label: 'Claude (Claude Code)' },
|
|
285
|
-
{ key: 'codex', label: 'Codex (OpenAI)' },
|
|
286
|
-
{ key: 'gemini', label: 'Gemini' },
|
|
287
|
-
{ key: 'opencode', label: 'OpenCode' }
|
|
288
|
-
];
|
|
316
|
+
const sources = buildSchedulerSources();
|
|
289
317
|
|
|
290
318
|
sources.forEach((source) => {
|
|
291
319
|
const state = getSchedulerState(source.key);
|
|
@@ -317,13 +345,18 @@ async function handleChannelStatus() {
|
|
|
317
345
|
module.exports = {
|
|
318
346
|
handleChannelManagement,
|
|
319
347
|
handleAddChannel,
|
|
320
|
-
handleChannelStatus
|
|
348
|
+
handleChannelStatus,
|
|
349
|
+
_test: {
|
|
350
|
+
buildSchedulerSources,
|
|
351
|
+
getChannelServices
|
|
352
|
+
}
|
|
321
353
|
};
|
|
322
354
|
|
|
323
355
|
/**
|
|
324
356
|
* 统一的多渠道选择处理
|
|
325
357
|
*/
|
|
326
358
|
async function handleChannelToggle(channels, services, cliType) {
|
|
359
|
+
const normalizedCliType = normalizePlatformKey(cliType || 'claude');
|
|
327
360
|
const choices = channels.map(channel => {
|
|
328
361
|
const enabled = channel.enabled !== false;
|
|
329
362
|
const detailParts = [];
|
|
@@ -333,14 +366,31 @@ async function handleChannelToggle(channels, services, cliType) {
|
|
|
333
366
|
detailParts.push(cleaned);
|
|
334
367
|
}
|
|
335
368
|
|
|
336
|
-
if (
|
|
369
|
+
if (normalizedCliType === 'codex' && channel.providerKey) {
|
|
337
370
|
detailParts.push(`provider ${channel.providerKey}`);
|
|
338
371
|
}
|
|
339
372
|
|
|
340
|
-
if (
|
|
373
|
+
if (normalizedCliType === 'gemini' && channel.model) {
|
|
341
374
|
detailParts.push(`model ${channel.model}`);
|
|
342
375
|
}
|
|
343
376
|
|
|
377
|
+
if (normalizedCliType === 'omp') {
|
|
378
|
+
if (channel.providerKey) {
|
|
379
|
+
detailParts.push(`provider ${channel.providerKey}`);
|
|
380
|
+
}
|
|
381
|
+
if (channel.model) {
|
|
382
|
+
detailParts.push(`model ${channel.model}`);
|
|
383
|
+
}
|
|
384
|
+
const allowedModels = Array.isArray(channel.allowedModels) && channel.allowedModels.length > 0
|
|
385
|
+
? channel.allowedModels
|
|
386
|
+
: channel.models;
|
|
387
|
+
if (Array.isArray(allowedModels) && allowedModels.length > 0) {
|
|
388
|
+
const preview = allowedModels.slice(0, 3).join(', ');
|
|
389
|
+
const suffix = allowedModels.length > 3 ? ` +${allowedModels.length - 3}` : '';
|
|
390
|
+
detailParts.push(`models ${preview}${suffix}`);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
344
394
|
if (channel.maxConcurrency) {
|
|
345
395
|
detailParts.push(`并发 ${channel.maxConcurrency}`);
|
|
346
396
|
}
|
|
@@ -389,10 +439,10 @@ async function handleChannelToggle(channels, services, cliType) {
|
|
|
389
439
|
}
|
|
390
440
|
|
|
391
441
|
if (hasChanged) {
|
|
392
|
-
broadcastSchedulerSnapshot(
|
|
442
|
+
broadcastSchedulerSnapshot(normalizedCliType);
|
|
393
443
|
}
|
|
394
444
|
|
|
395
|
-
await handleAdvancedConfig(services,
|
|
445
|
+
await handleAdvancedConfig(services, normalizedCliType);
|
|
396
446
|
|
|
397
447
|
console.log(
|
|
398
448
|
chalk.cyan(
|
|
@@ -503,15 +553,26 @@ async function handleAdvancedConfig(services, cliType) {
|
|
|
503
553
|
}
|
|
504
554
|
|
|
505
555
|
function broadcastSchedulerSnapshot(source = 'claude') {
|
|
556
|
+
const normalizedSource = normalizePlatformKey(source || 'claude');
|
|
506
557
|
try {
|
|
507
558
|
const { getSchedulerState } = require('../server/services/channel-scheduler');
|
|
508
559
|
const { broadcastSchedulerState } = require('../server/websocket-server');
|
|
509
|
-
broadcastSchedulerState(
|
|
560
|
+
broadcastSchedulerState(normalizedSource, getSchedulerState(normalizedSource));
|
|
510
561
|
} catch (err) {
|
|
511
562
|
// ignore when scheduler not available
|
|
512
563
|
}
|
|
513
564
|
}
|
|
514
565
|
|
|
566
|
+
function buildSchedulerSources() {
|
|
567
|
+
return [
|
|
568
|
+
{ key: 'claude', label: 'Claude (Claude Code)' },
|
|
569
|
+
{ key: 'codex', label: 'Codex (OpenAI)' },
|
|
570
|
+
{ key: 'gemini', label: 'Gemini' },
|
|
571
|
+
{ key: 'opencode', label: 'OpenCode' },
|
|
572
|
+
{ key: 'omp', label: 'OMP' }
|
|
573
|
+
];
|
|
574
|
+
}
|
|
575
|
+
|
|
515
576
|
async function handleLegacySwitch(channels, services) {
|
|
516
577
|
const choices = channels.map(channel => {
|
|
517
578
|
let name = channel.enabled !== false ? chalk.green('● ') : ' ';
|
package/src/commands/daemon.js
CHANGED
|
@@ -11,7 +11,6 @@ const {
|
|
|
11
11
|
getPortToolIssue,
|
|
12
12
|
formatPortToolIssue
|
|
13
13
|
} = require('../utils/port-helper');
|
|
14
|
-
const { hasHostFlag } = require('../utils/cli-flags');
|
|
15
14
|
|
|
16
15
|
const PM2_APP_NAME = 'cc-tool';
|
|
17
16
|
const STARTUP_LOG_FILE = 'cc-tool-out.log';
|
|
@@ -260,7 +259,8 @@ function getManagedPorts(config = loadConfig()) {
|
|
|
260
259
|
config.ports?.proxy || 20088,
|
|
261
260
|
config.ports?.codexProxy || 20089,
|
|
262
261
|
config.ports?.geminiProxy || 20090,
|
|
263
|
-
config.ports?.opencodeProxy || 20091
|
|
262
|
+
config.ports?.opencodeProxy || 20091,
|
|
263
|
+
config.ports?.ompProxy || 20092
|
|
264
264
|
].filter((port, index, list) => Number.isInteger(port) && port > 0 && list.indexOf(port) === index);
|
|
265
265
|
}
|
|
266
266
|
|
|
@@ -500,7 +500,7 @@ async function handleStart() {
|
|
|
500
500
|
try {
|
|
501
501
|
const config = loadConfig();
|
|
502
502
|
const port = config.ports?.webUI || 19999;
|
|
503
|
-
const enableHost =
|
|
503
|
+
const enableHost = process.argv.includes('--host');
|
|
504
504
|
const enableHttps = process.argv.includes('--https');
|
|
505
505
|
fs.mkdirSync(PATHS.logs, { recursive: true });
|
|
506
506
|
|
|
@@ -668,6 +668,7 @@ async function handleStatus() {
|
|
|
668
668
|
const codexActive = fs.existsSync(PATHS.activeChannel.codex);
|
|
669
669
|
const geminiActive = fs.existsSync(PATHS.activeChannel.gemini);
|
|
670
670
|
const opencodeActive = fs.existsSync(PATHS.activeChannel.opencode);
|
|
671
|
+
const ompActive = fs.existsSync(PATHS.activeChannel.omp);
|
|
671
672
|
|
|
672
673
|
console.log(chalk.bold('\n[PROXY] 代理服务:'));
|
|
673
674
|
|
|
@@ -683,6 +684,9 @@ async function handleStatus() {
|
|
|
683
684
|
console.log(chalk.gray(' OpenCode:') + (opencodeActive ? chalk.green('[OK] 运行中') : chalk.gray('[STOP] 未启动')) +
|
|
684
685
|
chalk.gray(` (http://localhost:${config.ports?.opencodeProxy || 20091})`));
|
|
685
686
|
|
|
687
|
+
console.log(chalk.gray(' OMP: ') + (ompActive ? chalk.green('[OK] 运行中') : chalk.gray('[STOP] 未启动')) +
|
|
688
|
+
chalk.gray(` (http://localhost:${config.ports?.ompProxy || 20092})`));
|
|
689
|
+
|
|
686
690
|
console.log(chalk.bold('\n[TIP] 提示:'));
|
|
687
691
|
console.log(chalk.gray(' • 代理服务通过 Web UI 界面控制'));
|
|
688
692
|
console.log(chalk.gray(' • 使用 ') + chalk.cyan('ctx logs [type]') + chalk.gray(' 查看日志'));
|
|
@@ -734,7 +738,6 @@ module.exports = {
|
|
|
734
738
|
_test: {
|
|
735
739
|
detectStalePm2RuntimeIssue,
|
|
736
740
|
extractMissingPm2ForkScriptPath,
|
|
737
|
-
buildStartOptions,
|
|
738
741
|
shouldTreatPortOwnershipAsReady,
|
|
739
742
|
getManagedPorts,
|
|
740
743
|
shouldStopPM2Process
|
package/src/commands/doctor.js
CHANGED
|
@@ -148,13 +148,7 @@ async function checkConfigFiles() {
|
|
|
148
148
|
*/
|
|
149
149
|
async function checkPorts() {
|
|
150
150
|
const config = loadConfig();
|
|
151
|
-
const ports =
|
|
152
|
-
'Web UI': config.ports?.webUI || 19999,
|
|
153
|
-
'Claude Proxy': config.ports?.proxy || 20088,
|
|
154
|
-
'Codex Proxy': config.ports?.codexProxy || 20089,
|
|
155
|
-
'Gemini Proxy': config.ports?.geminiProxy || 20090,
|
|
156
|
-
'OpenCode Proxy': config.ports?.opencodeProxy || 20091
|
|
157
|
-
};
|
|
151
|
+
const ports = buildPortCheckMap(config);
|
|
158
152
|
|
|
159
153
|
const conflicts = [];
|
|
160
154
|
|
|
@@ -181,6 +175,17 @@ async function checkPorts() {
|
|
|
181
175
|
}
|
|
182
176
|
}
|
|
183
177
|
|
|
178
|
+
function buildPortCheckMap(config = {}) {
|
|
179
|
+
return {
|
|
180
|
+
'Web UI': config.ports?.webUI || 19999,
|
|
181
|
+
'Claude Proxy': config.ports?.proxy || 20088,
|
|
182
|
+
'Codex Proxy': config.ports?.codexProxy || 20089,
|
|
183
|
+
'Gemini Proxy': config.ports?.geminiProxy || 20090,
|
|
184
|
+
'OpenCode Proxy': config.ports?.opencodeProxy || 20091,
|
|
185
|
+
'OMP Proxy': config.ports?.ompProxy || 20092
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
184
189
|
/**
|
|
185
190
|
* 检查 Claude Code 配置
|
|
186
191
|
*/
|
|
@@ -329,5 +334,9 @@ async function checkDiskSpace() {
|
|
|
329
334
|
}
|
|
330
335
|
|
|
331
336
|
module.exports = {
|
|
332
|
-
handleDoctor
|
|
337
|
+
handleDoctor,
|
|
338
|
+
_test: {
|
|
339
|
+
buildPortCheckMap,
|
|
340
|
+
checkPorts
|
|
341
|
+
}
|
|
333
342
|
};
|
package/src/commands/logs.js
CHANGED
|
@@ -3,6 +3,7 @@ const fs = require('fs');
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const { spawn } = require('child_process');
|
|
5
5
|
const { PATHS } = require('../config/paths');
|
|
6
|
+
const { normalizePlatformKey } = require('../shared/platforms');
|
|
6
7
|
|
|
7
8
|
const LOGS_DIR = PATHS.logs;
|
|
8
9
|
|
|
@@ -13,6 +14,32 @@ const LOG_FILES = {
|
|
|
13
14
|
gemini: 'gemini-proxy.log',
|
|
14
15
|
opencode: 'opencode-proxy.log'
|
|
15
16
|
};
|
|
17
|
+
const LOG_ALIASES = {
|
|
18
|
+
omp: 'ui'
|
|
19
|
+
};
|
|
20
|
+
const LOG_ALIAS_NOTES = {
|
|
21
|
+
omp: 'OMP 受管 provider 配置没有独立请求代理日志,相关活动记录在 UI/server 日志中。'
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function getSupportedLogTypes() {
|
|
25
|
+
return [...Object.keys(LOG_FILES), 'omp'];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function resolveLogType(type) {
|
|
29
|
+
const normalized = normalizePlatformKey(type);
|
|
30
|
+
if (!normalized) {
|
|
31
|
+
return { requestedType: type, type: null, file: null, note: null };
|
|
32
|
+
}
|
|
33
|
+
const aliasTarget = LOG_ALIASES[normalized];
|
|
34
|
+
const resolvedType = aliasTarget || normalized;
|
|
35
|
+
return {
|
|
36
|
+
requestedType: type,
|
|
37
|
+
normalizedType: normalized,
|
|
38
|
+
type: resolvedType,
|
|
39
|
+
file: LOG_FILES[resolvedType] || null,
|
|
40
|
+
note: LOG_ALIAS_NOTES[normalized] || null
|
|
41
|
+
};
|
|
42
|
+
}
|
|
16
43
|
|
|
17
44
|
/**
|
|
18
45
|
* 确保日志目录存在
|
|
@@ -44,12 +71,16 @@ async function handleLogs(type = null, options = {}) {
|
|
|
44
71
|
}
|
|
45
72
|
|
|
46
73
|
// 显示特定类型的日志
|
|
47
|
-
const
|
|
74
|
+
const resolved = resolveLogType(type);
|
|
75
|
+
const logFile = resolved.file;
|
|
48
76
|
if (!logFile) {
|
|
49
77
|
console.error(chalk.red(`\n[ERROR] 无效的日志类型: ${type}\n`));
|
|
50
|
-
console.log(chalk.gray('
|
|
78
|
+
console.log(chalk.gray(`支持的类型: ${getSupportedLogTypes().join(', ')}\n`));
|
|
51
79
|
process.exit(1);
|
|
52
80
|
}
|
|
81
|
+
if (resolved.note) {
|
|
82
|
+
console.log(chalk.yellow(`\n[INFO] ${resolved.note}\n`));
|
|
83
|
+
}
|
|
53
84
|
|
|
54
85
|
const logPath = path.join(LOGS_DIR, logFile);
|
|
55
86
|
|
|
@@ -60,7 +91,7 @@ async function handleLogs(type = null, options = {}) {
|
|
|
60
91
|
return;
|
|
61
92
|
}
|
|
62
93
|
|
|
63
|
-
console.log(chalk.cyan(`\n[LOG] ${type.toUpperCase()} 日志 ${follow ? '(实时)' : `(最近 ${lines} 行)`}\n`));
|
|
94
|
+
console.log(chalk.cyan(`\n[LOG] ${String(type).toUpperCase()} 日志 ${follow ? '(实时)' : `(最近 ${lines} 行)`}\n`));
|
|
64
95
|
console.log(chalk.gray(`=`.repeat(60)) + '\n');
|
|
65
96
|
|
|
66
97
|
if (follow) {
|
|
@@ -116,7 +147,7 @@ function showAllLogs(lines, follow) {
|
|
|
116
147
|
});
|
|
117
148
|
|
|
118
149
|
console.log(chalk.gray(`\n=`.repeat(60)));
|
|
119
|
-
console.log(chalk.gray(`\n[TIP] 使用 `) + chalk.cyan(`ctx logs ${
|
|
150
|
+
console.log(chalk.gray(`\n[TIP] 使用 `) + chalk.cyan(`ctx logs ${getSupportedLogTypes().join('|')}`) + chalk.gray(` 查看特定类型日志\n`));
|
|
120
151
|
}
|
|
121
152
|
|
|
122
153
|
/**
|
|
@@ -226,11 +257,18 @@ function clearLogs(type) {
|
|
|
226
257
|
console.log(chalk.green(`\n[OK] 共清空 ${cleared} 个日志文件\n`));
|
|
227
258
|
} else {
|
|
228
259
|
// 清空特定类型日志
|
|
229
|
-
const
|
|
260
|
+
const resolved = resolveLogType(type);
|
|
261
|
+
const logFile = resolved.file;
|
|
230
262
|
if (!logFile) {
|
|
231
263
|
console.error(chalk.red(`\n[ERROR] 无效的日志类型: ${type}\n`));
|
|
264
|
+
console.log(chalk.gray(`支持的类型: ${getSupportedLogTypes().join(', ')}\n`));
|
|
232
265
|
process.exit(1);
|
|
233
266
|
}
|
|
267
|
+
if (resolved.note) {
|
|
268
|
+
console.log(chalk.yellow(`\n[INFO] ${resolved.note}`));
|
|
269
|
+
console.log(chalk.gray('如需清空该日志,请使用 ctx logs ui --clear。\n'));
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
234
272
|
|
|
235
273
|
const logPath = path.join(LOGS_DIR, logFile);
|
|
236
274
|
if (fs.existsSync(logPath)) {
|
|
@@ -289,6 +327,8 @@ function getTypeColor(type) {
|
|
|
289
327
|
module.exports = {
|
|
290
328
|
handleLogs,
|
|
291
329
|
_test: {
|
|
292
|
-
buildFollowProcessSpec
|
|
330
|
+
buildFollowProcessSpec,
|
|
331
|
+
getSupportedLogTypes,
|
|
332
|
+
resolveLogType
|
|
293
333
|
}
|
|
294
334
|
};
|
|
@@ -2,115 +2,118 @@
|
|
|
2
2
|
const chalk = require('chalk');
|
|
3
3
|
const inquirer = require('inquirer');
|
|
4
4
|
const { loadConfig, saveConfig } = require('../config/loader');
|
|
5
|
+
const { HOME_DIR } = require('../config/paths');
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
async function handlePortConfig() {
|
|
10
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
11
|
-
console.log(chalk.yellow('\n当前环境不支持交互式端口配置,请在本地终端中运行 `ctx port`。\n'));
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
console.clear();
|
|
16
|
-
console.log(chalk.bold.cyan('\n╔=======================================╗'));
|
|
17
|
-
console.log(chalk.bold.cyan('║ 端口配置 ║'));
|
|
18
|
-
console.log(chalk.bold.cyan('╚=======================================╝\n'));
|
|
19
|
-
|
|
20
|
-
const config = loadConfig();
|
|
21
|
-
|
|
22
|
-
console.log(chalk.cyan('当前端口配置:'));
|
|
23
|
-
console.log(chalk.gray(`• Web UI 页面端口: ${config.ports.webUI} (同时用于 WebSocket)`));
|
|
24
|
-
console.log(chalk.gray(`• Claude 代理端口: ${config.ports.proxy}`));
|
|
25
|
-
console.log(chalk.gray(`• Codex 代理端口: ${config.ports.codexProxy || 20089}`));
|
|
26
|
-
console.log(chalk.gray(`• Gemini 代理端口: ${config.ports.geminiProxy || 20090}`));
|
|
27
|
-
console.log(chalk.gray(`• OpenCode 代理端口: ${config.ports.opencodeProxy || 20091}\n`));
|
|
7
|
+
function normalizePort(value) {
|
|
8
|
+
return parseInt(value, 10);
|
|
9
|
+
}
|
|
28
10
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
11
|
+
function validatePort(input) {
|
|
12
|
+
const port = normalizePort(input);
|
|
13
|
+
if (isNaN(port) || port < 1024 || port > 65535) {
|
|
14
|
+
return '端口必须是 1024-65535 之间的数字';
|
|
15
|
+
}
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
33
18
|
|
|
34
|
-
|
|
19
|
+
function buildPortQuestions(config = {}) {
|
|
20
|
+
const ports = config.ports || {};
|
|
21
|
+
return [
|
|
35
22
|
{
|
|
36
23
|
type: 'input',
|
|
37
24
|
name: 'webUI',
|
|
38
25
|
message: 'Web UI 页面端口 (同时用于 WebSocket):',
|
|
39
|
-
default:
|
|
40
|
-
validate:
|
|
41
|
-
const port = parseInt(input);
|
|
42
|
-
if (isNaN(port) || port < 1024 || port > 65535) {
|
|
43
|
-
return '端口必须是 1024-65535 之间的数字';
|
|
44
|
-
}
|
|
45
|
-
return true;
|
|
46
|
-
},
|
|
26
|
+
default: ports.webUI || 19999,
|
|
27
|
+
validate: validatePort,
|
|
47
28
|
},
|
|
48
29
|
{
|
|
49
30
|
type: 'input',
|
|
50
31
|
name: 'proxy',
|
|
51
32
|
message: 'Claude 代理服务端口:',
|
|
52
|
-
default:
|
|
53
|
-
validate:
|
|
54
|
-
const port = parseInt(input);
|
|
55
|
-
if (isNaN(port) || port < 1024 || port > 65535) {
|
|
56
|
-
return '端口必须是 1024-65535 之间的数字';
|
|
57
|
-
}
|
|
58
|
-
return true;
|
|
59
|
-
},
|
|
33
|
+
default: ports.proxy || 20088,
|
|
34
|
+
validate: validatePort,
|
|
60
35
|
},
|
|
61
36
|
{
|
|
62
37
|
type: 'input',
|
|
63
38
|
name: 'codexProxy',
|
|
64
39
|
message: 'Codex 代理服务端口:',
|
|
65
|
-
default:
|
|
66
|
-
validate:
|
|
67
|
-
const port = parseInt(input);
|
|
68
|
-
if (isNaN(port) || port < 1024 || port > 65535) {
|
|
69
|
-
return '端口必须是 1024-65535 之间的数字';
|
|
70
|
-
}
|
|
71
|
-
return true;
|
|
72
|
-
},
|
|
40
|
+
default: ports.codexProxy || 20089,
|
|
41
|
+
validate: validatePort,
|
|
73
42
|
},
|
|
74
43
|
{
|
|
75
44
|
type: 'input',
|
|
76
45
|
name: 'geminiProxy',
|
|
77
46
|
message: 'Gemini 代理服务端口:',
|
|
78
|
-
default:
|
|
79
|
-
validate:
|
|
80
|
-
const port = parseInt(input);
|
|
81
|
-
if (isNaN(port) || port < 1024 || port > 65535) {
|
|
82
|
-
return '端口必须是 1024-65535 之间的数字';
|
|
83
|
-
}
|
|
84
|
-
return true;
|
|
85
|
-
},
|
|
47
|
+
default: ports.geminiProxy || 20090,
|
|
48
|
+
validate: validatePort,
|
|
86
49
|
},
|
|
87
50
|
{
|
|
88
51
|
type: 'input',
|
|
89
52
|
name: 'opencodeProxy',
|
|
90
53
|
message: 'OpenCode 代理服务端口:',
|
|
91
|
-
default:
|
|
92
|
-
validate:
|
|
93
|
-
const port = parseInt(input);
|
|
94
|
-
if (isNaN(port) || port < 1024 || port > 65535) {
|
|
95
|
-
return '端口必须是 1024-65535 之间的数字';
|
|
96
|
-
}
|
|
97
|
-
return true;
|
|
98
|
-
},
|
|
54
|
+
default: ports.opencodeProxy || 20091,
|
|
55
|
+
validate: validatePort,
|
|
99
56
|
},
|
|
100
|
-
|
|
57
|
+
{
|
|
58
|
+
type: 'input',
|
|
59
|
+
name: 'ompProxy',
|
|
60
|
+
message: 'OMP 代理服务端口:',
|
|
61
|
+
default: ports.ompProxy || 20092,
|
|
62
|
+
validate: validatePort,
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
}
|
|
101
66
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
webUI:
|
|
105
|
-
proxy:
|
|
106
|
-
codexProxy:
|
|
107
|
-
geminiProxy:
|
|
108
|
-
opencodeProxy:
|
|
67
|
+
function buildPortsConfig(answers = {}) {
|
|
68
|
+
return {
|
|
69
|
+
webUI: normalizePort(answers.webUI),
|
|
70
|
+
proxy: normalizePort(answers.proxy),
|
|
71
|
+
codexProxy: normalizePort(answers.codexProxy),
|
|
72
|
+
geminiProxy: normalizePort(answers.geminiProxy),
|
|
73
|
+
opencodeProxy: normalizePort(answers.opencodeProxy),
|
|
74
|
+
ompProxy: normalizePort(answers.ompProxy),
|
|
109
75
|
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 配置端口
|
|
80
|
+
*/
|
|
81
|
+
async function handlePortConfig() {
|
|
82
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
83
|
+
console.log(chalk.yellow('\n当前环境不支持交互式端口配置,请在本地终端中运行 `ctx port`。\n'));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.clear();
|
|
88
|
+
console.log(chalk.bold.cyan('\n╔=======================================╗'));
|
|
89
|
+
console.log(chalk.bold.cyan('║ 端口配置 ║'));
|
|
90
|
+
console.log(chalk.bold.cyan('╚=======================================╝\n'));
|
|
91
|
+
|
|
92
|
+
const config = loadConfig();
|
|
93
|
+
const ports = config.ports || {};
|
|
94
|
+
|
|
95
|
+
console.log(chalk.cyan('当前端口配置:'));
|
|
96
|
+
console.log(chalk.gray(`• Web UI 页面端口: ${ports.webUI || 19999} (同时用于 WebSocket)`));
|
|
97
|
+
console.log(chalk.gray(`• Claude 代理端口: ${ports.proxy || 20088}`));
|
|
98
|
+
console.log(chalk.gray(`• Codex 代理端口: ${ports.codexProxy || 20089}`));
|
|
99
|
+
console.log(chalk.gray(`• Gemini 代理端口: ${ports.geminiProxy || 20090}`));
|
|
100
|
+
console.log(chalk.gray(`• OpenCode 代理端口: ${ports.opencodeProxy || 20091}`));
|
|
101
|
+
console.log(chalk.gray(`• OMP 代理端口: ${ports.ompProxy || 20092}\n`));
|
|
102
|
+
|
|
103
|
+
console.log(chalk.yellow('说明:'));
|
|
104
|
+
console.log(chalk.gray('• 端口范围: 1024-65535'));
|
|
105
|
+
console.log(chalk.gray('• 修改后需要重启相关服务才能生效'));
|
|
106
|
+
console.log(chalk.gray('• 如果端口被占用,请修改为其他端口\n'));
|
|
107
|
+
|
|
108
|
+
const answers = await inquirer.prompt(buildPortQuestions(config));
|
|
109
|
+
|
|
110
|
+
// 更新配置
|
|
111
|
+
config.ports = buildPortsConfig(answers);
|
|
110
112
|
|
|
111
113
|
// 保存配置(保留其余字段)
|
|
112
114
|
saveConfig({
|
|
113
115
|
...config,
|
|
116
|
+
projectsDir: config.projectsDir.replace(HOME_DIR, '~'),
|
|
114
117
|
ports: config.ports,
|
|
115
118
|
});
|
|
116
119
|
|
|
@@ -130,4 +133,9 @@ async function handlePortConfig() {
|
|
|
130
133
|
|
|
131
134
|
module.exports = {
|
|
132
135
|
handlePortConfig,
|
|
136
|
+
_test: {
|
|
137
|
+
buildPortQuestions,
|
|
138
|
+
buildPortsConfig,
|
|
139
|
+
validatePort
|
|
140
|
+
}
|
|
133
141
|
};
|