coding-tool-x 3.6.13 → 3.7.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/README.md +46 -33
- package/dist/web/assets/{Analytics-CnA75GxO.js → Analytics-Dma_j2Rq.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-D8XEscQY.js → ConfigTemplates-C49Y1PLH.js} +1 -1
- package/dist/web/assets/Home-DGGC8H4g.js +1 -0
- package/dist/web/assets/Home-ewgeIhF7.css +1 -0
- package/dist/web/assets/{PluginManager-BzKZ8TYu.js → PluginManager-B1ECTHyY.js} +1 -1
- package/dist/web/assets/{ProjectList-DL4JK6ci.css → ProjectList-B_L55-l9.css} +1 -1
- package/dist/web/assets/ProjectList-DyiGyT-H.js +1 -0
- package/dist/web/assets/SessionList-CH5GwwiJ.js +1 -0
- package/dist/web/assets/{SkillManager-fxOvFTYD.js → SkillManager-CRyVhjYQ.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-Ch2mM8jm.js → WorkspaceManager-CO9tclVt.js} +1 -1
- package/dist/web/assets/{icons-QFXzEgqM.js → icons-Dop5j7Bf.js} +1 -1
- package/dist/web/assets/index-CM26UrnJ.css +1 -0
- package/dist/web/assets/index-EQq56pRa.js +2 -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 +2 -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 +48 -6
- package/src/commands/port-config.js +83 -75
- package/src/commands/proxy-control.js +52 -19
- 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 +99 -11
- package/src/index.js +11 -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/pi-channels.js +349 -0
- package/src/server/api/pi-config.js +164 -0
- package/src/server/api/pi-projects.js +76 -0
- package/src/server/api/pi-proxy.js +154 -0
- package/src/server/api/pi-sessions.js +334 -0
- package/src/server/api/pi-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/pi-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 +275 -18
- 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/pi-auth-providers.js +294 -0
- package/src/server/services/pi-channels.js +94 -0
- package/src/server/services/pi-config.js +287 -0
- package/src/server/services/pi-sessions.js +490 -0
- package/src/server/services/pi-settings-manager.js +571 -0
- package/src/server/services/pi-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 +9 -3
- 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/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
|
@@ -5,6 +5,7 @@ const inquirer = require('inquirer');
|
|
|
5
5
|
const { loadConfig } = require('../config/loader');
|
|
6
6
|
const { PATHS } = require('../config/paths');
|
|
7
7
|
const { clearNativeOAuth } = require('../server/services/native-oauth-adapters');
|
|
8
|
+
const { normalizePlatformKey } = require('../shared/platforms');
|
|
8
9
|
const SETTINGS_MANAGERS = {
|
|
9
10
|
claude: () => require('../server/services/settings-manager'),
|
|
10
11
|
codex: () => require('../server/services/codex-settings-manager'),
|
|
@@ -16,10 +17,11 @@ const SETTINGS_MANAGERS = {
|
|
|
16
17
|
* 获取当前类型的代理服务
|
|
17
18
|
*/
|
|
18
19
|
function getProxyServices(cliType) {
|
|
19
|
-
|
|
20
|
+
const normalizedCliType = normalizePlatformKey(cliType || 'claude');
|
|
21
|
+
if (normalizedCliType === 'claude') {
|
|
20
22
|
const { getProxyStatus, startProxyServer, stopProxyServer } = require('../server/proxy-server');
|
|
21
23
|
return { getProxyStatus, startProxyServer, stopProxyServer, defaultPort: 20088 };
|
|
22
|
-
} else if (
|
|
24
|
+
} else if (normalizedCliType === 'codex') {
|
|
23
25
|
const { getCodexProxyStatus, startCodexProxyServer, stopCodexProxyServer } = require('../server/codex-proxy-server');
|
|
24
26
|
return {
|
|
25
27
|
getProxyStatus: getCodexProxyStatus,
|
|
@@ -27,7 +29,7 @@ function getProxyServices(cliType) {
|
|
|
27
29
|
stopProxyServer: stopCodexProxyServer,
|
|
28
30
|
defaultPort: 20089
|
|
29
31
|
};
|
|
30
|
-
} else if (
|
|
32
|
+
} else if (normalizedCliType === 'gemini') {
|
|
31
33
|
const { getGeminiProxyStatus, startGeminiProxyServer, stopGeminiProxyServer } = require('../server/gemini-proxy-server');
|
|
32
34
|
return {
|
|
33
35
|
getProxyStatus: getGeminiProxyStatus,
|
|
@@ -35,7 +37,7 @@ function getProxyServices(cliType) {
|
|
|
35
37
|
stopProxyServer: stopGeminiProxyServer,
|
|
36
38
|
defaultPort: 20090
|
|
37
39
|
};
|
|
38
|
-
} else if (
|
|
40
|
+
} else if (normalizedCliType === 'opencode') {
|
|
39
41
|
const { getOpenCodeProxyStatus, startOpenCodeProxyServer, stopOpenCodeProxyServer } = require('../server/opencode-proxy-server');
|
|
40
42
|
return {
|
|
41
43
|
getProxyStatus: getOpenCodeProxyStatus,
|
|
@@ -43,11 +45,24 @@ function getProxyServices(cliType) {
|
|
|
43
45
|
stopProxyServer: stopOpenCodeProxyServer,
|
|
44
46
|
defaultPort: 20091
|
|
45
47
|
};
|
|
48
|
+
} else if (normalizedCliType === 'pi') {
|
|
49
|
+
const { getPiProxyStatus, startPiProxyServer, stopPiProxyServer } = require('../server/pi-proxy-server');
|
|
50
|
+
const config = loadConfig();
|
|
51
|
+
return {
|
|
52
|
+
getProxyStatus: getPiProxyStatus,
|
|
53
|
+
startProxyServer: startPiProxyServer,
|
|
54
|
+
stopProxyServer: stopPiProxyServer,
|
|
55
|
+
defaultPort: config.ports?.piProxy || 20092,
|
|
56
|
+
managedProviderConfig: true
|
|
57
|
+
};
|
|
46
58
|
}
|
|
59
|
+
|
|
60
|
+
return null;
|
|
47
61
|
}
|
|
48
62
|
|
|
49
63
|
function getSettingsManager(cliType) {
|
|
50
|
-
const
|
|
64
|
+
const normalizedCliType = normalizePlatformKey(cliType || 'claude');
|
|
65
|
+
const loader = SETTINGS_MANAGERS[normalizedCliType] || SETTINGS_MANAGERS.claude;
|
|
51
66
|
const manager = loader();
|
|
52
67
|
return {
|
|
53
68
|
setProxyConfig: manager.setProxyConfig,
|
|
@@ -58,7 +73,8 @@ function getSettingsManager(cliType) {
|
|
|
58
73
|
}
|
|
59
74
|
|
|
60
75
|
function removeActiveChannelMarker(cliType) {
|
|
61
|
-
const
|
|
76
|
+
const normalizedCliType = normalizePlatformKey(cliType || 'claude');
|
|
77
|
+
const markerPath = PATHS.activeChannel?.[normalizedCliType];
|
|
62
78
|
if (!markerPath) {
|
|
63
79
|
return;
|
|
64
80
|
}
|
|
@@ -73,7 +89,8 @@ function removeActiveChannelMarker(cliType) {
|
|
|
73
89
|
}
|
|
74
90
|
|
|
75
91
|
function loadActiveChannelId(cliType) {
|
|
76
|
-
const
|
|
92
|
+
const normalizedCliType = normalizePlatformKey(cliType || 'claude');
|
|
93
|
+
const markerPath = PATHS.activeChannel?.[normalizedCliType];
|
|
77
94
|
if (!markerPath) {
|
|
78
95
|
return null;
|
|
79
96
|
}
|
|
@@ -116,27 +133,28 @@ function pickRestoredChannel(cliType, channels = []) {
|
|
|
116
133
|
}
|
|
117
134
|
|
|
118
135
|
function restoreSingleChannelMode(cliType) {
|
|
119
|
-
|
|
136
|
+
const normalizedCliType = normalizePlatformKey(cliType || 'claude');
|
|
137
|
+
if (normalizedCliType === 'claude') {
|
|
120
138
|
const { getAllChannels, applyChannelToSettings } = require('../server/services/channels');
|
|
121
|
-
const target = pickRestoredChannel(
|
|
139
|
+
const target = pickRestoredChannel(normalizedCliType, getAllChannels());
|
|
122
140
|
return target ? applyChannelToSettings(target.id) : null;
|
|
123
141
|
}
|
|
124
142
|
|
|
125
|
-
if (
|
|
143
|
+
if (normalizedCliType === 'codex') {
|
|
126
144
|
const { getChannels, applyChannelToSettings } = require('../server/services/codex-channels');
|
|
127
|
-
const target = pickRestoredChannel(
|
|
145
|
+
const target = pickRestoredChannel(normalizedCliType, getChannels().channels || []);
|
|
128
146
|
return target ? applyChannelToSettings(target.id) : null;
|
|
129
147
|
}
|
|
130
148
|
|
|
131
|
-
if (
|
|
149
|
+
if (normalizedCliType === 'gemini') {
|
|
132
150
|
const { getChannels, applyChannelToSettings } = require('../server/services/gemini-channels');
|
|
133
|
-
const target = pickRestoredChannel(
|
|
151
|
+
const target = pickRestoredChannel(normalizedCliType, getChannels().channels || []);
|
|
134
152
|
return target ? applyChannelToSettings(target.id) : null;
|
|
135
153
|
}
|
|
136
154
|
|
|
137
|
-
if (
|
|
155
|
+
if (normalizedCliType === 'opencode') {
|
|
138
156
|
const { getChannels, applyChannelToSettings } = require('../server/services/opencode-channels');
|
|
139
|
-
const target = pickRestoredChannel(
|
|
157
|
+
const target = pickRestoredChannel(normalizedCliType, getChannels().channels || []);
|
|
140
158
|
return target ? applyChannelToSettings(target.id) : null;
|
|
141
159
|
}
|
|
142
160
|
|
|
@@ -148,7 +166,7 @@ function restoreSingleChannelMode(cliType) {
|
|
|
148
166
|
*/
|
|
149
167
|
async function handleToggleProxy() {
|
|
150
168
|
const config = loadConfig();
|
|
151
|
-
const cliType = config.currentCliType || 'claude';
|
|
169
|
+
const cliType = normalizePlatformKey(config.currentCliType || 'claude');
|
|
152
170
|
const services = getProxyServices(cliType);
|
|
153
171
|
if (!services) {
|
|
154
172
|
console.log(chalk.red(`\n[ERROR] 当前 CLI 类型 (${cliType}) 暂不支持动态切换\n`));
|
|
@@ -179,21 +197,33 @@ async function handleStartProxy(cliType, services) {
|
|
|
179
197
|
claude: 'Claude Code',
|
|
180
198
|
codex: 'Codex',
|
|
181
199
|
gemini: 'Gemini',
|
|
182
|
-
opencode: 'OpenCode'
|
|
200
|
+
opencode: 'OpenCode',
|
|
201
|
+
pi: 'OMP'
|
|
183
202
|
};
|
|
184
203
|
const toolName = toolNameMap[cliType] || 'Claude Code';
|
|
185
204
|
const defaultPort = services.defaultPort;
|
|
186
205
|
|
|
187
206
|
console.log(chalk.cyan('动态切换功能说明:'));
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
207
|
+
if (services.managedProviderConfig) {
|
|
208
|
+
console.log(chalk.gray('• 开启后会把已启用的 OMP 渠道写入受管 models.yml provider 配置'));
|
|
209
|
+
console.log(chalk.gray(`• 可以通过 Web UI 或"渠道管理"功能调整启用的 ${toolName} provider`));
|
|
210
|
+
console.log(chalk.gray(`• 状态端口: http://127.0.0.1:${defaultPort}\n`));
|
|
211
|
+
} else {
|
|
212
|
+
console.log(chalk.gray('• 开启后会在本地启动一个代理服务'));
|
|
213
|
+
console.log(chalk.gray(`• 可以在不重启 ${toolName} 的情况下动态管理渠道`));
|
|
214
|
+
console.log(chalk.gray('• 通过 Web UI 或"渠道管理"功能快速调整启用的线路'));
|
|
215
|
+
console.log(chalk.gray(`• 代理服务地址: http://127.0.0.1:${defaultPort}\n`));
|
|
216
|
+
}
|
|
192
217
|
|
|
193
218
|
console.log(chalk.yellow('[WARN] 重要提示:'));
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
219
|
+
if (services.managedProviderConfig) {
|
|
220
|
+
console.log(chalk.yellow('• 启用后会写入 Coding-Tool 管理的 OMP models.yml provider 配置'));
|
|
221
|
+
console.log(chalk.yellow('• 关闭会移除受管 provider,不会清理 OMP 原生 auth.json\n'));
|
|
222
|
+
} else {
|
|
223
|
+
console.log(chalk.yellow('• 开启期间请勿关闭 CLI 终端窗口'));
|
|
224
|
+
console.log(chalk.yellow('• 如果异常关闭导致代理失效,请运行: ctx reset'));
|
|
225
|
+
console.log(chalk.yellow('• 或使用主菜单的"恢复默认配置"功能\n'));
|
|
226
|
+
}
|
|
197
227
|
|
|
198
228
|
const { confirm } = await inquirer.prompt([
|
|
199
229
|
{
|
|
@@ -210,29 +240,47 @@ async function handleStartProxy(cliType, services) {
|
|
|
210
240
|
}
|
|
211
241
|
|
|
212
242
|
try {
|
|
213
|
-
console.log(chalk.cyan(
|
|
243
|
+
console.log(chalk.cyan(services.managedProviderConfig
|
|
244
|
+
? '\n[START] 正在启用 OMP models.yml 受管 provider...\n'
|
|
245
|
+
: '\n[START] 正在启动代理服务...\n'));
|
|
214
246
|
|
|
215
247
|
// 启动代理服务器
|
|
216
248
|
const proxyResult = await services.startProxyServer();
|
|
217
249
|
|
|
218
250
|
if (!proxyResult.success) {
|
|
219
|
-
throw new Error('代理服务器启动失败');
|
|
251
|
+
throw new Error(services.managedProviderConfig ? 'OMP 受管 provider 启用失败' : '代理服务器启动失败');
|
|
220
252
|
}
|
|
221
253
|
|
|
222
|
-
|
|
254
|
+
if (services.managedProviderConfig) {
|
|
255
|
+
console.log(chalk.green('[OK] OMP models.yml 受管 provider 已启用'));
|
|
256
|
+
if (proxyResult.port) {
|
|
257
|
+
console.log(chalk.gray(`状态端口: http://127.0.0.1:${proxyResult.port}`));
|
|
258
|
+
}
|
|
259
|
+
(proxyResult.warnings || []).forEach((warning) => {
|
|
260
|
+
console.log(chalk.yellow(`[WARN] ${warning}`));
|
|
261
|
+
});
|
|
262
|
+
} else {
|
|
263
|
+
console.log(chalk.green(`[OK] 代理服务已启动: http://127.0.0.1:${proxyResult.port}`));
|
|
264
|
+
}
|
|
223
265
|
|
|
224
266
|
// 修改配置文件
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
267
|
+
if (!services.managedProviderConfig) {
|
|
268
|
+
const settingsManager = getSettingsManager(cliType);
|
|
269
|
+
clearNativeOAuth(cliType);
|
|
270
|
+
settingsManager.setProxyConfig(proxyResult.port);
|
|
271
|
+
console.log(chalk.green('[OK] 配置文件已更新'));
|
|
272
|
+
|
|
273
|
+
if (settingsManager.hasBackup()) {
|
|
274
|
+
console.log(chalk.green('[OK] 原配置已备份'));
|
|
275
|
+
}
|
|
232
276
|
}
|
|
233
277
|
|
|
234
|
-
console.log(chalk.cyan('\n[TIP] 动态切换已启用!'));
|
|
235
|
-
|
|
278
|
+
console.log(chalk.cyan(services.managedProviderConfig ? '\n[TIP] OMP 受管 provider 已启用!' : '\n[TIP] 动态切换已启用!'));
|
|
279
|
+
if (services.managedProviderConfig) {
|
|
280
|
+
console.log(chalk.gray(' 现在可以通过"渠道管理"功能调整已启用的 OMP provider,并重新同步 models.yml\n'));
|
|
281
|
+
} else {
|
|
282
|
+
console.log(chalk.gray(` 现在可以通过"渠道管理"功能快速调整,无需重启 ${toolName}\n`));
|
|
283
|
+
}
|
|
236
284
|
|
|
237
285
|
await inquirer.prompt([
|
|
238
286
|
{
|
|
@@ -267,20 +315,32 @@ async function handleStopProxy(cliType, services) {
|
|
|
267
315
|
claude: 'Claude Code',
|
|
268
316
|
codex: 'Codex',
|
|
269
317
|
gemini: 'Gemini',
|
|
270
|
-
opencode: 'OpenCode'
|
|
318
|
+
opencode: 'OpenCode',
|
|
319
|
+
pi: 'OMP'
|
|
271
320
|
};
|
|
272
321
|
const toolName = toolNameMap[cliType] || 'Claude Code';
|
|
273
322
|
const proxyStatus = services.getProxyStatus();
|
|
274
323
|
|
|
275
324
|
console.log(chalk.cyan('当前状态:'));
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
325
|
+
if (services.managedProviderConfig) {
|
|
326
|
+
console.log(chalk.gray(`• 受管配置: ${chalk.green('已启用')}`));
|
|
327
|
+
console.log(chalk.gray(`• 配置模式: ${proxyStatus.mode || 'models-yml-provider-config'}`));
|
|
328
|
+
console.log(chalk.gray(`• 状态端口: http://127.0.0.1:${proxyStatus.port || proxyStatus.defaultPort}\n`));
|
|
329
|
+
} else {
|
|
330
|
+
console.log(chalk.gray(`• 代理服务: ${chalk.green('运行中')}`));
|
|
331
|
+
console.log(chalk.gray(`• 代理端口: ${proxyStatus.port}`));
|
|
332
|
+
console.log(chalk.gray(`• 代理地址: http://127.0.0.1:${proxyStatus.port}\n`));
|
|
333
|
+
}
|
|
279
334
|
|
|
280
335
|
console.log(chalk.yellow('关闭后:'));
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
336
|
+
if (services.managedProviderConfig) {
|
|
337
|
+
console.log(chalk.gray('• OMP 受管 models.yml provider 配置将被移除'));
|
|
338
|
+
console.log(chalk.gray(`• 之后调整 ${toolName} provider 需要重新启用受管配置\n`));
|
|
339
|
+
} else {
|
|
340
|
+
console.log(chalk.gray('• 代理服务将被停止'));
|
|
341
|
+
console.log(chalk.gray('• 配置将恢复为当前激活渠道的单渠道模式'));
|
|
342
|
+
console.log(chalk.gray(`• 之后管理渠道将需要重启 ${toolName}\n`));
|
|
343
|
+
}
|
|
284
344
|
|
|
285
345
|
const { confirm } = await inquirer.prompt([
|
|
286
346
|
{
|
|
@@ -297,25 +357,38 @@ async function handleStopProxy(cliType, services) {
|
|
|
297
357
|
}
|
|
298
358
|
|
|
299
359
|
try {
|
|
300
|
-
console.log(chalk.cyan(
|
|
360
|
+
console.log(chalk.cyan(services.managedProviderConfig
|
|
361
|
+
? '\n[STOP] 正在移除 OMP models.yml 受管 provider...\n'
|
|
362
|
+
: '\n[STOP] 正在停止代理服务...\n'));
|
|
301
363
|
|
|
302
364
|
// 停止代理服务器
|
|
303
365
|
await services.stopProxyServer();
|
|
304
|
-
console.log(chalk.green(
|
|
366
|
+
console.log(chalk.green(services.managedProviderConfig
|
|
367
|
+
? '[OK] OMP models.yml 受管 provider 已移除'
|
|
368
|
+
: '[OK] 代理服务已停止'));
|
|
305
369
|
|
|
306
370
|
// 恢复配置文件
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
371
|
+
if (!services.managedProviderConfig) {
|
|
372
|
+
const settingsManager = getSettingsManager(cliType);
|
|
373
|
+
settingsManager.deleteBackup?.();
|
|
374
|
+
const restoredChannel = restoreSingleChannelMode(cliType);
|
|
375
|
+
removeActiveChannelMarker(cliType);
|
|
376
|
+
if (restoredChannel?.name) {
|
|
377
|
+
console.log(chalk.green(`[OK] 已恢复到渠道: ${restoredChannel.name}`));
|
|
378
|
+
} else {
|
|
379
|
+
console.log(chalk.green('[OK] 已清理代理接管状态'));
|
|
380
|
+
}
|
|
313
381
|
} else {
|
|
382
|
+
removeActiveChannelMarker(cliType);
|
|
314
383
|
console.log(chalk.green('[OK] 已清理代理接管状态'));
|
|
315
384
|
}
|
|
316
385
|
|
|
317
|
-
console.log(chalk.cyan('\n[TIP] 动态切换已关闭'));
|
|
318
|
-
|
|
386
|
+
console.log(chalk.cyan(services.managedProviderConfig ? '\n[TIP] OMP 受管 provider 已关闭' : '\n[TIP] 动态切换已关闭'));
|
|
387
|
+
if (services.managedProviderConfig) {
|
|
388
|
+
console.log(chalk.gray(' 如需恢复受管 provider 配置,请再次运行 ctx omp start\n'));
|
|
389
|
+
} else {
|
|
390
|
+
console.log(chalk.gray(` 现在调整渠道需要重启 ${toolName} 才能生效\n`));
|
|
391
|
+
}
|
|
319
392
|
|
|
320
393
|
await inquirer.prompt([
|
|
321
394
|
{
|
|
@@ -339,4 +412,9 @@ async function handleStopProxy(cliType, services) {
|
|
|
339
412
|
|
|
340
413
|
module.exports = {
|
|
341
414
|
handleToggleProxy,
|
|
415
|
+
_test: {
|
|
416
|
+
getProxyServices,
|
|
417
|
+
pickRestoredChannel,
|
|
418
|
+
restoreSingleChannelMode
|
|
419
|
+
}
|
|
342
420
|
};
|
package/src/commands/ui.js
CHANGED
|
@@ -3,14 +3,13 @@ const { startServer } = require('../server');
|
|
|
3
3
|
const open = require('open');
|
|
4
4
|
const { getProxyStatus } = require('../server/proxy-server');
|
|
5
5
|
const { loadConfig } = require('../config/loader');
|
|
6
|
-
const { hasHostFlag } = require('../utils/cli-flags');
|
|
7
6
|
|
|
8
7
|
async function handleUI() {
|
|
9
8
|
// 检查是否为 daemon 模式(PM2 启动)
|
|
10
9
|
const isDaemon = process.argv.includes('--daemon');
|
|
11
10
|
|
|
12
|
-
// 检查是否启用 LAN 访问 (--host
|
|
13
|
-
const enableHost =
|
|
11
|
+
// 检查是否启用 LAN 访问 (--host 标志)
|
|
12
|
+
const enableHost = process.argv.includes('--host');
|
|
14
13
|
const enableHttps = process.argv.includes('--https');
|
|
15
14
|
const host = enableHost ? '0.0.0.0' : '127.0.0.1';
|
|
16
15
|
const protocol = enableHttps ? 'https' : 'http';
|
|
@@ -19,7 +18,7 @@ async function handleUI() {
|
|
|
19
18
|
console.clear();
|
|
20
19
|
console.log(chalk.cyan.bold('\n[NET] 启动 Coding-Tool Web UI...\n'));
|
|
21
20
|
if (enableHost) {
|
|
22
|
-
console.log(chalk.yellow('[WARN] LAN 访问已启用 (--host
|
|
21
|
+
console.log(chalk.yellow('[WARN] LAN 访问已启用 (--host)\n'));
|
|
23
22
|
}
|
|
24
23
|
if (enableHttps) {
|
|
25
24
|
console.log(chalk.yellow('[LOCK] 已启用本地 HTTPS 模式 (--https)\n'));
|
|
@@ -188,8 +188,6 @@ async function createWorkspace() {
|
|
|
188
188
|
|
|
189
189
|
let createWorktree = false;
|
|
190
190
|
let branch = '';
|
|
191
|
-
let branchMode = 'existing';
|
|
192
|
-
let baseBranch = '';
|
|
193
191
|
|
|
194
192
|
if (isGit) {
|
|
195
193
|
const { shouldCreateWorktree } = await inquirer.prompt([
|
|
@@ -202,24 +200,11 @@ async function createWorkspace() {
|
|
|
202
200
|
]);
|
|
203
201
|
|
|
204
202
|
if (shouldCreateWorktree) {
|
|
205
|
-
const { selectedBranchMode } = await inquirer.prompt([
|
|
206
|
-
{
|
|
207
|
-
type: 'list',
|
|
208
|
-
name: 'selectedBranchMode',
|
|
209
|
-
message: '选择 worktree 分支模式:',
|
|
210
|
-
choices: [
|
|
211
|
-
{ name: '使用已有分支', value: 'existing' },
|
|
212
|
-
{ name: '新建分支', value: 'new' }
|
|
213
|
-
],
|
|
214
|
-
default: 'existing'
|
|
215
|
-
}
|
|
216
|
-
]);
|
|
217
|
-
|
|
218
203
|
const { branchName } = await inquirer.prompt([
|
|
219
204
|
{
|
|
220
205
|
type: 'input',
|
|
221
206
|
name: 'branchName',
|
|
222
|
-
message:
|
|
207
|
+
message: '输入分支名:',
|
|
223
208
|
validate: input => {
|
|
224
209
|
if (!input || !input.trim()) {
|
|
225
210
|
return '分支名不能为空';
|
|
@@ -229,20 +214,7 @@ async function createWorkspace() {
|
|
|
229
214
|
}
|
|
230
215
|
]);
|
|
231
216
|
|
|
232
|
-
if (selectedBranchMode === 'new') {
|
|
233
|
-
const { baseBranchName } = await inquirer.prompt([
|
|
234
|
-
{
|
|
235
|
-
type: 'input',
|
|
236
|
-
name: 'baseBranchName',
|
|
237
|
-
message: '输入基础分支(可选,如 main):',
|
|
238
|
-
default: ''
|
|
239
|
-
}
|
|
240
|
-
]);
|
|
241
|
-
baseBranch = baseBranchName;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
217
|
createWorktree = true;
|
|
245
|
-
branchMode = selectedBranchMode;
|
|
246
218
|
branch = branchName;
|
|
247
219
|
}
|
|
248
220
|
}
|
|
@@ -251,9 +223,7 @@ async function createWorkspace() {
|
|
|
251
223
|
sourcePath,
|
|
252
224
|
name: linkName,
|
|
253
225
|
createWorktree,
|
|
254
|
-
branch
|
|
255
|
-
branchMode,
|
|
256
|
-
baseBranch
|
|
226
|
+
branch
|
|
257
227
|
});
|
|
258
228
|
|
|
259
229
|
console.log(chalk.green(`\n[v] 已添加: ${linkName}\n`));
|
|
@@ -275,9 +245,7 @@ async function createWorkspace() {
|
|
|
275
245
|
projects.forEach((proj, index) => {
|
|
276
246
|
console.log(chalk.gray(` ${index + 1}. ${proj.name} → ${proj.sourcePath}`));
|
|
277
247
|
if (proj.createWorktree) {
|
|
278
|
-
|
|
279
|
-
const baseLabel = proj.branchMode === 'new' && proj.baseBranch ? `,基于 ${proj.baseBranch}` : '';
|
|
280
|
-
console.log(chalk.gray(` (创建 worktree: ${modeLabel} ${proj.branch}${baseLabel})`));
|
|
248
|
+
console.log(chalk.gray(` (创建 worktree: ${proj.branch})`));
|
|
281
249
|
}
|
|
282
250
|
});
|
|
283
251
|
console.log('');
|
package/src/config/default.js
CHANGED
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
// 默认配置
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const { resolvePreferredHomeDir } = require('../utils/home-dir');
|
|
2
5
|
const modelMetadataConfig = require('./model-metadata.json');
|
|
3
6
|
|
|
7
|
+
const HOME_DIR = resolvePreferredHomeDir(process.platform, process.env, os.homedir());
|
|
8
|
+
const CLAUDE_CONFIG_DIR = (typeof process.env.CLAUDE_CONFIG_DIR === 'string' && process.env.CLAUDE_CONFIG_DIR.trim())
|
|
9
|
+
? process.env.CLAUDE_CONFIG_DIR.trim()
|
|
10
|
+
: path.join(HOME_DIR, '.claude');
|
|
11
|
+
|
|
4
12
|
const DEFAULT_CONFIG = {
|
|
13
|
+
projectsDir: path.join(CLAUDE_CONFIG_DIR, 'projects'),
|
|
5
14
|
defaultProject: null,
|
|
6
15
|
maxDisplaySessions: 100,
|
|
7
16
|
pageSize: 15,
|
|
8
|
-
currentCliType: 'claude', // 当前CLI工具类型: claude, codex, gemini, opencode
|
|
17
|
+
currentCliType: 'claude', // 当前CLI工具类型: claude, codex, gemini, opencode, pi
|
|
9
18
|
ports: {
|
|
10
19
|
webUI: 19999, // Web UI 页面端口 (同时用于 WebSocket)
|
|
11
20
|
proxy: 20088, // Claude 代理服务端口
|
|
12
21
|
codexProxy: 20089, // Codex 代理服务端口
|
|
13
22
|
geminiProxy: 20090, // Gemini 代理服务端口
|
|
14
|
-
opencodeProxy: 20091
|
|
23
|
+
opencodeProxy: 20091, // OpenCode 代理服务端口
|
|
24
|
+
piProxy: 20092 // OMP 托管 provider 状态端口占位
|
|
15
25
|
},
|
|
16
26
|
maxLogs: 100,
|
|
17
27
|
statsInterval: 30,
|
|
@@ -62,6 +72,13 @@ const DEFAULT_CONFIG = {
|
|
|
62
72
|
output: 15,
|
|
63
73
|
cacheRead: 0.25,
|
|
64
74
|
models: {}
|
|
75
|
+
},
|
|
76
|
+
pi: {
|
|
77
|
+
mode: 'auto',
|
|
78
|
+
input: 2.5,
|
|
79
|
+
output: 15,
|
|
80
|
+
cacheRead: 0.25,
|
|
81
|
+
models: {}
|
|
65
82
|
}
|
|
66
83
|
}
|
|
67
84
|
};
|
package/src/config/loader.js
CHANGED
|
@@ -3,7 +3,7 @@ const fs = require('fs');
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const os = require('os');
|
|
5
5
|
const DEFAULT_CONFIG = require('./default');
|
|
6
|
-
const { PATHS,
|
|
6
|
+
const { PATHS, ensureStorageDirMigrated } = require('./paths');
|
|
7
7
|
const { resolvePreferredHomeDir } = require('../utils/home-dir');
|
|
8
8
|
const eventBus = require('../plugins/event-bus');
|
|
9
9
|
|
|
@@ -23,79 +23,12 @@ function getConfigFilePath() {
|
|
|
23
23
|
* 展开 ~ 为用户主目录
|
|
24
24
|
*/
|
|
25
25
|
function expandHome(filepath) {
|
|
26
|
-
if (typeof filepath !== 'string') {
|
|
27
|
-
return filepath;
|
|
28
|
-
}
|
|
29
26
|
if (filepath.startsWith('~')) {
|
|
30
27
|
return path.join(HOME_DIR, filepath.slice(1));
|
|
31
28
|
}
|
|
32
29
|
return filepath;
|
|
33
30
|
}
|
|
34
31
|
|
|
35
|
-
function collapseHome(filepath) {
|
|
36
|
-
if (typeof filepath !== 'string') {
|
|
37
|
-
return filepath;
|
|
38
|
-
}
|
|
39
|
-
if (filepath === HOME_DIR) {
|
|
40
|
-
return '~';
|
|
41
|
-
}
|
|
42
|
-
const prefix = `${HOME_DIR}${path.sep}`;
|
|
43
|
-
if (filepath.startsWith(prefix)) {
|
|
44
|
-
return path.join('~', filepath.slice(prefix.length));
|
|
45
|
-
}
|
|
46
|
-
return filepath;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function getNativeClaudeProjectsDir() {
|
|
50
|
-
return NATIVE_PATHS?.claude?.projects || path.join(HOME_DIR, '.claude', 'projects');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function hasExplicitProjectsDir(config = {}) {
|
|
54
|
-
return Object.prototype.hasOwnProperty.call(config, 'projectsDir') &&
|
|
55
|
-
typeof config.projectsDir === 'string' &&
|
|
56
|
-
config.projectsDir.trim() !== '';
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function resolveClaudeProjectsDir(config = {}) {
|
|
60
|
-
if (!hasExplicitProjectsDir(config) || isDefaultProjectsDir(config.projectsDir)) {
|
|
61
|
-
return getNativeClaudeProjectsDir();
|
|
62
|
-
}
|
|
63
|
-
return expandHome(config.projectsDir);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function normalizeComparablePath(filepath) {
|
|
67
|
-
if (typeof filepath !== 'string' || !filepath.trim()) {
|
|
68
|
-
return '';
|
|
69
|
-
}
|
|
70
|
-
return path.normalize(expandHome(filepath.trim()));
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function isDefaultProjectsDir(projectsDir) {
|
|
74
|
-
const normalized = normalizeComparablePath(projectsDir);
|
|
75
|
-
if (!normalized) {
|
|
76
|
-
return true;
|
|
77
|
-
}
|
|
78
|
-
const nativeDefault = normalizeComparablePath(getNativeClaudeProjectsDir());
|
|
79
|
-
const legacyHomeDefault = normalizeComparablePath(path.join(HOME_DIR, '.claude', 'projects'));
|
|
80
|
-
if (normalized === nativeDefault || normalized === legacyHomeDefault) {
|
|
81
|
-
return true;
|
|
82
|
-
}
|
|
83
|
-
const normalizedParts = normalized.replace(/\\/g, '/').split('/').filter(Boolean);
|
|
84
|
-
const last = normalizedParts[normalizedParts.length - 1];
|
|
85
|
-
const previous = normalizedParts[normalizedParts.length - 2];
|
|
86
|
-
return previous === '.claude' && last === 'projects' && !fs.existsSync(normalized);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function normalizeConfigForSave(config = {}) {
|
|
90
|
-
const normalized = { ...config };
|
|
91
|
-
if (isDefaultProjectsDir(normalized.projectsDir)) {
|
|
92
|
-
delete normalized.projectsDir;
|
|
93
|
-
} else if (typeof normalized.projectsDir === 'string') {
|
|
94
|
-
normalized.projectsDir = collapseHome(normalized.projectsDir);
|
|
95
|
-
}
|
|
96
|
-
return normalized;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
32
|
function mergePricing(defaultPricing, overrides = {}) {
|
|
100
33
|
const merged = {};
|
|
101
34
|
Object.keys(defaultPricing).forEach((key) => {
|
|
@@ -163,7 +96,7 @@ function loadConfig() {
|
|
|
163
96
|
if (fs.existsSync(configFilePath)) {
|
|
164
97
|
const userConfig = readJsonFile(configFilePath);
|
|
165
98
|
const config = { ...DEFAULT_CONFIG, ...userConfig };
|
|
166
|
-
config.projectsDir =
|
|
99
|
+
config.projectsDir = expandHome(config.projectsDir || DEFAULT_CONFIG.projectsDir);
|
|
167
100
|
|
|
168
101
|
// 合并 ports 配置
|
|
169
102
|
config.ports = { ...DEFAULT_CONFIG.ports, ...userConfig.ports };
|
|
@@ -187,7 +120,7 @@ function loadConfig() {
|
|
|
187
120
|
}
|
|
188
121
|
const defaultConfig = {
|
|
189
122
|
...DEFAULT_CONFIG,
|
|
190
|
-
projectsDir:
|
|
123
|
+
projectsDir: expandHome(DEFAULT_CONFIG.projectsDir),
|
|
191
124
|
currentProject: DEFAULT_CONFIG.defaultProject
|
|
192
125
|
};
|
|
193
126
|
eventBus.emitSync('config:loaded', { config: defaultConfig });
|
|
@@ -204,9 +137,8 @@ function saveConfig(config) {
|
|
|
204
137
|
if (!fs.existsSync(dir)) {
|
|
205
138
|
fs.mkdirSync(dir, { recursive: true });
|
|
206
139
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
eventBus.emitSync('config:saved', { config: normalizedConfig });
|
|
140
|
+
fs.writeFileSync(configFilePath, JSON.stringify(config, null, 2), 'utf8');
|
|
141
|
+
eventBus.emitSync('config:saved', { config });
|
|
210
142
|
} catch (error) {
|
|
211
143
|
console.error(`保存配置失败 (${configFilePath}):`, error.message);
|
|
212
144
|
}
|
|
@@ -216,8 +148,5 @@ module.exports = {
|
|
|
216
148
|
loadConfig,
|
|
217
149
|
saveConfig,
|
|
218
150
|
expandHome,
|
|
219
|
-
resolveClaudeProjectsDir,
|
|
220
|
-
normalizeConfigForSave,
|
|
221
|
-
isDefaultProjectsDir,
|
|
222
151
|
getConfigFilePath
|
|
223
152
|
};
|