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 crypto = require('crypto');
|
|
4
4
|
const { PATHS, NATIVE_PATHS } = require('../../../config/paths');
|
|
5
5
|
const { resolveChannelWebsiteUrl } = require('../../../config/channel-preset-websites');
|
|
6
|
-
const {
|
|
6
|
+
const { readNativeOAuth, clearGeminiChannelConfig } = require('../../native-oauth-adapters');
|
|
7
7
|
const { normalizeGatewaySourceType } = require('../../../shared/proxy-utils');
|
|
8
|
+
const { readJsoncFile, updateJsoncFile, updateEnvFile } = require('../../../utils/native-config-patcher');
|
|
8
9
|
const {
|
|
9
10
|
createSkippedResult,
|
|
10
11
|
isLocalProxyBaseUrl,
|
|
@@ -84,12 +85,16 @@ function applyNativeGeminiOAuth(channel) {
|
|
|
84
85
|
clearGeminiChannelConfig();
|
|
85
86
|
|
|
86
87
|
const settingsPath = configuredNative.settings;
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
88
|
+
updateJsoncFile(settingsPath, (settings) => {
|
|
89
|
+
settings.security = settings.security && typeof settings.security === 'object' && !Array.isArray(settings.security)
|
|
90
|
+
? settings.security
|
|
91
|
+
: {};
|
|
92
|
+
settings.security.auth = settings.security.auth && typeof settings.security.auth === 'object'
|
|
93
|
+
&& !Array.isArray(settings.security.auth)
|
|
94
|
+
? settings.security.auth
|
|
95
|
+
: {};
|
|
96
|
+
settings.security.auth.selectedType = 'oauth-personal';
|
|
97
|
+
});
|
|
93
98
|
console.log(`[Gemini Channels] Applied native OAuth channel ${channel.name}`);
|
|
94
99
|
return channel;
|
|
95
100
|
}
|
|
@@ -168,14 +173,11 @@ function readExistingGeminiEnv() {
|
|
|
168
173
|
|
|
169
174
|
function writeGeminiEnv(env = {}) {
|
|
170
175
|
const envPath = path.join(getGeminiDir(), '.env');
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if (process.platform !== 'win32') {
|
|
177
|
-
fs.chmodSync(envPath, 0o600);
|
|
178
|
-
}
|
|
176
|
+
const managedKeys = ['GOOGLE_GEMINI_BASE_URL', 'GEMINI_API_KEY', 'GEMINI_MODEL'];
|
|
177
|
+
updateEnvFile(envPath, Object.fromEntries(managedKeys.map(key => [
|
|
178
|
+
key,
|
|
179
|
+
Object.prototype.hasOwnProperty.call(env, key) ? env[key] : undefined
|
|
180
|
+
])), { atomic: true, mode: 0o600 });
|
|
179
181
|
}
|
|
180
182
|
|
|
181
183
|
// 检查是否在代理模式
|
|
@@ -515,8 +517,6 @@ function applyChannelToSettings(channelId, channels = null) {
|
|
|
515
517
|
return applyNativeGeminiOAuth(channel);
|
|
516
518
|
}
|
|
517
519
|
|
|
518
|
-
clearNativeOAuth('gemini');
|
|
519
|
-
|
|
520
520
|
const geminiDir = getGeminiDir();
|
|
521
521
|
|
|
522
522
|
if (!fs.existsSync(geminiDir)) {
|
|
@@ -532,22 +532,16 @@ function applyChannelToSettings(channelId, channels = null) {
|
|
|
532
532
|
|
|
533
533
|
// 确保 settings.json 存在并配置正确的认证模式
|
|
534
534
|
const settingsPath = path.join(geminiDir, 'settings.json');
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
// 设置认证模式为 gemini-api-key(第三方 API)
|
|
546
|
-
settings.security = settings.security || {};
|
|
547
|
-
settings.security.auth = settings.security.auth || {};
|
|
548
|
-
settings.security.auth.selectedType = 'gemini-api-key';
|
|
549
|
-
|
|
550
|
-
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
|
|
535
|
+
updateJsoncFile(settingsPath, (settings) => {
|
|
536
|
+
settings.security = settings.security && typeof settings.security === 'object' && !Array.isArray(settings.security)
|
|
537
|
+
? settings.security
|
|
538
|
+
: {};
|
|
539
|
+
settings.security.auth = settings.security.auth && typeof settings.security.auth === 'object'
|
|
540
|
+
&& !Array.isArray(settings.security.auth)
|
|
541
|
+
? settings.security.auth
|
|
542
|
+
: {};
|
|
543
|
+
settings.security.auth.selectedType = 'gemini-api-key';
|
|
544
|
+
});
|
|
551
545
|
|
|
552
546
|
console.log(`[Gemini Channels] Applied channel ${channel.name} to .env`);
|
|
553
547
|
return channel;
|
|
@@ -600,6 +594,17 @@ function writeGeminiConfigForMultiChannel(allChannels) {
|
|
|
600
594
|
delete env.GEMINI_API_KEY;
|
|
601
595
|
delete env.GEMINI_MODEL;
|
|
602
596
|
writeGeminiEnv(env);
|
|
597
|
+
const settingsPath = path.join(geminiDir, 'settings.json');
|
|
598
|
+
if (fs.existsSync(settingsPath)) {
|
|
599
|
+
updateJsoncFile(settingsPath, (settings) => {
|
|
600
|
+
const auth = settings.security?.auth;
|
|
601
|
+
if (auth?.selectedType === 'gemini-api-key') {
|
|
602
|
+
delete auth.selectedType;
|
|
603
|
+
if (Object.keys(auth).length === 0) delete settings.security.auth;
|
|
604
|
+
if (Object.keys(settings.security).length === 0) delete settings.security;
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
}
|
|
603
608
|
return;
|
|
604
609
|
}
|
|
605
610
|
|
|
@@ -611,22 +616,13 @@ function writeGeminiConfigForMultiChannel(allChannels) {
|
|
|
611
616
|
|
|
612
617
|
// 确保 settings.json 存在并配置正确的认证模式
|
|
613
618
|
const settingsPath = path.join(geminiDir, 'settings.json');
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
// 设置认证模式为 gemini-api-key(第三方 API)
|
|
625
|
-
settings.security = settings.security || {};
|
|
626
|
-
settings.security.auth = settings.security.auth || {};
|
|
627
|
-
settings.security.auth.selectedType = 'gemini-api-key';
|
|
628
|
-
|
|
629
|
-
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
|
|
619
|
+
updateJsoncFile(settingsPath, (settings) => {
|
|
620
|
+
settings.security = settings.security && typeof settings.security === 'object' ? settings.security : {};
|
|
621
|
+
settings.security.auth = settings.security.auth && typeof settings.security.auth === 'object'
|
|
622
|
+
? settings.security.auth
|
|
623
|
+
: {};
|
|
624
|
+
settings.security.auth.selectedType = 'gemini-api-key';
|
|
625
|
+
});
|
|
630
626
|
}
|
|
631
627
|
|
|
632
628
|
// 获取所有启用的渠道(供调度器使用)
|
|
@@ -682,7 +678,7 @@ function disableAllChannels() {
|
|
|
682
678
|
data.channels.forEach(ch => { ch.enabled = false; });
|
|
683
679
|
saveChannels(data);
|
|
684
680
|
if (!getGeminiProxyState().running) {
|
|
685
|
-
|
|
681
|
+
writeGeminiConfigForMultiChannel(data.channels);
|
|
686
682
|
}
|
|
687
683
|
}
|
|
688
684
|
|
|
@@ -691,11 +687,7 @@ function readGeminiSettings() {
|
|
|
691
687
|
if (!settingsPath || !fs.existsSync(settingsPath)) {
|
|
692
688
|
return {};
|
|
693
689
|
}
|
|
694
|
-
|
|
695
|
-
return JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
696
|
-
} catch {
|
|
697
|
-
return {};
|
|
698
|
-
}
|
|
690
|
+
return readJsoncFile(settingsPath);
|
|
699
691
|
}
|
|
700
692
|
|
|
701
693
|
function normalizeGeminiChannel(channel = {}) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { NATIVE_PATHS } = require('../../../config/paths');
|
|
4
|
+
const { readJsoncFile, writeJsoncFile, updateEnvFile } = require('../../../utils/native-config-patcher');
|
|
4
5
|
const DEFAULT_NATIVE_PATHS = NATIVE_PATHS.gemini;
|
|
5
6
|
let nativePaths = DEFAULT_NATIVE_PATHS;
|
|
6
7
|
|
|
@@ -64,25 +65,14 @@ function readEnv() {
|
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
67
|
|
|
67
|
-
// 将环境对象转换为 .env 字符串
|
|
68
|
-
function envToString(env) {
|
|
69
|
-
let content = '';
|
|
70
|
-
for (const [key, value] of Object.entries(env)) {
|
|
71
|
-
content += `${key}=${value}\n`;
|
|
72
|
-
}
|
|
73
|
-
return content;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
68
|
// 写入 .env
|
|
77
69
|
function writeEnv(env) {
|
|
78
70
|
try {
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
fs.chmodSync(getEnvPath(), 0o600);
|
|
85
|
-
}
|
|
71
|
+
const managedKeys = ['GOOGLE_GEMINI_BASE_URL', 'GEMINI_API_KEY', 'GEMINI_MODEL'];
|
|
72
|
+
updateEnvFile(getEnvPath(), Object.fromEntries(managedKeys.map(key => [
|
|
73
|
+
key,
|
|
74
|
+
Object.prototype.hasOwnProperty.call(env || {}, key) ? env[key] : undefined
|
|
75
|
+
])), { atomic: true, mode: 0o600 });
|
|
86
76
|
} catch (err) {
|
|
87
77
|
throw new Error('Failed to write .env: ' + err.message);
|
|
88
78
|
}
|
|
@@ -94,8 +84,7 @@ function readSettings() {
|
|
|
94
84
|
if (!settingsExists()) {
|
|
95
85
|
return {};
|
|
96
86
|
}
|
|
97
|
-
|
|
98
|
-
return JSON.parse(content);
|
|
87
|
+
return readJsoncFile(getSettingsPath());
|
|
99
88
|
} catch (err) {
|
|
100
89
|
throw new Error('Failed to read settings.json: ' + err.message);
|
|
101
90
|
}
|
|
@@ -104,8 +93,7 @@ function readSettings() {
|
|
|
104
93
|
// 写入 settings.json
|
|
105
94
|
function writeSettings(settings) {
|
|
106
95
|
try {
|
|
107
|
-
|
|
108
|
-
fs.writeFileSync(getSettingsPath(), content, 'utf8');
|
|
96
|
+
writeJsoncFile(getSettingsPath(), settings);
|
|
109
97
|
} catch (err) {
|
|
110
98
|
throw new Error('Failed to write settings.json: ' + err.message);
|
|
111
99
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { createNativeSnapshotMethods } = require('./native-config-snapshot');
|
|
4
|
+
|
|
5
|
+
function createDriver(context = {}) {
|
|
6
|
+
return {
|
|
7
|
+
platform: context.platform,
|
|
8
|
+
capability: 'nativeConfig',
|
|
9
|
+
...context,
|
|
10
|
+
...createNativeSnapshotMethods({}, {
|
|
11
|
+
platform: context.platform,
|
|
12
|
+
runtime: context.runtime,
|
|
13
|
+
manifest: context.manifest,
|
|
14
|
+
pathContext: context.pathContext,
|
|
15
|
+
paths: context.paths
|
|
16
|
+
})
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = { createDriver };
|
|
@@ -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)
|
|
@@ -1,9 +1,65 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { ok, failed } = require('../../../shared/driver-result');
|
|
6
|
+
const { getOmpPaths } = require('./config');
|
|
7
|
+
|
|
8
|
+
const MANAGED_PROMPT_NAMESPACE = 'coding-tool-x';
|
|
9
|
+
const MANAGED_PROMPT_FILE = 'prompt.md';
|
|
10
|
+
|
|
11
|
+
function resolvePromptsDir(context = {}) {
|
|
12
|
+
const native = context.pathContext?.native || {};
|
|
13
|
+
return native.prompts
|
|
14
|
+
|| context.paths?.prompts
|
|
15
|
+
|| getOmpPaths(process.env, { resolveRuntime: false }).prompts;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function stripFrontmatter(content) {
|
|
19
|
+
return String(content || '').replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n(?:\r?\n)?/, '');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function buildTemplateContent(content) {
|
|
23
|
+
return `---\ndescription: "coding-tool-x prompt"\n---\n\n${content}`;
|
|
24
|
+
}
|
|
4
25
|
|
|
5
26
|
function createDriver(context = {}) {
|
|
6
|
-
|
|
27
|
+
const promptsDir = resolvePromptsDir(context);
|
|
28
|
+
const managedDir = path.join(promptsDir, MANAGED_PROMPT_NAMESPACE);
|
|
29
|
+
const promptPath = path.join(managedDir, MANAGED_PROMPT_FILE);
|
|
30
|
+
|
|
31
|
+
const invoke = (operation, args) => {
|
|
32
|
+
try {
|
|
33
|
+
if (operation === 'read') {
|
|
34
|
+
return ok('omp', 'prompts', operation,
|
|
35
|
+
fs.existsSync(promptPath) ? stripFrontmatter(fs.readFileSync(promptPath, 'utf8')) : '');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (operation === 'write') {
|
|
39
|
+
const [content] = args;
|
|
40
|
+
if (typeof content !== 'string') throw new Error('提示词内容必须是字符串');
|
|
41
|
+
fs.mkdirSync(managedDir, { recursive: true });
|
|
42
|
+
fs.writeFileSync(promptPath, buildTemplateContent(content), 'utf8');
|
|
43
|
+
return ok('omp', 'prompts', operation, content);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (fs.existsSync(managedDir)) {
|
|
47
|
+
fs.rmSync(managedDir, { recursive: true, force: true });
|
|
48
|
+
}
|
|
49
|
+
return ok('omp', 'prompts', operation, true);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
return failed('omp', 'prompts', operation, error);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
platform: 'omp',
|
|
57
|
+
capability: 'prompts',
|
|
58
|
+
path: managedDir,
|
|
59
|
+
read: (...args) => invoke('read', args),
|
|
60
|
+
write: (...args) => invoke('write', args),
|
|
61
|
+
remove: (...args) => invoke('remove', args)
|
|
62
|
+
};
|
|
7
63
|
}
|
|
8
64
|
|
|
9
65
|
module.exports = { createDriver };
|
|
@@ -15,6 +15,7 @@ const {
|
|
|
15
15
|
const { createOmpGateway } = require('./gateway');
|
|
16
16
|
const { prepareManagedOmpChannels } = require('./gateway-routing');
|
|
17
17
|
const { probeOmpAuthGateways } = require('./auth-gateway-client');
|
|
18
|
+
const { restoreStoredOmpOAuthCredentials } = require('../../oauth-credentials-service');
|
|
18
19
|
|
|
19
20
|
let currentPort = null;
|
|
20
21
|
let lastSyncResult = null;
|
|
@@ -33,11 +34,24 @@ function withLifecycleLock(operation) {
|
|
|
33
34
|
|
|
34
35
|
async function startOmpProxyServerUnlocked(options = {}) {
|
|
35
36
|
if (gateway.status().listening) {
|
|
37
|
+
const oauthRestore = restoreStoredOmpOAuthCredentials(getEnabledChannels());
|
|
38
|
+
if (oauthRestore.warnings.length > 0) {
|
|
39
|
+
lastSyncResult = {
|
|
40
|
+
...(lastSyncResult || {}),
|
|
41
|
+
warnings: [
|
|
42
|
+
...(lastSyncResult?.warnings || []),
|
|
43
|
+
...oauthRestore.warnings
|
|
44
|
+
]
|
|
45
|
+
};
|
|
46
|
+
}
|
|
36
47
|
return {
|
|
37
48
|
success: true,
|
|
38
49
|
port: gateway.status().port,
|
|
39
50
|
sync: lastSyncResult,
|
|
40
|
-
warnings:
|
|
51
|
+
warnings: [
|
|
52
|
+
...(lastSyncResult?.warnings || []),
|
|
53
|
+
...(oauthRestore.warnings || [])
|
|
54
|
+
]
|
|
41
55
|
};
|
|
42
56
|
}
|
|
43
57
|
const preserveStartTime = options.preserveStartTime || false;
|
|
@@ -56,6 +70,7 @@ async function startOmpProxyServerUnlocked(options = {}) {
|
|
|
56
70
|
});
|
|
57
71
|
currentPort = gatewayStatus.port;
|
|
58
72
|
try {
|
|
73
|
+
const oauthRestore = restoreStoredOmpOAuthCredentials(channels);
|
|
59
74
|
const oauthSupport = await probeOmpAuthGateways(channels);
|
|
60
75
|
const descriptor = {
|
|
61
76
|
...gateway.descriptor(),
|
|
@@ -71,10 +86,19 @@ async function startOmpProxyServerUnlocked(options = {}) {
|
|
|
71
86
|
lastSyncResult = {
|
|
72
87
|
...lastSyncResult,
|
|
73
88
|
warnings: [
|
|
89
|
+
...(oauthRestore.warnings || []),
|
|
74
90
|
...(lastSyncResult?.warnings || []),
|
|
75
91
|
...oauthSupport.warnings
|
|
76
92
|
]
|
|
77
93
|
};
|
|
94
|
+
} else if (oauthRestore.warnings.length > 0) {
|
|
95
|
+
lastSyncResult = {
|
|
96
|
+
...lastSyncResult,
|
|
97
|
+
warnings: [
|
|
98
|
+
...(oauthRestore.warnings || []),
|
|
99
|
+
...(lastSyncResult?.warnings || [])
|
|
100
|
+
]
|
|
101
|
+
};
|
|
78
102
|
}
|
|
79
103
|
} catch (error) {
|
|
80
104
|
if (wasEnabled) {
|