@xxxyz/dsh-mcp-manager 2.2.0 → 2.2.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/lib/client.js +8 -1
- package/lib/index.js +48 -9
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -404,6 +404,11 @@ const factory = (require) => {
|
|
|
404
404
|
collapsed ? null : pItems.map((s) => {
|
|
405
405
|
const overridden = s.provider === 'dsh-mcp-manager-override'
|
|
406
406
|
const available = !!(s.invocation && s.invocation.modelInvocable)
|
|
407
|
+
// User-level filesystem skills are view-only: they live in
|
|
408
|
+
// the scoped layer which a global-layer override cannot
|
|
409
|
+
// shadow, and the plugin sandbox blocks writing their
|
|
410
|
+
// SKILL.md — so no enable/disable button is offered.
|
|
411
|
+
const userLevel = s.source === 'user-dsh' || s.source === 'user-agents'
|
|
407
412
|
return React.createElement('div', { className: 'mcpm-row', key: s.name },
|
|
408
413
|
React.createElement('div', { className: 'mcpm-row-head' },
|
|
409
414
|
React.createElement('span', { className: 'mcpm-name' }, s.name),
|
|
@@ -412,7 +417,9 @@ const factory = (require) => {
|
|
|
412
417
|
React.createElement('span', { className: 'mcpm-chip live' }, s.provider)),
|
|
413
418
|
React.createElement('div', { className: 'mcpm-sub' }, s.description || ''),
|
|
414
419
|
React.createElement('div', { className: 'mcpm-row-actions' },
|
|
415
|
-
|
|
420
|
+
userLevel
|
|
421
|
+
? React.createElement('span', { className: 'mcpm-note' }, '仅查看(由 DSH 文件系统管理)')
|
|
422
|
+
: React.createElement('button', { className: 'mcpm-btn', disabled: busy === s.name, onClick: () => toggle(s) }, overridden ? '启用' : '禁用')))
|
|
416
423
|
}))
|
|
417
424
|
}))
|
|
418
425
|
}))
|
package/lib/index.js
CHANGED
|
@@ -115,14 +115,26 @@ export default {
|
|
|
115
115
|
for (const name of await readState()) {
|
|
116
116
|
if (overrideSkills.has(name))
|
|
117
117
|
continue;
|
|
118
|
-
|
|
118
|
+
// Pre-2.2.2 migration: user-level skills are view-only now. Drop any
|
|
119
|
+
// stale state entry without touching the file — the sandbox blocks
|
|
120
|
+
// writing ~/.dsh/skills from a profile plugin, and an override could
|
|
121
|
+
// never shadow the scoped-layer filesystem entry anyway.
|
|
122
|
+
const userDef = await findUserSkill(name);
|
|
123
|
+
if (userDef) {
|
|
124
|
+
changed = true;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const def = await ctx.skills.get(name);
|
|
119
128
|
if (!def)
|
|
120
129
|
continue;
|
|
121
130
|
overrideSkills.set(name, { ...def, provider: OVERRIDE_PROVIDER, invocation: { modelInvocable: false, userInvocable: false } });
|
|
122
131
|
changed = true;
|
|
123
132
|
}
|
|
124
|
-
if (changed
|
|
125
|
-
|
|
133
|
+
if (changed) {
|
|
134
|
+
await saveState([...overrideSkills.keys()]);
|
|
135
|
+
if (skillProviderControl)
|
|
136
|
+
skillProviderControl.invalidate();
|
|
137
|
+
}
|
|
126
138
|
}
|
|
127
139
|
;
|
|
128
140
|
ctx.on('skills/change', () => { void ensureRestored(); });
|
|
@@ -130,11 +142,15 @@ export default {
|
|
|
130
142
|
// dsh-skill-filesystem in the agent-preset SCOPED layer, which a scope-less
|
|
131
143
|
// ctx.skills.snapshot({}) (global layer only) never reaches — so the
|
|
132
144
|
// management page would silently hide them. Scan the directory ourselves
|
|
133
|
-
// and merge into skill-list.
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
//
|
|
145
|
+
// and merge into skill-list.
|
|
146
|
+
//
|
|
147
|
+
// Disable/enable is NOT offered for user skills: (a) a rank-0 override in
|
|
148
|
+
// the global layer cannot shadow the scoped layer's filesystem entry
|
|
149
|
+
// (collectFresh merges layers; the nearest layer wins); (b) writing the
|
|
150
|
+
// SKILL.md frontmatter is blocked by the fs sandbox (profile plugin,
|
|
151
|
+
// workspace-write mode — ~/.dsh/skills is outside its scope). Users
|
|
152
|
+
// disable such skills by editing `user-invocable: false` in the file
|
|
153
|
+
// directly. DSH_MCP_MANAGER_SKILLS_DIR overrides the directory for tests.
|
|
138
154
|
function userSkillsDir(home, sep) {
|
|
139
155
|
const env = process.env.DSH_MCP_MANAGER_SKILLS_DIR;
|
|
140
156
|
return env && env.trim() ? env.trim() : home + sep + 'skills';
|
|
@@ -275,6 +291,18 @@ export default {
|
|
|
275
291
|
const skills = snap.skills.concat(extra).sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
276
292
|
return { ok: true, skills, complete: snap.complete !== false };
|
|
277
293
|
}
|
|
294
|
+
// User-level skills (~/.dsh/skills) are VIEW-ONLY in this page. They are
|
|
295
|
+
// discovered by dsh-skill-filesystem in the agent-preset SCOPED layer; a
|
|
296
|
+
// scope-less snapshot({}) (global layer only) never sees them, so we scan
|
|
297
|
+
// the directory and merge rows into skill-list. Disable/enable is NOT
|
|
298
|
+
// supported for them: (a) the rank-0 override provider lives in the global
|
|
299
|
+
// layer and a scoped read merges the scope chain with the scoped entry
|
|
300
|
+
// winning the duplicate name outright (collectFresh last-wins), so an
|
|
301
|
+
// override cannot hide them from the / menu; (b) rewriting the SKILL.md
|
|
302
|
+
// frontmatter is blocked by the fs sandbox (profile plugin, workspace-write
|
|
303
|
+
// mode — ~/.dsh/skills is outside its scope). Users disable such skills by
|
|
304
|
+
// editing `user-invocable: false` in the file directly.
|
|
305
|
+
const USER_SKILL_VIEW_ONLY_ERROR = '用户级技能由 DSH 文件系统管理,仅支持查看;如需禁用请直接编辑 SKILL.md 的 user-invocable 字段';
|
|
278
306
|
async function skillToggle(args) {
|
|
279
307
|
const name = String(args.name || '').trim();
|
|
280
308
|
if (!name)
|
|
@@ -284,6 +312,17 @@ export default {
|
|
|
284
312
|
await ensureRestored();
|
|
285
313
|
const enabled = args.enabled !== false;
|
|
286
314
|
return withWriteLock(async () => {
|
|
315
|
+
const userDef = await findUserSkill(name);
|
|
316
|
+
if (userDef) {
|
|
317
|
+
// Drop any stale override row created by older versions (pre-2.2.2
|
|
318
|
+
// migration) — it can never shadow the scoped entry anyway, and the
|
|
319
|
+
// toggle for user skills is not supported.
|
|
320
|
+
if (overrideSkills.delete(name)) {
|
|
321
|
+
await saveState([...overrideSkills.keys()]);
|
|
322
|
+
skillProviderControl?.invalidate();
|
|
323
|
+
}
|
|
324
|
+
return { ok: false, error: USER_SKILL_VIEW_ONLY_ERROR };
|
|
325
|
+
}
|
|
287
326
|
if (enabled) {
|
|
288
327
|
if (overrideSkills.delete(name)) {
|
|
289
328
|
await saveState([...overrideSkills.keys()]);
|
|
@@ -292,7 +331,7 @@ export default {
|
|
|
292
331
|
}
|
|
293
332
|
else {
|
|
294
333
|
if (!overrideSkills.has(name)) {
|
|
295
|
-
const def =
|
|
334
|
+
const def = await ctx.skills.get(name);
|
|
296
335
|
if (!def)
|
|
297
336
|
return { ok: false, error: '技能不存在: ' + name };
|
|
298
337
|
overrideSkills.set(name, { ...def, provider: OVERRIDE_PROVIDER, invocation: { modelInvocable: false, userInvocable: false } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xxxyz/dsh-mcp-manager",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.2",
|
|
4
4
|
"description": "DSH-standard MCP manager plugin: Settings UI + HTTP API + model-facing mcp_manager_* tools. Install with one command: dsh plugin --profile web add @xxxyz/dsh-mcp-manager@latest",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|