coding-tool-x 3.9.0 → 3.9.2
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 +2 -0
- package/dist/web/assets/AgentsDrawer-Dubq1kQI.css +1 -0
- package/dist/web/assets/AgentsDrawer-x29vGIIi.js +1 -0
- package/dist/web/assets/{Analytics-BXRwqC-P.js → Analytics-BJbR6XJO.js} +1 -1
- package/dist/web/assets/{AssetPathField-B-LHvOxq.js → AssetPathField-2W-16zgR.js} +1 -1
- package/dist/web/assets/{ChatHistoryDrawer-CoHlGAxb.js → ChatHistoryDrawer-C1URzQ_4.js} +1 -1
- package/dist/web/assets/{CollapseItem-kJkiHxFl.js → CollapseItem-DjkOkbwE.js} +1 -1
- package/dist/web/assets/{CommandsDrawer-DLQrikcw.js → CommandsDrawer-DcnfrHC4.js} +1 -1
- package/dist/web/assets/{ConfigExportDrawer-BItwo0FQ.js → ConfigExportDrawer-CKwG_RGJ.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-R3YzzBT3.js → ConfigTemplates-C1tNpfzi.js} +1 -1
- package/dist/web/assets/{ConfigTemplatesDrawer-C_nPKrhx.js → ConfigTemplatesDrawer-gZNPbcUR.js} +1 -1
- package/dist/web/assets/{ConfigTemplatesPanel-BiwrNyYN.js → ConfigTemplatesPanel-C-Fy53Qo.js} +1 -1
- package/dist/web/assets/{FavoritesDrawer-BAJ-AOpN.js → FavoritesDrawer-Bp8FYHPk.js} +1 -1
- package/dist/web/assets/GatewayConvertDrawer-CvUkvNQy.css +1 -0
- package/dist/web/assets/GatewayConvertDrawer-j5IJzb2Y.js +1 -0
- package/dist/web/assets/Home-2MAvBnml.js +1 -0
- package/dist/web/assets/Home-BRNWE0Qy.css +1 -0
- package/dist/web/assets/{MarkdownEditor-D75GGt5X.js → MarkdownEditor-BA_PcLed.js} +1 -1
- package/dist/web/assets/{McpDrawer-xNzJtT4X.js → McpDrawer-DAep5PP_.js} +1 -1
- package/dist/web/assets/{McpFormDrawer-B240mJ5y.js → McpFormDrawer-BRm-hzv1.js} +1 -1
- package/dist/web/assets/{PluginManager-CgLjBdlr.js → PluginManager-C2yY8-FP.js} +1 -1
- package/dist/web/assets/{PluginsDrawer-DvJdwMXx.js → PluginsDrawer-CxKnWzTO.js} +1 -1
- package/dist/web/assets/{PluginsPanel-CzGMK-Ap.css → PluginsPanel-DsmiW9TU.css} +1 -1
- package/dist/web/assets/PluginsPanel-DtbXSoXu.js +1 -0
- package/dist/web/assets/{ProjectList-CRtc3Hhx.js → ProjectList-Cb25pa2s.js} +1 -1
- package/dist/web/assets/{PromptsDrawer-CvT9dA2N.js → PromptsDrawer-D39OJVRP.js} +1 -1
- package/dist/web/assets/{RecentSessionsDrawer-DM1gf584.js → RecentSessionsDrawer-DkCjc4kS.js} +1 -1
- package/dist/web/assets/{SessionCard-cZR2V4Br.js → SessionCard-5Mwig8zy.js} +1 -1
- package/dist/web/assets/{SessionList-D7-P4Zme.js → SessionList-DVkIlraF.js} +1 -1
- package/dist/web/assets/{SettingsDrawer-gW8kh-S8.js → SettingsDrawer-Cr9qZDgO.js} +1 -1
- package/dist/web/assets/{SkillManager-D0BlI7CG.js → SkillManager-D935sPSV.js} +1 -1
- package/dist/web/assets/{SkillsDrawer-BMB6uzqP.js → SkillsDrawer-D1_3j74b.js} +1 -1
- package/dist/web/assets/{SkillsPanel-BzR828De.js → SkillsPanel-6SehnNG8.js} +1 -1
- package/dist/web/assets/{Upload-3fNyOQrx.js → Upload-38eKB6wv.js} +1 -1
- package/dist/web/assets/{WorkspaceDrawer-DvtBA58Q.js → WorkspaceDrawer-Bkfmlr5M.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-BW5KtsHO.js → WorkspaceManager-DFVQ5YoE.js} +1 -1
- package/dist/web/assets/{WorkspacePanel-BC_xpAEZ.js → WorkspacePanel-CEnogXEv.js} +1 -1
- package/dist/web/assets/client-oU6RT-6I.js +1 -0
- package/dist/web/assets/dsh-agent-presets-DjJRVMT6.js +1 -0
- package/dist/web/assets/index-CCATqHAD.js +2 -0
- package/dist/web/assets/index-p-yggtLj.css +1 -0
- package/dist/web/assets/{platforms-DrzAtnjY.js → platforms-DILqyP72.js} +1 -1
- package/dist/web/assets/platforms-JV36QBCw.js +1 -0
- package/dist/web/assets/{project-config-BVt5s-pH.js → project-config-CzFKBw_m.js} +1 -1
- package/dist/web/assets/{sessions-CaWUaysM.js → sessions-WXODMvLY.js} +1 -1
- package/dist/web/assets/{sessions-53-Fr68H.js → sessions-wvj5oaYr.js} +1 -1
- package/dist/web/assets/{useResponsiveDrawer-Ckgj760z.js → useResponsiveDrawer-BWRBXImg.js} +1 -1
- package/dist/web/assets/{workspaces-DQuZd4qw.js → workspaces-D2yLnT-B.js} +1 -1
- package/dist/web/index.html +5 -5
- package/package.json +4 -4
- package/src/platforms/agents-service.js +50 -12
- package/src/platforms/drivers/dsh/api-operations.js +60 -2
- package/src/platforms/drivers/dsh/common.js +222 -11
- package/src/platforms/drivers/dsh/native-config.js +2 -2
- package/src/platforms/drivers/dsh/native-logs.js +16 -1
- package/src/platforms/drivers/dsh/plugin-manager.js +64 -6
- package/src/platforms/drivers/omp/channels-implementation.js +1 -1
- package/src/platforms/drivers/omp/gateway.js +28 -2
- package/src/platforms/drivers/omp/model-role-settings.js +235 -0
- package/src/platforms/drivers/opencode/conversion.js +60 -8
- package/src/platforms/manifest-schema.js +4 -0
- package/src/platforms/manifests/dsh.json +29 -4
- package/src/platforms/manifests/omp.json +5 -4
- package/src/platforms/registry.js +1 -0
- package/src/server/api/convert.js +26 -76
- package/src/server/api/platforms.js +1 -0
- package/src/server/api/settings.js +27 -0
- package/src/server/services/model-definition-schema.js +2 -2
- package/src/server/services/model-detector.js +13 -22
- package/src/server/services/native-log-observer.js +1 -0
- package/src/server/services/plugins-service.js +13 -14
- package/src/server/services/request-logger.js +95 -0
- package/src/server/services/usage-log-utils.js +2 -1
- package/dist/web/assets/AgentsDrawer-DooV5Qhx.js +0 -1
- package/dist/web/assets/AgentsDrawer-Pw2k-ZAa.css +0 -1
- package/dist/web/assets/GatewayConvertDrawer-Av3yzwhP.js +0 -1
- package/dist/web/assets/GatewayConvertDrawer-CuNlf7AC.css +0 -1
- package/dist/web/assets/Home-B01aL_SR.js +0 -1
- package/dist/web/assets/Home-CSuFuJ9Y.css +0 -1
- package/dist/web/assets/PluginsPanel-kX14E_DT.js +0 -1
- package/dist/web/assets/client-BgcNg4Bt.js +0 -1
- package/dist/web/assets/index-BFqnddoI.js +0 -2
- package/dist/web/assets/index-Bn8BUnwP.css +0 -1
- package/dist/web/assets/platforms-mqqe_82d.js +0 -1
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
getOmpPaths,
|
|
5
|
+
readOmpSettingsStrict,
|
|
6
|
+
readYamlFile,
|
|
7
|
+
writeOmpSettingsAtomic
|
|
8
|
+
} = require('./config');
|
|
9
|
+
const { getPlatformCatalog } = require('../../../server/services/platform-catalog');
|
|
10
|
+
|
|
11
|
+
const OMP_MODEL_ROLE_KEYS = Object.freeze([
|
|
12
|
+
'default',
|
|
13
|
+
'smol',
|
|
14
|
+
'slow',
|
|
15
|
+
'plan',
|
|
16
|
+
'commit',
|
|
17
|
+
'vision',
|
|
18
|
+
'designer',
|
|
19
|
+
'task',
|
|
20
|
+
'advisor',
|
|
21
|
+
'tiny'
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
const OMP_THINKING_LEVELS = new Set([
|
|
25
|
+
'minimal',
|
|
26
|
+
'low',
|
|
27
|
+
'medium',
|
|
28
|
+
'high',
|
|
29
|
+
'xhigh',
|
|
30
|
+
'max'
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
function isPlainObject(value) {
|
|
34
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function parseModelRoleSelector(value) {
|
|
38
|
+
const selector = typeof value === 'string' ? value.trim() : '';
|
|
39
|
+
const suffix = selector.match(/:(minimal|low|medium|high|xhigh|max)$/i);
|
|
40
|
+
if (!suffix) return { model: selector, thinkingLevel: '' };
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
model: selector.slice(0, -suffix[0].length),
|
|
44
|
+
thinkingLevel: suffix[1].toLowerCase()
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function normalizeProviderId(value = '') {
|
|
49
|
+
return String(value || '')
|
|
50
|
+
.trim()
|
|
51
|
+
.toLowerCase()
|
|
52
|
+
.replace(/[^a-z0-9_-]/g, '-')
|
|
53
|
+
.replace(/-+/g, '-')
|
|
54
|
+
.replace(/^-|-$/g, '')
|
|
55
|
+
|| 'coding-tool-x';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function readEnabledOmpChannels() {
|
|
59
|
+
const driver = getPlatformCatalog().driver('omp', 'channels');
|
|
60
|
+
const result = driver?.getEnabled?.();
|
|
61
|
+
if (!result || result.status !== 'ok') {
|
|
62
|
+
throw new Error(result?.error || 'Unable to read enabled OMP channels');
|
|
63
|
+
}
|
|
64
|
+
const channels = Array.isArray(result.data)
|
|
65
|
+
? result.data
|
|
66
|
+
: (Array.isArray(result.data?.channels) ? result.data.channels : []);
|
|
67
|
+
return channels.filter(channel => channel && channel.enabled !== false);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function modelIdFromConfig(value) {
|
|
71
|
+
const candidate = typeof value === 'string' ? value : (value?.id || value?.name || '');
|
|
72
|
+
return String(candidate || '').trim().replace(/:(minimal|low|medium|high|xhigh|max|off)$/i, '');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function uniqueModelIds(values = []) {
|
|
76
|
+
const seen = new Set();
|
|
77
|
+
const result = [];
|
|
78
|
+
for (const value of values) {
|
|
79
|
+
const modelId = modelIdFromConfig(value);
|
|
80
|
+
const key = modelId.toLowerCase();
|
|
81
|
+
if (!modelId || seen.has(key)) continue;
|
|
82
|
+
seen.add(key);
|
|
83
|
+
result.push(modelId);
|
|
84
|
+
}
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function getManagedProviderId(channel = {}) {
|
|
89
|
+
const rawId = channel.managedProviderId
|
|
90
|
+
|| channel.extra?.managedProviderId
|
|
91
|
+
|| channel.providerKey
|
|
92
|
+
|| channel.provider
|
|
93
|
+
|| channel.name
|
|
94
|
+
|| channel.id;
|
|
95
|
+
const normalizedId = normalizeProviderId(rawId);
|
|
96
|
+
return normalizedId.startsWith('ctx-') ? normalizedId : `ctx-${normalizedId}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function findConfiguredProviderId(providers, managedProviderId) {
|
|
100
|
+
const target = normalizeProviderId(managedProviderId);
|
|
101
|
+
return Object.keys(providers).find(providerId => normalizeProviderId(providerId) === target)
|
|
102
|
+
|| managedProviderId;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function collectNativeOAuthModelOptions(channel) {
|
|
106
|
+
const rawProviderId = String(channel.oauthProviderId || channel.authRef?.providerId || '').trim();
|
|
107
|
+
if (!rawProviderId) return [];
|
|
108
|
+
const providerId = normalizeProviderId(rawProviderId);
|
|
109
|
+
const allowedModels = Array.isArray(channel.allowedModels) ? channel.allowedModels : [];
|
|
110
|
+
const candidates = allowedModels.length > 0
|
|
111
|
+
? allowedModels
|
|
112
|
+
: [
|
|
113
|
+
...(Array.isArray(channel.models) ? channel.models : []),
|
|
114
|
+
channel.model
|
|
115
|
+
];
|
|
116
|
+
return uniqueModelIds(candidates).map(modelId => `${providerId}/${modelId}`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function collectManagedChannelModelOptions(channel, providers) {
|
|
120
|
+
const managedProviderId = getManagedProviderId(channel);
|
|
121
|
+
const configuredProviderId = findConfiguredProviderId(providers, managedProviderId);
|
|
122
|
+
const configuredModels = providers[configuredProviderId]?.models;
|
|
123
|
+
const channelModels = [
|
|
124
|
+
...(Array.isArray(channel.models) ? channel.models : []),
|
|
125
|
+
...(Array.isArray(channel.allowedModels) ? channel.allowedModels : []),
|
|
126
|
+
channel.model,
|
|
127
|
+
channel.speedTestModel,
|
|
128
|
+
...(Array.isArray(channel.modelBindings)
|
|
129
|
+
? channel.modelBindings.map(binding => binding?.modelId || binding?.id || binding)
|
|
130
|
+
: [])
|
|
131
|
+
];
|
|
132
|
+
const modelIds = uniqueModelIds(Array.isArray(configuredModels) && configuredModels.length > 0
|
|
133
|
+
? configuredModels
|
|
134
|
+
: channelModels);
|
|
135
|
+
return modelIds.map(modelId => `${configuredProviderId}/${modelId}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function collectModelOptions(modelsConfig = {}, channels = []) {
|
|
139
|
+
const options = new Set();
|
|
140
|
+
const providers = isPlainObject(modelsConfig.providers) ? modelsConfig.providers : {};
|
|
141
|
+
for (const channel of channels) {
|
|
142
|
+
const isNativeOAuth = channel.authMode === 'oauth' && !String(channel.baseUrl || '').trim();
|
|
143
|
+
const channelOptions = isNativeOAuth
|
|
144
|
+
? collectNativeOAuthModelOptions(channel)
|
|
145
|
+
: collectManagedChannelModelOptions(channel, providers);
|
|
146
|
+
channelOptions.forEach(option => options.add(option));
|
|
147
|
+
}
|
|
148
|
+
return [...options].sort((left, right) => left.localeCompare(right));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function readOmpModelRoleSettings() {
|
|
152
|
+
const settings = readOmpSettingsStrict();
|
|
153
|
+
const modelRoles = isPlainObject(settings.modelRoles) ? settings.modelRoles : {};
|
|
154
|
+
const roles = Object.fromEntries(OMP_MODEL_ROLE_KEYS.map((key) => [
|
|
155
|
+
key,
|
|
156
|
+
parseModelRoleSelector(modelRoles[key])
|
|
157
|
+
]));
|
|
158
|
+
const modelsConfig = readYamlFile(getOmpPaths().modelsYml, {});
|
|
159
|
+
const modelOptions = collectModelOptions(modelsConfig, readEnabledOmpChannels());
|
|
160
|
+
const optionSet = new Set(modelOptions);
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
roles,
|
|
164
|
+
modelOptions,
|
|
165
|
+
unavailableRoles: Object.fromEntries(Object.entries(roles)
|
|
166
|
+
.filter(([, role]) => role.model && !optionSet.has(role.model))
|
|
167
|
+
.map(([key]) => [key, true]))
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function updateOmpModelRoleSettings(input) {
|
|
172
|
+
if (!isPlainObject(input)) {
|
|
173
|
+
throw new Error('roles must be an object');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const settings = readOmpSettingsStrict();
|
|
177
|
+
const modelRoles = isPlainObject(settings.modelRoles) ? { ...settings.modelRoles } : {};
|
|
178
|
+
const currentModelRoles = isPlainObject(settings.modelRoles) ? settings.modelRoles : {};
|
|
179
|
+
const modelOptions = new Set(collectModelOptions(
|
|
180
|
+
readYamlFile(getOmpPaths().modelsYml, {}),
|
|
181
|
+
readEnabledOmpChannels()
|
|
182
|
+
));
|
|
183
|
+
for (const [key, role] of Object.entries(input)) {
|
|
184
|
+
if (!OMP_MODEL_ROLE_KEYS.includes(key)) {
|
|
185
|
+
throw new Error(`roles.${key} is not a supported OMP model role`);
|
|
186
|
+
}
|
|
187
|
+
if (!isPlainObject(role) || typeof role.model !== 'string') {
|
|
188
|
+
throw new Error(`roles.${key}.model must be a string`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const suppliedModel = role.model.trim();
|
|
192
|
+
if (suppliedModel.length > 500) {
|
|
193
|
+
throw new Error(`roles.${key}.model must be 500 characters or fewer`);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const suppliedThinkingLevel = role.thinkingLevel == null ? '' : role.thinkingLevel;
|
|
197
|
+
if (typeof suppliedThinkingLevel !== 'string'
|
|
198
|
+
|| (suppliedThinkingLevel && !OMP_THINKING_LEVELS.has(suppliedThinkingLevel))) {
|
|
199
|
+
throw new Error(`roles.${key}.thinkingLevel must be one of ${[...OMP_THINKING_LEVELS].join(', ')}`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (!suppliedModel) {
|
|
203
|
+
delete modelRoles[key];
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const parsed = parseModelRoleSelector(suppliedModel);
|
|
208
|
+
if (!parsed.model) {
|
|
209
|
+
throw new Error(`roles.${key}.model must include a model selector`);
|
|
210
|
+
}
|
|
211
|
+
const thinkingLevel = suppliedThinkingLevel || parsed.thinkingLevel;
|
|
212
|
+
const nextSelector = thinkingLevel ? `${parsed.model}:${thinkingLevel}` : parsed.model;
|
|
213
|
+
const currentSelector = String(currentModelRoles[key] || '').trim();
|
|
214
|
+
if (!modelOptions.has(parsed.model) && nextSelector !== currentSelector) {
|
|
215
|
+
throw new Error(`roles.${key}.model must use a model from an enabled OMP channel`);
|
|
216
|
+
}
|
|
217
|
+
modelRoles[key] = nextSelector;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (Object.keys(modelRoles).length > 0) settings.modelRoles = modelRoles;
|
|
221
|
+
else delete settings.modelRoles;
|
|
222
|
+
|
|
223
|
+
writeOmpSettingsAtomic(settings);
|
|
224
|
+
return readOmpModelRoleSettings();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
module.exports = {
|
|
228
|
+
OMP_MODEL_ROLE_KEYS,
|
|
229
|
+
OMP_THINKING_LEVELS,
|
|
230
|
+
collectModelOptions,
|
|
231
|
+
readEnabledOmpChannels,
|
|
232
|
+
parseModelRoleSelector,
|
|
233
|
+
readOmpModelRoleSettings,
|
|
234
|
+
updateOmpModelRoleSettings
|
|
235
|
+
};
|
|
@@ -2,22 +2,69 @@
|
|
|
2
2
|
|
|
3
3
|
function createDriver(context = {}) {
|
|
4
4
|
const converter = require('./gateway-converter');
|
|
5
|
+
const {
|
|
6
|
+
loadLatestProxyRequestHeaders,
|
|
7
|
+
loadLatestProxyRequestSnapshot
|
|
8
|
+
} = require('../../../server/services/request-logger');
|
|
9
|
+
const target = context.platform || 'opencode';
|
|
10
|
+
|
|
11
|
+
function getOmpTargetContext() {
|
|
12
|
+
const snapshot = loadLatestProxyRequestSnapshot('omp');
|
|
13
|
+
const providerApi = snapshot?.route?.providerApi;
|
|
14
|
+
const targetApi = providerApi === 'openai-completions'
|
|
15
|
+
? 'chat.completions'
|
|
16
|
+
: ['openai-responses', 'openai-codex-responses', 'azure-openai-responses'].includes(providerApi)
|
|
17
|
+
? 'responses'
|
|
18
|
+
: null;
|
|
19
|
+
return { snapshot, providerApi, targetApi };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function convertPayload({ sourceType, payload, options = {} } = {}) {
|
|
23
|
+
const ompContext = target === 'omp' ? getOmpTargetContext() : null;
|
|
24
|
+
if (ompContext && !ompContext.targetApi) {
|
|
25
|
+
throw new Error('The latest OMP request log does not identify a supported OpenAI-compatible provider API. Send an OMP request through an OpenAI-compatible route, then retry.');
|
|
26
|
+
}
|
|
27
|
+
const requestedTargetApi = options.targetApi || options.api;
|
|
28
|
+
if (ompContext && requestedTargetApi && requestedTargetApi !== ompContext.targetApi) {
|
|
29
|
+
throw new Error(`The latest OMP request uses ${ompContext.providerApi}; conversion only supports ${ompContext.targetApi}.`);
|
|
30
|
+
}
|
|
31
|
+
const result = converter.convertToOpenCodePayload({
|
|
32
|
+
sourceType,
|
|
33
|
+
payload,
|
|
34
|
+
options: ompContext ? { ...options, targetApi: ompContext.targetApi } : options
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
...result,
|
|
38
|
+
target,
|
|
39
|
+
...(ompContext?.snapshot?.route?.path ? { endpoint: ompContext.snapshot.route.path } : {}),
|
|
40
|
+
requestHeaders: loadLatestProxyRequestHeaders(target)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
5
44
|
return {
|
|
6
45
|
platform: context.platform,
|
|
7
46
|
capability: 'conversion',
|
|
8
47
|
formats() {
|
|
48
|
+
const ompContext = target === 'omp' ? getOmpTargetContext() : null;
|
|
49
|
+
const targetApis = target === 'omp'
|
|
50
|
+
? (ompContext.targetApi ? [ompContext.targetApi] : [])
|
|
51
|
+
: converter.SUPPORTED_TARGET_APIS;
|
|
9
52
|
return {
|
|
10
53
|
sourceTypes: converter.SUPPORTED_SOURCE_TYPES,
|
|
11
54
|
formats: converter.SUPPORTED_SOURCE_TYPES.map(type => ({
|
|
12
55
|
id: type,
|
|
13
56
|
name: type === 'claude' ? 'Claude Code' : type === 'codex' ? 'Codex' : 'Gemini'
|
|
14
57
|
})),
|
|
15
|
-
targetApis
|
|
16
|
-
defaultTargetApi:
|
|
17
|
-
endpoints:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
58
|
+
targetApis,
|
|
59
|
+
defaultTargetApi: targetApis[0] || null,
|
|
60
|
+
endpoints: ompContext
|
|
61
|
+
? (ompContext.targetApi && ompContext.snapshot?.route?.path
|
|
62
|
+
? { [ompContext.targetApi]: ompContext.snapshot.route.path }
|
|
63
|
+
: {})
|
|
64
|
+
: {
|
|
65
|
+
responses: '/v1/responses',
|
|
66
|
+
'chat.completions': '/v1/chat/completions'
|
|
67
|
+
}
|
|
21
68
|
};
|
|
22
69
|
},
|
|
23
70
|
getFormats() {
|
|
@@ -30,10 +77,15 @@ function createDriver(context = {}) {
|
|
|
30
77
|
codex: converter.convertCodexToOpenCodePayload,
|
|
31
78
|
gemini: converter.convertGeminiToOpenCodePayload
|
|
32
79
|
};
|
|
33
|
-
|
|
80
|
+
if (target === 'omp') return convertPayload({ sourceType, payload, options });
|
|
81
|
+
return {
|
|
82
|
+
...(handlers[sourceType] || converter.convertToOpenCodePayload)({ sourceType, payload, options }),
|
|
83
|
+
target,
|
|
84
|
+
requestHeaders: loadLatestProxyRequestHeaders(target)
|
|
85
|
+
};
|
|
34
86
|
},
|
|
35
87
|
convert({ sourceType, payload, options = {} } = {}) {
|
|
36
|
-
return
|
|
88
|
+
return convertPayload({ sourceType, payload, options });
|
|
37
89
|
}
|
|
38
90
|
};
|
|
39
91
|
}
|
|
@@ -68,6 +68,7 @@ const CAPABILITY_OPERATIONS = Object.freeze({
|
|
|
68
68
|
'listProfileCapabilities',
|
|
69
69
|
'listProfileMcp',
|
|
70
70
|
'listProfilePrompts',
|
|
71
|
+
'listProfileAgentPresets',
|
|
71
72
|
'installPlugin',
|
|
72
73
|
'uninstallPlugin',
|
|
73
74
|
'updatePlugin',
|
|
@@ -75,6 +76,8 @@ const CAPABILITY_OPERATIONS = Object.freeze({
|
|
|
75
76
|
'deleteMcp',
|
|
76
77
|
'upsertPrompt',
|
|
77
78
|
'deletePrompt',
|
|
79
|
+
'upsertAgentPreset',
|
|
80
|
+
'deleteAgentPreset',
|
|
78
81
|
'listSkills',
|
|
79
82
|
'getSkill',
|
|
80
83
|
'createSkill',
|
|
@@ -113,6 +116,7 @@ const schema = {
|
|
|
113
116
|
portLabel: { type: 'string', minLength: 1 },
|
|
114
117
|
apiBasePath: { type: 'string', minLength: 1 },
|
|
115
118
|
logFile: { type: 'string', minLength: 1 },
|
|
119
|
+
logLayout: { enum: ['basic'] },
|
|
116
120
|
logAliases: { type: 'array', items: { type: 'string', minLength: 1 } },
|
|
117
121
|
logNote: { type: 'string' },
|
|
118
122
|
logColor: { type: 'string', minLength: 1 },
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
"command": "dsh",
|
|
6
6
|
"iconToken": "terminal",
|
|
7
7
|
"color": "#2563eb",
|
|
8
|
+
"logLayout": "basic",
|
|
8
9
|
"defaultVisible": true,
|
|
9
10
|
"defaultEnabled": false,
|
|
10
11
|
"sortOrder": 50,
|
|
@@ -25,7 +26,7 @@
|
|
|
25
26
|
"resourceTypes": {
|
|
26
27
|
"skills": true,
|
|
27
28
|
"commands": false,
|
|
28
|
-
"agents":
|
|
29
|
+
"agents": true,
|
|
29
30
|
"plugins": true,
|
|
30
31
|
"mcp": true,
|
|
31
32
|
"prompts": true
|
|
@@ -38,10 +39,10 @@
|
|
|
38
39
|
"refresh": true
|
|
39
40
|
},
|
|
40
41
|
"plugins": {
|
|
41
|
-
"repositories":
|
|
42
|
+
"repositories": false,
|
|
42
43
|
"market": false,
|
|
43
|
-
"install":
|
|
44
|
-
"uninstall":
|
|
44
|
+
"install": true,
|
|
45
|
+
"uninstall": true,
|
|
45
46
|
"import": false,
|
|
46
47
|
"syncRepos": false
|
|
47
48
|
}
|
|
@@ -477,6 +478,30 @@
|
|
|
477
478
|
"request": "default",
|
|
478
479
|
"response": "default"
|
|
479
480
|
},
|
|
481
|
+
{
|
|
482
|
+
"method": "GET",
|
|
483
|
+
"path": "/profiles/:profileName/agent-presets",
|
|
484
|
+
"capability": "api",
|
|
485
|
+
"operation": "listProfileAgentPresets",
|
|
486
|
+
"request": "default",
|
|
487
|
+
"response": "default"
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
"method": "POST",
|
|
491
|
+
"path": "/profiles/:profileName/agent-presets",
|
|
492
|
+
"capability": "api",
|
|
493
|
+
"operation": "upsertAgentPreset",
|
|
494
|
+
"request": "default",
|
|
495
|
+
"response": "default"
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
"method": "DELETE",
|
|
499
|
+
"path": "/profiles/:profileName/agent-presets/:presetId",
|
|
500
|
+
"capability": "api",
|
|
501
|
+
"operation": "deleteAgentPreset",
|
|
502
|
+
"request": "default",
|
|
503
|
+
"response": "default"
|
|
504
|
+
},
|
|
480
505
|
{
|
|
481
506
|
"method": "GET",
|
|
482
507
|
"path": "/skills",
|
|
@@ -45,11 +45,11 @@
|
|
|
45
45
|
"resourceTypes": {
|
|
46
46
|
"skills": true,
|
|
47
47
|
"commands": true,
|
|
48
|
-
"agents":
|
|
48
|
+
"agents": true,
|
|
49
49
|
"plugins": true
|
|
50
50
|
},
|
|
51
51
|
"agentCapabilities": {
|
|
52
|
-
"projectScope":
|
|
52
|
+
"projectScope": true,
|
|
53
53
|
"repoOperations": true
|
|
54
54
|
},
|
|
55
55
|
"skillActivation": {
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
]
|
|
74
74
|
},
|
|
75
75
|
"agents": {
|
|
76
|
-
"canonicalRoot":
|
|
76
|
+
"canonicalRoot": ".omp/agents"
|
|
77
77
|
},
|
|
78
78
|
"commands": {
|
|
79
79
|
"canonicalRoot": ".omp/commands",
|
|
@@ -484,6 +484,7 @@
|
|
|
484
484
|
"hooks": "legacy:omp",
|
|
485
485
|
"resourceSync": "legacy:omp",
|
|
486
486
|
"mcp": "legacy:omp",
|
|
487
|
-
"prompts": "legacy:omp"
|
|
487
|
+
"prompts": "legacy:omp",
|
|
488
|
+
"conversion": "opencode-conversion"
|
|
488
489
|
}
|
|
489
490
|
}
|
|
@@ -188,6 +188,7 @@ function createPlatformRegistry({
|
|
|
188
188
|
portLabel: platform.portLabel || null,
|
|
189
189
|
apiPrefix: platform.api?.prefix || null,
|
|
190
190
|
modelCatalogKey: platform.modelConfig?.catalogKey || platform.key,
|
|
191
|
+
...(platform.logLayout ? { logLayout: platform.logLayout } : {}),
|
|
191
192
|
capabilities
|
|
192
193
|
};
|
|
193
194
|
const resourceTypes = publicResourceTypes(platform.resourceTypes);
|
|
@@ -1,40 +1,28 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
2
|
const router = express.Router();
|
|
3
3
|
const { getPlatformCatalog } = require('../services/platform-catalog');
|
|
4
|
+
const { createSameOriginGuard } = require('../services/network-access');
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
router.use(createSameOriginGuard({
|
|
7
|
+
message: '禁止跨站访问请求转换接口'
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
function getConversionDriver(target) {
|
|
11
|
+
return getPlatformCatalog().driver(target, 'conversion');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function getConversionFormats(driver) {
|
|
11
15
|
const formats = driver?.formats?.() || driver?.getFormats?.();
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
const sourceTypes = formats.sourceTypes || [];
|
|
16
|
-
res.json({
|
|
17
|
-
formats: sourceTypes.map(type => ({
|
|
18
|
-
id: type,
|
|
19
|
-
name: (formats.formats || []).find(item => item.id === type)?.name
|
|
20
|
-
|| String(type).replace(/[-_]/g, ' ').replace(/\b\w/g, char => char.toUpperCase()),
|
|
21
|
-
description: `${type} session format`,
|
|
22
|
-
extension: '.jsonl',
|
|
23
|
-
icon: type
|
|
24
|
-
})),
|
|
25
|
-
conversions: sourceTypes.flatMap(sourceType => sourceTypes
|
|
26
|
-
.filter(targetType => targetType !== sourceType)
|
|
27
|
-
.map(targetType => ({ from: sourceType, to: targetType })))
|
|
28
|
-
});
|
|
29
|
-
});
|
|
16
|
+
return formats;
|
|
17
|
+
}
|
|
30
18
|
|
|
31
19
|
/**
|
|
32
|
-
*
|
|
33
|
-
* GET /api/convert/
|
|
20
|
+
* Read conversion capabilities for a target CLI.
|
|
21
|
+
* GET /api/convert/:target/formats
|
|
34
22
|
*/
|
|
35
|
-
router.get('/
|
|
36
|
-
const driver = getConversionDriver();
|
|
37
|
-
const formats =
|
|
23
|
+
router.get('/:target/formats', (req, res) => {
|
|
24
|
+
const driver = getConversionDriver(req.params.target);
|
|
25
|
+
const formats = getConversionFormats(driver);
|
|
38
26
|
if (!formats) {
|
|
39
27
|
return res.status(404).json({ success: false, error: 'Conversion is not supported' });
|
|
40
28
|
}
|
|
@@ -45,62 +33,24 @@ router.get('/opencode/formats', (req, res) => {
|
|
|
45
33
|
name: (formats.formats || []).find(item => item.id === type)?.name
|
|
46
34
|
|| String(type).replace(/[-_]/g, ' ').replace(/\b\w/g, char => char.toUpperCase())
|
|
47
35
|
})),
|
|
48
|
-
target:
|
|
36
|
+
target: req.params.target,
|
|
49
37
|
targetApis: formats.targetApis || [],
|
|
50
38
|
defaultTargetApi: formats.defaultTargetApi,
|
|
51
|
-
endpoints: formats.endpoints || {}
|
|
52
|
-
sourceEndpoints: Object.fromEntries(sourceTypes.map(type => [type, `/api/convert/opencode/${type}`]))
|
|
39
|
+
endpoints: formats.endpoints || {}
|
|
53
40
|
});
|
|
54
41
|
});
|
|
55
42
|
|
|
56
|
-
function getConversionDriver() {
|
|
57
|
-
return getPlatformCatalog().driver('opencode', 'conversion');
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function handleOpenCodeConvert(req, res, sourceType) {
|
|
61
|
-
try {
|
|
62
|
-
const { payload, options = {} } = req.body || {};
|
|
63
|
-
const driver = getConversionDriver();
|
|
64
|
-
|
|
65
|
-
if (!driver) {
|
|
66
|
-
return res.status(404).json({ success: false, error: 'Conversion is not supported' });
|
|
67
|
-
}
|
|
68
|
-
if (!payload) {
|
|
69
|
-
return res.status(400).json({
|
|
70
|
-
success: false,
|
|
71
|
-
error: 'Missing required parameters: payload'
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
const result = driver.convertSource
|
|
75
|
-
? driver.convertSource(sourceType, payload, options)
|
|
76
|
-
: driver.convert({ sourceType, payload, options });
|
|
77
|
-
return res.json({
|
|
78
|
-
success: true,
|
|
79
|
-
...result
|
|
80
|
-
});
|
|
81
|
-
} catch (error) {
|
|
82
|
-
console.error(`[Convert API] OpenCode ${sourceType} convert error:`, error);
|
|
83
|
-
return res.status(500).json({
|
|
84
|
-
success: false,
|
|
85
|
-
error: error.message
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
router.post('/opencode/:sourceType', (req, res) => {
|
|
91
|
-
return handleOpenCodeConvert(req, res, req.params.sourceType);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
43
|
/**
|
|
95
|
-
*
|
|
96
|
-
* POST /api/convert
|
|
44
|
+
* Online conversion to a CLI-compatible request format.
|
|
45
|
+
* POST /api/convert/:target
|
|
97
46
|
* Body: { sourceType, payload, options? }
|
|
98
47
|
*/
|
|
99
|
-
router.post('
|
|
48
|
+
router.post('/:target', (req, res) => {
|
|
100
49
|
try {
|
|
101
50
|
const { sourceType, payload, options = {} } = req.body || {};
|
|
102
|
-
const driver = getConversionDriver();
|
|
103
|
-
const
|
|
51
|
+
const driver = getConversionDriver(req.params.target);
|
|
52
|
+
const formats = getConversionFormats(driver);
|
|
53
|
+
const sourceTypes = formats?.sourceTypes || [];
|
|
104
54
|
const normalized = driver?.normalizeSourceType?.(sourceType) || sourceType;
|
|
105
55
|
|
|
106
56
|
if (!driver) {
|
|
@@ -124,7 +74,7 @@ router.post('/opencode', (req, res) => {
|
|
|
124
74
|
...driver.convert({ sourceType: normalized, payload, options })
|
|
125
75
|
});
|
|
126
76
|
} catch (error) {
|
|
127
|
-
console.error(
|
|
77
|
+
console.error(`[Convert API] ${req.params.target} convert error:`, error);
|
|
128
78
|
return res.status(500).json({
|
|
129
79
|
success: false,
|
|
130
80
|
error: error.message
|
|
@@ -9,6 +9,10 @@ const {
|
|
|
9
9
|
} = require('../../config/model-metadata');
|
|
10
10
|
const { getPlatformCatalog } = require('../services/platform-catalog');
|
|
11
11
|
const { loadConfig, saveConfig } = require('../../config/loader');
|
|
12
|
+
const {
|
|
13
|
+
readOmpModelRoleSettings,
|
|
14
|
+
updateOmpModelRoleSettings
|
|
15
|
+
} = require('../../platforms/drivers/omp/model-role-settings');
|
|
12
16
|
const {
|
|
13
17
|
MODEL_SCHEMA_VERSION,
|
|
14
18
|
getPublicModelFieldSchema,
|
|
@@ -269,4 +273,27 @@ function handleDeleteModelOverride(req, res) {
|
|
|
269
273
|
router.delete('/model-settings/:modelId', handleDeleteModelOverride);
|
|
270
274
|
router.delete('/model-metadata/:modelId', handleDeleteModelOverride);
|
|
271
275
|
|
|
276
|
+
router.get('/omp-model-roles', (req, res) => {
|
|
277
|
+
try {
|
|
278
|
+
res.json({ success: true, ...readOmpModelRoleSettings() });
|
|
279
|
+
} catch (error) {
|
|
280
|
+
console.error('Error getting OMP model roles:', error);
|
|
281
|
+
res.status(500).json({ error: error.message });
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
router.put('/omp-model-roles', (req, res) => {
|
|
286
|
+
try {
|
|
287
|
+
const { roles } = req.body || {};
|
|
288
|
+
if (!roles || typeof roles !== 'object' || Array.isArray(roles)) {
|
|
289
|
+
return res.status(400).json({ error: 'roles must be an object' });
|
|
290
|
+
}
|
|
291
|
+
res.json({ success: true, ...updateOmpModelRoleSettings(roles) });
|
|
292
|
+
} catch (error) {
|
|
293
|
+
console.error('Error saving OMP model roles:', error);
|
|
294
|
+
const isValidationError = /^roles(?:\.| must be)/.test(error.message);
|
|
295
|
+
res.status(isValidationError ? 400 : 500).json({ error: error.message });
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
|
|
272
299
|
module.exports = router;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const MODEL_SCHEMA_VERSION = 2;
|
|
4
4
|
const MODEL_METADATA_MODES = ['auto', 'hybrid', 'manual'];
|
|
5
5
|
const THINKING_EFFORT_LEVELS = ['minimal', 'low', 'medium', 'high', 'xhigh'];
|
|
6
|
-
const THINKING_SELECTOR_LEVELS = [...THINKING_EFFORT_LEVELS, 'off'];
|
|
6
|
+
const THINKING_SELECTOR_LEVELS = [...THINKING_EFFORT_LEVELS, 'max', 'off'];
|
|
7
7
|
const THINKING_MODES = [
|
|
8
8
|
'effort',
|
|
9
9
|
'budget',
|
|
@@ -173,7 +173,7 @@ function compactObject(value = {}) {
|
|
|
173
173
|
|
|
174
174
|
function splitModelSelector(value = '') {
|
|
175
175
|
const raw = String(value || '').trim();
|
|
176
|
-
const match = raw.match(/:(minimal|low|medium|high|xhigh|off)$/i);
|
|
176
|
+
const match = raw.match(/:(minimal|low|medium|high|xhigh|max|off)$/i);
|
|
177
177
|
if (!match) return { modelId: raw, thinkingLevel: null };
|
|
178
178
|
return {
|
|
179
179
|
modelId: raw.slice(0, -match[0].length),
|