@adversity/coding-tool-x 3.1.2 → 3.1.3

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 (59) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/web/assets/Analytics-BIqc8Rin.css +1 -0
  3. package/dist/web/assets/Analytics-D2V09DHH.js +39 -0
  4. package/dist/web/assets/{ConfigTemplates-DvcbKKdS.js → ConfigTemplates-Bf_11LhH.js} +1 -1
  5. package/dist/web/assets/{Home-Cw-F_Wnu.js → Home-BRnW4FTS.js} +1 -1
  6. package/dist/web/assets/{Home-BJKPCBuk.css → Home-CyCIx4BA.css} +1 -1
  7. package/dist/web/assets/{PluginManager-jy_4GVxI.js → PluginManager-B9J32GhW.js} +1 -1
  8. package/dist/web/assets/{ProjectList-Df1-NcNr.js → ProjectList-5a19MWJk.js} +1 -1
  9. package/dist/web/assets/SessionList-CXUr6S7w.css +1 -0
  10. package/dist/web/assets/SessionList-Cxg5bAdT.js +1 -0
  11. package/dist/web/assets/{SkillManager-IRdseMKB.js → SkillManager-CVBr0CLi.js} +1 -1
  12. package/dist/web/assets/{Terminal-BasTyDut.js → Terminal-D2Xe_Q0H.js} +1 -1
  13. package/dist/web/assets/{WorkspaceManager-D-D2kK1V.js → WorkspaceManager-C7dwV94C.js} +1 -1
  14. package/dist/web/assets/icons-BxcwoY5F.js +1 -0
  15. package/dist/web/assets/index-BS9RA6SN.js +2 -0
  16. package/dist/web/assets/index-DUNAVDGb.css +1 -0
  17. package/dist/web/assets/naive-ui-BIXcURHZ.js +1 -0
  18. package/dist/web/assets/{vendors-CO3Upi1d.js → vendors-i5CBGnlm.js} +1 -1
  19. package/dist/web/assets/{vue-vendor-DqyWIXEb.js → vue-vendor-PKd8utv_.js} +1 -1
  20. package/dist/web/index.html +6 -6
  21. package/package.json +1 -1
  22. package/src/config/default.js +7 -29
  23. package/src/config/loader.js +6 -3
  24. package/src/config/model-metadata.js +102 -350
  25. package/src/config/model-metadata.json +125 -0
  26. package/src/server/api/channels.js +16 -39
  27. package/src/server/api/codex-channels.js +15 -43
  28. package/src/server/api/commands.js +0 -77
  29. package/src/server/api/config.js +4 -1
  30. package/src/server/api/gemini-channels.js +16 -40
  31. package/src/server/api/opencode-channels.js +25 -51
  32. package/src/server/api/opencode-proxy.js +1 -1
  33. package/src/server/api/opencode-sessions.js +0 -7
  34. package/src/server/api/sessions.js +11 -68
  35. package/src/server/api/settings.js +66 -39
  36. package/src/server/api/skills.js +0 -44
  37. package/src/server/api/statistics.js +115 -1
  38. package/src/server/codex-proxy-server.js +26 -55
  39. package/src/server/gemini-proxy-server.js +15 -14
  40. package/src/server/index.js +0 -3
  41. package/src/server/opencode-proxy-server.js +45 -121
  42. package/src/server/proxy-server.js +2 -4
  43. package/src/server/services/commands-service.js +0 -29
  44. package/src/server/services/config-templates-service.js +38 -28
  45. package/src/server/services/env-checker.js +73 -8
  46. package/src/server/services/plugins-service.js +37 -28
  47. package/src/server/services/pty-manager.js +22 -18
  48. package/src/server/services/skill-service.js +1 -49
  49. package/src/server/services/speed-test.js +40 -3
  50. package/src/server/services/statistics-service.js +238 -1
  51. package/src/server/utils/pricing.js +51 -60
  52. package/dist/web/assets/SessionList-BGJWyneI.css +0 -1
  53. package/dist/web/assets/SessionList-UWcZtC2r.js +0 -1
  54. package/dist/web/assets/icons-kcfLIMBB.js +0 -1
  55. package/dist/web/assets/index-CoB3zF0K.css +0 -1
  56. package/dist/web/assets/index-CryrSLv8.js +0 -2
  57. package/dist/web/assets/naive-ui-CSrLusZZ.js +0 -1
  58. package/src/server/api/convert.js +0 -260
  59. package/src/server/services/session-converter.js +0 -577
@@ -0,0 +1,125 @@
1
+ {
2
+ "lastUpdated": "2026-02-27",
3
+ "defaultModels": {
4
+ "claude": [
5
+ "claude-opus-4-6",
6
+ "claude-sonnet-4-6",
7
+ "claude-opus-4-5-20251101",
8
+ "claude-sonnet-4-5-20250929",
9
+ "claude-haiku-4-5-20251001"
10
+ ],
11
+ "codex": [
12
+ "gpt-5.3-codex",
13
+ "gpt-5.2-codex",
14
+ "gpt-5.1-codex-max",
15
+ "gpt-5.1-codex",
16
+ "gpt-5.1-codex-mini",
17
+ "gpt-5-codex",
18
+ "gpt-5.2",
19
+ "gpt-5.1",
20
+ "gpt-5"
21
+ ],
22
+ "gemini": [
23
+ "gemini-3.1-pro",
24
+ "gemini-3-pro-preview",
25
+ "gemini-3-flash-preview",
26
+ "gemini-2.5-pro",
27
+ "gemini-2.5-flash",
28
+ "gemini-2.5-flash-lite"
29
+ ]
30
+ },
31
+ "defaultSpeedTestModels": {
32
+ "claude": "claude-haiku-4-5",
33
+ "codex": "gpt-5.2",
34
+ "gemini": "gemini-2.5-pro"
35
+ },
36
+ "aliases": {
37
+ "claude-opus-4-6": "claude-opus-4-6",
38
+ "claude-sonnet-4-6": "claude-sonnet-4-6",
39
+ "claude-opus-4-5": "claude-opus-4-5-20251101",
40
+ "claude-sonnet-4-5": "claude-sonnet-4-5-20250929",
41
+ "claude-haiku-4-5": "claude-haiku-4-5-20251001"
42
+ },
43
+ "models": {
44
+ "claude-opus-4-6": {
45
+ "limit": { "context": 200000, "output": 32000 },
46
+ "pricing": { "input": 15, "output": 75, "cacheCreation": 18.75, "cacheRead": 1.5 }
47
+ },
48
+ "claude-sonnet-4-6": {
49
+ "limit": { "context": 200000, "output": 64000 },
50
+ "pricing": { "input": 3, "output": 15, "cacheCreation": 3.75, "cacheRead": 0.3 }
51
+ },
52
+ "claude-opus-4-5-20251101": {
53
+ "limit": { "context": 200000, "output": 32000 },
54
+ "pricing": { "input": 5, "output": 25, "cacheCreation": 6.25, "cacheRead": 0.5 }
55
+ },
56
+ "claude-sonnet-4-5-20250929": {
57
+ "limit": { "context": 200000, "output": 64000 },
58
+ "pricing": { "input": 3, "output": 15, "cacheCreation": 3.75, "cacheRead": 0.3 }
59
+ },
60
+ "claude-haiku-4-5-20251001": {
61
+ "limit": { "context": 200000, "output": 8096 },
62
+ "pricing": { "input": 1, "output": 5, "cacheCreation": 1.25, "cacheRead": 0.1 }
63
+ },
64
+ "gpt-5": {
65
+ "limit": { "context": 1000000, "output": 32768 },
66
+ "pricing": { "input": 2, "output": 8 }
67
+ },
68
+ "gpt-5.1": {
69
+ "limit": { "context": 1000000, "output": 32768 },
70
+ "pricing": { "input": 2, "output": 8 }
71
+ },
72
+ "gpt-5.2": {
73
+ "limit": { "context": 1000000, "output": 32768 },
74
+ "pricing": { "input": 2, "output": 8 }
75
+ },
76
+ "gpt-5-codex": {
77
+ "limit": { "context": 1000000, "output": 32768 },
78
+ "pricing": { "input": 2, "output": 8 }
79
+ },
80
+ "gpt-5.1-codex": {
81
+ "limit": { "context": 1000000, "output": 32768 },
82
+ "pricing": { "input": 2, "output": 8 }
83
+ },
84
+ "gpt-5.1-codex-mini": {
85
+ "limit": { "context": 1000000, "output": 32768 },
86
+ "pricing": { "input": 1.5, "output": 6 }
87
+ },
88
+ "gpt-5.1-codex-max": {
89
+ "limit": { "context": 1000000, "output": 32768 },
90
+ "pricing": { "input": 3, "output": 12 }
91
+ },
92
+ "gpt-5.2-codex": {
93
+ "limit": { "context": 1000000, "output": 32768 },
94
+ "pricing": { "input": 2, "output": 8 }
95
+ },
96
+ "gpt-5.3-codex": {
97
+ "limit": { "context": 1000000, "output": 32768 },
98
+ "pricing": { "input": 2, "output": 8 }
99
+ },
100
+ "gemini-3.1-pro": {
101
+ "limit": { "context": 2097152, "output": 65536 },
102
+ "pricing": { "input": 2.5, "output": 15 }
103
+ },
104
+ "gemini-3-pro-preview": {
105
+ "limit": { "context": 2097152, "output": 65536 },
106
+ "pricing": { "input": 2.5, "output": 15 }
107
+ },
108
+ "gemini-3-flash-preview": {
109
+ "limit": { "context": 1048576, "output": 65536 },
110
+ "pricing": { "input": 0.3, "output": 1.2 }
111
+ },
112
+ "gemini-2.5-pro": {
113
+ "limit": { "context": 1048576, "output": 65536 },
114
+ "pricing": { "input": 1.25, "output": 10 }
115
+ },
116
+ "gemini-2.5-flash": {
117
+ "limit": { "context": 1048576, "output": 65536 },
118
+ "pricing": { "input": 0.15, "output": 0.6 }
119
+ },
120
+ "gemini-2.5-flash-lite": {
121
+ "limit": { "context": 1048576, "output": 65536 },
122
+ "pricing": { "input": 0.1, "output": 0.4 }
123
+ }
124
+ }
125
+ }
@@ -18,14 +18,15 @@ const {
18
18
  sanitizeBatchConcurrency,
19
19
  runWithConcurrencyLimit
20
20
  } = require('../services/speed-test');
21
- const {
22
- probeModelAvailability,
23
- fetchModelsFromProvider
24
- } = require('../services/model-detector');
21
+ const { getDefaultSpeedTestModelByToolType } = require('../../config/model-metadata');
25
22
  const { broadcastLog, broadcastProxyState, broadcastSchedulerState } = require('../websocket-server');
26
23
  const { clearRedirectCache } = require('../proxy-server');
27
24
  const CLAUDE_GATEWAY_SOURCE_TYPE = 'claude';
28
25
 
26
+ function getDefaultClaudeModel() {
27
+ return getDefaultSpeedTestModelByToolType('claude');
28
+ }
29
+
29
30
  // GET /api/channels - Get all channels with health status
30
31
  router.get('/', (req, res) => {
31
32
  try {
@@ -271,41 +272,17 @@ router.get('/:id/models', async (req, res) => {
271
272
  }
272
273
 
273
274
  const gatewaySourceType = CLAUDE_GATEWAY_SOURCE_TYPE;
274
- const listResult = await fetchModelsFromProvider(channel, 'openai_compatible');
275
- const listedModels = Array.isArray(listResult.models) ? listResult.models : [];
276
- let result;
277
-
278
- if (listedModels.length > 0) {
279
- result = {
280
- models: listedModels,
281
- supported: true,
282
- cached: !!listResult.cached,
283
- fallbackUsed: false,
284
- lastChecked: listResult.lastChecked || new Date().toISOString(),
285
- error: null,
286
- errorHint: null
287
- };
288
- } else {
289
- const usingConfiguredProbe = !!listResult.disabledByConfig;
290
- const probe = await probeModelAvailability(channel, gatewaySourceType, {
291
- stopOnFirstAvailable: false
292
- });
293
- const probedModels = Array.isArray(probe.availableModels) ? probe.availableModels : [];
294
-
295
- result = {
296
- models: probedModels,
297
- supported: probedModels.length > 0,
298
- cached: !!probe.cached || !!listResult.cached,
299
- fallbackUsed: probedModels.length > 0,
300
- lastChecked: probe.lastChecked || listResult.lastChecked || new Date().toISOString(),
301
- error: probedModels.length > 0 ? null : (listResult.error || '无法获取可用模型'),
302
- errorHint: probedModels.length > 0
303
- ? (usingConfiguredProbe ? '已按设置跳过 /v1/models,使用默认模型探测结果' : '模型列表接口不可用,已使用模型探测结果')
304
- : (listResult.errorHint || (usingConfiguredProbe
305
- ? '已按设置跳过 /v1/models,且默认模型探测无可用结果'
306
- : '模型列表接口不可用且模型探测无可用结果'))
307
- };
308
- }
275
+ const models = [getDefaultClaudeModel()];
276
+ const now = new Date().toISOString();
277
+ const result = {
278
+ models,
279
+ supported: models.length > 0,
280
+ cached: false,
281
+ fallbackUsed: false,
282
+ lastChecked: now,
283
+ error: models.length > 0 ? null : '未配置默认模型列表',
284
+ errorHint: models.length > 0 ? null : '请在设置中配置 Claude 默认模型'
285
+ };
309
286
 
310
287
  res.json({
311
288
  channelId: id,
@@ -20,12 +20,13 @@ const {
20
20
  runWithConcurrencyLimit
21
21
  } = require('../services/speed-test');
22
22
  const { clearCodexRedirectCache } = require('../codex-proxy-server');
23
- const {
24
- fetchModelsFromProvider,
25
- probeModelAvailability,
26
- } = require('../services/model-detector');
23
+ const { getDefaultSpeedTestModelByToolType } = require('../../config/model-metadata');
27
24
  const CODEX_GATEWAY_SOURCE_TYPE = 'codex';
28
25
 
26
+ function getDefaultCodexModel() {
27
+ return getDefaultSpeedTestModelByToolType('codex');
28
+ }
29
+
29
30
  module.exports = (config) => {
30
31
  /**
31
32
  * GET /api/codex/channels
@@ -68,45 +69,16 @@ module.exports = (config) => {
68
69
  }
69
70
 
70
71
  const gatewaySourceType = CODEX_GATEWAY_SOURCE_TYPE;
71
- const listResult = await fetchModelsFromProvider(channel, 'openai_compatible');
72
- const listedModels = Array.isArray(listResult.models) ? listResult.models : [];
73
- let result;
74
-
75
- if (listedModels.length > 0) {
76
- result = listResult;
77
- } else {
78
- const usingConfiguredProbe = !!listResult.disabledByConfig;
79
- const probe = await probeModelAvailability(channel, 'codex');
80
- const probedModels = Array.isArray(probe.availableModels) ? probe.availableModels : [];
81
-
82
- if (probedModels.length > 0) {
83
- result = {
84
- models: probedModels,
85
- supported: true,
86
- cached: !!probe.cached,
87
- fallbackUsed: false,
88
- lastChecked: probe.lastChecked || listResult.lastChecked || new Date().toISOString(),
89
- error: null,
90
- errorHint: listResult.error
91
- ? (usingConfiguredProbe
92
- ? '已按设置跳过 /v1/models,使用默认模型探测结果'
93
- : '模型列表接口不可用,已自动切换为模型探测结果')
94
- : null
95
- };
96
- } else {
97
- result = {
98
- models: [],
99
- supported: false,
100
- cached: !!probe.cached || !!listResult.cached,
101
- fallbackUsed: false,
102
- lastChecked: probe.lastChecked || listResult.lastChecked || new Date().toISOString(),
103
- error: listResult.error || '无法探测可用模型',
104
- errorHint: listResult.errorHint || (usingConfiguredProbe
105
- ? '已按设置跳过 /v1/models,且默认模型探测无可用结果'
106
- : '模型列表接口不可用且模型探测无可用结果')
107
- };
108
- }
109
- }
72
+ const models = [getDefaultCodexModel()];
73
+ const result = {
74
+ models,
75
+ supported: models.length > 0,
76
+ cached: false,
77
+ fallbackUsed: false,
78
+ lastChecked: new Date().toISOString(),
79
+ error: models.length > 0 ? null : '未配置默认模型列表',
80
+ errorHint: models.length > 0 ? null : '请在设置中配置 Codex 默认模型'
81
+ };
110
82
 
111
83
  res.json({
112
84
  channelId: id,
@@ -479,81 +479,4 @@ router.post('/uninstall', (req, res) => {
479
479
  }
480
480
  });
481
481
 
482
- // ==================== 格式转换 API ====================
483
-
484
- /**
485
- * 转换命令格式
486
- * POST /api/commands/convert
487
- * Body: { content, targetFormat }
488
- * - content: 命令内容
489
- * - targetFormat: 目标格式 ('claude' | 'codex')
490
- */
491
- router.post('/convert', (req, res) => {
492
- try {
493
- const { platform, service } = getCommandsService(req);
494
- const { content, targetFormat } = req.body;
495
-
496
- if (!content) {
497
- return res.status(400).json({
498
- success: false,
499
- message: '请提供命令内容'
500
- });
501
- }
502
-
503
- if (!['claude', 'codex'].includes(targetFormat)) {
504
- return res.status(400).json({
505
- success: false,
506
- message: '目标格式必须是 claude 或 codex'
507
- });
508
- }
509
-
510
- const result = service.convertCommandFormat(content, targetFormat);
511
-
512
- res.json({
513
- success: true,
514
- platform,
515
- ...result
516
- });
517
- } catch (err) {
518
- console.error('[Commands API] Convert command error:', err);
519
- res.status(500).json({
520
- success: false,
521
- message: err.message
522
- });
523
- }
524
- });
525
-
526
- /**
527
- * 检测命令格式
528
- * POST /api/commands/detect-format
529
- * Body: { content }
530
- */
531
- router.post('/detect-format', (req, res) => {
532
- try {
533
- const { platform, service } = getCommandsService(req);
534
- const { content } = req.body;
535
-
536
- if (!content) {
537
- return res.status(400).json({
538
- success: false,
539
- message: '请提供命令内容'
540
- });
541
- }
542
-
543
- const format = service.detectFormat(content);
544
-
545
- res.json({
546
- success: true,
547
- platform,
548
- format
549
- });
550
- } catch (err) {
551
- console.error('[Commands API] Detect format error:', err);
552
- res.status(500).json({
553
- success: false,
554
- message: err.message
555
- });
556
- }
557
- });
558
-
559
482
  module.exports = router;
@@ -44,9 +44,12 @@ function sanitizePricing(inputPricing, currentPricing) {
44
44
  }
45
45
 
46
46
  function normalizeModelDiscovery(modelDiscovery, currentValue = DEFAULT_CONFIG.modelDiscovery) {
47
+ const defaultModelDiscovery = DEFAULT_CONFIG.modelDiscovery && typeof DEFAULT_CONFIG.modelDiscovery === 'object'
48
+ ? DEFAULT_CONFIG.modelDiscovery
49
+ : { useV1ModelsEndpoint: false };
47
50
  const current = currentValue && typeof currentValue === 'object'
48
51
  ? currentValue
49
- : DEFAULT_CONFIG.modelDiscovery;
52
+ : defaultModelDiscovery;
50
53
  const input = modelDiscovery && typeof modelDiscovery === 'object'
51
54
  ? modelDiscovery
52
55
  : {};
@@ -19,12 +19,13 @@ const {
19
19
  runWithConcurrencyLimit
20
20
  } = require('../services/speed-test');
21
21
  const { clearGeminiRedirectCache } = require('../gemini-proxy-server');
22
- const {
23
- probeModelAvailability,
24
- fetchModelsFromProvider
25
- } = require('../services/model-detector');
22
+ const { getDefaultSpeedTestModelByToolType } = require('../../config/model-metadata');
26
23
  const GEMINI_GATEWAY_SOURCE_TYPE = 'gemini';
27
24
 
25
+ function getDefaultGeminiModel() {
26
+ return getDefaultSpeedTestModelByToolType('gemini');
27
+ }
28
+
28
29
  module.exports = (config) => {
29
30
  /**
30
31
  * GET /api/gemini/channels
@@ -67,41 +68,16 @@ module.exports = (config) => {
67
68
  }
68
69
 
69
70
  const gatewaySourceType = GEMINI_GATEWAY_SOURCE_TYPE;
70
- const listResult = await fetchModelsFromProvider(channel, 'openai_compatible');
71
- const listedModels = Array.isArray(listResult.models) ? listResult.models : [];
72
- let result;
73
-
74
- if (listedModels.length > 0) {
75
- result = {
76
- models: listedModels,
77
- supported: true,
78
- cached: !!listResult.cached,
79
- fallbackUsed: false,
80
- lastChecked: listResult.lastChecked || new Date().toISOString(),
81
- error: null,
82
- errorHint: null
83
- };
84
- } else {
85
- const usingConfiguredProbe = !!listResult.disabledByConfig;
86
- const probe = await probeModelAvailability(channel, gatewaySourceType, {
87
- stopOnFirstAvailable: false
88
- });
89
- const probedModels = Array.isArray(probe.availableModels) ? probe.availableModels : [];
90
-
91
- result = {
92
- models: probedModels,
93
- supported: probedModels.length > 0,
94
- cached: !!probe.cached || !!listResult.cached,
95
- fallbackUsed: false,
96
- lastChecked: probe.lastChecked || listResult.lastChecked || new Date().toISOString(),
97
- error: probedModels.length > 0 ? null : (listResult.error || '无法获取可用模型'),
98
- errorHint: probedModels.length > 0
99
- ? (usingConfiguredProbe ? '已按设置跳过 /v1/models,使用默认模型探测结果' : '模型列表接口不可用,已自动切换为模型探测结果')
100
- : (listResult.errorHint || (usingConfiguredProbe
101
- ? '已按设置跳过 /v1/models,且默认模型探测无可用结果'
102
- : '模型列表接口不可用且模型探测无可用结果'))
103
- };
104
- }
71
+ const models = [getDefaultGeminiModel()];
72
+ const result = {
73
+ models,
74
+ supported: models.length > 0,
75
+ cached: false,
76
+ fallbackUsed: false,
77
+ lastChecked: new Date().toISOString(),
78
+ error: models.length > 0 ? null : '未配置默认模型列表',
79
+ errorHint: models.length > 0 ? null : '请在设置中配置 Gemini 默认模型'
80
+ };
105
81
 
106
82
  res.json({
107
83
  channelId: id,
@@ -157,7 +133,7 @@ module.exports = (config) => {
157
133
  return res.status(400).json({ error: 'Missing required fields: apiKey' });
158
134
  }
159
135
 
160
- const channel = createChannel(name, baseUrl, apiKey, model || 'gemini-2.5-pro', {
136
+ const channel = createChannel(name, baseUrl, apiKey, model || getDefaultGeminiModel(), {
161
137
  websiteUrl,
162
138
  enabled,
163
139
  weight,
@@ -24,9 +24,9 @@ const {
24
24
  const { setProxyConfig } = require('../services/opencode-settings-manager');
25
25
  const {
26
26
  fetchModelsFromProvider,
27
- probeModelAvailability,
28
27
  clearCache
29
28
  } = require('../services/model-detector');
29
+ const { getDefaultSpeedTestModelByToolType } = require('../../config/model-metadata');
30
30
 
31
31
  module.exports = (config) => {
32
32
  function uniqueModels(models = []) {
@@ -44,31 +44,6 @@ module.exports = (config) => {
44
44
  return result;
45
45
  }
46
46
 
47
- function collectChannelPreferredModels(channel) {
48
- const candidates = [];
49
- if (!channel || typeof channel !== 'object') return candidates;
50
-
51
- candidates.push(channel.model);
52
- candidates.push(channel.speedTestModel);
53
-
54
- const modelConfig = channel.modelConfig;
55
- if (modelConfig && typeof modelConfig === 'object') {
56
- candidates.push(modelConfig.model);
57
- candidates.push(modelConfig.opusModel);
58
- candidates.push(modelConfig.sonnetModel);
59
- candidates.push(modelConfig.haikuModel);
60
- }
61
-
62
- if (Array.isArray(channel.modelRedirects)) {
63
- channel.modelRedirects.forEach((rule) => {
64
- candidates.push(rule?.from);
65
- candidates.push(rule?.to);
66
- });
67
- }
68
-
69
- return uniqueModels(candidates);
70
- }
71
-
72
47
  function resolveGatewaySourceType(channel) {
73
48
  const value = String(channel?.gatewaySourceType || '').trim().toLowerCase();
74
49
  if (value === 'claude') return 'claude';
@@ -80,11 +55,17 @@ module.exports = (config) => {
80
55
  return resolveGatewaySourceType(channel);
81
56
  }
82
57
 
83
- function isConverterPresetChannel(channel) {
58
+ function isConverterEntryChannel(channel) {
84
59
  const presetId = String(channel?.presetId || '').trim().toLowerCase();
85
60
  return presetId === 'entry_claude' || presetId === 'entry_codex' || presetId === 'entry_gemini';
86
61
  }
87
62
 
63
+ function getDefaultModelsByGatewaySourceType(gatewaySourceType) {
64
+ if (gatewaySourceType === 'claude') return [getDefaultSpeedTestModelByToolType('claude')];
65
+ if (gatewaySourceType === 'gemini') return [getDefaultSpeedTestModelByToolType('gemini')];
66
+ return [getDefaultSpeedTestModelByToolType('codex')];
67
+ }
68
+
88
69
  function refreshEditedChannelModelCache(channelId) {
89
70
  if (!channelId) return;
90
71
  clearCache(channelId);
@@ -216,10 +197,24 @@ module.exports = (config) => {
216
197
  }
217
198
 
218
199
  const gatewaySourceType = resolveGatewaySourceType(channel);
219
- const preferredModels = collectChannelPreferredModels(channel);
200
+ if (isConverterEntryChannel(channel)) {
201
+ const models = uniqueModels(getDefaultModelsByGatewaySourceType(gatewaySourceType));
202
+ const now = new Date().toISOString();
203
+ return res.json({
204
+ channelId: channelId,
205
+ gatewaySourceType,
206
+ models,
207
+ supported: models.length > 0,
208
+ cached: false,
209
+ fallbackUsed: false,
210
+ fetchedAt: now,
211
+ error: models.length > 0 ? null : '未配置默认模型列表',
212
+ errorHint: models.length > 0 ? null : '请在设置中配置对应工具类型的默认模型'
213
+ });
214
+ }
215
+
220
216
  const listResult = await fetchModelsFromProvider(channel, 'openai_compatible');
221
217
  const listedModels = Array.isArray(listResult.models) ? uniqueModels(listResult.models) : [];
222
- const shouldProbeByDefault = !!listResult.disabledByConfig;
223
218
  let result;
224
219
 
225
220
  if (listedModels.length > 0) {
@@ -232,28 +227,7 @@ module.exports = (config) => {
232
227
  error: null,
233
228
  errorHint: null
234
229
  };
235
- } else if (shouldProbeByDefault || isConverterPresetChannel(channel)) {
236
- const probe = await probeModelAvailability(channel, gatewaySourceType, {
237
- stopOnFirstAvailable: false,
238
- preferredModels
239
- });
240
- const probedModels = Array.isArray(probe.availableModels) ? uniqueModels(probe.availableModels) : [];
241
-
242
- result = {
243
- models: probedModels,
244
- supported: probedModels.length > 0,
245
- cached: !!probe.cached || !!listResult.cached,
246
- fallbackUsed: false,
247
- lastChecked: probe.lastChecked || listResult.lastChecked || new Date().toISOString(),
248
- error: probedModels.length > 0 ? null : (listResult.error || '无法获取可用模型'),
249
- errorHint: probedModels.length > 0
250
- ? (shouldProbeByDefault ? '已按设置跳过 /v1/models,使用默认模型探测结果' : '模型列表接口不可用,已自动切换为模型探测结果')
251
- : (listResult.errorHint || (shouldProbeByDefault
252
- ? '已按设置跳过 /v1/models,且默认模型探测无可用结果'
253
- : '模型列表接口不可用且模型探测无可用结果'))
254
- };
255
230
  } else {
256
- // 非入口转换器渠道:只请求 /v1/models,失败则返回空列表
257
231
  result = {
258
232
  models: [],
259
233
  supported: false,
@@ -261,7 +235,7 @@ module.exports = (config) => {
261
235
  fallbackUsed: false,
262
236
  lastChecked: listResult.lastChecked || new Date().toISOString(),
263
237
  error: listResult.error || '该渠道未返回可用模型列表',
264
- errorHint: listResult.errorHint || '此类型渠道不执行模型探测,请检查 /v1/models 接口'
238
+ errorHint: listResult.errorHint || ' OpenCode 非转换入口使用 /v1/models,请检查接口配置'
265
239
  };
266
240
  }
267
241
 
@@ -103,7 +103,7 @@ router.post('/start', async (req, res) => {
103
103
  // 4. 设置代理配置(写入 OpenCode 配置文件)
104
104
  // 收集每个渠道的模型列表,生成 per-channel provider 配置
105
105
 
106
- // 若渠道未显式填写模型,回退使用代理聚合模型(含 /v1/models 与模型探测结果)。
106
+ // 若渠道未显式填写模型,回退使用代理聚合模型(来自 /v1/models)。
107
107
  let detectedModels = [];
108
108
  try {
109
109
  detectedModels = await collectProxyModelList(enabledChannels, {
@@ -289,13 +289,6 @@ module.exports = (config) => {
289
289
 
290
290
  const { exec } = require('child_process');
291
291
  const { projectName, sessionId } = req.params;
292
- const { targetTool } = req.body || {};
293
-
294
- if (targetTool && targetTool !== 'opencode') {
295
- return res.status(400).json({
296
- error: 'OpenCode 会话暂不支持直接切换到其他 CLI,请使用 OpenCode 启动'
297
- });
298
- }
299
292
 
300
293
  const session = getSessionById(sessionId);
301
294
  if (!session) {