coding-tool-x 3.7.8 → 3.7.10

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.
@@ -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-CeTaG4IX.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-LYvv7wtx.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-CVyvdmg1.js">
11
+ <link rel="modulepreload" crossorigin href="/assets/naive-ui-BALVpMgd.js">
12
12
  <link rel="modulepreload" crossorigin href="/assets/icons-nGdSKQn2.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-S8wrU2Nf.css">
15
+ <link rel="stylesheet" crossorigin href="/assets/index-C5nLTbOk.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.7.8",
3
+ "version": "3.7.10",
4
4
  "description": "Vibe Coding 增强工作助手 - 智能会话管理、动态渠道切换、全局搜索、实时监控",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -91,7 +91,15 @@
91
91
  },
92
92
  "gpt-5.5": {
93
93
  "limit": { "context": 1050000, "output": 128000 },
94
- "pricing": { "input": 5, "output": 30, "cacheRead": 0.5 }
94
+ "pricing": { "input": 5, "output": 30, "cacheRead": 0.5 },
95
+ "reasoning": true,
96
+ "thinking": {
97
+ "mode": "effort",
98
+ "efforts": ["low", "medium", "high", "xhigh"],
99
+ "defaultLevel": "medium"
100
+ },
101
+ "input": ["text", "image"],
102
+ "supportsTools": true
95
103
  },
96
104
  "gpt-5.4": {
97
105
  "limit": { "context": 1050000, "output": 128000 },
@@ -26,9 +26,37 @@ const {
26
26
  getCachedOmpAuthProviderSnapshot,
27
27
  getOmpAuthProviderCacheMeta
28
28
  } = require('../services/omp-auth-providers');
29
+ const {
30
+ MODEL_SCHEMA_VERSION,
31
+ MODEL_METADATA_MODES,
32
+ getPublicModelFieldSchema,
33
+ normalizeCatalogModelList,
34
+ redactSensitiveFields,
35
+ validateModelDefinitions,
36
+ validateProviderConfig
37
+ } = require('../services/model-definition-schema');
38
+ const { getOmpCatalogModels } = require('../services/omp-settings-manager');
29
39
 
30
40
  const CHANNEL_LIST_AUTH_OPTIONS = { accountCheck: false, includeStatus: false };
31
41
 
42
+ function validateOmpModelPayload(payload = {}) {
43
+ if (payload.modelMetadataMode !== undefined && !MODEL_METADATA_MODES.includes(payload.modelMetadataMode)) {
44
+ return `modelMetadataMode must be one of: ${MODEL_METADATA_MODES.join(', ')}`;
45
+ }
46
+ if (payload.models !== undefined) {
47
+ const validation = validateModelDefinitions(payload.models);
48
+ if (!validation.valid) return validation.error;
49
+ }
50
+ if (payload.modelBindings !== undefined && !Array.isArray(payload.modelBindings)) {
51
+ return 'modelBindings must be an array';
52
+ }
53
+ if (payload.providerConfig !== undefined) {
54
+ const validation = validateProviderConfig(payload.providerConfig);
55
+ if (!validation.valid) return validation.error;
56
+ }
57
+ return null;
58
+ }
59
+
32
60
  function uniqueModels(models = []) {
33
61
  const seen = new Set();
34
62
  const result = [];
@@ -322,6 +350,44 @@ module.exports = () => {
322
350
  }
323
351
  });
324
352
 
353
+ router.post('/catalog-metadata', (req, res) => {
354
+ try {
355
+ const providerKey = String(req.body?.providerKey || '').trim();
356
+ if (!providerKey) {
357
+ return res.status(400).json({ error: 'providerKey is required' });
358
+ }
359
+ const requestedModelIds = uniqueModels([
360
+ req.body?.model,
361
+ req.body?.speedTestModel,
362
+ ...(Array.isArray(req.body?.allowedModels) ? req.body.allowedModels : []),
363
+ ...(Array.isArray(req.body?.models)
364
+ ? req.body.models.map(model => typeof model === 'string' ? model : (model?.id || model?.name))
365
+ : [])
366
+ ]);
367
+ const rawModels = getOmpCatalogModels(providerKey, {
368
+ forceCatalogRefresh: req.body?.forceRefresh === true,
369
+ catalogTimeout: 5000,
370
+ requestedModelIds
371
+ });
372
+ const normalizedCatalog = normalizeCatalogModelList(rawModels);
373
+ const models = normalizedCatalog.models.map(model => redactSensitiveFields(model));
374
+ const warnings = normalizedCatalog.warnings.slice();
375
+ if (models.length === 0) {
376
+ warnings.push('OMP catalog did not return model metadata; existing manual definitions were left unchanged.');
377
+ }
378
+ res.json({
379
+ schemaVersion: MODEL_SCHEMA_VERSION,
380
+ fieldSchema: getPublicModelFieldSchema(),
381
+ providerKey,
382
+ models,
383
+ warnings
384
+ });
385
+ } catch (error) {
386
+ console.error('[OMP Channels API] Error reading OMP catalog metadata:', error);
387
+ res.status(502).json({ error: 'Failed to read OMP model metadata' });
388
+ }
389
+ });
390
+
325
391
  router.post('/', (req, res) => {
326
392
  try {
327
393
  const {
@@ -344,9 +410,18 @@ module.exports = () => {
344
410
  presetId,
345
411
  websiteUrl,
346
412
  balanceToken,
347
- balanceUserId
413
+ balanceUserId,
414
+ models,
415
+ modelMetadataMode,
416
+ modelBindings,
417
+ providerConfig
348
418
  } = req.body || {};
349
419
 
420
+ const modelPayloadError = validateOmpModelPayload(req.body || {});
421
+ if (modelPayloadError) {
422
+ return res.status(400).json({ error: modelPayloadError });
423
+ }
424
+
350
425
  if (!name || !baseUrl) {
351
426
  return res.status(400).json({ error: 'Missing required fields: name and baseUrl' });
352
427
  }
@@ -372,7 +447,11 @@ module.exports = () => {
372
447
  presetId,
373
448
  websiteUrl: websiteUrl || '',
374
449
  balanceToken: balanceToken || '',
375
- balanceUserId: balanceUserId || null
450
+ balanceUserId: balanceUserId || null,
451
+ models: Array.isArray(models) ? models : [],
452
+ modelMetadataMode: modelMetadataMode || 'auto',
453
+ modelBindings: Array.isArray(modelBindings) ? modelBindings : [],
454
+ providerConfig: providerConfig || {}
376
455
  });
377
456
  res.json(channel);
378
457
  broadcastSchedulerState('omp', getSchedulerState('omp'));
@@ -384,6 +463,10 @@ module.exports = () => {
384
463
 
385
464
  router.put('/:channelId', (req, res) => {
386
465
  try {
466
+ const modelPayloadError = validateOmpModelPayload(req.body || {});
467
+ if (modelPayloadError) {
468
+ return res.status(400).json({ error: modelPayloadError });
469
+ }
387
470
  const channel = updateChannel(req.params.channelId, req.body || {});
388
471
  res.json(channel);
389
472
  broadcastSchedulerState('omp', getSchedulerState('omp'));
@@ -7,21 +7,68 @@ const {
7
7
  saveDefaultSpeedTestModels
8
8
  } = require('../../config/model-metadata');
9
9
  const { loadConfig, saveConfig } = require('../../config/loader');
10
+ const {
11
+ MODEL_SCHEMA_VERSION,
12
+ getPublicModelFieldSchema,
13
+ isPlainObject,
14
+ redactSensitiveFields,
15
+ resolveModelDefinition,
16
+ validateModelDefinitions
17
+ } = require('../services/model-definition-schema');
18
+
19
+ function mergeMetadata(base = {}, override = {}) {
20
+ const result = { ...base };
21
+ for (const [key, value] of Object.entries(override || {})) {
22
+ if (isPlainObject(value) && isPlainObject(result[key])) result[key] = { ...result[key], ...value };
23
+ else if (value !== undefined) result[key] = value;
24
+ }
25
+ return result;
26
+ }
27
+
28
+ function normalizeDefinitions(value) {
29
+ if (Array.isArray(value)) {
30
+ return Object.fromEntries(value
31
+ .filter(item => item && typeof item === 'object' && item.id)
32
+ .map(item => [String(item.id), item]));
33
+ }
34
+ return isPlainObject(value) ? value : {};
35
+ }
36
+
37
+ function buildResolvedModelSettings(definitions, overrides) {
38
+ const ids = new Set([
39
+ ...Object.keys(MODEL_METADATA),
40
+ ...Object.keys(overrides || {}),
41
+ ...Object.keys(definitions || {})
42
+ ]);
43
+ const resolvedModels = {};
44
+ const provenance = {};
45
+ const warnings = {};
46
+ for (const id of ids) {
47
+ const stored = definitions[id] || {
48
+ id,
49
+ metadataMode: 'hybrid',
50
+ overrides: overrides[id] || {}
51
+ };
52
+ const resolved = resolveModelDefinition(stored, { builtin: MODEL_METADATA[id] || {} });
53
+ resolvedModels[id] = resolved.spec;
54
+ provenance[id] = resolved.provenance;
55
+ if (resolved.warnings.length > 0) warnings[id] = resolved.warnings;
56
+ }
57
+ return { resolvedModels, provenance, warnings };
58
+ }
10
59
 
11
60
  function handleGetModelSettings(req, res) {
12
61
  try {
13
62
  const config = loadConfig();
14
63
  const overrides = config.modelMetadataOverrides || {};
64
+ const definitions = normalizeDefinitions(config.modelDefinitions);
15
65
  const defaultSpeedTestModels = getDefaultSpeedTestModels();
16
66
 
17
67
  // Build merged table: built-in + user overrides
18
68
  const merged = {};
19
69
  for (const [id, meta] of Object.entries(MODEL_METADATA)) {
20
70
  merged[id] = overrides[id]
21
- ? {
22
- limit: { ...meta.limit, ...(overrides[id].limit || {}) },
23
- pricing: { ...meta.pricing, ...(overrides[id].pricing || {}) }
24
- }
71
+ ? mergeMetadata(meta, overrides[id])
25
72
  : meta;
26
73
  }
27
74
 
@@ -32,9 +79,16 @@ function handleGetModelSettings(req, res) {
32
79
  }
33
80
  }
34
81
 
82
+ const resolved = buildResolvedModelSettings(definitions, overrides);
35
83
  res.json({
36
- models: merged,
37
- overrides,
84
+ schemaVersion: MODEL_SCHEMA_VERSION,
85
+ fieldSchema: getPublicModelFieldSchema(),
86
+ models: redactSensitiveFields(merged),
87
+ overrides: redactSensitiveFields(overrides),
88
+ definitions: redactSensitiveFields(definitions),
89
+ resolvedModels: redactSensitiveFields(resolved.resolvedModels),
90
+ provenance: resolved.provenance,
91
+ warnings: resolved.warnings,
38
92
  builtinModelIds: Object.keys(MODEL_METADATA),
39
93
  lastUpdated: METADATA_LAST_UPDATED,
40
94
  defaultSpeedTestModels
@@ -52,7 +106,7 @@ router.get('/model-metadata', handleGetModelSettings);
52
106
 
53
107
  function handleSaveModelSettings(req, res) {
54
108
  try {
55
- const { overrides, defaultSpeedTestModels } = req.body || {};
109
+ const { overrides, definitions, defaultSpeedTestModels } = req.body || {};
56
110
  if (overrides !== undefined && (typeof overrides !== 'object' || overrides === null || Array.isArray(overrides))) {
57
111
  return res.status(400).json({ error: 'overrides must be an object' });
58
112
  }
@@ -63,6 +117,9 @@ function handleSaveModelSettings(req, res) {
63
117
  if (typeof modelId !== 'string' || !modelId.trim()) {
64
118
  return res.status(400).json({ error: `Invalid model ID: "${modelId}"` });
65
119
  }
120
+ if (!isPlainObject(meta)) {
121
+ return res.status(400).json({ error: `${modelId}: metadata must be an object` });
122
+ }
66
123
  if (meta.limit !== undefined) {
67
124
  if (typeof meta.limit !== 'object') {
68
125
  return res.status(400).json({ error: `${modelId}: limit must be an object` });
@@ -87,19 +144,46 @@ function handleSaveModelSettings(req, res) {
87
144
  }
88
145
  }
89
146
 
147
+ if (definitions !== undefined && !Array.isArray(definitions) && !isPlainObject(definitions)) {
148
+ return res.status(400).json({ error: 'definitions must be an object or array' });
149
+ }
150
+ if (Array.isArray(definitions)) {
151
+ const validation = validateModelDefinitions(definitions);
152
+ if (!validation.valid) {
153
+ return res.status(400).json({ error: validation.error });
154
+ }
155
+ }
156
+ const normalizedDefinitions = definitions === undefined ? undefined : normalizeDefinitions(definitions);
157
+ if (normalizedDefinitions) {
158
+ const definitionList = Object.entries(normalizedDefinitions).map(([id, definition]) => ({
159
+ ...(isPlainObject(definition) ? definition : {}),
160
+ id: isPlainObject(definition) && definition.id ? definition.id : id
161
+ }));
162
+ const validation = validateModelDefinitions(definitionList);
163
+ if (!validation.valid) {
164
+ return res.status(400).json({ error: validation.error });
165
+ }
166
+ }
167
+
90
168
  const config = loadConfig();
91
169
  const newConfig = {
92
170
  ...config,
93
171
  modelMetadataOverrides: overrides && typeof overrides === 'object'
94
172
  ? overrides
95
- : (config.modelMetadataOverrides || {})
173
+ : (config.modelMetadataOverrides || {}),
174
+ modelDefinitions: normalizedDefinitions === undefined
175
+ ? (config.modelDefinitions || {})
176
+ : normalizedDefinitions,
177
+ modelMetadataSchemaVersion: MODEL_SCHEMA_VERSION
96
178
  };
97
179
  saveConfig(newConfig);
98
180
  const persistedDefaultSpeedTestModels = saveDefaultSpeedTestModels(defaultSpeedTestModels);
99
181
 
100
182
  res.json({
101
183
  success: true,
102
- overrides: newConfig.modelMetadataOverrides,
184
+ schemaVersion: MODEL_SCHEMA_VERSION,
185
+ overrides: redactSensitiveFields(newConfig.modelMetadataOverrides),
186
+ definitions: redactSensitiveFields(newConfig.modelDefinitions),
103
187
  defaultSpeedTestModels: persistedDefaultSpeedTestModels
104
188
  });
105
189
  } catch (error) {