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
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const { createDriver: createNativeConfigDriver } = require('./native-config');
|
|
4
4
|
const { createDriver: createProjectsDriver } = require('./projects');
|
|
5
5
|
const { createDriver: createSessionsDriver } = require('./sessions');
|
|
6
|
-
const { listProfiles,
|
|
6
|
+
const { listProfiles, listManageableProfilePlugins, listProfileCapabilities, listProfileMcp, listProfilePrompts, listProfileAgentPresets, restoreAgentPresetRedactions, listPlugins, upsertProfilePatchRow, deleteProfilePatchRow } = require('./common');
|
|
7
7
|
const { listSkills, getSkill, createSkill, updateSkill, deleteSkill } = require('./resources');
|
|
8
8
|
const { installPlugin, uninstallPlugin, updatePlugin } = require('./plugin-manager');
|
|
9
9
|
const { createDriver: createChannelsDriver } = require('./channels');
|
|
@@ -53,6 +53,25 @@ function promptConfigFromRequest(request = {}) {
|
|
|
53
53
|
return config;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
function agentPresetConfigFromRequest(request = {}) {
|
|
57
|
+
const body = request.body || {};
|
|
58
|
+
const source = body.config && typeof body.config === 'object' && !Array.isArray(body.config) ? body.config : body;
|
|
59
|
+
const id = String(source.id || '').trim();
|
|
60
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(id)) throw new Error('Agent preset id must use lowercase letters, numbers, and hyphens');
|
|
61
|
+
if (!Array.isArray(source.plugins) || source.plugins.some(plugin => !plugin || typeof plugin !== 'object' || Array.isArray(plugin))) {
|
|
62
|
+
throw new Error('Agent preset plugins must be an array of plugin declarations');
|
|
63
|
+
}
|
|
64
|
+
const config = {
|
|
65
|
+
id,
|
|
66
|
+
plugins: source.plugins
|
|
67
|
+
};
|
|
68
|
+
for (const key of ['name', 'description']) {
|
|
69
|
+
if (typeof source[key] === 'string' && source[key].trim()) config[key] = source[key].trim();
|
|
70
|
+
}
|
|
71
|
+
if (Number.isFinite(Number(source.order))) config.order = Number(source.order);
|
|
72
|
+
return config;
|
|
73
|
+
}
|
|
74
|
+
|
|
56
75
|
function patchResult(context, request, profileName, operation, patch) {
|
|
57
76
|
return {
|
|
58
77
|
profile: profileName,
|
|
@@ -211,7 +230,7 @@ function createDriver(context = {}) {
|
|
|
211
230
|
driver.listPlugins = request => resultFor(request, 'ok', listPlugins(context));
|
|
212
231
|
driver.listProfilePlugins = async (request = {}) => {
|
|
213
232
|
const profile = request.params?.profileName;
|
|
214
|
-
const result =
|
|
233
|
+
const result = listManageableProfilePlugins(context, profile);
|
|
215
234
|
return result ? resultFor(request, 'ok', result) : resultFor(request, 'unsupported');
|
|
216
235
|
};
|
|
217
236
|
driver.listProfileCapabilities = async (request = {}) => {
|
|
@@ -229,6 +248,45 @@ function createDriver(context = {}) {
|
|
|
229
248
|
const result = listProfilePrompts(context, profile);
|
|
230
249
|
return result ? resultFor(request, 'ok', result) : resultFor(request, 'unsupported');
|
|
231
250
|
};
|
|
251
|
+
driver.listProfileAgentPresets = async (request = {}) => {
|
|
252
|
+
const result = listProfileAgentPresets(context, request.params?.profileName);
|
|
253
|
+
return result ? resultFor(request, 'ok', result) : resultFor(request, 'unsupported');
|
|
254
|
+
};
|
|
255
|
+
driver.upsertAgentPreset = async (request = {}) => {
|
|
256
|
+
const profileName = request.params?.profileName;
|
|
257
|
+
const requestedConfig = agentPresetConfigFromRequest(request);
|
|
258
|
+
const config = restoreAgentPresetRedactions(context, profileName, `preset-${requestedConfig.id}`, requestedConfig);
|
|
259
|
+
const id = `preset-${config.id}`;
|
|
260
|
+
const patch = requirePatchResult(upsertProfilePatchRow(context, profileName, {
|
|
261
|
+
id,
|
|
262
|
+
name: '@deepseek-ai/dsh-agent-preset',
|
|
263
|
+
config
|
|
264
|
+
}, profilePatchOptions(request)), profileName);
|
|
265
|
+
return resultFor(request, 'ok', {
|
|
266
|
+
profile: profileName,
|
|
267
|
+
operation: 'upsertAgentPreset',
|
|
268
|
+
patch,
|
|
269
|
+
...listProfileAgentPresets(context, profileName)
|
|
270
|
+
});
|
|
271
|
+
};
|
|
272
|
+
driver.deleteAgentPreset = async (request = {}) => {
|
|
273
|
+
const profileName = request.params?.profileName;
|
|
274
|
+
const id = requiredId(request.params?.presetId, 'Agent preset id');
|
|
275
|
+
const current = listProfileAgentPresets(context, profileName);
|
|
276
|
+
if (!current) return resultFor(request, 'unsupported');
|
|
277
|
+
const preset = current.presets.find(item => item.id === id);
|
|
278
|
+
if (!preset) return resultFor(request, 'ok', current);
|
|
279
|
+
if (preset.builtIn) {
|
|
280
|
+
return resultFor(request, 'failed', undefined, 'Built-in Agent presets can be customized but cannot be deleted');
|
|
281
|
+
}
|
|
282
|
+
const patch = requirePatchResult(deleteProfilePatchRow(context, profileName, preset.patchId, profilePatchOptions(request)), profileName);
|
|
283
|
+
return resultFor(request, 'ok', {
|
|
284
|
+
profile: profileName,
|
|
285
|
+
operation: 'deleteAgentPreset',
|
|
286
|
+
patch,
|
|
287
|
+
...listProfileAgentPresets(context, profileName)
|
|
288
|
+
});
|
|
289
|
+
};
|
|
232
290
|
driver.installPlugin = async (request = {}) => {
|
|
233
291
|
const result = await installPlugin(context, request);
|
|
234
292
|
return resultFor(request, 'ok', result);
|
|
@@ -34,6 +34,45 @@ function clone(value) {
|
|
|
34
34
|
return JSON.parse(JSON.stringify(value));
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
function resolveDshInstallRoot(context = {}) {
|
|
38
|
+
const command = String(
|
|
39
|
+
context.dshCommand
|
|
40
|
+
|| context.dependencies?.dshCommand
|
|
41
|
+
|| process.env.DSH_COMMAND
|
|
42
|
+
|| 'dsh'
|
|
43
|
+
).trim();
|
|
44
|
+
if (!command) return null;
|
|
45
|
+
const commandHasPath = path.isAbsolute(command) || command.includes(path.sep);
|
|
46
|
+
if (!commandHasPath && /\s/.test(command)) return null;
|
|
47
|
+
|
|
48
|
+
const candidates = commandHasPath
|
|
49
|
+
? [path.resolve(command)]
|
|
50
|
+
: String(context.dshEnv?.PATH || process.env.PATH || '')
|
|
51
|
+
.split(path.delimiter)
|
|
52
|
+
.filter(Boolean)
|
|
53
|
+
.map(directory => path.join(directory, command));
|
|
54
|
+
|
|
55
|
+
for (const candidate of candidates) {
|
|
56
|
+
let executablePath;
|
|
57
|
+
try {
|
|
58
|
+
fs.accessSync(candidate, fs.constants.X_OK);
|
|
59
|
+
executablePath = fs.realpathSync(candidate);
|
|
60
|
+
} catch (_) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let directory = path.dirname(executablePath);
|
|
65
|
+
for (let depth = 0; depth < 8; depth += 1) {
|
|
66
|
+
const packageJson = readJsonFile(path.join(directory, 'package.json'), null);
|
|
67
|
+
if (packageJson?.name === '@deepseek-ai/dsh') return directory;
|
|
68
|
+
const parent = path.dirname(directory);
|
|
69
|
+
if (parent === directory) break;
|
|
70
|
+
directory = parent;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
37
76
|
function resolvePaths(context = {}) {
|
|
38
77
|
const paths = context.paths || context.pathContext?.native || {};
|
|
39
78
|
const home = path.resolve(paths.dir || paths.home || context.pathContext?.home || process.env.DSH_HOME || path.join(require('os').homedir(), '.dsh'));
|
|
@@ -46,7 +85,7 @@ function resolvePaths(context = {}) {
|
|
|
46
85
|
patch: paths.patch || path.join(home, 'cordis.patch.yml')
|
|
47
86
|
};
|
|
48
87
|
const installAnchor = paths.installAnchor || context.installAnchor || process.env.DSH_INSTALL_ANCHOR;
|
|
49
|
-
const installRoot = paths.installRoot || context.installRoot || process.env.DSH_INSTALL_ROOT;
|
|
88
|
+
const installRoot = paths.installRoot || context.installRoot || process.env.DSH_INSTALL_ROOT || resolveDshInstallRoot(context);
|
|
50
89
|
if (installAnchor) resolved.installAnchor = installAnchor;
|
|
51
90
|
if (installRoot) resolved.installRoot = installRoot;
|
|
52
91
|
return resolved;
|
|
@@ -246,13 +285,19 @@ function listProfilePlugins(context = {}, profileName) {
|
|
|
246
285
|
const bundleNames = Array.isArray(profileConfig.bundles) ? profileConfig.bundles : [];
|
|
247
286
|
const dependencyNames = Object.keys(isObject(manifest.dependencies) ? manifest.dependencies : {});
|
|
248
287
|
const names = [...new Set([...bundleNames, ...dependencyNames])];
|
|
249
|
-
const
|
|
288
|
+
const describePlugin = name => {
|
|
250
289
|
const packageDir = resolvePackageDir(profile.profileDir, name, paths);
|
|
251
290
|
const packageJsonPath = packageDir ? path.join(packageDir, 'package.json') : null;
|
|
252
291
|
const packageJson = packageJsonPath ? readJsonFile(packageJsonPath, {}) : {};
|
|
253
292
|
const bundle = isObject(packageJson.dsh?.bundle) ? packageJson.dsh.bundle : null;
|
|
254
|
-
const
|
|
255
|
-
|
|
293
|
+
const declaredPatches = Array.isArray(bundle?.patch)
|
|
294
|
+
? bundle.patch
|
|
295
|
+
: bundle?.patch ? [bundle.patch] : [];
|
|
296
|
+
const patchPaths = packageDir
|
|
297
|
+
? declaredPatches.filter(item => typeof item === 'string' && item.trim()).map(item => path.resolve(packageDir, item))
|
|
298
|
+
: [];
|
|
299
|
+
const patchPath = patchPaths[0] || null;
|
|
300
|
+
const patchExists = patchPaths.length > 0 && patchPaths.every(filePath => fs.existsSync(filePath));
|
|
256
301
|
return {
|
|
257
302
|
name,
|
|
258
303
|
requestedVersion: manifest.dependencies?.[name] || manifest.devDependencies?.[name] || null,
|
|
@@ -261,11 +306,31 @@ function listProfilePlugins(context = {}, profileName) {
|
|
|
261
306
|
bundle: Boolean(bundle),
|
|
262
307
|
patch: bundle?.patch || null,
|
|
263
308
|
patchPath,
|
|
309
|
+
patchPaths,
|
|
264
310
|
patchExists,
|
|
311
|
+
description: packageJson.description || '',
|
|
265
312
|
inBundleList: bundleNames.includes(name),
|
|
313
|
+
builtIn: isBuiltInProfilePackage(packageDir, name, profile.profileDir, paths),
|
|
266
314
|
management: 'ctx+dsh-plugin'
|
|
267
315
|
};
|
|
268
|
-
}
|
|
316
|
+
};
|
|
317
|
+
const declaredPlugins = names.map(describePlugin);
|
|
318
|
+
const builtinNames = bundledPluginNames(declaredPlugins);
|
|
319
|
+
const plugins = [...declaredPlugins];
|
|
320
|
+
for (const name of builtinNames) {
|
|
321
|
+
if (names.includes(name)) continue;
|
|
322
|
+
const plugin = describePlugin(name);
|
|
323
|
+
plugin.installed = true;
|
|
324
|
+
if (!plugin.builtIn) {
|
|
325
|
+
const packageDir = resolvePackageDir(profile.profileDir, name, paths);
|
|
326
|
+
const profileModules = path.join(profile.profileDir, 'node_modules');
|
|
327
|
+
const sharedProfileModules = path.join(paths.profiles, 'node_modules');
|
|
328
|
+
if (!packageDir || (!isPathInside(profileModules, packageDir) && !isPathInside(sharedProfileModules, packageDir))) {
|
|
329
|
+
plugin.builtIn = true;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
plugins.push(plugin);
|
|
333
|
+
}
|
|
269
334
|
return {
|
|
270
335
|
profile: profile.name,
|
|
271
336
|
profilePath: profile.profileDir,
|
|
@@ -276,14 +341,57 @@ function listProfilePlugins(context = {}, profileName) {
|
|
|
276
341
|
};
|
|
277
342
|
}
|
|
278
343
|
|
|
344
|
+
function isPathInside(parentPath, targetPath) {
|
|
345
|
+
if (!parentPath || !targetPath) return false;
|
|
346
|
+
const parent = path.resolve(parentPath);
|
|
347
|
+
const target = path.resolve(targetPath);
|
|
348
|
+
return target === parent || target.startsWith(`${parent}${path.sep}`);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function isBuiltInProfilePackage(packageDir, packageName, profileDir, paths) {
|
|
352
|
+
const profileModules = path.join(profileDir, 'node_modules');
|
|
353
|
+
const sharedProfileModules = path.join(paths.profiles, 'node_modules');
|
|
354
|
+
if (packageDir && (isPathInside(profileModules, packageDir) || isPathInside(sharedProfileModules, packageDir))) return false;
|
|
355
|
+
if (/^@deepseek-ai\/dsh-/.test(packageName)) return true;
|
|
356
|
+
if (!packageDir) return false;
|
|
357
|
+
return true;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function bundledPluginNames(plugins) {
|
|
361
|
+
const names = new Set();
|
|
362
|
+
for (const plugin of plugins) {
|
|
363
|
+
if (!plugin.inBundleList || !plugin.builtIn) continue;
|
|
364
|
+
for (const filePath of plugin.patchPaths || []) {
|
|
365
|
+
if (!fs.existsSync(filePath)) continue;
|
|
366
|
+
const rows = collectPatchRows(readYamlFile(filePath, []), [], filePath);
|
|
367
|
+
for (const row of rows) {
|
|
368
|
+
const match = typeof row.name === 'string'
|
|
369
|
+
? row.name.match(/^(@deepseek-ai\/[a-z0-9._-]+)(?:\/[a-z0-9._-]+)*$/i)
|
|
370
|
+
: null;
|
|
371
|
+
if (match) names.add(match[1]);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return names;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function listManageableProfilePlugins(context = {}, profileName) {
|
|
379
|
+
const result = listProfilePlugins(context, profileName);
|
|
380
|
+
if (!result) return null;
|
|
381
|
+
return {
|
|
382
|
+
...result,
|
|
383
|
+
plugins: result.plugins.filter(plugin => !plugin.builtIn)
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
|
|
279
387
|
function profilePatchFiles(context = {}, profileName) {
|
|
280
388
|
const paths = resolvePaths(context);
|
|
281
389
|
const pluginInfo = listProfilePlugins(context, profileName);
|
|
282
390
|
if (!pluginInfo) return null;
|
|
283
391
|
const profile = readProfileManifest(paths, profileName);
|
|
284
392
|
const bundleFiles = pluginInfo.plugins
|
|
285
|
-
.filter(plugin => plugin.inBundleList
|
|
286
|
-
.
|
|
393
|
+
.filter(plugin => plugin.inBundleList)
|
|
394
|
+
.flatMap(plugin => plugin.patchPaths || (plugin.patchPath ? [plugin.patchPath] : []));
|
|
287
395
|
const files = [...new Set([
|
|
288
396
|
...bundleFiles,
|
|
289
397
|
path.join(profile.profileDir, 'cordis.patch.yml'),
|
|
@@ -399,9 +507,11 @@ function upsertProfilePatchRow(context = {}, profileName, entry, options = {}) {
|
|
|
399
507
|
const id = patchEntryId(entry);
|
|
400
508
|
if (!id) throw new Error('DSH patch entry id is required');
|
|
401
509
|
return updateProfilePatch(context, profileName, (document, meta) => {
|
|
402
|
-
const
|
|
510
|
+
const hasComposedRowOutsideProfile = meta.composedRows.some(row => (
|
|
511
|
+
patchEntryId(row.entry) === id && path.resolve(row.source) !== path.resolve(meta.patchPath)
|
|
512
|
+
));
|
|
403
513
|
const withoutOwn = removeManagedRows(document, row => patchEntryId(row) === id);
|
|
404
|
-
if (
|
|
514
|
+
if (hasComposedRowOutsideProfile) withoutOwn.push(entry);
|
|
405
515
|
else withoutOwn.push({ insert: [entry] });
|
|
406
516
|
return withoutOwn;
|
|
407
517
|
}, options);
|
|
@@ -428,6 +538,7 @@ function collectPatchRows(value, rows = [], source = null, options = {}) {
|
|
|
428
538
|
id: entry.id || null,
|
|
429
539
|
name: entry.name || null,
|
|
430
540
|
disabled: entry.disabled === true,
|
|
541
|
+
hasDisabled: Object.prototype.hasOwnProperty.call(entry, 'disabled'),
|
|
431
542
|
hasConfig: Object.prototype.hasOwnProperty.call(entry, 'config'),
|
|
432
543
|
source
|
|
433
544
|
};
|
|
@@ -462,7 +573,20 @@ function effectivePatchRows(info) {
|
|
|
462
573
|
if (row.operation === 'delete') {
|
|
463
574
|
byId.delete(row.id);
|
|
464
575
|
} else {
|
|
465
|
-
byId.
|
|
576
|
+
const current = byId.get(row.id);
|
|
577
|
+
if (row.operation === 'insert' || !current) {
|
|
578
|
+
byId.set(row.id, row);
|
|
579
|
+
} else {
|
|
580
|
+
byId.set(row.id, {
|
|
581
|
+
...current,
|
|
582
|
+
...row,
|
|
583
|
+
name: row.name || current.name,
|
|
584
|
+
disabled: row.hasDisabled ? row.disabled : current.disabled,
|
|
585
|
+
hasDisabled: row.hasDisabled || current.hasDisabled,
|
|
586
|
+
config: row.hasConfig ? row.config : current.config,
|
|
587
|
+
hasConfig: row.hasConfig || current.hasConfig
|
|
588
|
+
});
|
|
589
|
+
}
|
|
466
590
|
}
|
|
467
591
|
}
|
|
468
592
|
}
|
|
@@ -545,10 +669,94 @@ function listPlugins(context = {}) {
|
|
|
545
669
|
return {
|
|
546
670
|
profiles: listProfiles(context).map(profile => listProfilePlugins(context, profile.name)).filter(Boolean),
|
|
547
671
|
management: 'ctx+dsh-plugin',
|
|
548
|
-
note: '
|
|
672
|
+
note: '内置插件只读展示;仅 Profile 安装的外置插件由此处管理。'
|
|
549
673
|
};
|
|
550
674
|
}
|
|
551
675
|
|
|
676
|
+
function listProfileAgentPresets(context = {}, profileName) {
|
|
677
|
+
const info = readProfilePatchContributions(context, profileName);
|
|
678
|
+
if (!info) return null;
|
|
679
|
+
const patchPath = path.join(info.profile.profileDir, 'cordis.patch.yml');
|
|
680
|
+
const bundledPatchPaths = new Set(info.pluginInfo.plugins
|
|
681
|
+
.filter(plugin => plugin.inBundleList && plugin.builtIn && plugin.patchPath)
|
|
682
|
+
.flatMap(plugin => plugin.patchPaths || [plugin.patchPath])
|
|
683
|
+
.map(filePath => path.resolve(filePath)));
|
|
684
|
+
const bundledPresetIds = new Set(info.contributions
|
|
685
|
+
.filter(contribution => bundledPatchPaths.has(path.resolve(contribution.path)))
|
|
686
|
+
.flatMap(contribution => contribution.rows)
|
|
687
|
+
.filter(row => row.name === '@deepseek-ai/dsh-agent-preset')
|
|
688
|
+
.map(row => row.id));
|
|
689
|
+
const presets = effectivePatchRows(info)
|
|
690
|
+
.filter(row => row.name === '@deepseek-ai/dsh-agent-preset' && isObject(row.config))
|
|
691
|
+
.map(row => ({
|
|
692
|
+
id: String(row.config.id || String(row.id || '').replace(/^preset-/, '')),
|
|
693
|
+
patchId: row.id,
|
|
694
|
+
name: typeof row.config.name === 'string' ? row.config.name : '',
|
|
695
|
+
description: typeof row.config.description === 'string' ? row.config.description : '',
|
|
696
|
+
order: Number.isFinite(row.config.order) ? row.config.order : 0,
|
|
697
|
+
plugins: Array.isArray(row.config.plugins) ? redactPatchConfig(row.config.plugins) : [],
|
|
698
|
+
builtIn: bundledPresetIds.has(row.id),
|
|
699
|
+
source: row.source
|
|
700
|
+
}))
|
|
701
|
+
.filter(preset => /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(preset.id));
|
|
702
|
+
return {
|
|
703
|
+
profile: info.pluginInfo.profile,
|
|
704
|
+
revision: fileRevision(patchPath),
|
|
705
|
+
presets,
|
|
706
|
+
management: 'ctx-profile-patch'
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
function effectiveRawPatchEntry(context = {}, profileName, entryId) {
|
|
711
|
+
const info = profilePatchFiles(context, profileName);
|
|
712
|
+
if (!info) return null;
|
|
713
|
+
const byId = new Map();
|
|
714
|
+
for (const filePath of info.files) {
|
|
715
|
+
if (!fs.existsSync(filePath)) continue;
|
|
716
|
+
for (const { operation, entry } of patchRowsFromDocument(readYamlFile(filePath, []))) {
|
|
717
|
+
const id = patchEntryId(entry);
|
|
718
|
+
if (!id) continue;
|
|
719
|
+
if (operation === 'delete') {
|
|
720
|
+
byId.delete(id);
|
|
721
|
+
continue;
|
|
722
|
+
}
|
|
723
|
+
const current = byId.get(id);
|
|
724
|
+
if (operation === 'insert' || !current) {
|
|
725
|
+
byId.set(id, { ...entry, source: filePath });
|
|
726
|
+
} else {
|
|
727
|
+
byId.set(id, { ...current, ...entry, id, source: filePath });
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
return byId.get(entryId) || null;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
function restoreRedactedValues(value, original) {
|
|
735
|
+
if (value === '[REDACTED]' || value === '[EXPRESSION]') return clone(original);
|
|
736
|
+
if (Array.isArray(value)) {
|
|
737
|
+
const originalArray = Array.isArray(original) ? original : [];
|
|
738
|
+
return value.map((entry, index) => {
|
|
739
|
+
const entryId = isObject(entry) ? entry.id : null;
|
|
740
|
+
const originalEntry = entryId
|
|
741
|
+
? originalArray.find(candidate => isObject(candidate) && candidate.id === entryId)
|
|
742
|
+
: originalArray[index];
|
|
743
|
+
return restoreRedactedValues(entry, originalEntry);
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
if (!isObject(value)) return value;
|
|
747
|
+
const originalObject = isObject(original) ? original : {};
|
|
748
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
|
|
749
|
+
key,
|
|
750
|
+
restoreRedactedValues(entry, originalObject[key])
|
|
751
|
+
]));
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
function restoreAgentPresetRedactions(context = {}, profileName, presetId, config) {
|
|
755
|
+
const original = effectiveRawPatchEntry(context, profileName, presetId);
|
|
756
|
+
if (!original || !isObject(original.config)) return config;
|
|
757
|
+
return restoreRedactedValues(config, original.config);
|
|
758
|
+
}
|
|
759
|
+
|
|
552
760
|
module.exports = {
|
|
553
761
|
clone,
|
|
554
762
|
resolvePaths,
|
|
@@ -562,9 +770,12 @@ module.exports = {
|
|
|
562
770
|
credentialMetadata,
|
|
563
771
|
listProfiles,
|
|
564
772
|
listProfilePlugins,
|
|
773
|
+
listManageableProfilePlugins,
|
|
565
774
|
listProfileCapabilities,
|
|
566
775
|
listProfileMcp,
|
|
567
776
|
listProfilePrompts,
|
|
777
|
+
listProfileAgentPresets,
|
|
778
|
+
restoreAgentPresetRedactions,
|
|
568
779
|
listPlugins,
|
|
569
780
|
profilePatchFiles,
|
|
570
781
|
readProfilePatchContributions,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const { writeYamlFile } = require('../../../utils/native-config-patcher');
|
|
5
|
-
const { resolvePaths, readYamlFile, mergeSettings, fileRevision, credentialMetadata, redactSecrets, listProfiles,
|
|
5
|
+
const { resolvePaths, readYamlFile, mergeSettings, fileRevision, credentialMetadata, redactSecrets, listProfiles, listManageableProfilePlugins, listProfileMcp, listProfilePrompts, DSH_YAML_SOURCE_TAGS } = require('./common');
|
|
6
6
|
const { resolveSkillRoots } = require('./resources');
|
|
7
7
|
|
|
8
8
|
function createDriver(context = {}) {
|
|
@@ -62,7 +62,7 @@ function createDriver(context = {}) {
|
|
|
62
62
|
cwd: request.query?.cwd
|
|
63
63
|
});
|
|
64
64
|
const profiles = listProfiles(context).map(profile => {
|
|
65
|
-
const plugins =
|
|
65
|
+
const plugins = listManageableProfilePlugins(context, profile.name);
|
|
66
66
|
const mcp = listProfileMcp(context, profile.name);
|
|
67
67
|
const prompts = listProfilePrompts(context, profile.name);
|
|
68
68
|
return {
|
|
@@ -37,6 +37,20 @@ function extractUsage(record = {}) {
|
|
|
37
37
|
);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
function hasCacheUsage(usage = {}) {
|
|
41
|
+
const aliases = [
|
|
42
|
+
'cached', 'cached_tokens', 'cachedTokens', 'cached_input_tokens', 'cachedInputTokens',
|
|
43
|
+
'cache_read_input_tokens', 'cacheReadInputTokens', 'cacheRead', 'cache_read', 'cachedContentTokenCount'
|
|
44
|
+
];
|
|
45
|
+
const hasField = source => aliases.some(key => (
|
|
46
|
+
source && Object.prototype.hasOwnProperty.call(source, key) && source[key] !== undefined && source[key] !== null
|
|
47
|
+
));
|
|
48
|
+
if (hasField(usage)) return true;
|
|
49
|
+
|
|
50
|
+
return [usage.input_tokens_details, usage.inputTokensDetails, usage.prompt_tokens_details, usage.promptTokensDetails, usage.usageMetadata]
|
|
51
|
+
.some(hasField) || Boolean(usage.cache && (usage.cache.read !== undefined || usage.cache.readTokens !== undefined));
|
|
52
|
+
}
|
|
53
|
+
|
|
40
54
|
function normalizeTimestamp(value) {
|
|
41
55
|
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
42
56
|
if (value > 1e11) return value;
|
|
@@ -75,6 +89,7 @@ function normalizeDshEvent(record = {}, filePath = '', index = 0) {
|
|
|
75
89
|
provider,
|
|
76
90
|
model,
|
|
77
91
|
tokens,
|
|
92
|
+
cacheUsageAvailable: hasCacheUsage(usage),
|
|
78
93
|
cost,
|
|
79
94
|
channelId: record.channelId || data.channelId || message.channelId || source.channelId,
|
|
80
95
|
channel: record.channel || data.channel || message.channel || provider
|
|
@@ -113,4 +128,4 @@ function createDriver({ nativeRoot, pathContext, paths, fsImpl = fs } = {}) {
|
|
|
113
128
|
};
|
|
114
129
|
}
|
|
115
130
|
|
|
116
|
-
module.exports = { createDriver, extractUsage, normalizeDshEvent };
|
|
131
|
+
module.exports = { createDriver, extractUsage, hasCacheUsage, normalizeDshEvent };
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const { execFile } = require('child_process');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { promisify } = require('util');
|
|
6
|
-
const { resolvePaths, listProfilePlugins, safeProfileName } = require('./common');
|
|
6
|
+
const { resolvePaths, listProfilePlugins, listManageableProfilePlugins, safeProfileName } = require('./common');
|
|
7
7
|
|
|
8
8
|
const execFileAsync = promisify(execFile);
|
|
9
9
|
|
|
@@ -25,6 +25,40 @@ function normalizeSpecs(value) {
|
|
|
25
25
|
return specs;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
function packageNameFromSpec(spec) {
|
|
29
|
+
const value = String(spec || '').trim();
|
|
30
|
+
if (!value || /^(?:https?:|git\+|github:|file:)/i.test(value)) return null;
|
|
31
|
+
const slash = value.lastIndexOf('/');
|
|
32
|
+
const versionSeparator = value.lastIndexOf('@');
|
|
33
|
+
if (versionSeparator > slash) return value.slice(0, versionSeparator);
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function profilePluginInventory(context, profileName) {
|
|
38
|
+
const plugins = listProfilePlugins(context, profileName);
|
|
39
|
+
if (!plugins) {
|
|
40
|
+
const error = new Error(`DSH profile not found: ${profileName}`);
|
|
41
|
+
error.statusCode = 404;
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
return plugins.plugins;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function requireExternalPluginSpecs(context, profileName, specs, { allowNew = false } = {}) {
|
|
48
|
+
const plugins = profilePluginInventory(context, profileName);
|
|
49
|
+
const byName = new Map(plugins.map(plugin => [plugin.name, plugin]));
|
|
50
|
+
const builtinNames = new Set(plugins.filter(plugin => plugin.builtIn).map(plugin => plugin.name));
|
|
51
|
+
for (const spec of specs) {
|
|
52
|
+
const name = packageNameFromSpec(spec);
|
|
53
|
+
if (name && builtinNames.has(name)) {
|
|
54
|
+
throw new Error(`DSH built-in plugin cannot be managed here: ${name}`);
|
|
55
|
+
}
|
|
56
|
+
if (!allowNew && (!name || !byName.has(name) || byName.get(name).builtIn)) {
|
|
57
|
+
throw new Error(`Only installed external DSH plugins can be managed here: ${spec}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
28
62
|
function resolveWorkingDirectory(paths, value) {
|
|
29
63
|
if (value === undefined || value === null || value === '') return paths.dir;
|
|
30
64
|
const cwd = path.resolve(String(value));
|
|
@@ -85,13 +119,16 @@ async function runDshPlugin(context = {}, profileName, args, options = {}) {
|
|
|
85
119
|
exitCode: 0,
|
|
86
120
|
stdout: redactCommandOutput(result.stdout),
|
|
87
121
|
stderr: redactCommandOutput(result.stderr),
|
|
88
|
-
plugins:
|
|
122
|
+
plugins: listManageableProfilePlugins(context, profile)
|
|
89
123
|
};
|
|
90
124
|
}
|
|
91
125
|
|
|
92
126
|
async function installPlugin(context = {}, request = {}) {
|
|
93
127
|
const body = request.body || {};
|
|
94
|
-
|
|
128
|
+
const profileName = request.params?.profileName;
|
|
129
|
+
const specs = normalizeSpecs(body.specs || body.spec || body.package);
|
|
130
|
+
requireExternalPluginSpecs(context, profileName, specs, { allowNew: true });
|
|
131
|
+
return runDshPlugin(context, profileName, ['add', ...specs], {
|
|
95
132
|
cwd: body.cwd
|
|
96
133
|
});
|
|
97
134
|
}
|
|
@@ -99,14 +136,35 @@ async function installPlugin(context = {}, request = {}) {
|
|
|
99
136
|
async function uninstallPlugin(context = {}, request = {}) {
|
|
100
137
|
const body = request.body || {};
|
|
101
138
|
const specs = normalizeSpecs(body.specs || body.spec || request.params?.pluginName);
|
|
102
|
-
|
|
139
|
+
const profileName = request.params?.profileName;
|
|
140
|
+
requireExternalPluginSpecs(context, profileName, specs);
|
|
141
|
+
return runDshPlugin(context, profileName, ['remove', ...specs], { cwd: body.cwd });
|
|
103
142
|
}
|
|
104
143
|
|
|
105
144
|
async function updatePlugin(context = {}, request = {}) {
|
|
106
145
|
const body = request.body || {};
|
|
107
146
|
const specs = body.specs || body.spec || body.package;
|
|
108
|
-
const
|
|
109
|
-
|
|
147
|
+
const profileName = request.params?.profileName;
|
|
148
|
+
const inventory = profilePluginInventory(context, profileName);
|
|
149
|
+
let targets;
|
|
150
|
+
if (specs === undefined) {
|
|
151
|
+
targets = inventory.filter(plugin => !plugin.builtIn).map(plugin => plugin.name);
|
|
152
|
+
} else {
|
|
153
|
+
targets = normalizeSpecs(specs);
|
|
154
|
+
requireExternalPluginSpecs(context, profileName, targets);
|
|
155
|
+
}
|
|
156
|
+
if (!targets.length) {
|
|
157
|
+
const profile = safeProfileName(profileName);
|
|
158
|
+
return {
|
|
159
|
+
profile,
|
|
160
|
+
exitCode: 0,
|
|
161
|
+
stdout: '',
|
|
162
|
+
stderr: '',
|
|
163
|
+
skipped: 'No installed external DSH plugins to update',
|
|
164
|
+
plugins: listManageableProfilePlugins(context, profile)
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
return runDshPlugin(context, profileName, ['update', ...targets], { cwd: body.cwd });
|
|
110
168
|
}
|
|
111
169
|
|
|
112
170
|
module.exports = {
|
|
@@ -24,7 +24,7 @@ const {
|
|
|
24
24
|
upsertSyncedChannels
|
|
25
25
|
} = require('../../../server/services/channel-sync-utils');
|
|
26
26
|
|
|
27
|
-
const OMP_THINKING_SUFFIX_RE = /:(minimal|low|medium|high|xhigh|off)
|
|
27
|
+
const OMP_THINKING_SUFFIX_RE = /:(minimal|low|medium|high|xhigh|max|off)$/i;
|
|
28
28
|
|
|
29
29
|
let configuredState = {
|
|
30
30
|
channels: PATHS.channels?.omp,
|
|
@@ -9,6 +9,7 @@ const { allocateChannel, releaseChannel } = require('../../../server/services/ch
|
|
|
9
9
|
const { recordSuccess, recordFailure } = require('../../../server/services/channel-health');
|
|
10
10
|
const { attachServerShutdownHandling, expediteServerShutdown } = require('../../../server/services/server-shutdown');
|
|
11
11
|
const { resolveOmpGatewayRoute } = require('./gateway-routing');
|
|
12
|
+
const { persistProxyRequestSnapshot } = require('../../../server/services/request-logger');
|
|
12
13
|
|
|
13
14
|
const RETRYABLE_STATUS = new Set([401, 403, 429, 502, 503, 504]);
|
|
14
15
|
const MAX_REQUEST_BYTES = 100 * 1024 * 1024;
|
|
@@ -157,7 +158,7 @@ function extractModelId(body, contentType = '') {
|
|
|
157
158
|
}
|
|
158
159
|
|
|
159
160
|
function normalizeModelId(value) {
|
|
160
|
-
return String(value || '').trim().replace(/:(minimal|low|medium|high|xhigh|off)
|
|
161
|
+
return String(value || '').trim().replace(/:(minimal|low|medium|high|xhigh|max|off)$/i, '');
|
|
161
162
|
}
|
|
162
163
|
|
|
163
164
|
function channelSupportsModel(channel, modelId) {
|
|
@@ -183,6 +184,17 @@ function writeJson(res, statusCode, payload) {
|
|
|
183
184
|
res.end(body);
|
|
184
185
|
}
|
|
185
186
|
|
|
187
|
+
function redactSensitiveQuery(path) {
|
|
188
|
+
const url = new URL(String(path || '/'), 'http://127.0.0.1');
|
|
189
|
+
for (const key of [...url.searchParams.keys()]) {
|
|
190
|
+
const normalizedKey = key.toLowerCase().replace(/_/g, '-');
|
|
191
|
+
if (/(^|[-])(api-?key|key|auth|authorization|oauth|access-token|token|secret|credential|password|signature|jwt|cookie|session)([-]|$)/i.test(normalizedKey)) {
|
|
192
|
+
url.searchParams.delete(key);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return `${url.pathname}${url.search}`;
|
|
196
|
+
}
|
|
197
|
+
|
|
186
198
|
function constantTimeEqual(left, right) {
|
|
187
199
|
const leftBuffer = Buffer.from(String(left || ''));
|
|
188
200
|
const rightBuffer = Buffer.from(String(right || ''));
|
|
@@ -465,6 +477,19 @@ function createOmpGateway(options = {}) {
|
|
|
465
477
|
}
|
|
466
478
|
route.upstreamPath = `${route.upstreamPath}${parsed.search}`;
|
|
467
479
|
|
|
480
|
+
persistProxyRequestSnapshot('omp', {
|
|
481
|
+
timestamp: Date.now(),
|
|
482
|
+
source: 'omp',
|
|
483
|
+
request: {
|
|
484
|
+
method: req.method,
|
|
485
|
+
headers: req.headers
|
|
486
|
+
},
|
|
487
|
+
route: {
|
|
488
|
+
providerApi: route.providerApi,
|
|
489
|
+
path: redactSensitiveQuery(route.upstreamPath)
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
|
|
468
493
|
inflightRequests++;
|
|
469
494
|
const abortController = new AbortController();
|
|
470
495
|
const abortDownstream = () => abortController.abort();
|
|
@@ -685,5 +710,6 @@ function createOmpGateway(options = {}) {
|
|
|
685
710
|
}
|
|
686
711
|
|
|
687
712
|
module.exports = {
|
|
688
|
-
createOmpGateway
|
|
713
|
+
createOmpGateway,
|
|
714
|
+
redactSensitiveQuery
|
|
689
715
|
};
|