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
|
@@ -3,8 +3,9 @@ const path = require('path');
|
|
|
3
3
|
const BaseChannelService = require('../../../shared/base-channel-service');
|
|
4
4
|
const { isProxyConfig } = require('./native-config-implementation');
|
|
5
5
|
const { PATHS, NATIVE_PATHS } = require('../../../config/paths');
|
|
6
|
-
const {
|
|
6
|
+
const { readNativeOAuth, clearClaudeChannelConfig } = require('../../native-oauth-adapters');
|
|
7
7
|
const { isWindowsLikePlatform } = require('../../../utils/home-dir');
|
|
8
|
+
const { updateJsoncFile } = require('../../../utils/native-config-patcher');
|
|
8
9
|
const { normalizeGatewaySourceType } = require('../../../shared/proxy-utils');
|
|
9
10
|
const {
|
|
10
11
|
createSkippedResult,
|
|
@@ -96,62 +97,46 @@ function buildApiKeyHelperCommand(value) {
|
|
|
96
97
|
|
|
97
98
|
// ── Claude 原生设置写入 ──
|
|
98
99
|
|
|
99
|
-
function updateClaudeSettingsWithModelConfig(channel) {
|
|
100
|
-
clearNativeOAuth('claude');
|
|
100
|
+
function updateClaudeSettingsWithModelConfig(channel, managedChannels = []) {
|
|
101
101
|
const settingsPath = getClaudeSettingsPath();
|
|
102
|
-
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
|
|
103
|
-
|
|
104
|
-
let settings = {};
|
|
105
|
-
if (fs.existsSync(settingsPath)) {
|
|
106
|
-
settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (!settings.env) {
|
|
110
|
-
settings.env = {};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
102
|
const { baseUrl, apiKey, modelConfig, presetId, proxyUrl } = channel;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
103
|
+
const managedProxyUrls = new Set([
|
|
104
|
+
...managedChannels.map(item => item?.proxyUrl),
|
|
105
|
+
...service.loadChannels().channels.map(item => item?.proxyUrl)
|
|
106
|
+
].map(value => String(value || '').trim()).filter(Boolean));
|
|
107
|
+
updateJsoncFile(settingsPath, (settings) => {
|
|
108
|
+
if (!settings.env || typeof settings.env !== 'object' || Array.isArray(settings.env)) settings.env = {};
|
|
109
|
+
settings.env.ANTHROPIC_BASE_URL = baseUrl;
|
|
110
|
+
settings.env.ANTHROPIC_API_KEY = apiKey;
|
|
111
|
+
delete settings.env.ANTHROPIC_AUTH_TOKEN;
|
|
112
|
+
delete settings.env.CLAUDE_CODE_OAUTH_TOKEN;
|
|
113
|
+
|
|
114
|
+
const modelKeys = {
|
|
115
|
+
ANTHROPIC_MODEL: modelConfig?.model,
|
|
116
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: modelConfig?.haikuModel,
|
|
117
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: modelConfig?.sonnetModel,
|
|
118
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: modelConfig?.opusModel
|
|
119
|
+
};
|
|
120
|
+
if (presetId && presetId !== 'official' && modelConfig) {
|
|
121
|
+
Object.entries(modelKeys).forEach(([key, value]) => {
|
|
122
|
+
if (value) settings.env[key] = value;
|
|
123
|
+
else delete settings.env[key];
|
|
124
|
+
});
|
|
125
|
+
} else {
|
|
126
|
+
Object.keys(modelKeys).forEach(key => delete settings.env[key]);
|
|
132
127
|
}
|
|
133
|
-
} else {
|
|
134
|
-
delete settings.env.ANTHROPIC_MODEL;
|
|
135
|
-
delete settings.env.ANTHROPIC_DEFAULT_HAIKU_MODEL;
|
|
136
|
-
delete settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL;
|
|
137
|
-
delete settings.env.ANTHROPIC_DEFAULT_OPUS_MODEL;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (proxyUrl) {
|
|
141
|
-
settings.env.HTTPS_PROXY = proxyUrl;
|
|
142
|
-
settings.env.HTTP_PROXY = proxyUrl;
|
|
143
|
-
} else {
|
|
144
|
-
delete settings.env.HTTPS_PROXY;
|
|
145
|
-
delete settings.env.HTTP_PROXY;
|
|
146
|
-
delete settings.env.NO_PROXY;
|
|
147
|
-
}
|
|
148
128
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
129
|
+
if (proxyUrl) {
|
|
130
|
+
settings.env.HTTPS_PROXY = proxyUrl;
|
|
131
|
+
settings.env.HTTP_PROXY = proxyUrl;
|
|
132
|
+
} else {
|
|
133
|
+
['HTTPS_PROXY', 'HTTP_PROXY'].forEach((key) => {
|
|
134
|
+
if (managedProxyUrls.has(String(settings.env[key] || '').trim())) delete settings.env[key];
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
if (Object.keys(settings.env).length === 0) delete settings.env;
|
|
138
|
+
settings.apiKeyHelper = buildApiKeyHelperCommand(apiKey);
|
|
139
|
+
});
|
|
155
140
|
}
|
|
156
141
|
|
|
157
142
|
function createClaudeOAuthError(message, code, statusCode) {
|
|
@@ -213,35 +198,20 @@ function validateClaudeOAuthMutation(channel, operation) {
|
|
|
213
198
|
|
|
214
199
|
function updateClaudeSettings(baseUrl, apiKey) {
|
|
215
200
|
const settingsPath = getClaudeSettingsPath();
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
settings =
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
settings.env.ANTHROPIC_BASE_URL = baseUrl;
|
|
231
|
-
|
|
232
|
-
if (useAuthToken || (!useAuthToken && !useApiKey)) {
|
|
233
|
-
settings.env.ANTHROPIC_AUTH_TOKEN = apiKey;
|
|
234
|
-
delete settings.env.ANTHROPIC_API_KEY;
|
|
235
|
-
} else {
|
|
236
|
-
settings.env.ANTHROPIC_API_KEY = apiKey;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
if (settings.env && Object.keys(settings.env).length === 0) {
|
|
240
|
-
delete settings.env;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
settings.apiKeyHelper = buildApiKeyHelperCommand(apiKey);
|
|
244
|
-
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
|
|
201
|
+
updateJsoncFile(settingsPath, (settings) => {
|
|
202
|
+
if (!settings.env || typeof settings.env !== 'object' || Array.isArray(settings.env)) settings.env = {};
|
|
203
|
+
const useAuthToken = settings.env.ANTHROPIC_AUTH_TOKEN !== undefined;
|
|
204
|
+
const useApiKey = settings.env.ANTHROPIC_API_KEY !== undefined;
|
|
205
|
+
settings.env.ANTHROPIC_BASE_URL = baseUrl;
|
|
206
|
+
if (useAuthToken || (!useAuthToken && !useApiKey)) {
|
|
207
|
+
settings.env.ANTHROPIC_AUTH_TOKEN = apiKey;
|
|
208
|
+
delete settings.env.ANTHROPIC_API_KEY;
|
|
209
|
+
} else {
|
|
210
|
+
settings.env.ANTHROPIC_API_KEY = apiKey;
|
|
211
|
+
}
|
|
212
|
+
if (Object.keys(settings.env).length === 0) delete settings.env;
|
|
213
|
+
settings.apiKeyHelper = buildApiKeyHelperCommand(apiKey);
|
|
214
|
+
});
|
|
245
215
|
}
|
|
246
216
|
|
|
247
217
|
function resolveCurrentManagedChannel(channels = []) {
|
|
@@ -334,9 +304,9 @@ class ClaudeChannelService extends BaseChannelService {
|
|
|
334
304
|
this._cacheInitialized = true;
|
|
335
305
|
}
|
|
336
306
|
|
|
337
|
-
_onAfterCreate(channel,
|
|
307
|
+
_onAfterCreate(channel, allChannels) {
|
|
338
308
|
if (!isClaudeProxyRunning() && channel.enabled !== false && !isOpenAiCompatibleGateway(channel)) {
|
|
339
|
-
this._applyToNativeSettings(channel);
|
|
309
|
+
this._applyToNativeSettings(channel, allChannels);
|
|
340
310
|
}
|
|
341
311
|
}
|
|
342
312
|
|
|
@@ -347,7 +317,7 @@ class ClaudeChannelService extends BaseChannelService {
|
|
|
347
317
|
|
|
348
318
|
if (oldChannel.enabled === false && nextChannel.enabled !== false) {
|
|
349
319
|
if (!isOpenAiCompatibleGateway(nextChannel)) {
|
|
350
|
-
this._applyToNativeSettings(nextChannel);
|
|
320
|
+
this._applyToNativeSettings(nextChannel, [...allChannels, oldChannel]);
|
|
351
321
|
}
|
|
352
322
|
return;
|
|
353
323
|
}
|
|
@@ -358,19 +328,38 @@ class ClaudeChannelService extends BaseChannelService {
|
|
|
358
328
|
&& activeChannel?.id === nextChannel.id
|
|
359
329
|
&& !isOpenAiCompatibleGateway(nextChannel)
|
|
360
330
|
) {
|
|
361
|
-
this._applyToNativeSettings(nextChannel);
|
|
331
|
+
this._applyToNativeSettings(nextChannel, [...allChannels, oldChannel]);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
if (oldChannel.enabled !== false && nextChannel.enabled === false) {
|
|
335
|
+
if (activeChannel && !isOpenAiCompatibleGateway(activeChannel)) {
|
|
336
|
+
this._applyToNativeSettings(activeChannel, [...allChannels, oldChannel]);
|
|
337
|
+
} else if (!activeChannel) {
|
|
338
|
+
clearClaudeChannelConfig([...allChannels, oldChannel].map(item => item?.proxyUrl));
|
|
339
|
+
}
|
|
362
340
|
}
|
|
363
341
|
}
|
|
364
342
|
|
|
365
|
-
_onAfterDelete(
|
|
343
|
+
_onAfterDelete(channel, allChannels) {
|
|
366
344
|
if (isClaudeProxyRunning()) {
|
|
367
345
|
return;
|
|
368
346
|
}
|
|
369
347
|
|
|
370
348
|
const activeChannel = resolveCurrentManagedChannel(allChannels);
|
|
371
349
|
if (activeChannel && !isOpenAiCompatibleGateway(activeChannel)) {
|
|
372
|
-
this._applyToNativeSettings(activeChannel);
|
|
350
|
+
this._applyToNativeSettings(activeChannel, [...allChannels, channel]);
|
|
351
|
+
} else if (!activeChannel) {
|
|
352
|
+
clearClaudeChannelConfig([...allChannels, channel].map(item => item?.proxyUrl));
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
disableAllChannels() {
|
|
357
|
+
const channels = this.loadChannels().channels;
|
|
358
|
+
const result = super.disableAllChannels();
|
|
359
|
+
if (!isClaudeProxyRunning()) {
|
|
360
|
+
clearClaudeChannelConfig(channels.map(item => item?.proxyUrl));
|
|
373
361
|
}
|
|
362
|
+
return result;
|
|
374
363
|
}
|
|
375
364
|
|
|
376
365
|
applyChannelToSettings(channelId) {
|
|
@@ -388,12 +377,12 @@ class ClaudeChannelService extends BaseChannelService {
|
|
|
388
377
|
return super.applyChannelToSettings(channelId);
|
|
389
378
|
}
|
|
390
379
|
|
|
391
|
-
_applyToNativeSettings(channel) {
|
|
380
|
+
_applyToNativeSettings(channel, managedChannels = []) {
|
|
392
381
|
if (channel.authMode === 'oauth') {
|
|
393
382
|
applyNativeClaudeOAuth(channel);
|
|
394
383
|
return;
|
|
395
384
|
}
|
|
396
|
-
updateClaudeSettingsWithModelConfig(channel);
|
|
385
|
+
updateClaudeSettingsWithModelConfig(channel, managedChannels);
|
|
397
386
|
}
|
|
398
387
|
|
|
399
388
|
getEffectiveApiKey(channel) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const { NATIVE_PATHS } = require('../../../config/paths');
|
|
3
3
|
const { isWindowsLikePlatform } = require('../../../utils/home-dir');
|
|
4
|
+
const { readJsoncFile, writeJsoncFile } = require('../../../utils/native-config-patcher');
|
|
4
5
|
const DEFAULT_NATIVE_PATHS = NATIVE_PATHS.claude;
|
|
5
6
|
let nativePaths = DEFAULT_NATIVE_PATHS;
|
|
6
7
|
|
|
@@ -40,8 +41,7 @@ function hasBackup() {
|
|
|
40
41
|
// 读取配置文件
|
|
41
42
|
function readSettings() {
|
|
42
43
|
try {
|
|
43
|
-
|
|
44
|
-
return JSON.parse(content);
|
|
44
|
+
return readJsoncFile(getSettingsPath());
|
|
45
45
|
} catch (err) {
|
|
46
46
|
throw new Error('Failed to read settings.json: ' + err.message);
|
|
47
47
|
}
|
|
@@ -50,8 +50,7 @@ function readSettings() {
|
|
|
50
50
|
// 写入配置文件
|
|
51
51
|
function writeSettings(settings) {
|
|
52
52
|
try {
|
|
53
|
-
|
|
54
|
-
fs.writeFileSync(getSettingsPath(), content, 'utf8');
|
|
53
|
+
writeJsoncFile(getSettingsPath(), settings);
|
|
55
54
|
} catch (err) {
|
|
56
55
|
throw new Error('Failed to write settings.json: ' + err.message);
|
|
57
56
|
}
|
|
@@ -2,8 +2,8 @@ const fs = require('fs');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const crypto = require('crypto');
|
|
4
4
|
const toml = require('toml');
|
|
5
|
-
const tomlStringify = require('@iarna/toml').stringify;
|
|
6
5
|
const { PATHS } = require('../../../config/paths');
|
|
6
|
+
const { writeTomlFile } = require('../../../utils/native-config-patcher');
|
|
7
7
|
const { getCodexDir } = require('./config');
|
|
8
8
|
const { isProxyConfig, readConfig } = require('./native-config-implementation');
|
|
9
9
|
const { syncCodexUserEnvironment } = require('./env-manager');
|
|
@@ -175,21 +175,24 @@ function syncAllChannelEnvVars() {
|
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
function writeAnnotatedCodexConfig(configPath, config
|
|
179
|
-
|
|
180
|
-
if (comments.length > 0) {
|
|
181
|
-
tomlContent = comments.join('\n') + '\n\n' + tomlContent;
|
|
182
|
-
}
|
|
183
|
-
fs.writeFileSync(configPath, tomlContent, 'utf8');
|
|
178
|
+
function writeAnnotatedCodexConfig(configPath, config) {
|
|
179
|
+
writeTomlFile(configPath, config, { atomic: true });
|
|
184
180
|
}
|
|
185
181
|
|
|
186
|
-
function
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
182
|
+
function mergeCodexProviderConfig(existing = {}, channel = {}) {
|
|
183
|
+
const provider = {
|
|
184
|
+
...existing,
|
|
185
|
+
name: channel.name,
|
|
186
|
+
base_url: channel.baseUrl,
|
|
187
|
+
wire_api: channel.wireApi || 'responses',
|
|
188
|
+
env_key: CODEX_MANAGED_ENV_KEY,
|
|
189
|
+
requires_openai_auth: channelRequiresOpenaiAuth(channel)
|
|
190
|
+
};
|
|
191
|
+
delete provider.query_params;
|
|
192
|
+
if (channel.queryParams && Object.keys(channel.queryParams).length > 0) {
|
|
193
|
+
provider.query_params = channel.queryParams;
|
|
192
194
|
}
|
|
195
|
+
return provider;
|
|
193
196
|
}
|
|
194
197
|
|
|
195
198
|
function writeCodexConfigForMultiChannel(channels) {
|
|
@@ -201,8 +204,7 @@ function writeCodexConfigForMultiChannel(channels) {
|
|
|
201
204
|
try {
|
|
202
205
|
config = toml.parse(fs.readFileSync(configPath, 'utf8'));
|
|
203
206
|
} catch (err) {
|
|
204
|
-
|
|
205
|
-
config = {};
|
|
207
|
+
throw new Error(`Failed to parse existing config.toml: ${err.message}`);
|
|
206
208
|
}
|
|
207
209
|
}
|
|
208
210
|
|
|
@@ -229,26 +231,17 @@ function writeCodexConfigForMultiChannel(channels) {
|
|
|
229
231
|
config.model_provider = primary.providerKey;
|
|
230
232
|
|
|
231
233
|
for (const ch of enabledChannels) {
|
|
232
|
-
config.model_providers[ch.providerKey] =
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
env_key: CODEX_MANAGED_ENV_KEY,
|
|
237
|
-
requires_openai_auth: channelRequiresOpenaiAuth(ch)
|
|
238
|
-
};
|
|
239
|
-
if (ch.queryParams && Object.keys(ch.queryParams).length > 0) {
|
|
240
|
-
config.model_providers[ch.providerKey].query_params = ch.queryParams;
|
|
241
|
-
}
|
|
234
|
+
config.model_providers[ch.providerKey] = mergeCodexProviderConfig(
|
|
235
|
+
config.model_providers[ch.providerKey],
|
|
236
|
+
ch
|
|
237
|
+
);
|
|
242
238
|
}
|
|
243
239
|
} else {
|
|
244
240
|
clearManagedCodexConfig();
|
|
245
241
|
return;
|
|
246
242
|
}
|
|
247
243
|
|
|
248
|
-
writeAnnotatedCodexConfig(configPath, config
|
|
249
|
-
'# Codex Configuration',
|
|
250
|
-
'# Managed by Coding-Tool (multi-channel)'
|
|
251
|
-
]);
|
|
244
|
+
writeAnnotatedCodexConfig(configPath, config);
|
|
252
245
|
}
|
|
253
246
|
|
|
254
247
|
// ── CodexChannelService ──
|
|
@@ -315,6 +308,12 @@ class CodexChannelService extends BaseChannelService {
|
|
|
315
308
|
this._applyToNativeSettings(_next);
|
|
316
309
|
return;
|
|
317
310
|
}
|
|
311
|
+
if (_old.enabled !== false && _next.enabled === false) {
|
|
312
|
+
const activeChannel = resolveCurrentManagedChannel(allChannels);
|
|
313
|
+
if (activeChannel) this._applyToNativeSettings(activeChannel);
|
|
314
|
+
else clearManagedCodexConfig();
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
318
317
|
const activeChannel = resolveCurrentManagedChannel(allChannels);
|
|
319
318
|
if (_next.enabled !== false && activeChannel?.id === _next.id) {
|
|
320
319
|
this._applyToNativeSettings(_next);
|
|
@@ -347,7 +346,7 @@ class CodexChannelService extends BaseChannelService {
|
|
|
347
346
|
try {
|
|
348
347
|
config = toml.parse(fs.readFileSync(configPath, 'utf8'));
|
|
349
348
|
} catch (err) {
|
|
350
|
-
config
|
|
349
|
+
throw new Error(`Failed to parse existing config.toml: ${err.message}`);
|
|
351
350
|
}
|
|
352
351
|
}
|
|
353
352
|
|
|
@@ -356,26 +355,12 @@ class CodexChannelService extends BaseChannelService {
|
|
|
356
355
|
if (!config.model_providers || typeof config.model_providers !== 'object') {
|
|
357
356
|
config.model_providers = {};
|
|
358
357
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
name: channel.name,
|
|
364
|
-
base_url: channel.baseUrl,
|
|
365
|
-
wire_api: channel.wireApi || 'responses',
|
|
366
|
-
env_key: CODEX_MANAGED_ENV_KEY,
|
|
367
|
-
requires_openai_auth: channelRequiresOpenaiAuth(channel)
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
if (channel.queryParams && Object.keys(channel.queryParams).length > 0) {
|
|
371
|
-
config.model_providers[channel.providerKey].query_params = channel.queryParams;
|
|
372
|
-
}
|
|
358
|
+
config.model_providers[channel.providerKey] = mergeCodexProviderConfig(
|
|
359
|
+
config.model_providers[channel.providerKey],
|
|
360
|
+
channel
|
|
361
|
+
);
|
|
373
362
|
|
|
374
|
-
writeAnnotatedCodexConfig(configPath, config
|
|
375
|
-
'# Codex Configuration',
|
|
376
|
-
'# Managed by Coding-Tool',
|
|
377
|
-
`# Current provider: ${channel.name}`
|
|
378
|
-
]);
|
|
363
|
+
writeAnnotatedCodexConfig(configPath, config);
|
|
379
364
|
console.log(`[Codex Channels] Applied channel ${channel.name} to config.toml`);
|
|
380
365
|
syncAllChannelEnvVars();
|
|
381
366
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const toml = require('toml');
|
|
4
|
-
const
|
|
4
|
+
const { writeTomlFile } = require('../../../utils/native-config-patcher');
|
|
5
5
|
const { NATIVE_PATHS } = require('../../../config/paths');
|
|
6
6
|
const { syncCodexUserEnvironment } = require('./env-manager');
|
|
7
7
|
const DEFAULT_NATIVE_PATHS = NATIVE_PATHS.codex;
|
|
@@ -83,53 +83,10 @@ function readConfig() {
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
// 将配置对象转换为 TOML 字符串
|
|
87
|
-
function configToToml(config) {
|
|
88
|
-
let content = `# Codex Configuration
|
|
89
|
-
# Managed by Coding-Tool (Proxy Mode)
|
|
90
|
-
|
|
91
|
-
`;
|
|
92
|
-
|
|
93
|
-
// 写入顶级字段
|
|
94
|
-
for (const [key, value] of Object.entries(config)) {
|
|
95
|
-
if (key === 'model_providers') continue; // 稍后处理
|
|
96
|
-
if (typeof value === 'string') {
|
|
97
|
-
content += `${key} = "${value}"\n`;
|
|
98
|
-
} else if (typeof value === 'boolean') {
|
|
99
|
-
content += `${key} = ${value}\n`;
|
|
100
|
-
} else if (typeof value === 'number') {
|
|
101
|
-
content += `${key} = ${value}\n`;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
content += '\n';
|
|
106
|
-
|
|
107
|
-
// 写入 model_providers
|
|
108
|
-
if (config.model_providers) {
|
|
109
|
-
for (const [providerKey, providerConfig] of Object.entries(config.model_providers)) {
|
|
110
|
-
content += `[model_providers.${providerKey}]\n`;
|
|
111
|
-
for (const [key, value] of Object.entries(providerConfig)) {
|
|
112
|
-
if (typeof value === 'string') {
|
|
113
|
-
content += `${key} = "${value}"\n`;
|
|
114
|
-
} else if (typeof value === 'boolean') {
|
|
115
|
-
content += `${key} = ${value}\n`;
|
|
116
|
-
} else if (typeof value === 'number') {
|
|
117
|
-
content += `${key} = ${value}\n`;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
content += '\n';
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return content;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
86
|
// 写入 config.toml
|
|
128
87
|
function writeConfig(config) {
|
|
129
88
|
try {
|
|
130
|
-
|
|
131
|
-
const content = tomlStringify(safeConfig);
|
|
132
|
-
fs.writeFileSync(getConfigPath(), content, 'utf8');
|
|
89
|
+
writeTomlFile(getConfigPath(), config || {}, { atomic: true });
|
|
133
90
|
} catch (err) {
|
|
134
91
|
throw new Error('Failed to write config.toml: ' + err.message);
|
|
135
92
|
}
|
|
@@ -254,12 +211,7 @@ function setProxyConfig(proxyPort) {
|
|
|
254
211
|
}
|
|
255
212
|
|
|
256
213
|
// 读取当前配置(不存在时使用空配置)
|
|
257
|
-
|
|
258
|
-
try {
|
|
259
|
-
config = readConfig();
|
|
260
|
-
} catch (err) {
|
|
261
|
-
config = {};
|
|
262
|
-
}
|
|
214
|
+
const config = configExists() ? readConfig() : {};
|
|
263
215
|
|
|
264
216
|
// 设置 model_provider 为 proxy
|
|
265
217
|
config.model_provider = 'cc-proxy';
|
|
@@ -186,7 +186,7 @@ function createDriver(context = {}) {
|
|
|
186
186
|
|
|
187
187
|
// Keep the aggregate DSH API driver compatible with the manifest consistency
|
|
188
188
|
// contract. Actual descriptor requests are still dispatched by capability.
|
|
189
|
-
for (const operation of ['list', 'current', 'enabled', 'create', 'update', 'remove', 'applyToSettings', 'sync', 'order', 'models', 'probeModels', 'speedTest'
|
|
189
|
+
for (const operation of ['list', 'current', 'enabled', 'create', 'update', 'remove', 'applyToSettings', 'sync', 'order', 'models', 'probeModels', 'speedTest']) {
|
|
190
190
|
driver[operation] = (...args) => channels[operation](...args);
|
|
191
191
|
}
|
|
192
192
|
for (const operation of ['status', 'start', 'stop']) {
|
|
@@ -4,11 +4,12 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { getPlatformStatePath: resolveStatePath } = require('../../../config/paths');
|
|
6
6
|
const BaseChannelService = require('../../../shared/base-channel-service');
|
|
7
|
+
const { writeYamlFile } = require('../../../utils/native-config-patcher');
|
|
7
8
|
const {
|
|
8
9
|
resolvePaths,
|
|
9
10
|
readYamlFile,
|
|
10
11
|
writeAtomic,
|
|
11
|
-
|
|
12
|
+
DSH_YAML_SOURCE_TAGS,
|
|
12
13
|
isObject
|
|
13
14
|
} = require('./common');
|
|
14
15
|
|
|
@@ -202,7 +203,7 @@ class DshChannelService extends BaseChannelService {
|
|
|
202
203
|
}
|
|
203
204
|
|
|
204
205
|
_activeChannel(channels = []) {
|
|
205
|
-
return channels.find(channel => channel.enabled !== false) ||
|
|
206
|
+
return channels.find(channel => channel.enabled !== false) || null;
|
|
206
207
|
}
|
|
207
208
|
|
|
208
209
|
_onAfterCreate(channel) {
|
|
@@ -217,9 +218,8 @@ class DshChannelService extends BaseChannelService {
|
|
|
217
218
|
this._removeNativeProvider(oldChannel.providerKey);
|
|
218
219
|
}
|
|
219
220
|
const active = this._activeChannel(allChannels);
|
|
220
|
-
if (active
|
|
221
|
-
|
|
222
|
-
}
|
|
221
|
+
if (active) this._applyToNativeSettings(active);
|
|
222
|
+
else this._removeNativeProvider(nextChannel.providerKey);
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
_onAfterDelete(channel, allChannels) {
|
|
@@ -234,7 +234,11 @@ class DshChannelService extends BaseChannelService {
|
|
|
234
234
|
}
|
|
235
235
|
|
|
236
236
|
_writeNativeSettings(settings) {
|
|
237
|
-
|
|
237
|
+
writeYamlFile(this.nativePaths.settings, settings, {
|
|
238
|
+
atomic: true,
|
|
239
|
+
mode: 0o600,
|
|
240
|
+
customTags: DSH_YAML_SOURCE_TAGS
|
|
241
|
+
});
|
|
238
242
|
}
|
|
239
243
|
|
|
240
244
|
_removeNativeProvider(providerKey) {
|
|
@@ -246,6 +250,15 @@ class DshChannelService extends BaseChannelService {
|
|
|
246
250
|
if (!Object.prototype.hasOwnProperty.call(providers, key)) return;
|
|
247
251
|
delete providers[key];
|
|
248
252
|
settings['llm-pi-ai'] = { ...llm, providers };
|
|
253
|
+
const selected = isObject(settings['agent-default-model'])
|
|
254
|
+
? { ...settings['agent-default-model'] }
|
|
255
|
+
: null;
|
|
256
|
+
if (selected?.provider === key) {
|
|
257
|
+
delete selected.provider;
|
|
258
|
+
delete selected.model;
|
|
259
|
+
if (Object.keys(selected).length > 0) settings['agent-default-model'] = selected;
|
|
260
|
+
else delete settings['agent-default-model'];
|
|
261
|
+
}
|
|
249
262
|
this._writeNativeSettings(settings);
|
|
250
263
|
}
|
|
251
264
|
|
|
@@ -255,7 +268,11 @@ class DshChannelService extends BaseChannelService {
|
|
|
255
268
|
if (!isObject(credentials.refs)) credentials.refs = {};
|
|
256
269
|
credentials.refs[channel.apiKeyEnv] = channel.apiKey || '';
|
|
257
270
|
if (!isObject(credentials.records)) credentials.records = {};
|
|
258
|
-
|
|
271
|
+
writeYamlFile(this.nativePaths.credentials, credentials, {
|
|
272
|
+
atomic: true,
|
|
273
|
+
mode: 0o600,
|
|
274
|
+
customTags: DSH_YAML_SOURCE_TAGS
|
|
275
|
+
});
|
|
259
276
|
}
|
|
260
277
|
|
|
261
278
|
_providerEntry(channel, baseUrl = channel.baseUrl, models = channel.models) {
|
|
@@ -273,7 +290,10 @@ class DshChannelService extends BaseChannelService {
|
|
|
273
290
|
const settings = this._nativeSettings();
|
|
274
291
|
const llm = isObject(settings['llm-pi-ai']) ? { ...settings['llm-pi-ai'] } : {};
|
|
275
292
|
const providers = isObject(llm.providers) ? { ...llm.providers } : {};
|
|
276
|
-
providers[channel.providerKey] =
|
|
293
|
+
providers[channel.providerKey] = {
|
|
294
|
+
...(isObject(providers[channel.providerKey]) ? providers[channel.providerKey] : {}),
|
|
295
|
+
...this._providerEntry(channel)
|
|
296
|
+
};
|
|
277
297
|
settings['llm-pi-ai'] = { ...llm, providers };
|
|
278
298
|
settings['agent-default-model'] = {
|
|
279
299
|
...(isObject(settings['agent-default-model']) ? settings['agent-default-model'] : {}),
|
|
@@ -149,24 +149,6 @@ function createDriver(context = {}) {
|
|
|
149
149
|
{ authSourceType: 'dsh' }
|
|
150
150
|
);
|
|
151
151
|
}),
|
|
152
|
-
speedTestAll: request => withAsyncResult(request, 'speedTestAll', async () => {
|
|
153
|
-
const { testMultipleChannels } = require('../../../server/services/speed-test');
|
|
154
|
-
const body = bodyOf(request);
|
|
155
|
-
const channels = implementation.getServiceInstance().getEnabledChannels();
|
|
156
|
-
const groups = new Map();
|
|
157
|
-
for (const channel of channels) {
|
|
158
|
-
const type = channelTypeForSpeed(channel);
|
|
159
|
-
const group = groups.get(type) || [];
|
|
160
|
-
group.push(channel);
|
|
161
|
-
groups.set(type, group);
|
|
162
|
-
}
|
|
163
|
-
const groupedResults = await Promise.all([...groups.entries()].map(([type, group]) => (
|
|
164
|
-
testMultipleChannels(group, body.timeout, type, body.concurrency)
|
|
165
|
-
)));
|
|
166
|
-
const resultById = new Map(groupedResults.flat().map(result => [result.channelId, result]));
|
|
167
|
-
const results = channels.map(channel => resultById.get(channel.id)).filter(Boolean);
|
|
168
|
-
return { results };
|
|
169
|
-
}),
|
|
170
152
|
saveOrder: request => withResult(request, 'order', () => {
|
|
171
153
|
const body = bodyOf(request);
|
|
172
154
|
implementation.getServiceInstance().saveChannelOrder(body.order || body.ids || []);
|
|
@@ -23,6 +23,12 @@ const DSH_YAML_SCHEMA = yaml.DEFAULT_SCHEMA.extend([
|
|
|
23
23
|
})
|
|
24
24
|
]);
|
|
25
25
|
|
|
26
|
+
const DSH_YAML_SOURCE_TAGS = [{
|
|
27
|
+
tag: 'tag:yaml.org,2002:js',
|
|
28
|
+
resolve: value => `!!js ${value}`,
|
|
29
|
+
stringify: item => String(item.value).replace(/^!!js\s*/, '')
|
|
30
|
+
}];
|
|
31
|
+
|
|
26
32
|
function clone(value) {
|
|
27
33
|
if (value === undefined) return undefined;
|
|
28
34
|
return JSON.parse(JSON.stringify(value));
|
|
@@ -548,6 +554,7 @@ module.exports = {
|
|
|
548
554
|
resolvePaths,
|
|
549
555
|
isObject,
|
|
550
556
|
readYamlFile,
|
|
557
|
+
DSH_YAML_SOURCE_TAGS,
|
|
551
558
|
readJsonFile,
|
|
552
559
|
fileRevision,
|
|
553
560
|
writeAtomic,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
|
-
const {
|
|
4
|
+
const { writeYamlFile } = require('../../../utils/native-config-patcher');
|
|
5
|
+
const { resolvePaths, readYamlFile, mergeSettings, fileRevision, credentialMetadata, redactSecrets, listProfiles, listProfilePlugins, listProfileMcp, listProfilePrompts, DSH_YAML_SOURCE_TAGS } = require('./common');
|
|
5
6
|
const { resolveSkillRoots } = require('./resources');
|
|
6
7
|
|
|
7
8
|
function createDriver(context = {}) {
|
|
@@ -35,7 +36,11 @@ function createDriver(context = {}) {
|
|
|
35
36
|
}
|
|
36
37
|
const current = readYamlFile(paths.settings, {});
|
|
37
38
|
const next = mergeSettings(current, body.namespace, body.patch, body.replace === true);
|
|
38
|
-
|
|
39
|
+
writeYamlFile(paths.settings, next, {
|
|
40
|
+
atomic: true,
|
|
41
|
+
mode: 0o600,
|
|
42
|
+
customTags: DSH_YAML_SOURCE_TAGS
|
|
43
|
+
});
|
|
39
44
|
return this.getConfig();
|
|
40
45
|
},
|
|
41
46
|
getConfigCapabilities() {
|