coding-tool-x 3.8.9 → 3.8.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.
- package/README.md +56 -388
- package/dist/web/assets/AgentsDrawer-Bam7kBjG.js +1 -0
- package/dist/web/assets/Analytics-D5dUWXEa.js +1 -0
- package/dist/web/assets/AssetPathField-CLqalYv-.js +1 -0
- package/dist/web/assets/{ChatHistoryDrawer-BTmQ1aQr.js → ChatHistoryDrawer-C1At65O3.js} +1 -1
- package/dist/web/assets/{CollapseItem-CwqJk3bE.js → CollapseItem-CVWojShK.js} +1 -1
- package/dist/web/assets/CommandsDrawer-BEoypGBf.js +1 -0
- package/dist/web/assets/ConfigExportDrawer-DBvFDDWK.js +1 -0
- package/dist/web/assets/{ConfigTemplates-gyERg9iC.js → ConfigTemplates-BpaWG-iC.js} +1 -1
- package/dist/web/assets/{ConfigTemplatesDrawer-B17RqFi4.js → ConfigTemplatesDrawer-C2lvyKMf.js} +1 -1
- package/dist/web/assets/ConfigTemplatesPanel-CorOY7kY.js +1 -0
- package/dist/web/assets/{Divider-DsdbkHrG.js → Divider-BLLhMTSt.js} +1 -1
- package/dist/web/assets/FavoritesDrawer-uW88N6te.js +1 -0
- package/dist/web/assets/GatewayConvertDrawer-i5Wt9Dd9.js +1 -0
- package/dist/web/assets/Home-BIxWvm29.js +1 -0
- package/dist/web/assets/{MarkdownEditor-9TTwTS-U.js → MarkdownEditor-Ul0bSGj-.js} +1 -1
- package/dist/web/assets/McpDrawer-Cc2xq77I.js +1 -0
- package/dist/web/assets/McpFormDrawer-DLHX_nrI.js +1 -0
- package/dist/web/assets/{PluginManager-BuRcWuBj.js → PluginManager-BGQ0xF2F.js} +1 -1
- package/dist/web/assets/{PluginsDrawer-_zZENaQn.js → PluginsDrawer-STwHEqiF.js} +1 -1
- package/dist/web/assets/PluginsPanel-1zuNP7da.js +1 -0
- package/dist/web/assets/ProjectList-_pe2nk9c.js +1 -0
- package/dist/web/assets/PromptsDrawer-CJN1xZk7.js +1 -0
- package/dist/web/assets/RecentSessionsDrawer-DX1HI8Nm.js +1 -0
- package/dist/web/assets/SessionCard-Op6PHvi0.js +1 -0
- package/dist/web/assets/SessionList-Da_TAEYr.js +1 -0
- package/dist/web/assets/SettingsDrawer-DKLd4Sjy.js +1 -0
- package/dist/web/assets/{SkillManager-S698Hns8.js → SkillManager-PcAa5EbF.js} +1 -1
- package/dist/web/assets/{SkillsDrawer-Ms4lPCow.js → SkillsDrawer-DPdU4p6C.js} +1 -1
- package/dist/web/assets/SkillsPanel-B9qzRqjd.js +1 -0
- package/dist/web/assets/Upload-e1M9Yp5z.js +1 -0
- package/dist/web/assets/{WorkspaceDrawer-L7HJRc6u.js → WorkspaceDrawer-oS6mB1VZ.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-DsoNg2z8.js → WorkspaceManager-6VvRWe3r.js} +1 -1
- package/dist/web/assets/WorkspacePanel-CYq0Z8iY.js +1 -0
- package/dist/web/assets/client-BgcNg4Bt.js +1 -0
- package/dist/web/assets/{headers-B5JWGn3v.js → headers-DbowkmZJ.js} +1 -1
- package/dist/web/assets/{icons-DmFe4b5j.js → icons-B-AzZoeE.js} +1 -1
- package/dist/web/assets/index-BlUXw84U.js +2 -0
- package/dist/web/assets/index-Bn8BUnwP.css +1 -0
- package/dist/web/assets/{light-0eavxNvt.js → light-C2X7hegj.js} +1 -1
- package/dist/web/assets/platforms-DrzAtnjY.js +1 -0
- package/dist/web/assets/platforms-mqqe_82d.js +1 -0
- package/dist/web/assets/project-config-BVt5s-pH.js +1 -0
- package/dist/web/assets/sessions-53-Fr68H.js +1 -0
- package/dist/web/assets/{sessions-D4yQtfKb.js → sessions-CaWUaysM.js} +1 -1
- package/dist/web/assets/use-message-DBsCm7FJ.js +1 -0
- package/dist/web/assets/{useResponsiveDrawer-V_onl9NV.js → useResponsiveDrawer-ChHZrTfl.js} +1 -1
- package/dist/web/assets/{workspaces-mjyPVGS8.js → workspaces-DQuZd4qw.js} +1 -1
- package/dist/web/index.html +8 -8
- package/package.json +4 -2
- package/src/commands/export-config.js +17 -211
- package/src/config/model-metadata.json +180 -16
- package/src/platforms/channel-auth-service.js +1 -1
- package/src/platforms/commands-service.js +28 -0
- package/src/platforms/driver-registry.js +1 -0
- package/src/platforms/drivers/claude/channels-implementation.js +79 -90
- package/src/platforms/drivers/claude/native-config-implementation.js +3 -4
- package/src/platforms/drivers/codex/channels-implementation.js +34 -49
- package/src/platforms/drivers/codex/native-config-implementation.js +3 -51
- package/src/platforms/drivers/dsh/api-operations.js +1 -1
- package/src/platforms/drivers/dsh/channels-implementation.js +28 -8
- package/src/platforms/drivers/dsh/channels.js +0 -18
- package/src/platforms/drivers/dsh/common.js +7 -0
- package/src/platforms/drivers/dsh/native-config.js +7 -2
- package/src/platforms/drivers/gemini/channels-implementation.js +47 -55
- package/src/platforms/drivers/gemini/native-config-implementation.js +8 -20
- package/src/platforms/drivers/generic-native-config.js +20 -0
- package/src/platforms/drivers/native-config-snapshot.js +30 -4
- package/src/platforms/drivers/omp/channels-implementation.js +51 -17
- package/src/platforms/drivers/omp/native-config-implementation.js +10 -17
- package/src/platforms/drivers/opencode/channels-implementation.js +6 -0
- package/src/platforms/drivers/opencode/native-config-implementation.js +3 -67
- package/src/platforms/manifest-schema.js +35 -2
- package/src/platforms/manifests/claude.json +0 -8
- package/src/platforms/manifests/codex.json +0 -8
- package/src/platforms/manifests/dsh.json +6 -9
- package/src/platforms/manifests/gemini.json +0 -8
- package/src/platforms/manifests/omp.json +0 -8
- package/src/platforms/manifests/opencode.json +0 -8
- package/src/platforms/native-oauth-adapters.js +33 -33
- package/src/server/index.js +2 -0
- package/src/server/services/bounded-content.js +60 -0
- package/src/server/services/channel-balance.js +10 -4
- package/src/server/services/config-export-service.js +2 -2
- package/src/server/services/config-templates-service.js +5 -1
- package/src/server/services/plugins-service.js +162 -85
- package/src/server/services/session-history-maintenance-worker.js +71 -0
- package/src/server/services/session-history-maintenance.js +166 -0
- package/src/server/services/skill-service.js +23 -3
- package/src/server/services/speed-test.js +0 -72
- package/src/shared/channel-driver.js +16 -0
- package/src/shared/driver-factories/api.js +1 -0
- package/src/utils/native-config-patcher.js +474 -0
- package/dist/web/assets/AgentsDrawer-Cj5CJ4d9.js +0 -1
- package/dist/web/assets/Analytics-1doS-8uC.js +0 -1
- package/dist/web/assets/AssetPathField-C9TOPl7o.js +0 -1
- package/dist/web/assets/CommandsDrawer-CaZWDI_G.js +0 -1
- package/dist/web/assets/ConfigExportDrawer-C3vSZEsS.js +0 -1
- package/dist/web/assets/ConfigTemplatesPanel-DKIC_FqG.js +0 -1
- package/dist/web/assets/FavoritesDrawer-DaziQmBp.js +0 -1
- package/dist/web/assets/GatewayConvertDrawer-OMgWUUSc.js +0 -1
- package/dist/web/assets/Home-DOSUTfrE.js +0 -1
- package/dist/web/assets/McpDrawer-I8rl8lGH.js +0 -1
- package/dist/web/assets/McpFormDrawer-DEP3C7ut.js +0 -1
- package/dist/web/assets/PluginsPanel-Bs5qZ-TK.js +0 -1
- package/dist/web/assets/ProjectList-BSxuL2td.js +0 -1
- package/dist/web/assets/PromptsDrawer-CoZ-ZtsY.js +0 -1
- package/dist/web/assets/RecentSessionsDrawer-B_17aJBG.js +0 -1
- package/dist/web/assets/SessionCard-BL_x0d3t.js +0 -1
- package/dist/web/assets/SessionList-DhRYZ6vl.js +0 -1
- package/dist/web/assets/SettingsDrawer-fYb0DU7X.js +0 -1
- package/dist/web/assets/SkillsPanel-rqnqAOGL.js +0 -1
- package/dist/web/assets/SpeedTestDrawer-Bbn38SfV.js +0 -1
- package/dist/web/assets/SpeedTestDrawer-Dc0WToJ_.css +0 -1
- package/dist/web/assets/Upload-CdyVHxCI.js +0 -1
- package/dist/web/assets/WorkspacePanel-Be8PVZah.js +0 -1
- package/dist/web/assets/client-UDwXBP9i.js +0 -1
- package/dist/web/assets/index-3Lp6z8TQ.css +0 -1
- package/dist/web/assets/index-BKlAWTH-.js +0 -2
- package/dist/web/assets/platforms-BmzzwnSw.js +0 -1
- package/dist/web/assets/platforms-DWXgmy2w.js +0 -1
- package/dist/web/assets/project-config-B2baPzYL.js +0 -1
- package/dist/web/assets/sessions-CC0j-KH_.js +0 -1
- package/dist/web/assets/use-message-DqCwefG6.js +0 -1
|
@@ -83,17 +83,43 @@ function writeEntry(spec, entry, overwrite) {
|
|
|
83
83
|
return 'success';
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
function
|
|
86
|
+
function resolveSnapshotPath(spec, { pathContext, paths, manifest } = {}) {
|
|
87
|
+
const rawPath = typeof spec?.path === 'string' ? spec.path.trim() : '';
|
|
88
|
+
if (!rawPath) return null;
|
|
89
|
+
|
|
90
|
+
const nativePaths = pathContext?.native || paths || {};
|
|
91
|
+
if (typeof nativePaths[rawPath] === 'string' && nativePaths[rawPath].trim()) {
|
|
92
|
+
return nativePaths[rawPath];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const home = pathContext?.home || nativePaths.home || manifest?.paths?.home || process.cwd();
|
|
96
|
+
const expanded = rawPath.replace(/\{home\}/g, home);
|
|
97
|
+
return path.isAbsolute(expanded) ? path.normalize(expanded) : path.resolve(home, expanded);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function resolveNativeSnapshotSpecs(declarations, context = {}) {
|
|
101
|
+
return Object.fromEntries(Object.entries(declarations || {})
|
|
102
|
+
.map(([key, spec]) => [key, {
|
|
103
|
+
...spec,
|
|
104
|
+
path: resolveSnapshotPath(spec, context)
|
|
105
|
+
}])
|
|
106
|
+
.filter(([, spec]) => spec.path));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function createNativeSnapshotMethods(specs, { platform, runtime, manifest, pathContext, paths } = {}) {
|
|
110
|
+
const resolvedSpecs = Object.keys(specs || {}).length > 0
|
|
111
|
+
? specs
|
|
112
|
+
: resolveNativeSnapshotSpecs(manifest?.nativeConfigSnapshot, { manifest, pathContext, paths });
|
|
87
113
|
const methods = {
|
|
88
114
|
exportSnapshot() {
|
|
89
|
-
return Object.fromEntries(Object.entries(
|
|
115
|
+
return Object.fromEntries(Object.entries(resolvedSpecs || {})
|
|
90
116
|
.map(([key, spec]) => [key, readEntry(spec)])
|
|
91
117
|
.filter(([, entry]) => entry));
|
|
92
118
|
},
|
|
93
119
|
importSnapshot(snapshot = {}, { overwrite = true } = {}) {
|
|
94
120
|
const result = { success: 0, imported: 0, skipped: 0, failed: 0 };
|
|
95
121
|
for (const [key, entry] of Object.entries(snapshot || {})) {
|
|
96
|
-
const status = writeEntry(
|
|
122
|
+
const status = writeEntry(resolvedSpecs[key], entry, overwrite);
|
|
97
123
|
if (status === 'success') {
|
|
98
124
|
result.success += 1;
|
|
99
125
|
result.imported += 1;
|
|
@@ -122,4 +148,4 @@ function createNativeSnapshotMethods(specs, { platform, runtime } = {}) {
|
|
|
122
148
|
return methods;
|
|
123
149
|
}
|
|
124
150
|
|
|
125
|
-
module.exports = { createNativeSnapshotMethods };
|
|
151
|
+
module.exports = { createNativeSnapshotMethods, resolveNativeSnapshotSpecs };
|
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
writeManagedOmpProviders,
|
|
11
11
|
removeManagedOmpProviders,
|
|
12
12
|
getLastManagedOmpSyncResult,
|
|
13
|
+
getManagedProviderId,
|
|
13
14
|
readModelsConfig,
|
|
14
15
|
readOmpSettingsConfig,
|
|
15
16
|
normalizeProviderId,
|
|
@@ -59,6 +60,15 @@ function selectLatestEnabledChannel(channels = []) {
|
|
|
59
60
|
}, enabledChannels[0]);
|
|
60
61
|
}
|
|
61
62
|
|
|
63
|
+
function getChannelManagedProviderId(channel = {}) {
|
|
64
|
+
if (typeof getManagedProviderId === 'function') {
|
|
65
|
+
return getManagedProviderId(channel);
|
|
66
|
+
}
|
|
67
|
+
const rawId = channel.managedProviderId || channel.providerKey || channel.provider || channel.name || channel.id;
|
|
68
|
+
const normalizedId = normalizeProviderId(rawId);
|
|
69
|
+
return normalizedId.startsWith('ctx-') ? normalizedId : `ctx-${normalizedId}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
62
72
|
function loadManagedOmpModeState() {
|
|
63
73
|
ensureStorageDirMigrated();
|
|
64
74
|
try {
|
|
@@ -269,16 +279,24 @@ class OmpChannelService extends BaseChannelService {
|
|
|
269
279
|
}
|
|
270
280
|
|
|
271
281
|
_applyToNativeSettings(channel) {
|
|
282
|
+
const knownProviderIds = this.getChannels().channels.map(getChannelManagedProviderId);
|
|
272
283
|
if (isManagedOmpModeEnabled()) {
|
|
273
284
|
const state = loadManagedOmpModeState();
|
|
274
285
|
enableManagedOmpMode(channel.id, state?.gateway || null);
|
|
275
286
|
writeManagedOmpProviders([channel], state?.gateway ? {
|
|
276
287
|
gateway: state.gateway,
|
|
277
|
-
activeChannelId: channel.id
|
|
278
|
-
|
|
288
|
+
activeChannelId: channel.id,
|
|
289
|
+
knownProviderIds
|
|
290
|
+
} : { knownProviderIds });
|
|
279
291
|
return;
|
|
280
292
|
}
|
|
281
|
-
writeManagedOmpProviders([channel], {});
|
|
293
|
+
writeManagedOmpProviders([channel], { knownProviderIds });
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
disableAllChannels() {
|
|
297
|
+
const result = super.disableAllChannels();
|
|
298
|
+
this.syncOmpProvidersForCurrentMode(this.getChannels().channels);
|
|
299
|
+
return result;
|
|
282
300
|
}
|
|
283
301
|
|
|
284
302
|
_onAfterCreate(_channel, allChannels) {
|
|
@@ -296,13 +314,18 @@ class OmpChannelService extends BaseChannelService {
|
|
|
296
314
|
syncOmpProvidersForCurrentMode(channels = this.getChannels().channels) {
|
|
297
315
|
if (isManagedOmpModeEnabled()) {
|
|
298
316
|
const state = loadManagedOmpModeState();
|
|
299
|
-
return this.syncManagedOmpProviders(channels,
|
|
300
|
-
gateway
|
|
301
|
-
|
|
302
|
-
|
|
317
|
+
return this.syncManagedOmpProviders(channels, {
|
|
318
|
+
...(state?.gateway ? {
|
|
319
|
+
gateway: state.gateway,
|
|
320
|
+
activeChannelId: state.activeChannelId
|
|
321
|
+
} : {}),
|
|
322
|
+
knownProviderIds: channels.map(getChannelManagedProviderId)
|
|
323
|
+
});
|
|
303
324
|
}
|
|
304
325
|
const activeChannel = selectLatestEnabledChannel(channels);
|
|
305
|
-
return this.syncManagedOmpProviders(activeChannel ? [activeChannel] : [], {
|
|
326
|
+
return this.syncManagedOmpProviders(activeChannel ? [activeChannel] : [], {
|
|
327
|
+
knownProviderIds: channels.map(getChannelManagedProviderId)
|
|
328
|
+
});
|
|
306
329
|
}
|
|
307
330
|
|
|
308
331
|
syncManagedOmpProviders(channels = this.getChannels().channels, options = {}) {
|
|
@@ -335,7 +358,9 @@ class OmpChannelService extends BaseChannelService {
|
|
|
335
358
|
|
|
336
359
|
try {
|
|
337
360
|
this.saveChannels(data);
|
|
338
|
-
const sync = this.syncManagedOmpProviders([channel], {
|
|
361
|
+
const sync = this.syncManagedOmpProviders([channel], {
|
|
362
|
+
knownProviderIds: data.channels.map(getChannelManagedProviderId)
|
|
363
|
+
});
|
|
339
364
|
if (!wasEnabled) {
|
|
340
365
|
clearOmpChannelBalanceCache(channel);
|
|
341
366
|
}
|
|
@@ -739,24 +764,33 @@ function getCatalogMetadata({
|
|
|
739
764
|
))
|
|
740
765
|
: [])
|
|
741
766
|
]
|
|
742
|
-
.map(value => String(value || '').trim()
|
|
743
|
-
.filter(Boolean)
|
|
767
|
+
.map(value => String(value || '').trim())
|
|
768
|
+
.filter(Boolean)
|
|
769
|
+
.filter((value, index, values) => values.findIndex(item => item.toLowerCase() === value.toLowerCase()) === index);
|
|
744
770
|
const normalizedProviderKey = String(providerKey || '').trim().toLowerCase();
|
|
745
771
|
|
|
746
772
|
const allEntries = Object.entries(MODEL_METADATA)
|
|
747
773
|
.filter(([, metadata]) => Array.isArray(metadata.toolTypes) && metadata.toolTypes.includes('omp'))
|
|
748
774
|
.map(([id, metadata]) => ({ ...structuredClone(metadata), id }));
|
|
749
775
|
|
|
750
|
-
const
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
||
|
|
754
|
-
|
|
776
|
+
const findRequestedEntry = requestedId => allEntries.find(entry => {
|
|
777
|
+
const normalizedRequestedId = requestedId.toLowerCase();
|
|
778
|
+
return normalizedRequestedId === entry.id.toLowerCase()
|
|
779
|
+
|| normalizedRequestedId === String(entry.sourceId || '').toLowerCase()
|
|
780
|
+
|| normalizedRequestedId === entry.id.split('/').pop().toLowerCase();
|
|
781
|
+
});
|
|
755
782
|
const matchesProvider = entry => (
|
|
756
783
|
String(entry.provider || '').toLowerCase() === normalizedProviderKey
|
|
757
784
|
|| String(entry.sourceId || '').toLowerCase().startsWith(`${normalizedProviderKey}/`)
|
|
758
785
|
);
|
|
759
|
-
const requestedEntries = requestedIds.length > 0
|
|
786
|
+
const requestedEntries = requestedIds.length > 0
|
|
787
|
+
? requestedIds
|
|
788
|
+
.map(requestedId => {
|
|
789
|
+
const entry = findRequestedEntry(requestedId);
|
|
790
|
+
return entry ? { ...entry, id: requestedId } : null;
|
|
791
|
+
})
|
|
792
|
+
.filter(Boolean)
|
|
793
|
+
: [];
|
|
760
794
|
const providerEntries = normalizedProviderKey ? allEntries.filter(matchesProvider) : [];
|
|
761
795
|
const entries = requestedIds.length > 0 ? requestedEntries : providerEntries;
|
|
762
796
|
|
|
@@ -2,6 +2,7 @@ const fs = require('fs');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { spawnSync } = require('child_process');
|
|
4
4
|
const yaml = require('js-yaml');
|
|
5
|
+
const { writeYamlFile } = require('../../../utils/native-config-patcher');
|
|
5
6
|
const ompConfig = require('./config');
|
|
6
7
|
const { prepareManagedOmpChannels } = require('./gateway-routing');
|
|
7
8
|
const { MODEL_METADATA, MODEL_ALIASES } = require('../../../config/model-metadata');
|
|
@@ -409,13 +410,7 @@ function readModelsConfig(filePath = getOmpPaths().modelsYml) {
|
|
|
409
410
|
}
|
|
410
411
|
|
|
411
412
|
function writeModelsConfig(config, filePath = getOmpPaths().modelsYml) {
|
|
412
|
-
|
|
413
|
-
const doc = yaml.dump(config || { providers: {} }, {
|
|
414
|
-
lineWidth: 120,
|
|
415
|
-
noRefs: true,
|
|
416
|
-
sortKeys: false
|
|
417
|
-
});
|
|
418
|
-
writeFileAtomic(filePath, doc);
|
|
413
|
+
writeYamlFile(filePath, config || { providers: {} }, { atomic: true });
|
|
419
414
|
}
|
|
420
415
|
|
|
421
416
|
function getOmpSettingsPath(paths = getOmpPaths()) {
|
|
@@ -447,13 +442,7 @@ function readOmpSettingsConfig(filePath = getOmpSettingsPath()) {
|
|
|
447
442
|
}
|
|
448
443
|
|
|
449
444
|
function writeOmpSettingsConfig(config, filePath = getOmpSettingsPath()) {
|
|
450
|
-
|
|
451
|
-
const doc = yaml.dump(config || {}, {
|
|
452
|
-
lineWidth: 120,
|
|
453
|
-
noRefs: true,
|
|
454
|
-
sortKeys: false
|
|
455
|
-
});
|
|
456
|
-
writeFileAtomic(filePath, doc);
|
|
445
|
+
writeYamlFile(filePath, config || {}, { atomic: true });
|
|
457
446
|
}
|
|
458
447
|
|
|
459
448
|
function getModelsBackupPrefix(filePath) {
|
|
@@ -669,9 +658,10 @@ function removeLegacyManagedExtension(paths = getOmpPaths()) {
|
|
|
669
658
|
}
|
|
670
659
|
}
|
|
671
660
|
|
|
672
|
-
function pruneManagedProviders(providers = {}) {
|
|
661
|
+
function pruneManagedProviders(providers = {}, knownProviderIds = null) {
|
|
662
|
+
const keep = knownProviderIds instanceof Set ? knownProviderIds : null;
|
|
673
663
|
Object.keys(providers).forEach((providerId) => {
|
|
674
|
-
if (isManagedProviderId(providerId)) {
|
|
664
|
+
if (isManagedProviderId(providerId) && (!keep || !keep.has(providerId))) {
|
|
675
665
|
delete providers[providerId];
|
|
676
666
|
}
|
|
677
667
|
});
|
|
@@ -684,7 +674,10 @@ function buildManagedModelsConfig(channels = [], baseConfig = readModelsConfig()
|
|
|
684
674
|
...(baseConfig.providers || {})
|
|
685
675
|
}
|
|
686
676
|
};
|
|
687
|
-
|
|
677
|
+
const knownProviderIds = Array.isArray(options.knownProviderIds)
|
|
678
|
+
? new Set(options.knownProviderIds)
|
|
679
|
+
: new Set((channels || []).map(getManagedProviderId));
|
|
680
|
+
pruneManagedProviders(next.providers, knownProviderIds);
|
|
688
681
|
|
|
689
682
|
channels
|
|
690
683
|
.filter(channel => channel && channel.enabled !== false && channel.baseUrl)
|
|
@@ -282,6 +282,10 @@ function updateChannel(channelId, updates) {
|
|
|
282
282
|
if (!isProxyRunning) {
|
|
283
283
|
if (oldChannel.enabled === false && merged.enabled !== false) {
|
|
284
284
|
syncManagedChannelConfig(data.channels, merged);
|
|
285
|
+
} else if (oldChannel.enabled !== false && merged.enabled === false) {
|
|
286
|
+
const activeChannel = resolveCurrentManagedChannel(data.channels);
|
|
287
|
+
if (activeChannel) syncManagedChannelConfig(data.channels, activeChannel);
|
|
288
|
+
else clearManagedChannelConfig();
|
|
285
289
|
} else {
|
|
286
290
|
const activeChannel = resolveCurrentManagedChannel(data.channels);
|
|
287
291
|
if (merged.enabled !== false && activeChannel?.id === merged.id) {
|
|
@@ -487,9 +491,11 @@ async function getEffectiveApiKey(channel) {
|
|
|
487
491
|
}
|
|
488
492
|
|
|
489
493
|
function disableAllChannels() {
|
|
494
|
+
const proxyRunning = getOpenCodeProxyRunning();
|
|
490
495
|
const data = loadChannels();
|
|
491
496
|
data.channels.forEach(ch => { ch.enabled = false; });
|
|
492
497
|
saveChannels(data);
|
|
498
|
+
if (!proxyRunning) clearManagedChannelConfig();
|
|
493
499
|
}
|
|
494
500
|
|
|
495
501
|
function getCurrentOpenCodeProviderId(config = {}) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const { readJsoncFile, writeJsoncFile } = require('../../../utils/native-config-patcher');
|
|
3
4
|
const { NATIVE_PATHS } = require('../../../config/paths');
|
|
4
5
|
const { resolveModelMetadata } = require('../../../config/model-metadata');
|
|
5
6
|
const { resolveModelPricing } = require('../../../server/utils/pricing');
|
|
@@ -61,79 +62,18 @@ function selectConfigPath() {
|
|
|
61
62
|
return CONFIG_PATHS.opencode;
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
function stripJsonComments(input) {
|
|
65
|
-
let result = '';
|
|
66
|
-
let inString = false;
|
|
67
|
-
let stringChar = '';
|
|
68
|
-
let i = 0;
|
|
69
|
-
|
|
70
|
-
while (i < input.length) {
|
|
71
|
-
const ch = input[i];
|
|
72
|
-
const next = input[i + 1];
|
|
73
|
-
|
|
74
|
-
if (inString) {
|
|
75
|
-
result += ch;
|
|
76
|
-
if (ch === '\\') {
|
|
77
|
-
if (next) {
|
|
78
|
-
result += next;
|
|
79
|
-
i += 2;
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
} else if (ch === stringChar) {
|
|
83
|
-
inString = false;
|
|
84
|
-
}
|
|
85
|
-
i += 1;
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (ch === '"' || ch === '\'') {
|
|
90
|
-
inString = true;
|
|
91
|
-
stringChar = ch;
|
|
92
|
-
result += ch;
|
|
93
|
-
i += 1;
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (ch === '/' && next === '/') {
|
|
98
|
-
i += 2;
|
|
99
|
-
while (i < input.length && input[i] !== '\n') i += 1;
|
|
100
|
-
continue;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (ch === '/' && next === '*') {
|
|
104
|
-
i += 2;
|
|
105
|
-
while (i < input.length - 1 && !(input[i] === '*' && input[i + 1] === '/')) i += 1;
|
|
106
|
-
i += 2;
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
result += ch;
|
|
111
|
-
i += 1;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return result;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
65
|
function readConfig(filePath) {
|
|
118
66
|
if (!fs.existsSync(filePath)) return {};
|
|
119
67
|
|
|
120
|
-
const raw = fs.readFileSync(filePath, 'utf8');
|
|
121
|
-
if (!raw.trim()) return {};
|
|
122
|
-
|
|
123
68
|
try {
|
|
124
|
-
|
|
125
|
-
return JSON.parse(stripJsonComments(raw));
|
|
126
|
-
}
|
|
127
|
-
return JSON.parse(raw);
|
|
69
|
+
return readJsoncFile(filePath);
|
|
128
70
|
} catch (err) {
|
|
129
71
|
throw new Error(`Failed to parse ${path.basename(filePath)}: ${err.message}`);
|
|
130
72
|
}
|
|
131
73
|
}
|
|
132
74
|
|
|
133
75
|
function writeConfig(filePath, config) {
|
|
134
|
-
|
|
135
|
-
const content = JSON.stringify(config, null, 2);
|
|
136
|
-
fs.writeFileSync(filePath, content, 'utf8');
|
|
76
|
+
writeJsoncFile(filePath, config);
|
|
137
77
|
}
|
|
138
78
|
|
|
139
79
|
function normalizeOpenCodeModel(modelId, providerId) {
|
|
@@ -520,7 +460,6 @@ function setProxyConfig(proxyPort, options = {}) {
|
|
|
520
460
|
|
|
521
461
|
function setChannelConfig(channel = {}) {
|
|
522
462
|
const filePath = selectConfigPath();
|
|
523
|
-
backupConfig(filePath);
|
|
524
463
|
|
|
525
464
|
const current = readConfig(filePath);
|
|
526
465
|
const next = (current && typeof current === 'object') ? current : {};
|
|
@@ -529,9 +468,6 @@ function setChannelConfig(channel = {}) {
|
|
|
529
468
|
next.provider = {};
|
|
530
469
|
}
|
|
531
470
|
|
|
532
|
-
const removedProviderKeys = clearManagedProviders(next);
|
|
533
|
-
clearManagedModelRef(next, removedProviderKeys);
|
|
534
|
-
|
|
535
471
|
const baseProviderKey = sanitizeProviderKey(channel.providerKey || channel.name || 'ctx-channel');
|
|
536
472
|
let providerKey = baseProviderKey;
|
|
537
473
|
let suffix = 2;
|
|
@@ -11,6 +11,7 @@ const DRIVER_IDS = new Set([
|
|
|
11
11
|
'generic-openai-compatible',
|
|
12
12
|
'generic-mcp',
|
|
13
13
|
'generic-prompt',
|
|
14
|
+
'generic-native-config',
|
|
14
15
|
'legacy:claude',
|
|
15
16
|
'legacy:codex',
|
|
16
17
|
'legacy:gemini',
|
|
@@ -80,7 +81,7 @@ const CAPABILITY_OPERATIONS = Object.freeze({
|
|
|
80
81
|
'updateSkill',
|
|
81
82
|
'deleteSkill'
|
|
82
83
|
]),
|
|
83
|
-
channels: new Set(['applyToSettings', 'bestForRestore', 'catalogMetadata', 'create', 'current', 'enabled', 'getAuth', 'getAuthQuota', 'list', 'models', 'order', 'poolStatus', 'probeModels', 'remove', 'resetHealth', 'speedTest', '
|
|
84
|
+
channels: new Set(['applyToSettings', 'bestForRestore', 'catalogMetadata', 'create', 'current', 'enabled', 'getAuth', 'getAuthQuota', 'list', 'models', 'order', 'poolStatus', 'probeModels', 'remove', 'resetHealth', 'speedTest', 'sync', 'syncLocalAuth', 'update']),
|
|
84
85
|
health: new Set(['healthCheck']),
|
|
85
86
|
hooks: new Set(['getHooks', 'saveHooks', 'testHooks']),
|
|
86
87
|
projects: new Set(['createProject', 'deleteProject', 'listProjects', 'saveProjectOrder']),
|
|
@@ -198,6 +199,19 @@ const schema = {
|
|
|
198
199
|
sessionMapping: { type: 'object', additionalProperties: { type: 'string' } },
|
|
199
200
|
sessionGlob: { type: 'string', minLength: 1 },
|
|
200
201
|
resourceMappings: { type: 'object', additionalProperties: { type: 'string' } },
|
|
202
|
+
nativeConfigSnapshot: {
|
|
203
|
+
type: 'object',
|
|
204
|
+
additionalProperties: {
|
|
205
|
+
type: 'object',
|
|
206
|
+
additionalProperties: false,
|
|
207
|
+
required: ['path', 'format'],
|
|
208
|
+
properties: {
|
|
209
|
+
path: { type: 'string', minLength: 1 },
|
|
210
|
+
format: { enum: ['text', 'json', 'yaml', 'directory'] },
|
|
211
|
+
mode: { type: 'integer', minimum: 0, maximum: 511 }
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
},
|
|
201
215
|
projectResources: {
|
|
202
216
|
type: 'object',
|
|
203
217
|
additionalProperties: false,
|
|
@@ -333,6 +347,17 @@ function validateProjectResources(manifest, errors) {
|
|
|
333
347
|
}
|
|
334
348
|
}
|
|
335
349
|
|
|
350
|
+
function validateNativeConfigSnapshot(manifest, errors) {
|
|
351
|
+
for (const [key, spec] of Object.entries(manifest?.nativeConfigSnapshot || {})) {
|
|
352
|
+
if (!isSafeRelativePath(spec?.path)) {
|
|
353
|
+
errors.push({
|
|
354
|
+
instancePath: `/nativeConfigSnapshot/${key}/path`,
|
|
355
|
+
message: 'must be a safe relative path or a declared native path name'
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
336
361
|
function validateApiRoutes(manifest, errors) {
|
|
337
362
|
const api = manifest?.api;
|
|
338
363
|
if (!api?.routes) return;
|
|
@@ -395,7 +420,8 @@ function validateManifest(manifest) {
|
|
|
395
420
|
'generic-filesystem': new Set(['resourceSync']),
|
|
396
421
|
'generic-openai-compatible': new Set(['channels']),
|
|
397
422
|
'generic-mcp': new Set(['mcp']),
|
|
398
|
-
'generic-prompt': new Set(['prompts'])
|
|
423
|
+
'generic-prompt': new Set(['prompts']),
|
|
424
|
+
'generic-native-config': new Set(['nativeConfig'])
|
|
399
425
|
};
|
|
400
426
|
|
|
401
427
|
for (const [capability, driver] of Object.entries(manifest && manifest.capabilities || {})) {
|
|
@@ -424,6 +450,13 @@ function validateManifest(manifest) {
|
|
|
424
450
|
}
|
|
425
451
|
}
|
|
426
452
|
|
|
453
|
+
if (manifest?.capabilities?.nativeConfig === 'generic-native-config' && !manifest.nativeConfigSnapshot) {
|
|
454
|
+
errors.push({
|
|
455
|
+
instancePath: '/nativeConfigSnapshot',
|
|
456
|
+
message: 'generic-native-config requires nativeConfigSnapshot declarations'
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
validateNativeConfigSnapshot(manifest, errors);
|
|
427
460
|
validateApiRoutes(manifest, errors);
|
|
428
461
|
return { valid: errors.length === 0, errors };
|
|
429
462
|
}
|
|
@@ -356,14 +356,6 @@
|
|
|
356
356
|
"request": "default",
|
|
357
357
|
"response": "default"
|
|
358
358
|
},
|
|
359
|
-
{
|
|
360
|
-
"method": "POST",
|
|
361
|
-
"path": "/channels/speed-test-all",
|
|
362
|
-
"capability": "channels",
|
|
363
|
-
"operation": "speedTestAll",
|
|
364
|
-
"request": "default",
|
|
365
|
-
"response": "default"
|
|
366
|
-
},
|
|
367
359
|
{
|
|
368
360
|
"method": "POST",
|
|
369
361
|
"path": "/channels/probe-models",
|
|
@@ -348,14 +348,6 @@
|
|
|
348
348
|
"request": "default",
|
|
349
349
|
"response": "default"
|
|
350
350
|
},
|
|
351
|
-
{
|
|
352
|
-
"method": "POST",
|
|
353
|
-
"path": "/channels/speed-test-all",
|
|
354
|
-
"capability": "channels",
|
|
355
|
-
"operation": "speedTestAll",
|
|
356
|
-
"request": "default",
|
|
357
|
-
"response": "default"
|
|
358
|
-
},
|
|
359
351
|
{
|
|
360
352
|
"method": "POST",
|
|
361
353
|
"path": "/channels/probe-models",
|
|
@@ -253,14 +253,6 @@
|
|
|
253
253
|
"request": "default",
|
|
254
254
|
"response": "default"
|
|
255
255
|
},
|
|
256
|
-
{
|
|
257
|
-
"method": "POST",
|
|
258
|
-
"path": "/channels/speed-test-all",
|
|
259
|
-
"capability": "channels",
|
|
260
|
-
"operation": "speedTestAll",
|
|
261
|
-
"request": "default",
|
|
262
|
-
"response": "default"
|
|
263
|
-
},
|
|
264
256
|
{
|
|
265
257
|
"method": "POST",
|
|
266
258
|
"path": "/channels/probe-models",
|
|
@@ -530,7 +522,7 @@
|
|
|
530
522
|
"capabilities": {
|
|
531
523
|
"projects": "dsh-projects",
|
|
532
524
|
"sessions": "dsh-sessions",
|
|
533
|
-
"nativeConfig": "
|
|
525
|
+
"nativeConfig": "generic-native-config",
|
|
534
526
|
"api": "dsh-api",
|
|
535
527
|
"channels": "dsh-channels",
|
|
536
528
|
"proxy": "dsh-proxy",
|
|
@@ -538,5 +530,10 @@
|
|
|
538
530
|
"nativeLogs": "dsh-native-logs",
|
|
539
531
|
"mcp": "dsh-api",
|
|
540
532
|
"prompts": "dsh-api"
|
|
533
|
+
},
|
|
534
|
+
"nativeConfigSnapshot": {
|
|
535
|
+
"settings": { "path": "settings", "format": "text", "mode": 384 },
|
|
536
|
+
"credentials": { "path": "credentials", "format": "text", "mode": 384 },
|
|
537
|
+
"patch": { "path": "patch", "format": "text", "mode": 384 }
|
|
541
538
|
}
|
|
542
539
|
}
|
|
@@ -347,14 +347,6 @@
|
|
|
347
347
|
"request": "default",
|
|
348
348
|
"response": "default"
|
|
349
349
|
},
|
|
350
|
-
{
|
|
351
|
-
"method": "POST",
|
|
352
|
-
"path": "/channels/speed-test-all",
|
|
353
|
-
"capability": "channels",
|
|
354
|
-
"operation": "speedTestAll",
|
|
355
|
-
"request": "default",
|
|
356
|
-
"response": "default"
|
|
357
|
-
},
|
|
358
350
|
{
|
|
359
351
|
"method": "POST",
|
|
360
352
|
"path": "/channels/probe-models",
|
|
@@ -365,14 +365,6 @@
|
|
|
365
365
|
"request": "default",
|
|
366
366
|
"response": "default"
|
|
367
367
|
},
|
|
368
|
-
{
|
|
369
|
-
"method": "POST",
|
|
370
|
-
"path": "/channels/speed-test-all",
|
|
371
|
-
"capability": "channels",
|
|
372
|
-
"operation": "speedTestAll",
|
|
373
|
-
"request": "default",
|
|
374
|
-
"response": "default"
|
|
375
|
-
},
|
|
376
368
|
{
|
|
377
369
|
"method": "POST",
|
|
378
370
|
"path": "/channels/probe-models",
|
|
@@ -349,14 +349,6 @@
|
|
|
349
349
|
"request": "default",
|
|
350
350
|
"response": "default"
|
|
351
351
|
},
|
|
352
|
-
{
|
|
353
|
-
"method": "POST",
|
|
354
|
-
"path": "/channels/speed-test-all",
|
|
355
|
-
"capability": "channels",
|
|
356
|
-
"operation": "speedTestAll",
|
|
357
|
-
"request": "default",
|
|
358
|
-
"response": "default"
|
|
359
|
-
},
|
|
360
352
|
{
|
|
361
353
|
"method": "POST",
|
|
362
354
|
"path": "/channels/probe-models",
|
|
@@ -4,7 +4,7 @@ const os = require('os');
|
|
|
4
4
|
const crypto = require('crypto');
|
|
5
5
|
const { execFileSync, spawnSync } = require('child_process');
|
|
6
6
|
const toml = require('toml');
|
|
7
|
-
const
|
|
7
|
+
const { writeTomlFile } = require('../utils/native-config-patcher');
|
|
8
8
|
const pathsModule = require('../config/paths');
|
|
9
9
|
const DEFAULT_NATIVE_PATHS = pathsModule.NATIVE_PATHS;
|
|
10
10
|
const DEFAULT_PATHS = pathsModule.PATHS;
|
|
@@ -185,29 +185,29 @@ function clearClaudeOAuth() {
|
|
|
185
185
|
ensureDir(path.dirname(NATIVE_PATHS.claude.settings));
|
|
186
186
|
claudeSettingsManager.writeSettings(settings);
|
|
187
187
|
}
|
|
188
|
-
function clearClaudeChannelConfig() {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
188
|
+
function clearClaudeChannelConfig(managedProxyUrls = []) {
|
|
189
|
+
if (!claudeSettingsManager.settingsExists()) return;
|
|
190
|
+
const settings = claudeSettingsManager.readSettings();
|
|
191
|
+
if (settings.env && typeof settings.env === 'object' && !Array.isArray(settings.env)) {
|
|
192
|
+
[
|
|
193
|
+
'ANTHROPIC_BASE_URL',
|
|
194
|
+
'ANTHROPIC_API_KEY',
|
|
195
|
+
'ANTHROPIC_AUTH_TOKEN',
|
|
196
|
+
'CLAUDE_CODE_OAUTH_TOKEN',
|
|
197
|
+
'ANTHROPIC_MODEL',
|
|
198
|
+
'ANTHROPIC_DEFAULT_HAIKU_MODEL',
|
|
199
|
+
'ANTHROPIC_DEFAULT_SONNET_MODEL',
|
|
200
|
+
'ANTHROPIC_DEFAULT_OPUS_MODEL'
|
|
201
|
+
].forEach((key) => {
|
|
202
|
+
delete settings.env[key];
|
|
203
|
+
});
|
|
204
|
+
const managedProxies = new Set((Array.isArray(managedProxyUrls) ? managedProxyUrls : [])
|
|
205
|
+
.map(value => String(value || '').trim()).filter(Boolean));
|
|
206
|
+
['HTTP_PROXY', 'HTTPS_PROXY'].forEach((key) => {
|
|
207
|
+
if (managedProxies.has(String(settings.env[key] || '').trim())) delete settings.env[key];
|
|
208
|
+
});
|
|
209
|
+
if (Object.keys(settings.env).length === 0) delete settings.env;
|
|
196
210
|
}
|
|
197
|
-
|
|
198
|
-
settings.env = settings.env || {};
|
|
199
|
-
[
|
|
200
|
-
'ANTHROPIC_BASE_URL',
|
|
201
|
-
'ANTHROPIC_API_KEY',
|
|
202
|
-
'ANTHROPIC_AUTH_TOKEN',
|
|
203
|
-
'CLAUDE_CODE_OAUTH_TOKEN',
|
|
204
|
-
'ANTHROPIC_MODEL',
|
|
205
|
-
'ANTHROPIC_DEFAULT_HAIKU_MODEL',
|
|
206
|
-
'ANTHROPIC_DEFAULT_SONNET_MODEL',
|
|
207
|
-
'ANTHROPIC_DEFAULT_OPUS_MODEL'
|
|
208
|
-
].forEach((key) => {
|
|
209
|
-
delete settings.env[key];
|
|
210
|
-
});
|
|
211
211
|
delete settings.apiKeyHelper;
|
|
212
212
|
ensureDir(path.dirname(NATIVE_PATHS.claude.settings));
|
|
213
213
|
claudeSettingsManager.writeSettings(settings);
|
|
@@ -444,16 +444,16 @@ function removeCodexChannelEnvVars() {
|
|
|
444
444
|
}
|
|
445
445
|
|
|
446
446
|
function clearCodexChannelConfig() {
|
|
447
|
-
removeCodexChannelEnvVars();
|
|
448
|
-
|
|
449
447
|
const configPath = NATIVE_PATHS.codex.config;
|
|
448
|
+
if (!fs.existsSync(configPath)) {
|
|
449
|
+
removeCodexChannelEnvVars();
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
450
452
|
let config = {};
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
config = {};
|
|
456
|
-
}
|
|
453
|
+
try {
|
|
454
|
+
config = toml.parse(fs.readFileSync(configPath, 'utf8'));
|
|
455
|
+
} catch (error) {
|
|
456
|
+
throw new Error(`Failed to parse existing Codex config.toml: ${error.message}`);
|
|
457
457
|
}
|
|
458
458
|
|
|
459
459
|
const managedProviderKeys = new Set(['cc-proxy']);
|
|
@@ -478,8 +478,8 @@ function clearCodexChannelConfig() {
|
|
|
478
478
|
}
|
|
479
479
|
}
|
|
480
480
|
|
|
481
|
-
|
|
482
|
-
|
|
481
|
+
writeTomlFile(configPath, config, { atomic: true });
|
|
482
|
+
removeCodexChannelEnvVars();
|
|
483
483
|
}
|
|
484
484
|
|
|
485
485
|
function applyCodexOAuth(credential) {
|