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.
Files changed (88) hide show
  1. package/README.md +45 -33
  2. package/dist/web/assets/{Analytics-CnA75GxO.js → Analytics-Dpje9L84.js} +1 -1
  3. package/dist/web/assets/{ConfigTemplates-D8XEscQY.js → ConfigTemplates-Cz6kasek.js} +1 -1
  4. package/dist/web/assets/Home-BwTaYu3q.js +1 -0
  5. package/dist/web/assets/Home-Jom_ZcdY.css +1 -0
  6. package/dist/web/assets/{PluginManager-BzKZ8TYu.js → PluginManager-DsN-xNXx.js} +1 -1
  7. package/dist/web/assets/ProjectList-4BCObimU.js +1 -0
  8. package/dist/web/assets/{ProjectList-DL4JK6ci.css → ProjectList-D3a0oRum.css} +1 -1
  9. package/dist/web/assets/SessionList-D4c8FRzq.js +1 -0
  10. package/dist/web/assets/{SkillManager-fxOvFTYD.js → SkillManager-C-KYbXB5.js} +1 -1
  11. package/dist/web/assets/{WorkspaceManager-Ch2mM8jm.js → WorkspaceManager-B-BlPJHX.js} +1 -1
  12. package/dist/web/assets/{icons-QFXzEgqM.js → icons-Dop5j7Bf.js} +1 -1
  13. package/dist/web/assets/index-Btfvh0GG.js +2 -0
  14. package/dist/web/assets/index-CLNa6DhH.css +1 -0
  15. package/dist/web/assets/{naive-ui-kcWDeJwl.js → naive-ui-CVyvdmg1.js} +1 -1
  16. package/dist/web/index.html +4 -4
  17. package/package.json +3 -1
  18. package/src/commands/channels.js +85 -24
  19. package/src/commands/daemon.js +7 -4
  20. package/src/commands/doctor.js +17 -8
  21. package/src/commands/logs.js +46 -6
  22. package/src/commands/port-config.js +83 -75
  23. package/src/commands/proxy-control.js +59 -26
  24. package/src/commands/stats.js +37 -19
  25. package/src/commands/switch.js +5 -1
  26. package/src/commands/toggle-proxy.js +131 -53
  27. package/src/commands/ui.js +3 -4
  28. package/src/commands/workspace.js +3 -35
  29. package/src/config/default.js +19 -2
  30. package/src/config/loader.js +5 -76
  31. package/src/config/paths.js +96 -11
  32. package/src/index.js +10 -16
  33. package/src/server/api/commands.js +1 -1
  34. package/src/server/api/config-export.js +4 -2
  35. package/src/server/api/config-registry.js +40 -34
  36. package/src/server/api/config.js +5 -1
  37. package/src/server/api/dashboard.js +22 -6
  38. package/src/server/api/mcp.js +5 -3
  39. package/src/server/api/omp-channels.js +349 -0
  40. package/src/server/api/omp-config.js +164 -0
  41. package/src/server/api/omp-projects.js +76 -0
  42. package/src/server/api/omp-proxy.js +154 -0
  43. package/src/server/api/omp-sessions.js +334 -0
  44. package/src/server/api/omp-statistics.js +40 -0
  45. package/src/server/api/plugins.js +1 -1
  46. package/src/server/api/prompts.js +3 -2
  47. package/src/server/api/settings.js +2 -0
  48. package/src/server/api/skills.js +1 -1
  49. package/src/server/api/workspaces.js +3 -41
  50. package/src/server/index.js +41 -0
  51. package/src/server/omp-proxy-server.js +66 -0
  52. package/src/server/services/agents-service.js +2 -1
  53. package/src/server/services/base/proxy-utils.js +1 -0
  54. package/src/server/services/channel-balance.js +151 -1
  55. package/src/server/services/channel-scheduler.js +7 -1
  56. package/src/server/services/commands-service.js +11 -2
  57. package/src/server/services/config-export-service.js +296 -39
  58. package/src/server/services/config-registry-service.js +20 -18
  59. package/src/server/services/config-sync-manager.js +115 -6
  60. package/src/server/services/config-sync-service.js +4 -2
  61. package/src/server/services/config-templates-service.js +183 -45
  62. package/src/server/services/mcp-service.js +217 -33
  63. package/src/server/services/notification-hooks.js +138 -6
  64. package/src/server/services/omp-auth-providers.js +294 -0
  65. package/src/server/services/omp-channels.js +94 -0
  66. package/src/server/services/omp-config.js +269 -0
  67. package/src/server/services/omp-sessions.js +490 -0
  68. package/src/server/services/omp-settings-manager.js +975 -0
  69. package/src/server/services/omp-statistics-service.js +317 -0
  70. package/src/server/services/plugins-service.js +673 -11
  71. package/src/server/services/prompts-service.js +112 -7
  72. package/src/server/services/proxy-log-helper.js +1 -0
  73. package/src/server/services/proxy-runtime.js +5 -1
  74. package/src/server/services/sessions.js +19 -35
  75. package/src/server/services/skill-service.js +13 -3
  76. package/src/server/services/speed-test.js +34 -28
  77. package/src/server/services/ui-config.js +50 -12
  78. package/src/server/services/workspace-service.js +117 -225
  79. package/src/server/websocket-server.js +7 -1
  80. package/src/shared/platforms.js +201 -0
  81. package/src/utils/session.js +4 -10
  82. package/dist/web/assets/Home-BiaKZMah.js +0 -1
  83. package/dist/web/assets/Home-CIua-b-o.css +0 -1
  84. package/dist/web/assets/ProjectList-BPfG_92X.js +0 -1
  85. package/dist/web/assets/SessionList-BIVtw00v.js +0 -1
  86. package/dist/web/assets/index-BpBFF3b2.js +0 -2
  87. package/dist/web/assets/index-Dsh4Q5Cg.css +0 -1
  88. 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
- if (cliType === 'claude') {
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 (cliType === 'codex') {
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 (cliType === 'gemini') {
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 (cliType === 'opencode') {
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 === 'omp') {
49
+ const { getOmpProxyStatus, startOmpProxyServer, stopOmpProxyServer } = require('../server/omp-proxy-server');
50
+ const config = loadConfig();
51
+ return {
52
+ getProxyStatus: getOmpProxyStatus,
53
+ startProxyServer: startOmpProxyServer,
54
+ stopProxyServer: stopOmpProxyServer,
55
+ defaultPort: config.ports?.ompProxy || 20092,
56
+ managedProviderConfig: true
57
+ };
46
58
  }
59
+
60
+ return null;
47
61
  }
48
62
 
49
63
  function getSettingsManager(cliType) {
50
- const loader = SETTINGS_MANAGERS[cliType] || SETTINGS_MANAGERS.claude;
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 markerPath = PATHS.activeChannel?.[cliType];
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 markerPath = PATHS.activeChannel?.[cliType];
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
- if (cliType === 'claude') {
136
+ const normalizedCliType = normalizePlatformKey(cliType || 'claude');
137
+ if (normalizedCliType === 'claude') {
120
138
  const { getAllChannels, applyChannelToSettings } = require('../server/services/channels');
121
- const target = pickRestoredChannel(cliType, getAllChannels());
139
+ const target = pickRestoredChannel(normalizedCliType, getAllChannels());
122
140
  return target ? applyChannelToSettings(target.id) : null;
123
141
  }
124
142
 
125
- if (cliType === 'codex') {
143
+ if (normalizedCliType === 'codex') {
126
144
  const { getChannels, applyChannelToSettings } = require('../server/services/codex-channels');
127
- const target = pickRestoredChannel(cliType, getChannels().channels || []);
145
+ const target = pickRestoredChannel(normalizedCliType, getChannels().channels || []);
128
146
  return target ? applyChannelToSettings(target.id) : null;
129
147
  }
130
148
 
131
- if (cliType === 'gemini') {
149
+ if (normalizedCliType === 'gemini') {
132
150
  const { getChannels, applyChannelToSettings } = require('../server/services/gemini-channels');
133
- const target = pickRestoredChannel(cliType, getChannels().channels || []);
151
+ const target = pickRestoredChannel(normalizedCliType, getChannels().channels || []);
134
152
  return target ? applyChannelToSettings(target.id) : null;
135
153
  }
136
154
 
137
- if (cliType === 'opencode') {
155
+ if (normalizedCliType === 'opencode') {
138
156
  const { getChannels, applyChannelToSettings } = require('../server/services/opencode-channels');
139
- const target = pickRestoredChannel(cliType, getChannels().channels || []);
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
+ omp: '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
- console.log(chalk.gray('• 开启后会在本地启动一个代理服务'));
189
- console.log(chalk.gray(`• 可以在不重启 ${toolName} 的情况下动态管理渠道`));
190
- console.log(chalk.gray('• 通过 Web UI 或"渠道管理"功能快速调整启用的线路'));
191
- console.log(chalk.gray(`• 代理服务地址: http://127.0.0.1:${defaultPort}\n`));
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
- console.log(chalk.yellow('• 开启期间请勿关闭 CLI 终端窗口'));
195
- console.log(chalk.yellow('• 如果异常关闭导致代理失效,请运行: ctx reset'));
196
- console.log(chalk.yellow('• 或使用主菜单的"恢复默认配置"功能\n'));
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('\n[START] 正在启动代理服务...\n'));
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
- console.log(chalk.green(`[OK] 代理服务已启动: http://127.0.0.1:${proxyResult.port}`));
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
- const settingsManager = getSettingsManager(cliType);
226
- clearNativeOAuth(cliType);
227
- settingsManager.setProxyConfig(proxyResult.port);
228
- console.log(chalk.green('[OK] 配置文件已更新'));
229
-
230
- if (settingsManager.hasBackup()) {
231
- console.log(chalk.green('[OK] 原配置已备份'));
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
- console.log(chalk.gray(` 现在可以通过"渠道管理"功能快速调整,无需重启 ${toolName}\n`));
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
+ omp: '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
- console.log(chalk.gray(`• 代理服务: ${chalk.green('运行中')}`));
277
- console.log(chalk.gray(`• 代理端口: ${proxyStatus.port}`));
278
- console.log(chalk.gray(`• 代理地址: http://127.0.0.1:${proxyStatus.port}\n`));
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
- console.log(chalk.gray('• 代理服务将被停止'));
282
- console.log(chalk.gray('• 配置将恢复为当前激活渠道的单渠道模式'));
283
- console.log(chalk.gray(`• 之后管理渠道将需要重启 ${toolName}\n`));
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('\n[STOP] 正在停止代理服务...\n'));
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('[OK] 代理服务已停止'));
366
+ console.log(chalk.green(services.managedProviderConfig
367
+ ? '[OK] OMP models.yml 受管 provider 已移除'
368
+ : '[OK] 代理服务已停止'));
305
369
 
306
370
  // 恢复配置文件
307
- const settingsManager = getSettingsManager(cliType);
308
- settingsManager.deleteBackup?.();
309
- const restoredChannel = restoreSingleChannelMode(cliType);
310
- removeActiveChannelMarker(cliType);
311
- if (restoredChannel?.name) {
312
- console.log(chalk.green(`[OK] 已恢复到渠道: ${restoredChannel.name}`));
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
- console.log(chalk.gray(` 现在调整渠道需要重启 ${toolName} 才能生效\n`));
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
  };
@@ -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/--hosts 标志)
13
- const enableHost = hasHostFlag();
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/--hosts)\n'));
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: selectedBranchMode === 'new' ? '输入新分支名:' : '输入已有分支名:',
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
- const modeLabel = proj.branchMode === 'new' ? '新建分支' : '已有分支';
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('');
@@ -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, omp
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 // OpenCode 代理服务端口
23
+ opencodeProxy: 20091, // OpenCode 代理服务端口
24
+ ompProxy: 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
+ omp: {
77
+ mode: 'auto',
78
+ input: 2.5,
79
+ output: 15,
80
+ cacheRead: 0.25,
81
+ models: {}
65
82
  }
66
83
  }
67
84
  };
@@ -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, NATIVE_PATHS, ensureStorageDirMigrated } = require('./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 = resolveClaudeProjectsDir(userConfig);
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: resolveClaudeProjectsDir(),
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
- const normalizedConfig = normalizeConfigForSave(config);
208
- fs.writeFileSync(configFilePath, JSON.stringify(normalizedConfig, null, 2), 'utf8');
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
  };