coding-tool-x 3.8.8 → 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-CrfS0KYV.js → ChatHistoryDrawer-C1At65O3.js} +1 -1
- package/dist/web/assets/{CollapseItem-ClMsJuUu.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-BpaWG-iC.js +1 -0
- package/dist/web/assets/{ConfigTemplatesDrawer-BqM32Im7.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-BMMUufSc.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-C4kJlpVX.js → PluginManager-BGQ0xF2F.js} +1 -1
- package/dist/web/assets/{PluginsDrawer-05R4k15x.js → PluginsDrawer-STwHEqiF.js} +1 -1
- package/dist/web/assets/PluginsPanel-1zuNP7da.js +1 -0
- package/dist/web/assets/ProjectList-VbIYSHbH.css +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-D9jkNuRM.js → SkillManager-PcAa5EbF.js} +1 -1
- package/dist/web/assets/{SkillsDrawer-0Z4IefxC.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-eujtWpoq.js → WorkspaceDrawer-oS6mB1VZ.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-CQ-8Ej68.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-DbowkmZJ.js +1 -0
- 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-DZd5rNk5.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/daemon.js +27 -4
- package/src/commands/export-config.js +17 -211
- package/src/config/model-metadata.json +180 -16
- package/src/platforms/channel-auth-registry.js +36 -6
- package/src/platforms/channel-auth-service.js +13 -3
- 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/omp/prompts.js +58 -2
- package/src/platforms/drivers/omp/proxy-implementation.js +25 -1
- package/src/platforms/drivers/opencode/channels-implementation.js +31 -22
- package/src/platforms/drivers/opencode/channels.js +6 -1
- 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 +2 -9
- package/src/platforms/manifests/opencode.json +28 -8
- package/src/platforms/native-oauth-adapters.js +351 -51
- package/src/platforms/oauth-credentials-service.js +344 -13
- package/src/server/index.js +37 -1
- 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-DRQuwi0m.js +0 -1
- package/dist/web/assets/Analytics-Cf5KwEdj.js +0 -1
- package/dist/web/assets/AssetPathField-DyniM0pF.js +0 -1
- package/dist/web/assets/CommandsDrawer-CzHelf56.js +0 -1
- package/dist/web/assets/ConfigExportDrawer-BEZ8wTby.js +0 -1
- package/dist/web/assets/ConfigTemplates-Crg3xWe5.js +0 -1
- package/dist/web/assets/ConfigTemplatesPanel-lTdutC8H.js +0 -1
- package/dist/web/assets/FavoritesDrawer-DDyWUBJR.js +0 -1
- package/dist/web/assets/GatewayConvertDrawer-Rigtt_5J.js +0 -1
- package/dist/web/assets/Home-tCSKmSHV.js +0 -1
- package/dist/web/assets/McpDrawer-CmE-d7VW.js +0 -1
- package/dist/web/assets/McpFormDrawer-ByvS18od.js +0 -1
- package/dist/web/assets/PluginsPanel-BCoEs7Og.js +0 -1
- package/dist/web/assets/ProjectList-BJt1OIaI.js +0 -1
- package/dist/web/assets/ProjectList-DG3yu8xZ.css +0 -1
- package/dist/web/assets/PromptsDrawer-DnwBf_oW.js +0 -1
- package/dist/web/assets/RecentSessionsDrawer-B7d-38wH.js +0 -1
- package/dist/web/assets/SessionCard-BUgjHQwX.js +0 -1
- package/dist/web/assets/SessionList-B-bXZRFk.js +0 -1
- package/dist/web/assets/SettingsDrawer-BITzjQLl.js +0 -1
- package/dist/web/assets/SkillsPanel-BKIVOj7n.js +0 -1
- package/dist/web/assets/SpeedTestDrawer-Bx1bYf5i.js +0 -1
- package/dist/web/assets/SpeedTestDrawer-Dc0WToJ_.css +0 -1
- package/dist/web/assets/Upload-CzC4rGI4.js +0 -1
- package/dist/web/assets/WorkspacePanel-Cql5qE2R.js +0 -1
- package/dist/web/assets/client-UDwXBP9i.js +0 -1
- package/dist/web/assets/headers-BqDmkMjy.js +0 -1
- package/dist/web/assets/index-Cmmwj3Fx.js +0 -2
- package/dist/web/assets/index-Cnbe696C.css +0 -1
- 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-DqlbocLJ.js +0 -1
- package/dist/web/assets/use-message-DqCwefG6.js +0 -1
|
@@ -68,6 +68,10 @@ function normalizeChannelName(value) {
|
|
|
68
68
|
.replace(/\s+/g, ' ');
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
function normalizeAuthMode(value) {
|
|
72
|
+
return ['api_key', 'oauth', 'none'].includes(value) ? value : 'api_key';
|
|
73
|
+
}
|
|
74
|
+
|
|
71
75
|
// 获取渠道存储文件路径
|
|
72
76
|
function getChannelsFilePath() {
|
|
73
77
|
const channelsDir = path.dirname(configuredState.channels);
|
|
@@ -98,24 +102,7 @@ function loadChannels() {
|
|
|
98
102
|
const data = JSON.parse(content);
|
|
99
103
|
// 确保渠道有必要字段(兼容旧数据)
|
|
100
104
|
if (data.channels) {
|
|
101
|
-
data.channels = data.channels.map(ch =>
|
|
102
|
-
const normalized = {
|
|
103
|
-
...ch,
|
|
104
|
-
enabled: ch.enabled !== false,
|
|
105
|
-
weight: ch.weight || 1,
|
|
106
|
-
maxConcurrency: ch.maxConcurrency || null,
|
|
107
|
-
balanceToken: ch.balanceToken || '',
|
|
108
|
-
balanceUserId: ch.balanceUserId || null,
|
|
109
|
-
modelRedirects: ch.modelRedirects || [],
|
|
110
|
-
speedTestModel: ch.speedTestModel || null,
|
|
111
|
-
wireApi: ch.wireApi || 'openai', // OpenCode 默认使用 OpenAI 兼容格式
|
|
112
|
-
gatewaySourceType: normalizeGatewaySourceType(ch.gatewaySourceType, 'codex'),
|
|
113
|
-
allowedModels: ch.allowedModels || []
|
|
114
|
-
};
|
|
115
|
-
normalized.providerKey = ch.providerKey || deriveProviderKey(normalized);
|
|
116
|
-
normalized.websiteUrl = resolveChannelWebsiteUrl('opencode', normalized);
|
|
117
|
-
return normalized;
|
|
118
|
-
});
|
|
105
|
+
data.channels = data.channels.map(ch => normalizeOpenCodeChannel(ch));
|
|
119
106
|
}
|
|
120
107
|
return data;
|
|
121
108
|
} catch (err) {
|
|
@@ -191,12 +178,18 @@ function getChannels() {
|
|
|
191
178
|
function createChannel(name, baseUrl, apiKey, extraConfig = {}) {
|
|
192
179
|
const data = loadChannels();
|
|
193
180
|
const isProxyRunning = getOpenCodeProxyRunning();
|
|
181
|
+
const authMode = normalizeAuthMode(extraConfig.authMode);
|
|
194
182
|
|
|
195
183
|
const newChannel = {
|
|
196
184
|
id: crypto.randomUUID(),
|
|
197
185
|
name,
|
|
198
|
-
baseUrl,
|
|
199
|
-
apiKey,
|
|
186
|
+
baseUrl: authMode === 'oauth' || authMode === 'none' ? '' : baseUrl,
|
|
187
|
+
apiKey: authMode === 'oauth' || authMode === 'none' ? '' : apiKey,
|
|
188
|
+
authMode,
|
|
189
|
+
authRef: authMode === 'oauth' ? (extraConfig.authRef || {}) : undefined,
|
|
190
|
+
authSource: authMode === 'oauth' ? (extraConfig.authSource || 'synced-local') : undefined,
|
|
191
|
+
authStatus: authMode === 'oauth' ? (extraConfig.authStatus || 'available') : undefined,
|
|
192
|
+
oauthProviderId: authMode === 'oauth' ? (extraConfig.oauthProviderId || extraConfig.providerKey || '') : '',
|
|
200
193
|
wireApi: extraConfig.wireApi || 'openai',
|
|
201
194
|
enabled: extraConfig.enabled !== false,
|
|
202
195
|
weight: extraConfig.weight || 1,
|
|
@@ -248,7 +241,7 @@ function updateChannel(channelId, updates) {
|
|
|
248
241
|
|
|
249
242
|
const oldChannel = data.channels[index];
|
|
250
243
|
|
|
251
|
-
const merged = {
|
|
244
|
+
const merged = normalizeOpenCodeChannel({
|
|
252
245
|
...oldChannel,
|
|
253
246
|
...updates,
|
|
254
247
|
id: channelId,
|
|
@@ -264,7 +257,7 @@ function updateChannel(channelId, updates) {
|
|
|
264
257
|
'codex'
|
|
265
258
|
),
|
|
266
259
|
updatedAt: Date.now()
|
|
267
|
-
};
|
|
260
|
+
});
|
|
268
261
|
merged.providerKey = updates.providerKey || oldChannel.providerKey || deriveProviderKey(merged);
|
|
269
262
|
merged.websiteUrl = resolveChannelWebsiteUrl('opencode', merged);
|
|
270
263
|
data.channels[index] = merged;
|
|
@@ -289,6 +282,10 @@ function updateChannel(channelId, updates) {
|
|
|
289
282
|
if (!isProxyRunning) {
|
|
290
283
|
if (oldChannel.enabled === false && merged.enabled !== false) {
|
|
291
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();
|
|
292
289
|
} else {
|
|
293
290
|
const activeChannel = resolveCurrentManagedChannel(data.channels);
|
|
294
291
|
if (merged.enabled !== false && activeChannel?.id === merged.id) {
|
|
@@ -494,9 +491,11 @@ async function getEffectiveApiKey(channel) {
|
|
|
494
491
|
}
|
|
495
492
|
|
|
496
493
|
function disableAllChannels() {
|
|
494
|
+
const proxyRunning = getOpenCodeProxyRunning();
|
|
497
495
|
const data = loadChannels();
|
|
498
496
|
data.channels.forEach(ch => { ch.enabled = false; });
|
|
499
497
|
saveChannels(data);
|
|
498
|
+
if (!proxyRunning) clearManagedChannelConfig();
|
|
500
499
|
}
|
|
501
500
|
|
|
502
501
|
function getCurrentOpenCodeProviderId(config = {}) {
|
|
@@ -524,6 +523,7 @@ function findOpenCodeExistingByProvider(channels = [], providerId = '', baseUrl
|
|
|
524
523
|
}
|
|
525
524
|
|
|
526
525
|
function normalizeOpenCodeChannel(channel = {}) {
|
|
526
|
+
const authMode = normalizeAuthMode(channel.authMode);
|
|
527
527
|
const normalized = {
|
|
528
528
|
...channel,
|
|
529
529
|
enabled: channel.enabled !== false,
|
|
@@ -537,6 +537,15 @@ function normalizeOpenCodeChannel(channel = {}) {
|
|
|
537
537
|
gatewaySourceType: normalizeGatewaySourceType(channel.gatewaySourceType, 'codex'),
|
|
538
538
|
allowedModels: Array.isArray(channel.allowedModels) ? channel.allowedModels : []
|
|
539
539
|
};
|
|
540
|
+
normalized.authMode = authMode;
|
|
541
|
+
normalized.authRef = authMode === 'oauth' ? (channel.authRef || {}) : undefined;
|
|
542
|
+
normalized.authSource = authMode === 'oauth' ? (channel.authSource || 'synced-local') : undefined;
|
|
543
|
+
normalized.authStatus = authMode === 'oauth' ? (channel.authStatus || 'available') : undefined;
|
|
544
|
+
normalized.oauthProviderId = authMode === 'oauth' ? (channel.oauthProviderId || channel.providerKey || '') : '';
|
|
545
|
+
if (authMode === 'oauth' || authMode === 'none') {
|
|
546
|
+
normalized.baseUrl = '';
|
|
547
|
+
normalized.apiKey = '';
|
|
548
|
+
}
|
|
540
549
|
normalized.providerKey = normalized.providerKey || deriveProviderKey(normalized);
|
|
541
550
|
normalized.websiteUrl = resolveChannelWebsiteUrl('opencode', normalized);
|
|
542
551
|
return normalized;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const { createChannelDriver } = require('../../../shared/channel-driver');
|
|
4
4
|
|
|
5
5
|
function createDriver(context = {}) {
|
|
6
|
-
|
|
6
|
+
const driver = createChannelDriver({
|
|
7
7
|
...context,
|
|
8
8
|
platform: 'opencode',
|
|
9
9
|
servicePath: './opencode/channels-implementation',
|
|
@@ -35,6 +35,11 @@ function createDriver(context = {}) {
|
|
|
35
35
|
},
|
|
36
36
|
dashboardChannelShape: 'array'
|
|
37
37
|
});
|
|
38
|
+
const auth = require('../../channel-auth-service');
|
|
39
|
+
driver.getAuth = context => auth.getChannelAuth('opencode', { channelId: context?.params?.channelId || context?.query?.channelId || '' });
|
|
40
|
+
driver.syncLocalAuth = context => auth.syncLocalChannelAuth('opencode', { channelId: context?.body?.channelId || context?.params?.channelId || context?.query?.channelId || '' });
|
|
41
|
+
driver.getAuthQuota = context => auth.fetchChannelAuthQuota('opencode', context?.params?.channelId, { refresh: context?.query?.refresh === 'true' });
|
|
42
|
+
return driver;
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
module.exports = { createDriver };
|
|
@@ -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",
|
|
@@ -491,6 +483,7 @@
|
|
|
491
483
|
"api": "legacy:omp",
|
|
492
484
|
"hooks": "legacy:omp",
|
|
493
485
|
"resourceSync": "legacy:omp",
|
|
494
|
-
"mcp": "legacy:omp"
|
|
486
|
+
"mcp": "legacy:omp",
|
|
487
|
+
"prompts": "legacy:omp"
|
|
495
488
|
}
|
|
496
489
|
}
|
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
"defaultEnabled": true,
|
|
10
10
|
"sortOrder": 30,
|
|
11
11
|
"modelConfig": { "catalogKey": "codex" },
|
|
12
|
+
"auth": {
|
|
13
|
+
"modes": ["api_key", "oauth", "none"],
|
|
14
|
+
"oauth": { "adapter": "opencode", "policy": "single-enabled", "quota": "codex" }
|
|
15
|
+
},
|
|
12
16
|
"helpLabel": "OpenCode",
|
|
13
17
|
"terminalColor": "yellow",
|
|
14
18
|
"cliSelectable": true,
|
|
@@ -257,6 +261,30 @@
|
|
|
257
261
|
"request": "default",
|
|
258
262
|
"response": "default"
|
|
259
263
|
},
|
|
264
|
+
{
|
|
265
|
+
"method": "GET",
|
|
266
|
+
"path": "/channels/auth",
|
|
267
|
+
"capability": "channels",
|
|
268
|
+
"operation": "getAuth",
|
|
269
|
+
"request": "default",
|
|
270
|
+
"response": "default"
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
"method": "POST",
|
|
274
|
+
"path": "/channels/auth/sync-local",
|
|
275
|
+
"capability": "channels",
|
|
276
|
+
"operation": "syncLocalAuth",
|
|
277
|
+
"request": "default",
|
|
278
|
+
"response": "default"
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
"method": "GET",
|
|
282
|
+
"path": "/channels/:channelId/auth/quota",
|
|
283
|
+
"capability": "channels",
|
|
284
|
+
"operation": "getAuthQuota",
|
|
285
|
+
"request": "default",
|
|
286
|
+
"response": "default"
|
|
287
|
+
},
|
|
260
288
|
{
|
|
261
289
|
"method": "POST",
|
|
262
290
|
"path": "/channels/sync-current",
|
|
@@ -321,14 +349,6 @@
|
|
|
321
349
|
"request": "default",
|
|
322
350
|
"response": "default"
|
|
323
351
|
},
|
|
324
|
-
{
|
|
325
|
-
"method": "POST",
|
|
326
|
-
"path": "/channels/speed-test-all",
|
|
327
|
-
"capability": "channels",
|
|
328
|
-
"operation": "speedTestAll",
|
|
329
|
-
"request": "default",
|
|
330
|
-
"response": "default"
|
|
331
|
-
},
|
|
332
352
|
{
|
|
333
353
|
"method": "POST",
|
|
334
354
|
"path": "/channels/probe-models",
|