@daweifu/capability-menu 0.1.3 → 0.1.5
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.en.md +74 -79
- package/README.md +76 -81
- package/cordis.patch.yml +7 -0
- package/lib/client.d.ts +43 -3
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +1172 -499
- package/lib/invoke.js +72 -2
- package/lib/invoke.js.map +1 -1
- package/lib/locations.js +168 -5
- package/lib/locations.js.map +1 -1
- package/lib/patch-file.js +33 -3
- package/lib/patch-file.js.map +1 -1
- package/lib/policy.js +33 -6
- package/lib/policy.js.map +1 -1
- package/lib/registry.js +75 -13
- package/lib/registry.js.map +1 -1
- package/lib/search.js +4 -4
- package/lib/search.js.map +1 -1
- package/lib/server/remote.js +34 -0
- package/lib/server/remote.js.map +1 -1
- package/lib/types/invoke.d.ts.map +1 -1
- package/lib/types/locations.d.ts +2 -0
- package/lib/types/locations.d.ts.map +1 -1
- package/lib/types/patch-file.d.ts.map +1 -1
- package/lib/types/policy.d.ts +8 -0
- package/lib/types/policy.d.ts.map +1 -1
- package/lib/types/registry.d.ts +23 -0
- package/lib/types/registry.d.ts.map +1 -1
- package/lib/types/search.d.ts +2 -2
- package/lib/types/server/remote.d.ts +20 -0
- package/lib/types/server/remote.d.ts.map +1 -1
- package/package.json +37 -21
package/lib/client.js
CHANGED
|
@@ -3,9 +3,97 @@ var module = { exports: {} };
|
|
|
3
3
|
var exports = module.exports;
|
|
4
4
|
|
|
5
5
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
6
|
+
//#region \0rolldown/runtime.js
|
|
7
|
+
var __create = Object.create;
|
|
8
|
+
var __defProp = Object.defineProperty;
|
|
9
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
10
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
11
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
12
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
15
|
+
key = keys[i];
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
17
|
+
get: ((k) => from[k]).bind(null, key),
|
|
18
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
return to;
|
|
22
|
+
};
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
24
|
+
value: mod,
|
|
25
|
+
enumerable: true
|
|
26
|
+
}) : target, mod));
|
|
27
|
+
//#endregion
|
|
6
28
|
let react = require("react");
|
|
7
29
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
30
|
+
_deepseek_ai_dsh_client_ui_primitives = __toESM(_deepseek_ai_dsh_client_ui_primitives, 1);
|
|
8
31
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
32
|
+
//#region src/client/settings-nav-icon.ts
|
|
33
|
+
/** Replace the settings shell's fallback gear with dsh's shared list glyph. */
|
|
34
|
+
const NAV_ICON_MARKER = "data-capability-menu-nav-icon";
|
|
35
|
+
const NAV_ROW_SELECTOR = "[role=\"dialog\"] nav button";
|
|
36
|
+
const NAV_ICON_SIZE = 16;
|
|
37
|
+
/** The geometry of dsh-client-ui-primitives' IconFlatListOutlineMedium. */
|
|
38
|
+
function flatListMaskUrl() {
|
|
39
|
+
return `data:image/svg+xml,${encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"#000\" stroke-linecap=\"round\" stroke-width=\"1.3\"><path d=\"M6 3.5h7.5M6 8h7.5M6 12.5h7.5\"/><path d=\"M2.6 3.5h.01M2.6 8h.01M2.6 12.5h.01\"/></svg>")}`;
|
|
40
|
+
}
|
|
41
|
+
function navIconCss(maskUrl) {
|
|
42
|
+
return [
|
|
43
|
+
`[${NAV_ICON_MARKER}] > svg { display: none; }`,
|
|
44
|
+
`[${NAV_ICON_MARKER}]::before {`,
|
|
45
|
+
" content: \"\";",
|
|
46
|
+
" flex: none;",
|
|
47
|
+
` width: ${NAV_ICON_SIZE}px;`,
|
|
48
|
+
` height: ${NAV_ICON_SIZE}px;`,
|
|
49
|
+
" background-color: currentColor;",
|
|
50
|
+
` -webkit-mask: url("${maskUrl}") center / ${NAV_ICON_SIZE}px ${NAV_ICON_SIZE}px no-repeat;`,
|
|
51
|
+
` mask: url("${maskUrl}") center / ${NAV_ICON_SIZE}px ${NAV_ICON_SIZE}px no-repeat;`,
|
|
52
|
+
"}"
|
|
53
|
+
].join("\n");
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The current settings.section slot has no icon option. Match this plugin's
|
|
57
|
+
* localized nav label and replace only its fallback gear, as dsh-market does.
|
|
58
|
+
*/
|
|
59
|
+
function installSettingsNavIcon(ctx, resolveLabel) {
|
|
60
|
+
if (typeof document === "undefined") return;
|
|
61
|
+
ctx.effect(() => {
|
|
62
|
+
const style = document.createElement("style");
|
|
63
|
+
style.dataset.plugin = "capability-menu";
|
|
64
|
+
style.dataset.pluginCss = "capability-menu/settings-nav-icon";
|
|
65
|
+
style.textContent = navIconCss(flatListMaskUrl());
|
|
66
|
+
document.head.appendChild(style);
|
|
67
|
+
let disposed = false;
|
|
68
|
+
let scheduled = false;
|
|
69
|
+
const sync = () => {
|
|
70
|
+
scheduled = false;
|
|
71
|
+
if (disposed) return;
|
|
72
|
+
const wanted = resolveLabel().trim();
|
|
73
|
+
for (const row of document.querySelectorAll(NAV_ROW_SELECTOR)) if (wanted.length > 0 && row.textContent?.trim() === wanted) row.setAttribute(NAV_ICON_MARKER, "");
|
|
74
|
+
else row.removeAttribute(NAV_ICON_MARKER);
|
|
75
|
+
};
|
|
76
|
+
const schedule = () => {
|
|
77
|
+
if (scheduled || disposed) return;
|
|
78
|
+
scheduled = true;
|
|
79
|
+
queueMicrotask(sync);
|
|
80
|
+
};
|
|
81
|
+
sync();
|
|
82
|
+
const observer = new MutationObserver(schedule);
|
|
83
|
+
observer.observe(document.body, {
|
|
84
|
+
childList: true,
|
|
85
|
+
subtree: true,
|
|
86
|
+
characterData: true
|
|
87
|
+
});
|
|
88
|
+
return () => {
|
|
89
|
+
disposed = true;
|
|
90
|
+
observer.disconnect();
|
|
91
|
+
for (const row of document.querySelectorAll(`[${NAV_ICON_MARKER}]`)) row.removeAttribute(NAV_ICON_MARKER);
|
|
92
|
+
style.remove();
|
|
93
|
+
};
|
|
94
|
+
}, "capability-menu: settings nav icon");
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
9
97
|
//#region node_modules/.pnpm/zod@4.5.2/node_modules/zod/v4/core/util.js
|
|
10
98
|
function getEnumValues(entries) {
|
|
11
99
|
const numericValues = Object.values(entries).filter((v) => typeof v === "number");
|
|
@@ -5190,6 +5278,18 @@ function superRefine(fn, params) {
|
|
|
5190
5278
|
* namespace and exports a `TYPERT_REMOTE` contribution that the browser
|
|
5191
5279
|
* plugin mounts via `ctx.remote.$mount(...)`.
|
|
5192
5280
|
*/
|
|
5281
|
+
/**
|
|
5282
|
+
* Bridge Typert's codec field rename across DSH 0.1 and 0.2. Older remotes
|
|
5283
|
+
* read `schema`; the 0.2 protocol lazily calls `create()`.
|
|
5284
|
+
*/
|
|
5285
|
+
function strictCodec(typeSymbol, schema) {
|
|
5286
|
+
return {
|
|
5287
|
+
mode: "strict",
|
|
5288
|
+
typeSymbol,
|
|
5289
|
+
schema,
|
|
5290
|
+
create: () => schema
|
|
5291
|
+
};
|
|
5292
|
+
}
|
|
5193
5293
|
const stringRecord$schema = record(string(), string());
|
|
5194
5294
|
const mcpLocation$schema = object({
|
|
5195
5295
|
id: string().readonly(),
|
|
@@ -5240,6 +5340,7 @@ const capabilityRow$schema = object({
|
|
|
5240
5340
|
name: string().readonly(),
|
|
5241
5341
|
server: string().optional().readonly(),
|
|
5242
5342
|
source: string().optional().readonly(),
|
|
5343
|
+
preset: string().optional().readonly(),
|
|
5243
5344
|
class: union([
|
|
5244
5345
|
literal("resident"),
|
|
5245
5346
|
literal("on-demand"),
|
|
@@ -5273,7 +5374,8 @@ const toolDetail$schema = object({
|
|
|
5273
5374
|
provider: string().readonly(),
|
|
5274
5375
|
serverName: string().optional().readonly(),
|
|
5275
5376
|
path: string().optional().readonly(),
|
|
5276
|
-
source: string().optional().readonly()
|
|
5377
|
+
source: string().optional().readonly(),
|
|
5378
|
+
preset: string().optional().readonly()
|
|
5277
5379
|
}).readonly(),
|
|
5278
5380
|
tags: array(string()).readonly(),
|
|
5279
5381
|
stats: object({
|
|
@@ -5294,11 +5396,7 @@ const TYPERT_REMOTE = {
|
|
|
5294
5396
|
method: "getConfig",
|
|
5295
5397
|
invocation: { kind: "direct" },
|
|
5296
5398
|
parameters: [],
|
|
5297
|
-
result:
|
|
5298
|
-
mode: "strict",
|
|
5299
|
-
typeSymbol: "Record<string, unknown>",
|
|
5300
|
-
schema: record(string(), unknown())
|
|
5301
|
-
},
|
|
5399
|
+
result: strictCodec("Record<string, unknown>", record(string(), unknown())),
|
|
5302
5400
|
sourceLocation: {
|
|
5303
5401
|
file: "src/server/remote.ts",
|
|
5304
5402
|
line: 61,
|
|
@@ -5315,17 +5413,9 @@ const TYPERT_REMOTE = {
|
|
|
5315
5413
|
name: "partial",
|
|
5316
5414
|
wire: "partial",
|
|
5317
5415
|
source: "json",
|
|
5318
|
-
codec:
|
|
5319
|
-
mode: "strict",
|
|
5320
|
-
typeSymbol: "Record<string, unknown>",
|
|
5321
|
-
schema: record(string(), unknown())
|
|
5322
|
-
}
|
|
5416
|
+
codec: strictCodec("Record<string, unknown>", record(string(), unknown()))
|
|
5323
5417
|
}],
|
|
5324
|
-
result:
|
|
5325
|
-
mode: "strict",
|
|
5326
|
-
typeSymbol: "void",
|
|
5327
|
-
schema: _undefined()
|
|
5328
|
-
},
|
|
5418
|
+
result: strictCodec("void", _undefined()),
|
|
5329
5419
|
sourceLocation: {
|
|
5330
5420
|
file: "src/server/remote.ts",
|
|
5331
5421
|
line: 67,
|
|
@@ -5339,11 +5429,7 @@ const TYPERT_REMOTE = {
|
|
|
5339
5429
|
method: "classifyAll",
|
|
5340
5430
|
invocation: { kind: "direct" },
|
|
5341
5431
|
parameters: [],
|
|
5342
|
-
result:
|
|
5343
|
-
mode: "strict",
|
|
5344
|
-
typeSymbol: "@daweifu/capability-menu#CapabilityRow",
|
|
5345
|
-
schema: array(capabilityRow$schema)
|
|
5346
|
-
},
|
|
5432
|
+
result: strictCodec("@daweifu/capability-menu#CapabilityRow", array(capabilityRow$schema)),
|
|
5347
5433
|
sourceLocation: {
|
|
5348
5434
|
file: "src/server/remote.ts",
|
|
5349
5435
|
line: 73,
|
|
@@ -5357,11 +5443,7 @@ const TYPERT_REMOTE = {
|
|
|
5357
5443
|
method: "refresh",
|
|
5358
5444
|
invocation: { kind: "direct" },
|
|
5359
5445
|
parameters: [],
|
|
5360
|
-
result:
|
|
5361
|
-
mode: "strict",
|
|
5362
|
-
typeSymbol: "void",
|
|
5363
|
-
schema: _undefined()
|
|
5364
|
-
},
|
|
5446
|
+
result: strictCodec("void", _undefined()),
|
|
5365
5447
|
sourceLocation: {
|
|
5366
5448
|
file: "src/server/remote.ts",
|
|
5367
5449
|
line: 84,
|
|
@@ -5378,27 +5460,15 @@ const TYPERT_REMOTE = {
|
|
|
5378
5460
|
name: "id",
|
|
5379
5461
|
wire: "id",
|
|
5380
5462
|
source: "json",
|
|
5381
|
-
codec:
|
|
5382
|
-
mode: "strict",
|
|
5383
|
-
typeSymbol: "string",
|
|
5384
|
-
schema: string()
|
|
5385
|
-
}
|
|
5463
|
+
codec: strictCodec("string", string())
|
|
5386
5464
|
}, {
|
|
5387
5465
|
name: "relPath",
|
|
5388
5466
|
wire: "relPath",
|
|
5389
5467
|
source: "json",
|
|
5390
5468
|
acceptsUndefined: true,
|
|
5391
|
-
codec:
|
|
5392
|
-
mode: "strict",
|
|
5393
|
-
typeSymbol: "string",
|
|
5394
|
-
schema: string().optional()
|
|
5395
|
-
}
|
|
5469
|
+
codec: strictCodec("string", string().optional())
|
|
5396
5470
|
}],
|
|
5397
|
-
result:
|
|
5398
|
-
mode: "strict",
|
|
5399
|
-
typeSymbol: "@daweifu/capability-menu#SkillFileEntry[]",
|
|
5400
|
-
schema: array(skillFileEntry$schema).optional()
|
|
5401
|
-
},
|
|
5471
|
+
result: strictCodec("@daweifu/capability-menu#SkillFileEntry[]", array(skillFileEntry$schema).optional()),
|
|
5402
5472
|
sourceLocation: {
|
|
5403
5473
|
file: "src/server/remote.ts",
|
|
5404
5474
|
line: 85,
|
|
@@ -5415,26 +5485,14 @@ const TYPERT_REMOTE = {
|
|
|
5415
5485
|
name: "id",
|
|
5416
5486
|
wire: "id",
|
|
5417
5487
|
source: "json",
|
|
5418
|
-
codec:
|
|
5419
|
-
mode: "strict",
|
|
5420
|
-
typeSymbol: "string",
|
|
5421
|
-
schema: string()
|
|
5422
|
-
}
|
|
5488
|
+
codec: strictCodec("string", string())
|
|
5423
5489
|
}, {
|
|
5424
5490
|
name: "relPath",
|
|
5425
5491
|
wire: "relPath",
|
|
5426
5492
|
source: "json",
|
|
5427
|
-
codec:
|
|
5428
|
-
mode: "strict",
|
|
5429
|
-
typeSymbol: "string",
|
|
5430
|
-
schema: string()
|
|
5431
|
-
}
|
|
5493
|
+
codec: strictCodec("string", string())
|
|
5432
5494
|
}],
|
|
5433
|
-
result:
|
|
5434
|
-
mode: "strict",
|
|
5435
|
-
typeSymbol: "string",
|
|
5436
|
-
schema: string().optional()
|
|
5437
|
-
},
|
|
5495
|
+
result: strictCodec("string", string().optional()),
|
|
5438
5496
|
sourceLocation: {
|
|
5439
5497
|
file: "src/server/remote.ts",
|
|
5440
5498
|
line: 91,
|
|
@@ -5451,17 +5509,9 @@ const TYPERT_REMOTE = {
|
|
|
5451
5509
|
name: "id",
|
|
5452
5510
|
wire: "id",
|
|
5453
5511
|
source: "json",
|
|
5454
|
-
codec:
|
|
5455
|
-
mode: "strict",
|
|
5456
|
-
typeSymbol: "string",
|
|
5457
|
-
schema: string()
|
|
5458
|
-
}
|
|
5512
|
+
codec: strictCodec("string", string())
|
|
5459
5513
|
}],
|
|
5460
|
-
result:
|
|
5461
|
-
mode: "strict",
|
|
5462
|
-
typeSymbol: "@daweifu/capability-menu#ToolDetail",
|
|
5463
|
-
schema: toolDetail$schema.optional()
|
|
5464
|
-
},
|
|
5514
|
+
result: strictCodec("@daweifu/capability-menu#ToolDetail", toolDetail$schema.optional()),
|
|
5465
5515
|
sourceLocation: {
|
|
5466
5516
|
file: "src/server/remote.ts",
|
|
5467
5517
|
line: 79,
|
|
@@ -5475,11 +5525,7 @@ const TYPERT_REMOTE = {
|
|
|
5475
5525
|
method: "getCatalogDocs",
|
|
5476
5526
|
invocation: { kind: "direct" },
|
|
5477
5527
|
parameters: [],
|
|
5478
|
-
result:
|
|
5479
|
-
mode: "strict",
|
|
5480
|
-
typeSymbol: "@daweifu/capability-menu#CatalogDocs",
|
|
5481
|
-
schema: catalogDocs$schema
|
|
5482
|
-
},
|
|
5528
|
+
result: strictCodec("@daweifu/capability-menu#CatalogDocs", catalogDocs$schema),
|
|
5483
5529
|
sourceLocation: {
|
|
5484
5530
|
file: "src/server/remote.ts",
|
|
5485
5531
|
line: 104,
|
|
@@ -5493,11 +5539,7 @@ const TYPERT_REMOTE = {
|
|
|
5493
5539
|
method: "listLocations",
|
|
5494
5540
|
invocation: { kind: "direct" },
|
|
5495
5541
|
parameters: [],
|
|
5496
|
-
result:
|
|
5497
|
-
mode: "strict",
|
|
5498
|
-
typeSymbol: "@daweifu/capability-menu#McpLocation[]",
|
|
5499
|
-
schema: array(mcpLocation$schema)
|
|
5500
|
-
},
|
|
5542
|
+
result: strictCodec("@daweifu/capability-menu#McpLocation[]", array(mcpLocation$schema)),
|
|
5501
5543
|
sourceLocation: {
|
|
5502
5544
|
file: "src/server/remote.ts",
|
|
5503
5545
|
line: 193,
|
|
@@ -5514,17 +5556,9 @@ const TYPERT_REMOTE = {
|
|
|
5514
5556
|
name: "input",
|
|
5515
5557
|
wire: "input",
|
|
5516
5558
|
source: "json",
|
|
5517
|
-
codec:
|
|
5518
|
-
mode: "strict",
|
|
5519
|
-
typeSymbol: "@daweifu/capability-menu#McpInput",
|
|
5520
|
-
schema: mcpInput$schema
|
|
5521
|
-
}
|
|
5559
|
+
codec: strictCodec("@daweifu/capability-menu#McpInput", mcpInput$schema)
|
|
5522
5560
|
}],
|
|
5523
|
-
result:
|
|
5524
|
-
mode: "strict",
|
|
5525
|
-
typeSymbol: "string",
|
|
5526
|
-
schema: string()
|
|
5527
|
-
},
|
|
5561
|
+
result: strictCodec("string", string()),
|
|
5528
5562
|
sourceLocation: {
|
|
5529
5563
|
file: "src/server/remote.ts",
|
|
5530
5564
|
line: 199,
|
|
@@ -5541,17 +5575,9 @@ const TYPERT_REMOTE = {
|
|
|
5541
5575
|
name: "id",
|
|
5542
5576
|
wire: "id",
|
|
5543
5577
|
source: "json",
|
|
5544
|
-
codec:
|
|
5545
|
-
mode: "strict",
|
|
5546
|
-
typeSymbol: "string",
|
|
5547
|
-
schema: string()
|
|
5548
|
-
}
|
|
5578
|
+
codec: strictCodec("string", string())
|
|
5549
5579
|
}],
|
|
5550
|
-
result:
|
|
5551
|
-
mode: "strict",
|
|
5552
|
-
typeSymbol: "boolean",
|
|
5553
|
-
schema: boolean()
|
|
5554
|
-
},
|
|
5580
|
+
result: strictCodec("boolean", boolean()),
|
|
5555
5581
|
sourceLocation: {
|
|
5556
5582
|
file: "src/server/remote.ts",
|
|
5557
5583
|
line: 205,
|
|
@@ -5568,26 +5594,14 @@ const TYPERT_REMOTE = {
|
|
|
5568
5594
|
name: "id",
|
|
5569
5595
|
wire: "id",
|
|
5570
5596
|
source: "json",
|
|
5571
|
-
codec:
|
|
5572
|
-
mode: "strict",
|
|
5573
|
-
typeSymbol: "string",
|
|
5574
|
-
schema: string()
|
|
5575
|
-
}
|
|
5597
|
+
codec: strictCodec("string", string())
|
|
5576
5598
|
}, {
|
|
5577
5599
|
name: "input",
|
|
5578
5600
|
wire: "input",
|
|
5579
5601
|
source: "json",
|
|
5580
|
-
codec:
|
|
5581
|
-
mode: "strict",
|
|
5582
|
-
typeSymbol: "@daweifu/capability-menu#McpUpdateInput",
|
|
5583
|
-
schema: mcpUpdateInput$schema
|
|
5584
|
-
}
|
|
5602
|
+
codec: strictCodec("@daweifu/capability-menu#McpUpdateInput", mcpUpdateInput$schema)
|
|
5585
5603
|
}],
|
|
5586
|
-
result:
|
|
5587
|
-
mode: "strict",
|
|
5588
|
-
typeSymbol: "boolean",
|
|
5589
|
-
schema: boolean()
|
|
5590
|
-
},
|
|
5604
|
+
result: strictCodec("boolean", boolean()),
|
|
5591
5605
|
sourceLocation: {
|
|
5592
5606
|
file: "src/server/remote.ts",
|
|
5593
5607
|
line: 211,
|
|
@@ -5601,11 +5615,7 @@ const TYPERT_REMOTE = {
|
|
|
5601
5615
|
method: "listSkillLocations",
|
|
5602
5616
|
invocation: { kind: "direct" },
|
|
5603
5617
|
parameters: [],
|
|
5604
|
-
result:
|
|
5605
|
-
mode: "strict",
|
|
5606
|
-
typeSymbol: "@daweifu/capability-menu#SkillLocation[]",
|
|
5607
|
-
schema: array(skillLocation$schema)
|
|
5608
|
-
},
|
|
5618
|
+
result: strictCodec("@daweifu/capability-menu#SkillLocation[]", array(skillLocation$schema)),
|
|
5609
5619
|
sourceLocation: {
|
|
5610
5620
|
file: "src/server/remote.ts",
|
|
5611
5621
|
line: 229,
|
|
@@ -5622,33 +5632,46 @@ const TYPERT_REMOTE = {
|
|
|
5622
5632
|
name: "dir",
|
|
5623
5633
|
wire: "dir",
|
|
5624
5634
|
source: "json",
|
|
5625
|
-
codec:
|
|
5626
|
-
mode: "strict",
|
|
5627
|
-
typeSymbol: "string",
|
|
5628
|
-
schema: string()
|
|
5629
|
-
}
|
|
5635
|
+
codec: strictCodec("string", string())
|
|
5630
5636
|
}, {
|
|
5631
5637
|
name: "projectPath",
|
|
5632
5638
|
wire: "projectPath",
|
|
5633
5639
|
source: "json",
|
|
5634
5640
|
acceptsUndefined: true,
|
|
5635
|
-
codec:
|
|
5636
|
-
mode: "strict",
|
|
5637
|
-
typeSymbol: "string",
|
|
5638
|
-
schema: string().optional()
|
|
5639
|
-
}
|
|
5641
|
+
codec: strictCodec("string", string().optional())
|
|
5640
5642
|
}],
|
|
5641
|
-
result:
|
|
5642
|
-
mode: "strict",
|
|
5643
|
-
typeSymbol: "string",
|
|
5644
|
-
schema: string()
|
|
5645
|
-
},
|
|
5643
|
+
result: strictCodec("string", string()),
|
|
5646
5644
|
sourceLocation: {
|
|
5647
5645
|
file: "src/server/remote.ts",
|
|
5648
5646
|
line: 252,
|
|
5649
5647
|
column: 3
|
|
5650
5648
|
}
|
|
5651
5649
|
},
|
|
5650
|
+
{
|
|
5651
|
+
id: "@daweifu/capability-menu#capabilityPolicy/importSkillFromGitHub",
|
|
5652
|
+
service: "capabilityPolicy",
|
|
5653
|
+
namespace: "capabilityPolicy",
|
|
5654
|
+
method: "importSkillFromGitHub",
|
|
5655
|
+
invocation: { kind: "direct" },
|
|
5656
|
+
parameters: [{
|
|
5657
|
+
name: "url",
|
|
5658
|
+
wire: "url",
|
|
5659
|
+
source: "json",
|
|
5660
|
+
codec: strictCodec("string", string())
|
|
5661
|
+
}, {
|
|
5662
|
+
name: "projectPath",
|
|
5663
|
+
wire: "projectPath",
|
|
5664
|
+
source: "json",
|
|
5665
|
+
acceptsUndefined: true,
|
|
5666
|
+
codec: strictCodec("string", string().optional())
|
|
5667
|
+
}],
|
|
5668
|
+
result: strictCodec("string", string()),
|
|
5669
|
+
sourceLocation: {
|
|
5670
|
+
file: "src/server/remote.ts",
|
|
5671
|
+
line: 263,
|
|
5672
|
+
column: 3
|
|
5673
|
+
}
|
|
5674
|
+
},
|
|
5652
5675
|
{
|
|
5653
5676
|
id: "@daweifu/capability-menu#capabilityPolicy/removeSkillLocation",
|
|
5654
5677
|
service: "capabilityPolicy",
|
|
@@ -5659,27 +5682,15 @@ const TYPERT_REMOTE = {
|
|
|
5659
5682
|
name: "name",
|
|
5660
5683
|
wire: "name",
|
|
5661
5684
|
source: "json",
|
|
5662
|
-
codec:
|
|
5663
|
-
mode: "strict",
|
|
5664
|
-
typeSymbol: "string",
|
|
5665
|
-
schema: string()
|
|
5666
|
-
}
|
|
5685
|
+
codec: strictCodec("string", string())
|
|
5667
5686
|
}, {
|
|
5668
5687
|
name: "entryDir",
|
|
5669
5688
|
wire: "entryDir",
|
|
5670
5689
|
source: "json",
|
|
5671
5690
|
acceptsUndefined: true,
|
|
5672
|
-
codec:
|
|
5673
|
-
mode: "strict",
|
|
5674
|
-
typeSymbol: "string",
|
|
5675
|
-
schema: string().optional()
|
|
5676
|
-
}
|
|
5691
|
+
codec: strictCodec("string", string().optional())
|
|
5677
5692
|
}],
|
|
5678
|
-
result:
|
|
5679
|
-
mode: "strict",
|
|
5680
|
-
typeSymbol: "boolean",
|
|
5681
|
-
schema: boolean()
|
|
5682
|
-
},
|
|
5693
|
+
result: strictCodec("boolean", boolean()),
|
|
5683
5694
|
sourceLocation: {
|
|
5684
5695
|
file: "src/server/remote.ts",
|
|
5685
5696
|
line: 258,
|
|
@@ -5696,17 +5707,9 @@ const TYPERT_REMOTE = {
|
|
|
5696
5707
|
name: "name",
|
|
5697
5708
|
wire: "name",
|
|
5698
5709
|
source: "json",
|
|
5699
|
-
codec:
|
|
5700
|
-
mode: "strict",
|
|
5701
|
-
typeSymbol: "string",
|
|
5702
|
-
schema: string()
|
|
5703
|
-
}
|
|
5710
|
+
codec: strictCodec("string", string())
|
|
5704
5711
|
}],
|
|
5705
|
-
result:
|
|
5706
|
-
mode: "strict",
|
|
5707
|
-
typeSymbol: "string",
|
|
5708
|
-
schema: string()
|
|
5709
|
-
},
|
|
5712
|
+
result: strictCodec("string", string()),
|
|
5710
5713
|
sourceLocation: {
|
|
5711
5714
|
file: "src/server/remote.ts",
|
|
5712
5715
|
line: 259,
|
|
@@ -5724,44 +5727,42 @@ const TYPERT_REMOTE = {
|
|
|
5724
5727
|
name: "name",
|
|
5725
5728
|
wire: "name",
|
|
5726
5729
|
source: "json",
|
|
5727
|
-
codec:
|
|
5728
|
-
mode: "strict",
|
|
5729
|
-
typeSymbol: "string",
|
|
5730
|
-
schema: string()
|
|
5731
|
-
}
|
|
5730
|
+
codec: strictCodec("string", string())
|
|
5732
5731
|
},
|
|
5733
5732
|
{
|
|
5734
5733
|
name: "dir",
|
|
5735
5734
|
wire: "dir",
|
|
5736
5735
|
source: "json",
|
|
5737
|
-
codec:
|
|
5738
|
-
mode: "strict",
|
|
5739
|
-
typeSymbol: "string",
|
|
5740
|
-
schema: string()
|
|
5741
|
-
}
|
|
5736
|
+
codec: strictCodec("string", string())
|
|
5742
5737
|
},
|
|
5743
5738
|
{
|
|
5744
5739
|
name: "entryDir",
|
|
5745
5740
|
wire: "entryDir",
|
|
5746
5741
|
source: "json",
|
|
5747
5742
|
acceptsUndefined: true,
|
|
5748
|
-
codec:
|
|
5749
|
-
mode: "strict",
|
|
5750
|
-
typeSymbol: "string",
|
|
5751
|
-
schema: string().optional()
|
|
5752
|
-
}
|
|
5743
|
+
codec: strictCodec("string", string().optional())
|
|
5753
5744
|
}
|
|
5754
5745
|
],
|
|
5755
|
-
result:
|
|
5756
|
-
mode: "strict",
|
|
5757
|
-
typeSymbol: "boolean",
|
|
5758
|
-
schema: boolean()
|
|
5759
|
-
},
|
|
5746
|
+
result: strictCodec("boolean", boolean()),
|
|
5760
5747
|
sourceLocation: {
|
|
5761
5748
|
file: "src/server/remote.ts",
|
|
5762
5749
|
line: 264,
|
|
5763
5750
|
column: 3
|
|
5764
5751
|
}
|
|
5752
|
+
},
|
|
5753
|
+
{
|
|
5754
|
+
id: "@daweifu/capability-menu#capabilityPolicy/catalogVersion",
|
|
5755
|
+
service: "capabilityPolicy",
|
|
5756
|
+
namespace: "capabilityPolicy",
|
|
5757
|
+
method: "catalogVersion",
|
|
5758
|
+
invocation: { kind: "direct" },
|
|
5759
|
+
parameters: [],
|
|
5760
|
+
result: strictCodec("number", number()),
|
|
5761
|
+
sourceLocation: {
|
|
5762
|
+
file: "src/server/remote.ts",
|
|
5763
|
+
line: 317,
|
|
5764
|
+
column: 3
|
|
5765
|
+
}
|
|
5765
5766
|
}
|
|
5766
5767
|
]
|
|
5767
5768
|
};
|
|
@@ -5796,15 +5797,52 @@ function unwrapMessage(result) {
|
|
|
5796
5797
|
* still followed by an authoritative one.
|
|
5797
5798
|
*/
|
|
5798
5799
|
let lastSnapshot;
|
|
5800
|
+
/** Short grace period for host tools that register just after the section mounts. */
|
|
5801
|
+
const EMPTY_SNAPSHOT_RETRY_DELAYS = [
|
|
5802
|
+
250,
|
|
5803
|
+
500,
|
|
5804
|
+
1e3,
|
|
5805
|
+
1500
|
|
5806
|
+
];
|
|
5799
5807
|
/** The previously loaded snapshot, when this page session has one. */
|
|
5800
5808
|
function cachedSnapshot() {
|
|
5801
5809
|
return lastSnapshot;
|
|
5802
5810
|
}
|
|
5803
|
-
/**
|
|
5804
|
-
async function
|
|
5805
|
-
|
|
5811
|
+
/** Read one authoritative classification list without changing the view cache. */
|
|
5812
|
+
async function readSnapshot(remote) {
|
|
5813
|
+
return { rows: unwrap(await remote.classifyAll(), "capabilityPolicy.classifyAll") };
|
|
5814
|
+
}
|
|
5815
|
+
/** Remember a snapshot after its contents are ready to replace the current view. */
|
|
5816
|
+
function rememberSnapshot(snapshot) {
|
|
5817
|
+
lastSnapshot = snapshot;
|
|
5806
5818
|
return lastSnapshot;
|
|
5807
5819
|
}
|
|
5820
|
+
/**
|
|
5821
|
+
* Load the classification list, retrying an empty result briefly because
|
|
5822
|
+
* built-in tools can register just after the settings section mounts.
|
|
5823
|
+
* Non-empty results remain authoritative and return immediately.
|
|
5824
|
+
*/
|
|
5825
|
+
async function loadSnapshot(remote, retryDelays = EMPTY_SNAPSHOT_RETRY_DELAYS) {
|
|
5826
|
+
let snapshot = await readSnapshot(remote);
|
|
5827
|
+
for (const delayMs of retryDelays) {
|
|
5828
|
+
if (snapshot.rows.length > 0) break;
|
|
5829
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
5830
|
+
snapshot = await readSnapshot(remote);
|
|
5831
|
+
}
|
|
5832
|
+
return rememberSnapshot(snapshot);
|
|
5833
|
+
}
|
|
5834
|
+
/** Wait briefly for an MCP's tools to enter the host catalog after registration. */
|
|
5835
|
+
async function waitForMcpTools(remote, serverName, retryDelays) {
|
|
5836
|
+
const prefix = `mcp__${serverName}__`;
|
|
5837
|
+
const hasServerTools = (snapshot) => snapshot.rows.some((row) => row.kind === "tool" && row.id.startsWith(prefix));
|
|
5838
|
+
let snapshot = await readSnapshot(remote);
|
|
5839
|
+
if (hasServerTools(snapshot)) return snapshot;
|
|
5840
|
+
for (const delayMs of retryDelays) {
|
|
5841
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
5842
|
+
snapshot = await readSnapshot(remote);
|
|
5843
|
+
if (hasServerTools(snapshot)) return snapshot;
|
|
5844
|
+
}
|
|
5845
|
+
}
|
|
5808
5846
|
//#endregion
|
|
5809
5847
|
//#region src/client/LocationModal.tsx
|
|
5810
5848
|
/**
|
|
@@ -5850,6 +5888,11 @@ textarea.lm-input{resize:vertical;font-family:var(--dsw-font-markdown-code-block
|
|
|
5850
5888
|
annotates and takes the caption colour so it does not read as another label. */
|
|
5851
5889
|
.lm-hint{grid-column:2;margin:0;font-size:11px;line-height:16px;color:var(--dsw-alias-label-caption)}
|
|
5852
5890
|
.lm-error{margin:0;font-size:12px;color:#b3261e;word-break:break-word}
|
|
5891
|
+
.lm-progress{display:flex;align-items:center;gap:8px;margin:0;font-size:12px;line-height:20px;color:var(--dsw-alias-label-secondary)}
|
|
5892
|
+
.lm-spinner{width:12px;height:12px;flex:none;border:2px solid var(--dsw-alias-border-l2);border-top-color:var(--dsw-alias-state-business-primary);border-radius:50%;animation:lm-spin .8s linear infinite}
|
|
5893
|
+
@keyframes lm-spin{to{transform:rotate(360deg)}}
|
|
5894
|
+
@media(prefers-reduced-motion:reduce){.lm-spinner{animation-duration:2s}}
|
|
5895
|
+
.mc-tab:disabled,.mc-preview-close:disabled{cursor:default;opacity:.6}
|
|
5853
5896
|
.lm-actions{display:flex;gap:8px;justify-content:flex-end;padding:4px 0 0}
|
|
5854
5897
|
/* Removal is irreversible, so it takes over the action row for one confirmation
|
|
5855
5898
|
step: the message states the consequence, then the buttons act. */
|
|
@@ -5889,8 +5932,9 @@ function LocationModal(props) {
|
|
|
5889
5932
|
const { remote, t, defaultKind = "mcp", editMcp, editSkill, onClose, onChanged } = props;
|
|
5890
5933
|
const editing = editMcp !== void 0 || editSkill !== void 0;
|
|
5891
5934
|
const [subTab, setSubTab] = (0, react.useState)(editSkill !== void 0 ? "skill" : editMcp !== void 0 ? "mcp" : defaultKind);
|
|
5892
|
-
const [
|
|
5935
|
+
const [errors, setErrors] = (0, react.useState)({});
|
|
5893
5936
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
5937
|
+
const [progressMessage, setProgressMessage] = (0, react.useState)(null);
|
|
5894
5938
|
/**
|
|
5895
5939
|
* Which action is waiting for confirmation, if any. Two need one: 移除, and
|
|
5896
5940
|
* 保存 on an entry that is a real directory — repointing unlinks the entry
|
|
@@ -5928,31 +5972,49 @@ function LocationModal(props) {
|
|
|
5928
5972
|
t
|
|
5929
5973
|
]);
|
|
5930
5974
|
/** Run a mutation; on success close and ask the parent to re-pull. */
|
|
5931
|
-
const submit = (0, react.useCallback)(async (action, noticeFor) => {
|
|
5975
|
+
const submit = (0, react.useCallback)(async (action, noticeFor, progress, registeredMcp) => {
|
|
5932
5976
|
if (busy) return;
|
|
5933
5977
|
setBusy(true);
|
|
5978
|
+
setProgressMessage(progress ?? null);
|
|
5979
|
+
setErrors((prev) => {
|
|
5980
|
+
const next = { ...prev };
|
|
5981
|
+
delete next[subTab];
|
|
5982
|
+
return next;
|
|
5983
|
+
});
|
|
5934
5984
|
try {
|
|
5935
5985
|
const changed = await action();
|
|
5936
5986
|
if (changed === false) throw new Error(t("entryNotFound"));
|
|
5937
|
-
|
|
5938
|
-
|
|
5987
|
+
setErrors((prev) => {
|
|
5988
|
+
const next = { ...prev };
|
|
5989
|
+
delete next[subTab];
|
|
5990
|
+
return next;
|
|
5991
|
+
});
|
|
5992
|
+
onChanged(noticeFor?.(changed), registeredMcp);
|
|
5939
5993
|
onClose();
|
|
5940
5994
|
} catch (e) {
|
|
5941
|
-
|
|
5995
|
+
setErrors((prev) => ({
|
|
5996
|
+
...prev,
|
|
5997
|
+
[subTab]: String(e)
|
|
5998
|
+
}));
|
|
5942
5999
|
} finally {
|
|
5943
6000
|
setBusy(false);
|
|
6001
|
+
setProgressMessage(null);
|
|
5944
6002
|
}
|
|
5945
6003
|
}, [
|
|
5946
6004
|
busy,
|
|
5947
6005
|
onChanged,
|
|
5948
6006
|
onClose,
|
|
6007
|
+
subTab,
|
|
5949
6008
|
t
|
|
5950
6009
|
]);
|
|
5951
6010
|
const submitMcp = (0, react.useCallback)(() => {
|
|
5952
6011
|
const trimmedTimeout = timeoutSec.trim();
|
|
5953
6012
|
const timeoutMs = trimmedTimeout.length === 0 ? void 0 : Math.round(Number(trimmedTimeout) * 1e3);
|
|
5954
6013
|
if (timeoutMs !== void 0 && !Number.isFinite(timeoutMs)) {
|
|
5955
|
-
|
|
6014
|
+
setErrors((prev) => ({
|
|
6015
|
+
...prev,
|
|
6016
|
+
mcp: t("timeoutInvalid")
|
|
6017
|
+
}));
|
|
5956
6018
|
return;
|
|
5957
6019
|
}
|
|
5958
6020
|
const parsedEnv = fromLines(env);
|
|
@@ -5973,7 +6035,7 @@ function LocationModal(props) {
|
|
|
5973
6035
|
submit(async () => editMcp !== void 0 ? unwrapMessage(await remote.updateLocation(editMcp.id, common)) : unwrapMessage(await remote.addLocation({
|
|
5974
6036
|
serverName: serverName.trim(),
|
|
5975
6037
|
...common
|
|
5976
|
-
})));
|
|
6038
|
+
})), void 0, void 0, editMcp?.serverName ?? serverName.trim());
|
|
5977
6039
|
}, [
|
|
5978
6040
|
submit,
|
|
5979
6041
|
remote,
|
|
@@ -5990,8 +6052,13 @@ function LocationModal(props) {
|
|
|
5990
6052
|
t
|
|
5991
6053
|
]);
|
|
5992
6054
|
const submitSkill = (0, react.useCallback)(() => {
|
|
6055
|
+
const source = skillDir.trim();
|
|
5993
6056
|
const asked = skillRoot === "project" ? projectPath.trim() : void 0;
|
|
5994
|
-
submit(async () =>
|
|
6057
|
+
submit(async () => {
|
|
6058
|
+
if (editSkill !== void 0) return unwrapMessage(await remote.updateSkillLocation(editSkill.name, source, editSkill.entryDir));
|
|
6059
|
+
if (/^https?:\/\//i.test(source)) return unwrapMessage(await remote.importSkillFromGitHub(source, asked));
|
|
6060
|
+
return unwrapMessage(await remote.addSkillLocation(source, asked));
|
|
6061
|
+
}, (result) => editSkill !== void 0 ? t("skillRepointed", { dir: source }) : t("skillRegisteredAt", { path: String(result) }), editSkill === void 0 && /^https:\/\//i.test(source) ? t("githubImporting") : void 0);
|
|
5995
6062
|
}, [
|
|
5996
6063
|
submit,
|
|
5997
6064
|
remote,
|
|
@@ -6067,7 +6134,7 @@ function LocationModal(props) {
|
|
|
6067
6134
|
if (needsSaveConfirm) setConfirm("save");
|
|
6068
6135
|
else onSubmit();
|
|
6069
6136
|
},
|
|
6070
|
-
children: editing ? t("save") : t("register")
|
|
6137
|
+
children: progressMessage !== null ? t("importing") : editing ? t("save") : t("register")
|
|
6071
6138
|
}), editing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6072
6139
|
type: "button",
|
|
6073
6140
|
className: "lm-btn lm-btn--danger",
|
|
@@ -6078,7 +6145,7 @@ function LocationModal(props) {
|
|
|
6078
6145
|
});
|
|
6079
6146
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6080
6147
|
className: "mc-preview-mask",
|
|
6081
|
-
onClick: onClose,
|
|
6148
|
+
onClick: busy ? void 0 : onClose,
|
|
6082
6149
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6083
6150
|
className: "mc-preview",
|
|
6084
6151
|
onClick: (e) => e.stopPropagation(),
|
|
@@ -6091,6 +6158,7 @@ function LocationModal(props) {
|
|
|
6091
6158
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6092
6159
|
type: "button",
|
|
6093
6160
|
className: "mc-preview-close",
|
|
6161
|
+
disabled: busy,
|
|
6094
6162
|
onClick: onClose,
|
|
6095
6163
|
children: t("previewClose")
|
|
6096
6164
|
})]
|
|
@@ -6107,6 +6175,7 @@ function LocationModal(props) {
|
|
|
6107
6175
|
className: "mc-tab",
|
|
6108
6176
|
"aria-selected": subTab === "mcp",
|
|
6109
6177
|
"data-active": subTab === "mcp" ? "true" : void 0,
|
|
6178
|
+
disabled: busy,
|
|
6110
6179
|
onClick: () => setSubTab("mcp"),
|
|
6111
6180
|
children: t("mcpServers")
|
|
6112
6181
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -6115,6 +6184,7 @@ function LocationModal(props) {
|
|
|
6115
6184
|
className: "mc-tab",
|
|
6116
6185
|
"aria-selected": subTab === "skill",
|
|
6117
6186
|
"data-active": subTab === "skill" ? "true" : void 0,
|
|
6187
|
+
disabled: busy,
|
|
6118
6188
|
onClick: () => setSubTab("skill"),
|
|
6119
6189
|
children: t("skillDirs")
|
|
6120
6190
|
})]
|
|
@@ -6122,222 +6192,234 @@ function LocationModal(props) {
|
|
|
6122
6192
|
}),
|
|
6123
6193
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6124
6194
|
className: "lm-body",
|
|
6125
|
-
children: [
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
className: "lm-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
children: t("
|
|
6195
|
+
children: [
|
|
6196
|
+
errors[subTab] !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6197
|
+
className: "lm-error",
|
|
6198
|
+
children: errors[subTab]
|
|
6199
|
+
}),
|
|
6200
|
+
progressMessage !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
6201
|
+
className: "lm-progress",
|
|
6202
|
+
role: "status",
|
|
6203
|
+
"aria-live": "polite",
|
|
6204
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6205
|
+
className: "lm-spinner",
|
|
6206
|
+
"aria-hidden": "true"
|
|
6207
|
+
}), progressMessage]
|
|
6208
|
+
}),
|
|
6209
|
+
subTab === "mcp" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6210
|
+
className: "lm-block",
|
|
6211
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6212
|
+
className: "lm-form",
|
|
6213
|
+
children: [
|
|
6214
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6215
|
+
className: "lm-field",
|
|
6216
|
+
children: [
|
|
6217
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6218
|
+
className: "lm-label",
|
|
6219
|
+
children: t("transport")
|
|
6220
|
+
}),
|
|
6221
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
6222
|
+
className: "lm-input",
|
|
6223
|
+
value: transport,
|
|
6224
|
+
onChange: (e) => setTransport(e.target.value === "stdio" ? "stdio" : "streamable-http"),
|
|
6225
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
6226
|
+
value: "stdio",
|
|
6227
|
+
children: t("transportStdio")
|
|
6228
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
6229
|
+
value: "streamable-http",
|
|
6230
|
+
children: t("transportHttp")
|
|
6231
|
+
})]
|
|
6232
|
+
}),
|
|
6233
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6234
|
+
className: "lm-hint",
|
|
6235
|
+
children: transport === "stdio" ? t("transportHintStdio") : t("transportHintHttp")
|
|
6236
|
+
})
|
|
6237
|
+
]
|
|
6238
|
+
}),
|
|
6239
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6240
|
+
className: "lm-field",
|
|
6241
|
+
children: [
|
|
6242
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6243
|
+
className: "lm-label",
|
|
6244
|
+
children: t("serverName")
|
|
6245
|
+
}),
|
|
6246
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6247
|
+
className: "lm-input",
|
|
6248
|
+
value: serverName,
|
|
6249
|
+
disabled: editMcp !== void 0,
|
|
6250
|
+
onChange: (e) => setServerName(e.target.value)
|
|
6251
|
+
}),
|
|
6252
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6253
|
+
className: "lm-hint",
|
|
6254
|
+
children: t("serverNameImmutable")
|
|
6255
|
+
})
|
|
6256
|
+
]
|
|
6257
|
+
}),
|
|
6258
|
+
transport === "stdio" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
6259
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6260
|
+
className: "lm-field",
|
|
6261
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6262
|
+
className: "lm-label",
|
|
6263
|
+
children: t("command")
|
|
6264
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6265
|
+
className: "lm-input",
|
|
6266
|
+
value: command,
|
|
6267
|
+
onChange: (e) => setCommand(e.target.value)
|
|
6150
6268
|
})]
|
|
6151
6269
|
}),
|
|
6152
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
6153
|
-
className: "lm-
|
|
6154
|
-
children:
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
className: "lm-label",
|
|
6163
|
-
children: t("serverName")
|
|
6270
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6271
|
+
className: "lm-field",
|
|
6272
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6273
|
+
className: "lm-label",
|
|
6274
|
+
children: t("args")
|
|
6275
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6276
|
+
className: "lm-input",
|
|
6277
|
+
value: args,
|
|
6278
|
+
onChange: (e) => setArgs(e.target.value)
|
|
6279
|
+
})]
|
|
6164
6280
|
}),
|
|
6165
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
6166
|
-
className: "lm-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6281
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6282
|
+
className: "lm-field",
|
|
6283
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6284
|
+
className: "lm-label",
|
|
6285
|
+
children: t("cwd")
|
|
6286
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6287
|
+
className: "lm-input",
|
|
6288
|
+
value: cwd,
|
|
6289
|
+
onChange: (e) => setCwd(e.target.value)
|
|
6290
|
+
})]
|
|
6170
6291
|
}),
|
|
6171
|
-
|
|
6172
|
-
className: "lm-
|
|
6173
|
-
children:
|
|
6292
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6293
|
+
className: "lm-field",
|
|
6294
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6295
|
+
className: "lm-label",
|
|
6296
|
+
children: t("env")
|
|
6297
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
6298
|
+
className: "lm-input",
|
|
6299
|
+
rows: 3,
|
|
6300
|
+
value: env,
|
|
6301
|
+
onChange: (e) => setEnv(e.target.value)
|
|
6302
|
+
})]
|
|
6174
6303
|
})
|
|
6175
|
-
]
|
|
6176
|
-
}),
|
|
6177
|
-
transport === "stdio" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
6178
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6304
|
+
] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6179
6305
|
className: "lm-field",
|
|
6180
6306
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6181
6307
|
className: "lm-label",
|
|
6182
|
-
children: t("
|
|
6308
|
+
children: t("url")
|
|
6183
6309
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6184
6310
|
className: "lm-input",
|
|
6185
|
-
value:
|
|
6186
|
-
onChange: (e) =>
|
|
6311
|
+
value: url,
|
|
6312
|
+
onChange: (e) => setUrl(e.target.value)
|
|
6187
6313
|
})]
|
|
6188
|
-
}),
|
|
6314
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6315
|
+
className: "lm-field",
|
|
6316
|
+
children: [
|
|
6317
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6318
|
+
className: "lm-label",
|
|
6319
|
+
children: t("headers")
|
|
6320
|
+
}),
|
|
6321
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
6322
|
+
className: "lm-input",
|
|
6323
|
+
rows: 4,
|
|
6324
|
+
value: headers,
|
|
6325
|
+
onChange: (e) => setHeaders(e.target.value)
|
|
6326
|
+
}),
|
|
6327
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6328
|
+
className: "lm-hint",
|
|
6329
|
+
children: t("headersHint")
|
|
6330
|
+
})
|
|
6331
|
+
]
|
|
6332
|
+
})] }),
|
|
6189
6333
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6190
6334
|
className: "lm-field",
|
|
6191
6335
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6192
6336
|
className: "lm-label",
|
|
6193
|
-
children: t("
|
|
6337
|
+
children: t("timeout")
|
|
6194
6338
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6195
6339
|
className: "lm-input",
|
|
6196
|
-
value:
|
|
6197
|
-
|
|
6340
|
+
value: timeoutSec,
|
|
6341
|
+
placeholder: "60",
|
|
6342
|
+
onChange: (e) => setTimeoutSec(e.target.value)
|
|
6198
6343
|
})]
|
|
6199
6344
|
}),
|
|
6345
|
+
renderActions(() => void submitMcp())
|
|
6346
|
+
]
|
|
6347
|
+
})
|
|
6348
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6349
|
+
className: "lm-block",
|
|
6350
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6351
|
+
className: "lm-form",
|
|
6352
|
+
children: [
|
|
6200
6353
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6201
6354
|
className: "lm-field",
|
|
6202
|
-
children: [
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6355
|
+
children: [
|
|
6356
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6357
|
+
className: "lm-label",
|
|
6358
|
+
children: t("skillRoot")
|
|
6359
|
+
}),
|
|
6360
|
+
editSkill !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6361
|
+
className: "lm-input",
|
|
6362
|
+
value: t(editSkill.root === "project" ? "skillRootProject" : "skillRootUser"),
|
|
6363
|
+
disabled: true
|
|
6364
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
6365
|
+
className: "lm-input",
|
|
6366
|
+
value: skillRoot,
|
|
6367
|
+
onChange: (e) => setSkillRoot(e.target.value === "project" ? "project" : "user"),
|
|
6368
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
6369
|
+
value: "user",
|
|
6370
|
+
children: t("skillRootUser")
|
|
6371
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
6372
|
+
value: "project",
|
|
6373
|
+
children: t("skillRootProject")
|
|
6374
|
+
})]
|
|
6375
|
+
}),
|
|
6376
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6377
|
+
className: "lm-hint",
|
|
6378
|
+
children: skillRoot === "project" || editSkill?.root === "project" ? t("skillRootHintProject") : t("skillRootHintUser")
|
|
6379
|
+
})
|
|
6380
|
+
]
|
|
6210
6381
|
}),
|
|
6211
6382
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6212
6383
|
className: "lm-field",
|
|
6213
|
-
children: [
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
}),
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
]
|
|
6251
|
-
})] }),
|
|
6252
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6253
|
-
className: "lm-field",
|
|
6254
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6255
|
-
className: "lm-label",
|
|
6256
|
-
children: t("timeout")
|
|
6257
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6258
|
-
className: "lm-input",
|
|
6259
|
-
value: timeoutSec,
|
|
6260
|
-
placeholder: "60",
|
|
6261
|
-
onChange: (e) => setTimeoutSec(e.target.value)
|
|
6262
|
-
})]
|
|
6263
|
-
}),
|
|
6264
|
-
renderActions(() => void submitMcp())
|
|
6265
|
-
]
|
|
6266
|
-
})
|
|
6267
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6268
|
-
className: "lm-block",
|
|
6269
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6270
|
-
className: "lm-form",
|
|
6271
|
-
children: [
|
|
6272
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6273
|
-
className: "lm-field",
|
|
6274
|
-
children: [
|
|
6275
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6276
|
-
className: "lm-label",
|
|
6277
|
-
children: t("skillRoot")
|
|
6278
|
-
}),
|
|
6279
|
-
editSkill !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6280
|
-
className: "lm-input",
|
|
6281
|
-
value: t(editSkill.root === "project" ? "skillRootProject" : "skillRootUser"),
|
|
6282
|
-
disabled: true
|
|
6283
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
6284
|
-
className: "lm-input",
|
|
6285
|
-
value: skillRoot,
|
|
6286
|
-
onChange: (e) => setSkillRoot(e.target.value === "project" ? "project" : "user"),
|
|
6287
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
6288
|
-
value: "user",
|
|
6289
|
-
children: t("skillRootUser")
|
|
6290
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
6291
|
-
value: "project",
|
|
6292
|
-
children: t("skillRootProject")
|
|
6293
|
-
})]
|
|
6294
|
-
}),
|
|
6295
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6296
|
-
className: "lm-hint",
|
|
6297
|
-
children: skillRoot === "project" || editSkill?.root === "project" ? t("skillRootHintProject") : t("skillRootHintUser")
|
|
6298
|
-
})
|
|
6299
|
-
]
|
|
6300
|
-
}),
|
|
6301
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6302
|
-
className: "lm-field",
|
|
6303
|
-
children: [
|
|
6304
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6305
|
-
className: "lm-label",
|
|
6306
|
-
children: t("skillDirPath")
|
|
6307
|
-
}),
|
|
6308
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6309
|
-
className: "lm-input",
|
|
6310
|
-
value: skillDir,
|
|
6311
|
-
onChange: (e) => setSkillDir(e.target.value)
|
|
6312
|
-
}),
|
|
6313
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6314
|
-
className: "lm-hint",
|
|
6315
|
-
children: t("skillDirHint")
|
|
6316
|
-
})
|
|
6317
|
-
]
|
|
6318
|
-
}),
|
|
6319
|
-
editSkill === void 0 && skillRoot === "project" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6320
|
-
className: "lm-field",
|
|
6321
|
-
children: [
|
|
6322
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6323
|
-
className: "lm-label",
|
|
6324
|
-
children: t("skillProjectPath")
|
|
6325
|
-
}),
|
|
6326
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6327
|
-
className: "lm-input",
|
|
6328
|
-
value: projectPath,
|
|
6329
|
-
onChange: (e) => setProjectPath(e.target.value)
|
|
6330
|
-
}),
|
|
6331
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6332
|
-
className: "lm-hint",
|
|
6333
|
-
children: t("skillProjectPathHint")
|
|
6334
|
-
})
|
|
6335
|
-
]
|
|
6336
|
-
}),
|
|
6337
|
-
renderActions(() => void submitSkill())
|
|
6338
|
-
]
|
|
6384
|
+
children: [
|
|
6385
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6386
|
+
className: "lm-label",
|
|
6387
|
+
children: t("skillDirPath")
|
|
6388
|
+
}),
|
|
6389
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6390
|
+
className: "lm-input",
|
|
6391
|
+
value: skillDir,
|
|
6392
|
+
onChange: (e) => setSkillDir(e.target.value)
|
|
6393
|
+
}),
|
|
6394
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6395
|
+
className: "lm-hint",
|
|
6396
|
+
children: t("skillDirHint")
|
|
6397
|
+
})
|
|
6398
|
+
]
|
|
6399
|
+
}),
|
|
6400
|
+
editSkill === void 0 && skillRoot === "project" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6401
|
+
className: "lm-field",
|
|
6402
|
+
children: [
|
|
6403
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6404
|
+
className: "lm-label",
|
|
6405
|
+
children: t("skillProjectPath")
|
|
6406
|
+
}),
|
|
6407
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6408
|
+
className: "lm-input",
|
|
6409
|
+
value: projectPath,
|
|
6410
|
+
onChange: (e) => setProjectPath(e.target.value)
|
|
6411
|
+
}),
|
|
6412
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6413
|
+
className: "lm-hint",
|
|
6414
|
+
children: t("skillProjectPathHint")
|
|
6415
|
+
})
|
|
6416
|
+
]
|
|
6417
|
+
}),
|
|
6418
|
+
renderActions(() => void submitSkill())
|
|
6419
|
+
]
|
|
6420
|
+
})
|
|
6339
6421
|
})
|
|
6340
|
-
|
|
6422
|
+
]
|
|
6341
6423
|
})
|
|
6342
6424
|
]
|
|
6343
6425
|
})
|
|
@@ -6361,6 +6443,210 @@ const PROJECT_SKILL_SOURCES = /* @__PURE__ */ new Set(["project-dsh", "project-a
|
|
|
6361
6443
|
*/
|
|
6362
6444
|
const ADOPTABLE_SKILL_SOURCES = /* @__PURE__ */ new Set(["user-agents", "custom"]);
|
|
6363
6445
|
//#endregion
|
|
6446
|
+
//#region src/client/skillGroups.ts
|
|
6447
|
+
/**
|
|
6448
|
+
* What the capability list decides about *which* rows it shows, kept out of
|
|
6449
|
+
* `CapabilitySection.tsx` as pure functions.
|
|
6450
|
+
*
|
|
6451
|
+
* The split is deliberate: the grouping rules are where this plugin got its one
|
|
6452
|
+
* real mis-grouping (preset skills filed under 全局技能), and they are also the
|
|
6453
|
+
* only part of the client a test can reach — this repo has no DOM harness, so
|
|
6454
|
+
* anything that lives inside the component can only be checked by hand.
|
|
6455
|
+
*/
|
|
6456
|
+
/** Group tools by server name; skills stay flat (no server), both name-sorted. */
|
|
6457
|
+
function groupRows(rows) {
|
|
6458
|
+
const byServer = /* @__PURE__ */ new Map();
|
|
6459
|
+
const skills = [];
|
|
6460
|
+
for (const row of rows) {
|
|
6461
|
+
if (row.kind === "skill") {
|
|
6462
|
+
skills.push(row);
|
|
6463
|
+
continue;
|
|
6464
|
+
}
|
|
6465
|
+
const server = row.server ?? "built-in";
|
|
6466
|
+
const list = byServer.get(server);
|
|
6467
|
+
if (list === void 0) byServer.set(server, [row]);
|
|
6468
|
+
else list.push(row);
|
|
6469
|
+
}
|
|
6470
|
+
return {
|
|
6471
|
+
servers: [...byServer.entries()].map(([server, tools]) => ({
|
|
6472
|
+
server,
|
|
6473
|
+
tools: [...tools].sort((a, b) => a.name.localeCompare(b.name))
|
|
6474
|
+
})).sort((a, b) => a.server.localeCompare(b.server)),
|
|
6475
|
+
skills: [...skills].sort((a, b) => a.name.localeCompare(b.name))
|
|
6476
|
+
};
|
|
6477
|
+
}
|
|
6478
|
+
/**
|
|
6479
|
+
* Compile the box. A `/…/` wrapper is optional — it is stripped when present, so
|
|
6480
|
+
* both `cordis|ptc` and `/cordis|ptc/` are read as patterns — and it keeps the
|
|
6481
|
+
* common case of pasting a slash-delimited pattern from working.
|
|
6482
|
+
*/
|
|
6483
|
+
function compileNameFilter(input) {
|
|
6484
|
+
const raw = input.trim();
|
|
6485
|
+
const needle = raw.toLowerCase();
|
|
6486
|
+
if (raw === "") return {
|
|
6487
|
+
needle,
|
|
6488
|
+
regex: false,
|
|
6489
|
+
test: () => true
|
|
6490
|
+
};
|
|
6491
|
+
const pattern = raw.length > 1 && raw.startsWith("/") && raw.endsWith("/") ? raw.slice(1, -1) : raw;
|
|
6492
|
+
try {
|
|
6493
|
+
const compiled = new RegExp(pattern, "i");
|
|
6494
|
+
return {
|
|
6495
|
+
needle,
|
|
6496
|
+
regex: true,
|
|
6497
|
+
test: (values) => values.some((v) => v !== void 0 && compiled.test(v))
|
|
6498
|
+
};
|
|
6499
|
+
} catch {
|
|
6500
|
+
return {
|
|
6501
|
+
needle,
|
|
6502
|
+
regex: false,
|
|
6503
|
+
test: (values) => values.some((v) => v !== void 0 && v.toLowerCase().includes(needle))
|
|
6504
|
+
};
|
|
6505
|
+
}
|
|
6506
|
+
}
|
|
6507
|
+
/**
|
|
6508
|
+
* Everything a row is matched against: its own name, plus the dimensions that
|
|
6509
|
+
* name the group it sits in — the MCP server (or the label the built-in group is
|
|
6510
|
+
* drawn with), the skill's source root, and the preset that ships it.
|
|
6511
|
+
*
|
|
6512
|
+
* Group headings are labels rather than row fields, which is exactly why typing
|
|
6513
|
+
* 系统 used to find nothing: the server id is `built-in`, and 系统内置 is only how
|
|
6514
|
+
* that id is painted. Both spellings are searchable now.
|
|
6515
|
+
*/
|
|
6516
|
+
function searchableText(row, labels = {}) {
|
|
6517
|
+
const values = [row.name];
|
|
6518
|
+
if (row.server !== void 0) {
|
|
6519
|
+
values.push(row.server);
|
|
6520
|
+
if (row.server === "built-in") values.push(labels.builtIn);
|
|
6521
|
+
}
|
|
6522
|
+
if (row.source !== void 0) values.push(row.source, labels.source?.(row.source));
|
|
6523
|
+
values.push(row.preset);
|
|
6524
|
+
return values.filter((v) => v !== void 0 && v !== "");
|
|
6525
|
+
}
|
|
6526
|
+
/**
|
|
6527
|
+
* Narrow a list by the filter. Applied *before* grouping so a server header's
|
|
6528
|
+
* own counts describe the rows actually listed under it.
|
|
6529
|
+
*/
|
|
6530
|
+
function filterRows(rows, filter, labels = {}) {
|
|
6531
|
+
if (filter.needle === "") return [...rows];
|
|
6532
|
+
return rows.filter((row) => filter.test(searchableText(row, labels)));
|
|
6533
|
+
}
|
|
6534
|
+
/**
|
|
6535
|
+
* Split skill rows across the three sub-tabs.
|
|
6536
|
+
*
|
|
6537
|
+
* Preset skills are split off by their `preset` field and never by `source`: a
|
|
6538
|
+
* preset's own `skill-filesystem` labels its skills `custom`, exactly like a
|
|
6539
|
+
* user-configured `customSkillDirs` entry, so a source-based rule drags both
|
|
6540
|
+
* into 全局技能 — filing a preset asset as global is precisely the mis-grouping
|
|
6541
|
+
* this split exists to fix. Rows keep the order they arrive in, so callers that
|
|
6542
|
+
* pass name-sorted rows get name-sorted groups.
|
|
6543
|
+
*/
|
|
6544
|
+
function splitSkillGroups(skills) {
|
|
6545
|
+
const presetSkills = [];
|
|
6546
|
+
const projectSkills = [];
|
|
6547
|
+
const globalSkills = [];
|
|
6548
|
+
const presetGroups = [];
|
|
6549
|
+
for (const skill of skills) {
|
|
6550
|
+
const preset = skill.preset;
|
|
6551
|
+
if (preset !== void 0) {
|
|
6552
|
+
presetSkills.push(skill);
|
|
6553
|
+
const group = presetGroups.find(([id]) => id === preset);
|
|
6554
|
+
if (group === void 0) presetGroups.push([preset, [skill]]);
|
|
6555
|
+
else group[1].push(skill);
|
|
6556
|
+
continue;
|
|
6557
|
+
}
|
|
6558
|
+
if (PROJECT_SKILL_SOURCES.has(skill.source ?? "")) projectSkills.push(skill);
|
|
6559
|
+
else globalSkills.push(skill);
|
|
6560
|
+
}
|
|
6561
|
+
presetGroups.sort(([a], [b]) => a.localeCompare(b));
|
|
6562
|
+
return {
|
|
6563
|
+
presetSkills,
|
|
6564
|
+
presetGroups,
|
|
6565
|
+
projectSkills,
|
|
6566
|
+
globalSkills
|
|
6567
|
+
};
|
|
6568
|
+
}
|
|
6569
|
+
/** Rows in one tier. Counts the *displayed* rows, not the whole catalog. */
|
|
6570
|
+
function countByClass(rows, cls) {
|
|
6571
|
+
return rows.reduce((n, row) => row.class === cls ? n + 1 : n, 0);
|
|
6572
|
+
}
|
|
6573
|
+
//#endregion
|
|
6574
|
+
//#region src/client/revalidate.ts
|
|
6575
|
+
/**
|
|
6576
|
+
* When to re-read the catalog, and how often to look — the decisions behind the
|
|
6577
|
+
* management page keeping itself fresh, kept as pure functions.
|
|
6578
|
+
*
|
|
6579
|
+
* Wiring lives in `CapabilitySection.tsx` (timers, the `visibilitychange`
|
|
6580
|
+
* listener, the two subscriptions); everything that could be got wrong lives
|
|
6581
|
+
* here, where a test can reach it. This repo has no DOM harness.
|
|
6582
|
+
*/
|
|
6583
|
+
/**
|
|
6584
|
+
* How often a visible, idle page asks the host for its catalog version.
|
|
6585
|
+
*
|
|
6586
|
+
* This is the resolution of the one case nothing on the wire announces — a file
|
|
6587
|
+
* dropped into an already-registered skill directory — so it doubles as "how
|
|
6588
|
+
* long will I stare at a stale list after an agent changes something next to
|
|
6589
|
+
* me". 5s keeps that from being annoying; the cost is one tiny request per tick,
|
|
6590
|
+
* and only while this settings page is on screen.
|
|
6591
|
+
*/
|
|
6592
|
+
const POLL_INTERVAL_MS = 5e3;
|
|
6593
|
+
/** Ceiling for the failure backoff, so a long outage does not spin. */
|
|
6594
|
+
const POLL_BACKOFF_MAX_MS = 6e4;
|
|
6595
|
+
/**
|
|
6596
|
+
* How long one version sample may take before it is treated as a failure.
|
|
6597
|
+
*
|
|
6598
|
+
* Without this, a request that neither resolves nor rejects would end the poll
|
|
6599
|
+
* for good: the next tick is only scheduled after the current one settles, and
|
|
6600
|
+
* nothing else restarts the effect while the page stays visible and idle.
|
|
6601
|
+
*/
|
|
6602
|
+
const POLL_SAMPLE_TIMEOUT_MS = 15e3;
|
|
6603
|
+
/**
|
|
6604
|
+
* Delay before the next version poll.
|
|
6605
|
+
*
|
|
6606
|
+
* `failures` is the run of consecutive failures ending at this decision: 0
|
|
6607
|
+
* means the last sample succeeded and the plain interval applies, otherwise the
|
|
6608
|
+
* delay doubles until it hits the ceiling. This is what turns "the carrier
|
|
6609
|
+
* died" from a hot loop into a slow retry that heals itself when it comes back.
|
|
6610
|
+
*/
|
|
6611
|
+
function pollDelayMs(failures) {
|
|
6612
|
+
if (!Number.isFinite(failures) || failures <= 0) return POLL_INTERVAL_MS;
|
|
6613
|
+
return Math.min(POLL_INTERVAL_MS * 2 ** failures, POLL_BACKOFF_MAX_MS);
|
|
6614
|
+
}
|
|
6615
|
+
/**
|
|
6616
|
+
* Whether a version sample means the catalog moved under the page.
|
|
6617
|
+
*
|
|
6618
|
+
* Deliberately `!==` rather than `>`: the counter is in-process, so it restarts
|
|
6619
|
+
* from zero when the plugin reloads, and a plain "greater than" test would sit
|
|
6620
|
+
* there believing a fresh host was the same host. `undefined` (nothing sampled
|
|
6621
|
+
* yet) only records — the page has just read the snapshot, and re-reading it
|
|
6622
|
+
* because it has no baseline would be busywork.
|
|
6623
|
+
*/
|
|
6624
|
+
function versionChanged(seen, current) {
|
|
6625
|
+
return seen !== void 0 && seen !== current;
|
|
6626
|
+
}
|
|
6627
|
+
/**
|
|
6628
|
+
* The value of `work`, or a rejection if it has not settled within `ms`.
|
|
6629
|
+
*
|
|
6630
|
+
* Used so a carrier that accepts a request and then goes quiet degrades into
|
|
6631
|
+
* the same retry path as a carrier that refuses one.
|
|
6632
|
+
*/
|
|
6633
|
+
function withDeadline(work, ms) {
|
|
6634
|
+
return new Promise((resolve, reject) => {
|
|
6635
|
+
const timer = setTimeout(() => reject(/* @__PURE__ */ new Error(`timed out after ${ms}ms`)), ms);
|
|
6636
|
+
work.then((value) => {
|
|
6637
|
+
clearTimeout(timer);
|
|
6638
|
+
resolve(value);
|
|
6639
|
+
}, (error) => {
|
|
6640
|
+
clearTimeout(timer);
|
|
6641
|
+
reject(error);
|
|
6642
|
+
});
|
|
6643
|
+
});
|
|
6644
|
+
}
|
|
6645
|
+
/** Whether a signal may act now, or has to be parked until the page is idle. */
|
|
6646
|
+
function canRevalidate(gate) {
|
|
6647
|
+
return gate.visible && gate.ready && !gate.busy && !gate.rebuilding;
|
|
6648
|
+
}
|
|
6649
|
+
//#endregion
|
|
6364
6650
|
//#region src/client/CapabilitySection.tsx
|
|
6365
6651
|
/**
|
|
6366
6652
|
* ⚠️ VERIFIED AGAINST REAL rc.8 CLIENT API.
|
|
@@ -6382,6 +6668,8 @@ const ADOPTABLE_SKILL_SOURCES = /* @__PURE__ */ new Set(["user-agents", "custom"
|
|
|
6382
6668
|
* with flat skill rows carrying the same clickable class
|
|
6383
6669
|
* chip plus a directory tree / file preview
|
|
6384
6670
|
*/
|
|
6671
|
+
const chevronIcons = _deepseek_ai_dsh_client_ui_primitives;
|
|
6672
|
+
const IconTriangleRightFill = chevronIcons.IconTriangleRightFillMedium ?? chevronIcons.IconTriangleRightFill14;
|
|
6385
6673
|
const CLASS_KEYS = [
|
|
6386
6674
|
"resident",
|
|
6387
6675
|
"on-demand",
|
|
@@ -6404,14 +6692,25 @@ const CSS_ID = "capability-menu-section-css";
|
|
|
6404
6692
|
const CSS = `
|
|
6405
6693
|
.mc-section{display:flex;flex-direction:column;gap:12px;color:var(--dsw-alias-label-primary)}
|
|
6406
6694
|
.mc-heading{margin:0;font-size:18px;font-weight:600}
|
|
6695
|
+
.mc-heading-row{display:flex;align-items:baseline;gap:8px;min-width:0;flex-wrap:wrap}
|
|
6696
|
+
.mc-heading-meta{display:flex;align-items:baseline;gap:8px;min-width:0}
|
|
6697
|
+
.mc-feedback-link{margin-left:auto;flex:none;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:20px;white-space:nowrap;text-decoration:none}
|
|
6698
|
+
.mc-feedback-link:hover,.mc-feedback-link:focus-visible{color:#527a9c;text-decoration:underline;text-underline-offset:3px}
|
|
6699
|
+
.mc-feedback-link:focus-visible{outline:2px solid #527a9c;outline-offset:2px;border-radius:2px}
|
|
6700
|
+
body[data-ds-dark-theme] .mc-feedback-link:hover,body[data-ds-dark-theme] .mc-feedback-link:focus-visible{color:#96b6d1}
|
|
6701
|
+
body[data-ds-dark-theme] .mc-feedback-link:focus-visible{outline-color:#96b6d1}
|
|
6702
|
+
.mc-package-name{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:20px}
|
|
6407
6703
|
.mc-desc{margin:0;color:var(--dsw-alias-label-tertiary);font-size:13px}
|
|
6408
6704
|
/* 说明行右侧放只读文档入口:把它从头部的计数行挪出来,计数多(Tools 常驻 ·
|
|
6409
6705
|
167)时那一行不再因此折行。 */
|
|
6410
6706
|
.mc-desc-row{display:flex;align-items:center;justify-content:space-between;gap:12px}
|
|
6411
6707
|
.mc-desc-row .mc-desc{flex:1 1 auto;min-width:0}
|
|
6708
|
+
.mc-version{flex:none;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:20px;white-space:nowrap}
|
|
6412
6709
|
.mc-summary{display:flex;gap:12px;flex-wrap:wrap;justify-content:flex-end;align-items:center;padding-bottom:8px}
|
|
6413
6710
|
.mc-catalog-btn{border:1px solid var(--dsw-alias-border-l2);border-radius:6px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font:inherit;font-size:12px;line-height:20px;padding:0 10px;cursor:pointer;white-space:nowrap}
|
|
6414
6711
|
.mc-catalog-btn:hover{border-color:var(--dsw-alias-border-l3);background:var(--dsw-alias-interactive-bg-hover)}
|
|
6712
|
+
/* 忙碌态靠变淡表示,而不是换文案:换文案会改按钮宽度,把这行挤到第二行。 */
|
|
6713
|
+
.mc-catalog-btn:disabled{cursor:default;opacity:.6}
|
|
6415
6714
|
.mc-catalog-tabs{padding:8px 16px 0}
|
|
6416
6715
|
.mc-catalog-path{padding:8px 16px 0;margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);word-break:break-all}
|
|
6417
6716
|
.mc-chip{display:inline-flex;align-items:center;gap:6px;font-size:12px;line-height:20px;white-space:nowrap}
|
|
@@ -6435,6 +6734,15 @@ body[data-ds-dark-theme] .mc-chip--resident{color:#96b6d1}
|
|
|
6435
6734
|
body[data-ds-dark-theme] .mc-chip--on-demand{color:#d4b26b}
|
|
6436
6735
|
body[data-ds-dark-theme] .mc-chip--disabled{color:#b8abad}
|
|
6437
6736
|
.mc-tabs{border-bottom:1px solid var(--dsw-alias-border-l2);display:flex;align-items:flex-end;justify-content:space-between;gap:22px}
|
|
6737
|
+
/* 名字过滤框:两个 tab 共用一条,宽度占满,与下方列表左对齐。
|
|
6738
|
+
它属于上面的主 tab 栏,不属于下面的列表,所以两侧都把 .mc-section 的 12px
|
|
6739
|
+
栏距收掉一部分:下划线—6px—输入框—8px—全局/项目 子页签文字(后者里的 4px
|
|
6740
|
+
是子页签自己的 padding-top)。改之前是 12+12=24px 和 12+4=16px,读起来像
|
|
6741
|
+
两个不相干的块。 */
|
|
6742
|
+
.mc-filter{display:flex;padding-top:0;margin-top:-6px;margin-bottom:-8px}
|
|
6743
|
+
.mc-filter input{box-sizing:border-box;width:100%;min-width:0;padding:6px 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;background:var(--dsw-alias-bg-layer-1);color:inherit;font:inherit;font-size:13px;line-height:18px}
|
|
6744
|
+
.mc-filter input:hover{border-color:var(--dsw-alias-border-l3)}
|
|
6745
|
+
.mc-filter input:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px;border-color:transparent}
|
|
6438
6746
|
/* Skills sub-tab bar (全局技能/项目技能): same underline chrome, no right-side summary. */
|
|
6439
6747
|
.mc-subtabs{border-bottom:1px solid var(--dsw-alias-border-l2);display:flex;align-items:flex-end;gap:22px}
|
|
6440
6748
|
.mc-tab-group{display:flex;align-items:flex-end;gap:22px}
|
|
@@ -6442,13 +6750,17 @@ body[data-ds-dark-theme] .mc-chip--disabled{color:#b8abad}
|
|
|
6442
6750
|
.mc-tab:hover,.mc-tab[data-active=true]{color:var(--dsw-alias-label-primary)}
|
|
6443
6751
|
.mc-tab[data-active=true]:after,.mc-tab:focus-visible:after{background:var(--dsw-alias-label-primary);content:"";border-radius:2px 2px 0 0;height:2px;position:absolute;bottom:-1px;left:0;right:0}
|
|
6444
6752
|
.mc-tab:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px;color:var(--dsw-alias-label-primary);border-radius:2px}
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
6753
|
+
/* 面板的第一行必须和另一个 tab 的第一行从过滤框下沿出发走同样远,否则切 tab 时
|
|
6754
|
+
首行会跳一下。这里留 4px,加上 .mc-section 的 4px 栏距(过滤框那侧被 -8px 收
|
|
6755
|
+
掉后剩 4px)= 8px,正好是 Skills 面板子页签文字的位置(4px 栏距 + 子页签自己
|
|
6756
|
+
的 4px padding-top)。原来这里是 12px,Tools 的首个分组因此落在过滤框下 16px
|
|
6757
|
+
处,比「全局技能」那行低一半。 */
|
|
6758
|
+
.mc-panel{min-width:0;padding-top:4px}
|
|
6759
|
+
/* Skills panel: its first row is the 全局技能/项目技能 sub-tab bar, and that bar
|
|
6760
|
+
belongs to the header stack above it (rule → name filter → sub-tabs) rather
|
|
6761
|
+
than to the list below. So the panel adds no padding of its own, and the
|
|
6762
|
+
sub-tab labels keep a 4px top padding instead of the 7px they share with the
|
|
6763
|
+
primary tabs. */
|
|
6452
6764
|
.mc-panel--tight{padding-top:0}
|
|
6453
6765
|
.mc-panel--tight .mc-subtabs .mc-tab{padding-top:4px}
|
|
6454
6766
|
.mc-panel-inner{display:flex;flex-direction:column;gap:14px}
|
|
@@ -6490,7 +6802,7 @@ body[data-ds-dark-theme] .mc-count--disabled{color:#b8abad}
|
|
|
6490
6802
|
.mc-error p{margin:0}
|
|
6491
6803
|
.mc-error p.mc-error-hint{margin-top:8px;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}
|
|
6492
6804
|
.mc-error-actions{display:flex;justify-content:flex-end;margin-top:10px}
|
|
6493
|
-
.mc-notice{padding:10px 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;color:var(--dsw-alias-label-secondary);font-size:13px}
|
|
6805
|
+
.mc-notice{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;color:var(--dsw-alias-label-secondary);font-size:13px}
|
|
6494
6806
|
.mc-skill{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;overflow:hidden;background:var(--dsw-alias-bg-layer-1)}
|
|
6495
6807
|
.mc-skill-row{box-sizing:border-box;display:flex;align-items:center;gap:10px;width:100%;min-width:0;padding:10px 12px;background:0 0;border:0;color:inherit;font:inherit;text-align:left;cursor:pointer}
|
|
6496
6808
|
.mc-skill-row:hover{background:var(--dsw-alias-interactive-bg-hover)}
|
|
@@ -6508,6 +6820,9 @@ body[data-ds-dark-theme] .mc-count--disabled{color:#b8abad}
|
|
|
6508
6820
|
.mc-confirm-path{font-family:var(--dsw-font-markdown-code-block-font-family);word-break:break-all}
|
|
6509
6821
|
.mc-confirm-actions{display:flex;justify-content:flex-end;gap:8px}
|
|
6510
6822
|
.mc-skill-body{border-top:1px solid var(--dsw-alias-border-l1);padding:8px 12px 12px}
|
|
6823
|
+
/* 预设技能组:预设 id 是页签之下的分组小标题,不是新一级页签。 */
|
|
6824
|
+
.mc-skill-group{display:flex;flex-direction:column;gap:8px;min-width:0}
|
|
6825
|
+
.mc-skill-group-title{font-size:12px;line-height:18px;font-weight:600;color:var(--dsw-alias-label-secondary);padding:0 2px}
|
|
6511
6826
|
.mc-tree{display:flex;flex-direction:column;gap:2px;font-size:13px;line-height:20px}
|
|
6512
6827
|
.mc-tree-row{display:flex;align-items:center;gap:8px;padding:3px 4px;border-radius:6px;cursor:pointer;min-width:0}
|
|
6513
6828
|
.mc-tree-row:hover{background:var(--dsw-alias-interactive-bg-hover)}
|
|
@@ -6521,7 +6836,13 @@ body[data-ds-dark-theme] .mc-count--disabled{color:#b8abad}
|
|
|
6521
6836
|
.mc-preview-title{font-size:13px;line-height:20px;font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}
|
|
6522
6837
|
.mc-preview-close{flex:none;padding:2px 8px;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font:inherit;font-size:12px;cursor:pointer}
|
|
6523
6838
|
.mc-preview-close:hover{background:var(--dsw-alias-interactive-bg-hover)}
|
|
6524
|
-
.mc-preview-body{overflow:auto;flex:1 1 auto;min-height:0;padding:14px 16px;
|
|
6839
|
+
.mc-preview-body{overflow:auto;flex:1 1 auto;min-height:0;padding:14px 16px;color:var(--dsw-alias-label-primary)}
|
|
6840
|
+
.mc-preview-body>pre,.mc-preview-plain{margin:0;font-family:var(--dsw-font-markdown-code-block-font-family);font-size:12px;line-height:20px;white-space:pre-wrap;overflow-wrap:anywhere}
|
|
6841
|
+
.mc-tool-description{padding-bottom:12px;margin-bottom:12px;border-bottom:1px solid var(--dsw-alias-border-l1)}
|
|
6842
|
+
.mc-frontmatter{width:100%;margin:0 0 20px;border-collapse:collapse;table-layout:fixed;font-size:13px;line-height:20px}
|
|
6843
|
+
.mc-frontmatter th,.mc-frontmatter td{padding:8px 10px;border-bottom:1px solid var(--dsw-alias-border-l1);vertical-align:top;text-align:left}
|
|
6844
|
+
.mc-frontmatter th{width:130px;font-weight:500;color:var(--dsw-alias-label-secondary)}
|
|
6845
|
+
.mc-frontmatter td{color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;white-space:pre-wrap}
|
|
6525
6846
|
.mc-preview-hint{padding:16px;text-align:center;font-size:13px;color:var(--dsw-alias-label-tertiary)}
|
|
6526
6847
|
`;
|
|
6527
6848
|
if (typeof document !== "undefined" && document.querySelector(`style[data-css-id="${CSS_ID}"]`) === null) {
|
|
@@ -6530,6 +6851,78 @@ if (typeof document !== "undefined" && document.querySelector(`style[data-css-id
|
|
|
6530
6851
|
tag.textContent = CSS;
|
|
6531
6852
|
document.head.appendChild(tag);
|
|
6532
6853
|
}
|
|
6854
|
+
/** Reuse dsh's safe Markdown renderer in tool descriptions and skill files. */
|
|
6855
|
+
function MarkdownPreview(props) {
|
|
6856
|
+
const { text, t } = props;
|
|
6857
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MarkdownText, {
|
|
6858
|
+
text,
|
|
6859
|
+
labels: {
|
|
6860
|
+
code: {
|
|
6861
|
+
copyLabel: t("markdownCopy"),
|
|
6862
|
+
copiedLabel: t("markdownCopied"),
|
|
6863
|
+
toolbarLabels: {
|
|
6864
|
+
codeLabel: t("markdownCode"),
|
|
6865
|
+
wrapLabel: t("markdownWrap"),
|
|
6866
|
+
unwrapLabel: t("markdownUnwrap")
|
|
6867
|
+
}
|
|
6868
|
+
},
|
|
6869
|
+
footnotes: t("markdownFootnotes")
|
|
6870
|
+
}
|
|
6871
|
+
});
|
|
6872
|
+
}
|
|
6873
|
+
/** Parse the simple property lines shown by GitHub above a Markdown document. */
|
|
6874
|
+
function parseFrontmatterProperties(source) {
|
|
6875
|
+
const properties = [];
|
|
6876
|
+
for (const line of source.split(/\r?\n/)) {
|
|
6877
|
+
const field = /^([A-Za-z0-9_.-]+):(?:[ \t]*(.*))?$/.exec(line);
|
|
6878
|
+
if (field !== null) {
|
|
6879
|
+
properties.push({
|
|
6880
|
+
name: field[1],
|
|
6881
|
+
value: field[2] ?? ""
|
|
6882
|
+
});
|
|
6883
|
+
continue;
|
|
6884
|
+
}
|
|
6885
|
+
const continuation = /^\s+(.+)$/.exec(line);
|
|
6886
|
+
if (continuation !== null && properties.length > 0) {
|
|
6887
|
+
const last = properties[properties.length - 1];
|
|
6888
|
+
last.value = last.value.length === 0 ? continuation[1] : `${last.value}\n${continuation[1]}`;
|
|
6889
|
+
}
|
|
6890
|
+
}
|
|
6891
|
+
return properties.map((property) => ({
|
|
6892
|
+
...property,
|
|
6893
|
+
value: property.value.trimEnd()
|
|
6894
|
+
}));
|
|
6895
|
+
}
|
|
6896
|
+
/** Split YAML properties from the Markdown body. */
|
|
6897
|
+
function splitMarkdownFrontmatter(text) {
|
|
6898
|
+
const match = /^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)/.exec(text);
|
|
6899
|
+
if (match === null) return { body: text };
|
|
6900
|
+
return {
|
|
6901
|
+
frontmatter: parseFrontmatterProperties(match[1] ?? ""),
|
|
6902
|
+
body: text.slice(match[0].length)
|
|
6903
|
+
};
|
|
6904
|
+
}
|
|
6905
|
+
function MarkdownFilePreview(props) {
|
|
6906
|
+
const { frontmatter, body } = splitMarkdownFrontmatter(props.text);
|
|
6907
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [frontmatter !== void 0 && frontmatter.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("table", {
|
|
6908
|
+
className: "mc-frontmatter",
|
|
6909
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: frontmatter.map((property) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {
|
|
6910
|
+
scope: "row",
|
|
6911
|
+
children: property.name
|
|
6912
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: property.value })] }, property.name)) })
|
|
6913
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkdownPreview, {
|
|
6914
|
+
text: body,
|
|
6915
|
+
t: props.t
|
|
6916
|
+
})] });
|
|
6917
|
+
}
|
|
6918
|
+
/**
|
|
6919
|
+
* Which build of the plugin is on screen. Several rounds of "is my profile
|
|
6920
|
+
* running the version I just installed?" had to be answered by grepping
|
|
6921
|
+
* `node_modules`, because the panel said nothing about itself. The value is
|
|
6922
|
+
* inlined by `tsdown` (see `src/client/env.d.ts`).
|
|
6923
|
+
*/
|
|
6924
|
+
const PLUGIN_VERSION = "0.1.5";
|
|
6925
|
+
const PLUGIN_TITLE = `capability-menu v${PLUGIN_VERSION}`;
|
|
6533
6926
|
/**
|
|
6534
6927
|
* dsh's skill source labels, shown on rows this plugin cannot edit — so "why is
|
|
6535
6928
|
* there no 编辑 button" has a visible answer. Labels that name one directory are
|
|
@@ -6555,32 +6948,22 @@ function sourceLabel(source, t) {
|
|
|
6555
6948
|
const key = SOURCE_LABEL_KEYS[source];
|
|
6556
6949
|
return key === void 0 ? source : t(key);
|
|
6557
6950
|
}
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
return {
|
|
6572
|
-
servers: [...byServer.entries()].map(([server, tools]) => ({
|
|
6573
|
-
server,
|
|
6574
|
-
tools: [...tools].sort((a, b) => a.name.localeCompare(b.name))
|
|
6575
|
-
})).sort((a, b) => a.server.localeCompare(b.server)),
|
|
6576
|
-
skills: [...skills].sort((a, b) => a.name.localeCompare(b.name))
|
|
6577
|
-
};
|
|
6578
|
-
}
|
|
6579
|
-
function countByClass(rows, cls) {
|
|
6580
|
-
return rows.reduce((n, row) => row.class === cls ? n + 1 : n, 0);
|
|
6951
|
+
/**
|
|
6952
|
+
* Whether the document is visible. A hidden settings tab does no polling and
|
|
6953
|
+
* takes no re-reads; coming back is itself a revalidation trigger.
|
|
6954
|
+
*/
|
|
6955
|
+
function useDocumentVisible() {
|
|
6956
|
+
const [visible, setVisible] = (0, react.useState)(true);
|
|
6957
|
+
(0, react.useEffect)(() => {
|
|
6958
|
+
const update = () => setVisible(document.visibilityState !== "hidden");
|
|
6959
|
+
update();
|
|
6960
|
+
document.addEventListener("visibilitychange", update);
|
|
6961
|
+
return () => document.removeEventListener("visibilitychange", update);
|
|
6962
|
+
}, []);
|
|
6963
|
+
return visible;
|
|
6581
6964
|
}
|
|
6582
6965
|
function CapabilitySection(props) {
|
|
6583
|
-
const { remote, t, mountError } = props;
|
|
6966
|
+
const { remote, t, mountError, subscribeSignals } = props;
|
|
6584
6967
|
const [state, setState] = (0, react.useState)(() => {
|
|
6585
6968
|
const cached = cachedSnapshot();
|
|
6586
6969
|
return cached === void 0 ? { status: "loading" } : {
|
|
@@ -6590,6 +6973,8 @@ function CapabilitySection(props) {
|
|
|
6590
6973
|
});
|
|
6591
6974
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
6592
6975
|
const [notice, setNotice] = (0, react.useState)(null);
|
|
6976
|
+
const [retryMcp, setRetryMcp] = (0, react.useState)(null);
|
|
6977
|
+
const queuedMcpRefresh = (0, react.useRef)(null);
|
|
6593
6978
|
const [openServers, setOpenServers] = (0, react.useState)(/* @__PURE__ */ new Set());
|
|
6594
6979
|
const [activeTab, setActiveTab] = (0, react.useState)("tools");
|
|
6595
6980
|
const reload = (0, react.useCallback)(async () => {
|
|
@@ -6625,6 +7010,7 @@ function CapabilitySection(props) {
|
|
|
6625
7010
|
const refreshCatalog = (0, react.useCallback)(async () => {
|
|
6626
7011
|
if (refreshing) return;
|
|
6627
7012
|
setRefreshing(true);
|
|
7013
|
+
setRetryMcp(null);
|
|
6628
7014
|
try {
|
|
6629
7015
|
if (remote !== void 0) unwrap(await remote.refresh(), "capabilityPolicy.refresh");
|
|
6630
7016
|
await reload();
|
|
@@ -6641,6 +7027,61 @@ function CapabilitySection(props) {
|
|
|
6641
7027
|
reload,
|
|
6642
7028
|
t
|
|
6643
7029
|
]);
|
|
7030
|
+
/**
|
|
7031
|
+
* A new MCP row is persisted before dsh applies it and registers its tools.
|
|
7032
|
+
* Wait for that asynchronous host reload instead of taking one snapshot too
|
|
7033
|
+
* early and requiring the operator to leave and reopen this settings page.
|
|
7034
|
+
*/
|
|
7035
|
+
const refreshRegisteredMcp = (0, react.useCallback)(async (serverName) => {
|
|
7036
|
+
if (refreshing) {
|
|
7037
|
+
queuedMcpRefresh.current = serverName;
|
|
7038
|
+
return;
|
|
7039
|
+
}
|
|
7040
|
+
if (remote === void 0) return;
|
|
7041
|
+
setRefreshing(true);
|
|
7042
|
+
setRetryMcp(null);
|
|
7043
|
+
try {
|
|
7044
|
+
unwrap(await remote.refresh(), "capabilityPolicy.refresh");
|
|
7045
|
+
const snapshot = await waitForMcpTools(remote, serverName, [
|
|
7046
|
+
250,
|
|
7047
|
+
500,
|
|
7048
|
+
750,
|
|
7049
|
+
1e3,
|
|
7050
|
+
1e3,
|
|
7051
|
+
1e3,
|
|
7052
|
+
1e3,
|
|
7053
|
+
1e3,
|
|
7054
|
+
1e3
|
|
7055
|
+
]);
|
|
7056
|
+
if (snapshot !== void 0) {
|
|
7057
|
+
setState({
|
|
7058
|
+
status: "ready",
|
|
7059
|
+
snapshot: rememberSnapshot(snapshot)
|
|
7060
|
+
});
|
|
7061
|
+
setNotice(null);
|
|
7062
|
+
setRetryMcp(null);
|
|
7063
|
+
} else {
|
|
7064
|
+
setNotice(t("mcpToolsPending", { name: serverName }));
|
|
7065
|
+
setRetryMcp(serverName);
|
|
7066
|
+
}
|
|
7067
|
+
} catch (e) {
|
|
7068
|
+
console.error("[capability-menu] MCP refresh failed:", e);
|
|
7069
|
+
setNotice(t("refreshFailed"));
|
|
7070
|
+
setRetryMcp(serverName);
|
|
7071
|
+
} finally {
|
|
7072
|
+
setRefreshing(false);
|
|
7073
|
+
}
|
|
7074
|
+
}, [
|
|
7075
|
+
refreshing,
|
|
7076
|
+
remote,
|
|
7077
|
+
t
|
|
7078
|
+
]);
|
|
7079
|
+
(0, react.useEffect)(() => {
|
|
7080
|
+
if (refreshing || queuedMcpRefresh.current === null) return;
|
|
7081
|
+
const serverName = queuedMcpRefresh.current;
|
|
7082
|
+
queuedMcpRefresh.current = null;
|
|
7083
|
+
refreshRegisteredMcp(serverName);
|
|
7084
|
+
}, [refreshing, refreshRegisteredMcp]);
|
|
6644
7085
|
const toggleServer = (0, react.useCallback)((server) => {
|
|
6645
7086
|
setOpenServers((prev) => {
|
|
6646
7087
|
const next = new Set(prev);
|
|
@@ -6649,6 +7090,84 @@ function CapabilitySection(props) {
|
|
|
6649
7090
|
return next;
|
|
6650
7091
|
});
|
|
6651
7092
|
}, []);
|
|
7093
|
+
const visible = useDocumentVisible();
|
|
7094
|
+
const [pendingSignal, setPendingSignal] = (0, react.useState)(false);
|
|
7095
|
+
const seenVersion = (0, react.useRef)(void 0);
|
|
7096
|
+
const pollFailures = (0, react.useRef)(0);
|
|
7097
|
+
const gate = (0, react.useRef)({
|
|
7098
|
+
visible: true,
|
|
7099
|
+
ready: false,
|
|
7100
|
+
busy: false,
|
|
7101
|
+
rebuilding: false
|
|
7102
|
+
});
|
|
7103
|
+
(0, react.useEffect)(() => {
|
|
7104
|
+
gate.current = {
|
|
7105
|
+
visible,
|
|
7106
|
+
ready: state.status === "ready",
|
|
7107
|
+
busy,
|
|
7108
|
+
rebuilding: refreshing
|
|
7109
|
+
};
|
|
7110
|
+
});
|
|
7111
|
+
/**
|
|
7112
|
+
* A signal that the host catalog may have moved. Runs now when the page is
|
|
7113
|
+
* idle; otherwise parks until it is, so a signal that lands mid-click is
|
|
7114
|
+
* never dropped (and never repaints the row under the cursor).
|
|
7115
|
+
*/
|
|
7116
|
+
const onExternalSignal = (0, react.useCallback)(() => {
|
|
7117
|
+
if (canRevalidate(gate.current)) refreshCatalog();
|
|
7118
|
+
else setPendingSignal(true);
|
|
7119
|
+
}, [refreshCatalog]);
|
|
7120
|
+
(0, react.useEffect)(() => {
|
|
7121
|
+
return subscribeSignals?.(onExternalSignal);
|
|
7122
|
+
}, [subscribeSignals, onExternalSignal]);
|
|
7123
|
+
(0, react.useEffect)(() => {
|
|
7124
|
+
if (!pendingSignal) return;
|
|
7125
|
+
if (!canRevalidate({
|
|
7126
|
+
visible,
|
|
7127
|
+
ready: state.status === "ready",
|
|
7128
|
+
busy,
|
|
7129
|
+
rebuilding: refreshing
|
|
7130
|
+
})) return;
|
|
7131
|
+
setPendingSignal(false);
|
|
7132
|
+
refreshCatalog();
|
|
7133
|
+
}, [
|
|
7134
|
+
pendingSignal,
|
|
7135
|
+
visible,
|
|
7136
|
+
state.status,
|
|
7137
|
+
busy,
|
|
7138
|
+
refreshing,
|
|
7139
|
+
refreshCatalog
|
|
7140
|
+
]);
|
|
7141
|
+
(0, react.useEffect)(() => {
|
|
7142
|
+
if (!visible || state.status !== "ready" || busy || refreshing) return;
|
|
7143
|
+
let stopped = false;
|
|
7144
|
+
let timer;
|
|
7145
|
+
const tick = async () => {
|
|
7146
|
+
try {
|
|
7147
|
+
const version = unwrap(await withDeadline(remote.catalogVersion(), POLL_SAMPLE_TIMEOUT_MS), "capabilityPolicy.catalogVersion");
|
|
7148
|
+
pollFailures.current = 0;
|
|
7149
|
+
if (versionChanged(seenVersion.current, version)) {
|
|
7150
|
+
seenVersion.current = version;
|
|
7151
|
+
await reload();
|
|
7152
|
+
}
|
|
7153
|
+
} catch {
|
|
7154
|
+
pollFailures.current += 1;
|
|
7155
|
+
}
|
|
7156
|
+
if (!stopped) timer = setTimeout(() => void tick(), pollDelayMs(pollFailures.current));
|
|
7157
|
+
};
|
|
7158
|
+
timer = setTimeout(() => void tick(), 0);
|
|
7159
|
+
return () => {
|
|
7160
|
+
stopped = true;
|
|
7161
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
7162
|
+
};
|
|
7163
|
+
}, [
|
|
7164
|
+
visible,
|
|
7165
|
+
state.status,
|
|
7166
|
+
busy,
|
|
7167
|
+
refreshing,
|
|
7168
|
+
remote,
|
|
7169
|
+
reload
|
|
7170
|
+
]);
|
|
6652
7171
|
/** Move one or more capability ids to the next class in the click cycle. */
|
|
6653
7172
|
const cycleClass = (0, react.useCallback)(async (ids, kind) => {
|
|
6654
7173
|
if (ids.length === 0 || busy) return;
|
|
@@ -6729,9 +7248,15 @@ function CapabilitySection(props) {
|
|
|
6729
7248
|
})
|
|
6730
7249
|
]
|
|
6731
7250
|
}),
|
|
6732
|
-
notice !== null && /* @__PURE__ */ (0, react_jsx_runtime.
|
|
7251
|
+
notice !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6733
7252
|
className: "mc-notice",
|
|
6734
|
-
children: notice
|
|
7253
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: notice }), retryMcp !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7254
|
+
type: "button",
|
|
7255
|
+
className: "mc-catalog-btn",
|
|
7256
|
+
disabled: refreshing,
|
|
7257
|
+
onClick: () => void refreshRegisteredMcp(retryMcp),
|
|
7258
|
+
children: t("retry")
|
|
7259
|
+
})]
|
|
6735
7260
|
}),
|
|
6736
7261
|
state.status === "ready" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReadyBody, {
|
|
6737
7262
|
remote,
|
|
@@ -6744,19 +7269,26 @@ function CapabilitySection(props) {
|
|
|
6744
7269
|
onToggleServer: toggleServer,
|
|
6745
7270
|
onCycle: cycleClass,
|
|
6746
7271
|
onRefresh: () => void refreshCatalog(),
|
|
6747
|
-
|
|
7272
|
+
onRefreshMcp: (serverName) => void refreshRegisteredMcp(serverName),
|
|
6748
7273
|
onNotice: setNotice
|
|
6749
7274
|
})
|
|
6750
7275
|
]
|
|
6751
7276
|
});
|
|
6752
7277
|
}
|
|
6753
7278
|
function ReadyBody(props) {
|
|
6754
|
-
const { remote, snapshot, openServers, busy, activeTab, t, onTabChange, onToggleServer, onCycle, onRefresh,
|
|
6755
|
-
|
|
6756
|
-
const
|
|
6757
|
-
const
|
|
7279
|
+
const { remote, snapshot, openServers, busy, activeTab, t, onTabChange, onToggleServer, onCycle, onRefresh, onRefreshMcp, onNotice } = props;
|
|
7280
|
+
/** Name filter, shared by both tabs: it narrows whichever list is shown. */
|
|
7281
|
+
const [filter, setFilter] = (0, react.useState)("");
|
|
7282
|
+
const nameFilter = compileNameFilter(filter);
|
|
7283
|
+
const needle = nameFilter.needle;
|
|
7284
|
+
const shownRows = filterRows(snapshot.rows, nameFilter, {
|
|
7285
|
+
builtIn: t("builtInGroup"),
|
|
7286
|
+
source: (source) => sourceLabel(source, t)
|
|
7287
|
+
});
|
|
7288
|
+
const { servers, skills } = groupRows(shownRows);
|
|
7289
|
+
const { presetSkills, presetGroups, projectSkills, globalSkills } = splitSkillGroups(skills);
|
|
6758
7290
|
const [skillTab, setSkillTab] = (0, react.useState)("global");
|
|
6759
|
-
const statRows = activeTab === "tools" ?
|
|
7291
|
+
const statRows = activeTab === "tools" ? shownRows.filter((r) => r.kind === "tool") : skillTab === "project" ? projectSkills : skillTab === "preset" ? presetSkills : globalSkills;
|
|
6760
7292
|
const summary = CLASS_KEYS.map((cls) => ({
|
|
6761
7293
|
cls,
|
|
6762
7294
|
count: countByClass(statRows, cls)
|
|
@@ -6895,9 +7427,33 @@ function ReadyBody(props) {
|
|
|
6895
7427
|
}
|
|
6896
7428
|
}, [remote]);
|
|
6897
7429
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
6898
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
6899
|
-
className: "mc-heading",
|
|
6900
|
-
children:
|
|
7430
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7431
|
+
className: "mc-heading-row",
|
|
7432
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7433
|
+
className: "mc-heading-meta",
|
|
7434
|
+
children: [
|
|
7435
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
7436
|
+
className: "mc-heading",
|
|
7437
|
+
children: t("title")
|
|
7438
|
+
}),
|
|
7439
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7440
|
+
className: "mc-package-name",
|
|
7441
|
+
children: t("packageName")
|
|
7442
|
+
}),
|
|
7443
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
7444
|
+
className: "mc-version",
|
|
7445
|
+
title: PLUGIN_TITLE,
|
|
7446
|
+
children: ["v", PLUGIN_VERSION]
|
|
7447
|
+
})
|
|
7448
|
+
]
|
|
7449
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
7450
|
+
className: "mc-feedback-link",
|
|
7451
|
+
href: "https://github.com/PKUfudawei/dsh-capability-menu",
|
|
7452
|
+
target: "_blank",
|
|
7453
|
+
rel: "noopener noreferrer",
|
|
7454
|
+
"aria-label": t("feedback"),
|
|
7455
|
+
children: t("feedback")
|
|
7456
|
+
})]
|
|
6901
7457
|
}),
|
|
6902
7458
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6903
7459
|
className: "mc-desc-row",
|
|
@@ -6936,35 +7492,36 @@ function ReadyBody(props) {
|
|
|
6936
7492
|
})]
|
|
6937
7493
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6938
7494
|
className: "mc-summary",
|
|
6939
|
-
children: [
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
6946
|
-
|
|
6947
|
-
|
|
6948
|
-
|
|
6949
|
-
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
6953
|
-
|
|
6954
|
-
|
|
6955
|
-
|
|
6956
|
-
disabled: refreshing,
|
|
6957
|
-
children: refreshing ? t("refreshing") : t("refresh")
|
|
6958
|
-
}),
|
|
6959
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6960
|
-
type: "button",
|
|
6961
|
-
className: "mc-catalog-btn",
|
|
6962
|
-
onClick: () => setRegisterOpen(true),
|
|
6963
|
-
children: t("registerCapability")
|
|
6964
|
-
})
|
|
6965
|
-
]
|
|
7495
|
+
children: [summary.map(({ cls, count }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
7496
|
+
className: `mc-chip mc-chip--${cls}`,
|
|
7497
|
+
children: [
|
|
7498
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7499
|
+
className: `mc-dot mc-dot--${cls}`,
|
|
7500
|
+
"aria-hidden": "true"
|
|
7501
|
+
}),
|
|
7502
|
+
t(CLASS_SHORT_KEYS[cls]),
|
|
7503
|
+
" · ",
|
|
7504
|
+
count
|
|
7505
|
+
]
|
|
7506
|
+
}, cls)), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7507
|
+
type: "button",
|
|
7508
|
+
className: "mc-catalog-btn",
|
|
7509
|
+
onClick: () => setRegisterOpen(true),
|
|
7510
|
+
children: t("registerCapability")
|
|
7511
|
+
})]
|
|
6966
7512
|
})]
|
|
6967
7513
|
}),
|
|
7514
|
+
snapshot.rows.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7515
|
+
className: "mc-filter",
|
|
7516
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
7517
|
+
type: "search",
|
|
7518
|
+
value: filter,
|
|
7519
|
+
placeholder: t("filterByName"),
|
|
7520
|
+
"aria-label": t("filterByName"),
|
|
7521
|
+
title: t("filterHint"),
|
|
7522
|
+
onChange: (e) => setFilter(e.target.value)
|
|
7523
|
+
})
|
|
7524
|
+
}),
|
|
6968
7525
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6969
7526
|
role: "tabpanel",
|
|
6970
7527
|
hidden: activeTab !== "tools",
|
|
@@ -6973,7 +7530,7 @@ function ReadyBody(props) {
|
|
|
6973
7530
|
className: "mc-panel-inner",
|
|
6974
7531
|
children: servers.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6975
7532
|
className: "mc-empty",
|
|
6976
|
-
children: t("emptyTools")
|
|
7533
|
+
children: needle === "" ? t("emptyTools") : t("filterNoMatch")
|
|
6977
7534
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: servers.map(({ server, tools }) => {
|
|
6978
7535
|
const open = openServers.has(server);
|
|
6979
7536
|
const counts = CLASS_KEYS.map((cls) => ({
|
|
@@ -6995,7 +7552,7 @@ function ReadyBody(props) {
|
|
|
6995
7552
|
}
|
|
6996
7553
|
},
|
|
6997
7554
|
children: [
|
|
6998
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
7555
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconTriangleRightFill, {
|
|
6999
7556
|
size: 12,
|
|
7000
7557
|
className: `mc-chevron${open ? " mc-chevron--open" : ""}`
|
|
7001
7558
|
}),
|
|
@@ -7090,35 +7647,47 @@ function ReadyBody(props) {
|
|
|
7090
7647
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7091
7648
|
role: "tabpanel",
|
|
7092
7649
|
hidden: activeTab !== "skills",
|
|
7093
|
-
className:
|
|
7650
|
+
className: `mc-panel${skills.length > 0 ? " mc-panel--tight" : ""}`,
|
|
7094
7651
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7095
7652
|
className: "mc-panel-inner",
|
|
7096
7653
|
children: skills.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
7097
7654
|
className: "mc-empty",
|
|
7098
|
-
children: t("emptySkills")
|
|
7655
|
+
children: needle === "" ? t("emptySkills") : t("filterNoMatch")
|
|
7099
7656
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7100
7657
|
className: "mc-subtabs",
|
|
7101
7658
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7102
7659
|
className: "mc-tab-group",
|
|
7103
7660
|
role: "tablist",
|
|
7104
7661
|
"aria-label": t("skillsGroup"),
|
|
7105
|
-
children: [
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
|
|
7117
|
-
|
|
7118
|
-
|
|
7119
|
-
|
|
7120
|
-
|
|
7121
|
-
|
|
7662
|
+
children: [
|
|
7663
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7664
|
+
type: "button",
|
|
7665
|
+
role: "tab",
|
|
7666
|
+
className: "mc-tab",
|
|
7667
|
+
"aria-selected": skillTab === "global",
|
|
7668
|
+
"data-active": skillTab === "global" ? "true" : void 0,
|
|
7669
|
+
onClick: () => setSkillTab("global"),
|
|
7670
|
+
children: t("globalSkills")
|
|
7671
|
+
}),
|
|
7672
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7673
|
+
type: "button",
|
|
7674
|
+
role: "tab",
|
|
7675
|
+
className: "mc-tab",
|
|
7676
|
+
"aria-selected": skillTab === "project",
|
|
7677
|
+
"data-active": skillTab === "project" ? "true" : void 0,
|
|
7678
|
+
onClick: () => setSkillTab("project"),
|
|
7679
|
+
children: t("projectSkills")
|
|
7680
|
+
}),
|
|
7681
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7682
|
+
type: "button",
|
|
7683
|
+
role: "tab",
|
|
7684
|
+
className: "mc-tab",
|
|
7685
|
+
"aria-selected": skillTab === "preset",
|
|
7686
|
+
"data-active": skillTab === "preset" ? "true" : void 0,
|
|
7687
|
+
onClick: () => setSkillTab("preset"),
|
|
7688
|
+
children: t("presetSkills")
|
|
7689
|
+
})
|
|
7690
|
+
]
|
|
7122
7691
|
})
|
|
7123
7692
|
}), skillTab === "global" ? globalSkills.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillList, {
|
|
7124
7693
|
skills: globalSkills,
|
|
@@ -7132,7 +7701,7 @@ function ReadyBody(props) {
|
|
|
7132
7701
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
7133
7702
|
className: "mc-empty",
|
|
7134
7703
|
children: t("emptyGlobalSkills")
|
|
7135
|
-
}) : projectSkills.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillList, {
|
|
7704
|
+
}) : skillTab === "project" ? projectSkills.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillList, {
|
|
7136
7705
|
skills: projectSkills,
|
|
7137
7706
|
remote,
|
|
7138
7707
|
busy,
|
|
@@ -7144,6 +7713,24 @@ function ReadyBody(props) {
|
|
|
7144
7713
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
7145
7714
|
className: "mc-empty",
|
|
7146
7715
|
children: t("emptyProjectSkills")
|
|
7716
|
+
}) : presetGroups.length > 0 ? presetGroups.map(([preset, group]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7717
|
+
className: "mc-skill-group",
|
|
7718
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7719
|
+
className: "mc-skill-group-title",
|
|
7720
|
+
children: preset
|
|
7721
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillList, {
|
|
7722
|
+
skills: group,
|
|
7723
|
+
remote,
|
|
7724
|
+
busy,
|
|
7725
|
+
t,
|
|
7726
|
+
onCycle,
|
|
7727
|
+
editableSkills,
|
|
7728
|
+
onEditSkill: openSkillEdit,
|
|
7729
|
+
onAdoptSkill: adoptSkill
|
|
7730
|
+
})]
|
|
7731
|
+
}, preset)) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
7732
|
+
className: "mc-empty",
|
|
7733
|
+
children: t("emptyPresetSkills")
|
|
7147
7734
|
})] })
|
|
7148
7735
|
})
|
|
7149
7736
|
}),
|
|
@@ -7174,16 +7761,24 @@ function ReadyBody(props) {
|
|
|
7174
7761
|
className: "mc-preview-hint",
|
|
7175
7762
|
children: toolDetail.message
|
|
7176
7763
|
}),
|
|
7177
|
-
toolDetail.status === "ready" && toolDetail.detail !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.
|
|
7764
|
+
toolDetail.status === "ready" && toolDetail.detail !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7178
7765
|
className: "mc-preview-body",
|
|
7179
|
-
children:
|
|
7180
|
-
|
|
7181
|
-
|
|
7182
|
-
|
|
7183
|
-
|
|
7184
|
-
|
|
7185
|
-
|
|
7186
|
-
|
|
7766
|
+
children: [toolDetail.detail.description.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7767
|
+
className: "mc-tool-description",
|
|
7768
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkdownPreview, {
|
|
7769
|
+
text: toolDetail.detail.description,
|
|
7770
|
+
t
|
|
7771
|
+
})
|
|
7772
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
7773
|
+
className: "mc-preview-plain",
|
|
7774
|
+
children: JSON.stringify({
|
|
7775
|
+
type: "function",
|
|
7776
|
+
function: {
|
|
7777
|
+
name: toolDetail.detail.name,
|
|
7778
|
+
parameters: toolDetail.detail.parameters
|
|
7779
|
+
}
|
|
7780
|
+
}, null, 2)
|
|
7781
|
+
})]
|
|
7187
7782
|
})
|
|
7188
7783
|
]
|
|
7189
7784
|
})
|
|
@@ -7199,8 +7794,9 @@ function ReadyBody(props) {
|
|
|
7199
7794
|
setEditMcp(void 0);
|
|
7200
7795
|
setEditSkill(void 0);
|
|
7201
7796
|
},
|
|
7202
|
-
onChanged: (notice) => {
|
|
7203
|
-
|
|
7797
|
+
onChanged: (notice, refreshMcpServer) => {
|
|
7798
|
+
if (refreshMcpServer !== void 0) onRefreshMcp(refreshMcpServer);
|
|
7799
|
+
else onRefresh();
|
|
7204
7800
|
if (notice !== void 0) onNotice(notice);
|
|
7205
7801
|
}
|
|
7206
7802
|
}),
|
|
@@ -7405,7 +8001,7 @@ function SkillList(props) {
|
|
|
7405
8001
|
}),
|
|
7406
8002
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7407
8003
|
className: "mc-tree-icon",
|
|
7408
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
8004
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconTriangleRightFill, {
|
|
7409
8005
|
size: 10,
|
|
7410
8006
|
className: `mc-chevron${open ? " mc-chevron--open" : ""}`
|
|
7411
8007
|
})
|
|
@@ -7469,7 +8065,7 @@ function SkillList(props) {
|
|
|
7469
8065
|
}
|
|
7470
8066
|
},
|
|
7471
8067
|
children: [
|
|
7472
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
8068
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconTriangleRightFill, {
|
|
7473
8069
|
size: 12,
|
|
7474
8070
|
className: `mc-chevron${open ? " mc-chevron--open" : ""}`
|
|
7475
8071
|
}),
|
|
@@ -7498,7 +8094,11 @@ function SkillList(props) {
|
|
|
7498
8094
|
"aria-hidden": "true"
|
|
7499
8095
|
}), t(CLASS_SHORT_KEYS[skill.class])]
|
|
7500
8096
|
}),
|
|
7501
|
-
!editableSkills.has(skill.id) && (
|
|
8097
|
+
!editableSkills.has(skill.id) && (skill.preset !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
8098
|
+
className: "mc-source",
|
|
8099
|
+
title: t("skillFromPresetHint"),
|
|
8100
|
+
children: t("skillFromPreset", { preset: skill.preset })
|
|
8101
|
+
}) : ADOPTABLE_SKILL_SOURCES.has(skill.source ?? "") ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7502
8102
|
type: "button",
|
|
7503
8103
|
className: "mc-count mc-count--action",
|
|
7504
8104
|
disabled: busy,
|
|
@@ -7597,9 +8197,15 @@ function SkillList(props) {
|
|
|
7597
8197
|
onClick: () => setPreview(null),
|
|
7598
8198
|
children: t("previewClose")
|
|
7599
8199
|
})]
|
|
7600
|
-
}), preview.content !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
8200
|
+
}), preview.content !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7601
8201
|
className: "mc-preview-body",
|
|
7602
|
-
children: preview.
|
|
8202
|
+
children: /\.md$/i.test(preview.relPath) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkdownFilePreview, {
|
|
8203
|
+
text: preview.content,
|
|
8204
|
+
t
|
|
8205
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
8206
|
+
className: "mc-preview-plain",
|
|
8207
|
+
children: preview.content
|
|
8208
|
+
})
|
|
7603
8209
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7604
8210
|
className: "mc-preview-hint",
|
|
7605
8211
|
children: preview.error ?? "…"
|
|
@@ -7626,6 +8232,8 @@ async function apply(ctx) {
|
|
|
7626
8232
|
const zh = {
|
|
7627
8233
|
nav: "能力菜单",
|
|
7628
8234
|
title: "能力菜单",
|
|
8235
|
+
packageName: "capability-menu",
|
|
8236
|
+
feedback: "反馈 ↗",
|
|
7629
8237
|
desc: "管理工具与技能的 常驻 / 按需 / 禁用 三档分类。",
|
|
7630
8238
|
resident: "Resident(常驻上下文)",
|
|
7631
8239
|
"on-demand": "On-demand(按需发现)",
|
|
@@ -7638,8 +8246,13 @@ async function apply(ctx) {
|
|
|
7638
8246
|
builtInGroup: "系统内置",
|
|
7639
8247
|
globalSkills: "全局技能",
|
|
7640
8248
|
projectSkills: "项目技能",
|
|
8249
|
+
presetSkills: "预设技能",
|
|
7641
8250
|
emptyGlobalSkills: "暂无全局技能",
|
|
7642
8251
|
emptyProjectSkills: "暂无项目技能",
|
|
8252
|
+
emptyPresetSkills: "暂无预设技能",
|
|
8253
|
+
filterByName: "按名字、分组或正则过滤…",
|
|
8254
|
+
filterHint: "忽略大小写。匹配工具/技能名、MCP server、技能来源标签与预设 id;也可以直接写正则,例如 cordis|ptc 或 /^mcp__dbx/。",
|
|
8255
|
+
filterNoMatch: "没有匹配的能力(清空过滤框可恢复完整列表)",
|
|
7643
8256
|
emptyTools: "暂无工具",
|
|
7644
8257
|
emptySkills: "暂无 Skill",
|
|
7645
8258
|
toolCount: "{count} 个工具",
|
|
@@ -7649,11 +8262,16 @@ async function apply(ctx) {
|
|
|
7649
8262
|
cycleHint: "点击标签切换分类(按需 → 禁用 → 常驻)",
|
|
7650
8263
|
notPreviewable: "该文件不是可预览的文本文件",
|
|
7651
8264
|
previewClose: "关闭",
|
|
8265
|
+
markdownCode: "代码",
|
|
8266
|
+
markdownWrap: "自动换行",
|
|
8267
|
+
markdownUnwrap: "不换行",
|
|
8268
|
+
markdownCopy: "复制",
|
|
8269
|
+
markdownCopied: "已复制",
|
|
8270
|
+
markdownFootnotes: "脚注",
|
|
7652
8271
|
detailNotFound: "未找到该工具的详情",
|
|
7653
8272
|
cycleOverridden: "分类未生效:{count} 个能力被更高优先级规则覆盖(如通配规则),可移除对应通配规则后重试",
|
|
7654
|
-
refresh: "刷新",
|
|
7655
|
-
refreshing: "刷新中…",
|
|
7656
8273
|
refreshFailed: "刷新失败,请查看日志",
|
|
8274
|
+
mcpToolsPending: "已注册「{name}」,但暂未加载出工具。请检查连接和认证后点刷新。",
|
|
7657
8275
|
retry: "重试",
|
|
7658
8276
|
carrierFailureHint: "请求未到达服务端:浏览器与 dsh 之间的连接已断开(Failed to fetch 属传输层失败,服务端方法未执行)。若刚重启过 dsh web,请先硬刷新页面(Ctrl/Cmd+Shift+R)再重试。",
|
|
7659
8277
|
registerCapability: "注册能力",
|
|
@@ -7687,20 +8305,24 @@ async function apply(ctx) {
|
|
|
7687
8305
|
skillAdopted: "已纳入管理:{path}",
|
|
7688
8306
|
skillSource: "来源:{source}",
|
|
7689
8307
|
skillSourceHint: "该技能由 dsh 从其它根发现,不在本插件管理的技能目录里,因此没有「编辑」。",
|
|
8308
|
+
skillFromPreset: "来自预设 {preset}",
|
|
8309
|
+
skillFromPresetHint: "该技能随 agent preset 分发,只对挂载了该预设的会话可见;不能纳入管理(那会把它软链进全局技能根,对所有会话生效)。",
|
|
8310
|
+
githubImporting: "正在从 GitHub 获取并校验 Skill,完成后会自动注册…",
|
|
8311
|
+
importing: "导入中…",
|
|
7690
8312
|
skillUnmanaged: "未纳入管理",
|
|
7691
8313
|
sourceCustom: "自定义技能目录",
|
|
7692
8314
|
sourceBundled: "随 dsh 预置",
|
|
7693
8315
|
adoptSkill: "纳入管理",
|
|
7694
8316
|
adoptSkillHint: "把它软链进 dsh 的技能根目录(默认 ~/.dsh/skills),内容不动;之后就能编辑与移除了。",
|
|
7695
8317
|
adoptSkillTitle: "纳入管理「{name}」?",
|
|
7696
|
-
skillDirHint: "
|
|
8318
|
+
skillDirHint: "支持本机目录或公开 GitHub HTTPS 技能链接。GitHub 导入会校验 SKILL.md,并只保留对应技能目录;运行 dsh 的机器需安装 Git。技能名取自 SKILL.md。",
|
|
7697
8319
|
serverName: "serverName",
|
|
7698
|
-
serverNameImmutable: "
|
|
8320
|
+
serverNameImmutable: "名称用于工具 ID 和权限规则,注册后不可更改。",
|
|
7699
8321
|
transport: "传输方式",
|
|
7700
8322
|
transportStdio: "stdio(本地子进程)",
|
|
7701
8323
|
transportHttp: "streamable-http(HTTP MCP 端点)",
|
|
7702
8324
|
transportHintStdio: "由客户端将该 MCP server 作为子进程启动,JSON-RPC 报文经标准输入 / 输出交换。",
|
|
7703
|
-
transportHintHttp: "
|
|
8325
|
+
transportHintHttp: "客户端通过支持 POST/GET 的 MCP 端点连接。",
|
|
7704
8326
|
command: "命令",
|
|
7705
8327
|
args: "参数(空格分隔)",
|
|
7706
8328
|
cwd: "工作目录",
|
|
@@ -7720,6 +8342,8 @@ async function apply(ctx) {
|
|
|
7720
8342
|
const en = {
|
|
7721
8343
|
nav: "Capability Management",
|
|
7722
8344
|
title: "Capability Management",
|
|
8345
|
+
packageName: "capability-menu",
|
|
8346
|
+
feedback: "Feedback ↗",
|
|
7723
8347
|
desc: "Manage the Resident / On-demand / Disabled classification of tools and skills.",
|
|
7724
8348
|
resident: "Resident",
|
|
7725
8349
|
"on-demand": "On-demand",
|
|
@@ -7732,8 +8356,13 @@ async function apply(ctx) {
|
|
|
7732
8356
|
builtInGroup: "System built-in",
|
|
7733
8357
|
globalSkills: "Global skills",
|
|
7734
8358
|
projectSkills: "Project skills",
|
|
8359
|
+
presetSkills: "Preset skills",
|
|
7735
8360
|
emptyGlobalSkills: "No global skills",
|
|
7736
8361
|
emptyProjectSkills: "No project skills",
|
|
8362
|
+
emptyPresetSkills: "No preset skills",
|
|
8363
|
+
filterByName: "Filter by name, group, or regex…",
|
|
8364
|
+
filterHint: "Case-insensitive. Matches tool/skill names, MCP servers, skill source labels and preset ids; a regular expression works too, e.g. cordis|ptc or /^mcp__dbx/.",
|
|
8365
|
+
filterNoMatch: "No capability matches (clear the filter to see the full list)",
|
|
7737
8366
|
emptyTools: "No tools",
|
|
7738
8367
|
emptySkills: "No skills",
|
|
7739
8368
|
toolCount: "{count} tools",
|
|
@@ -7743,11 +8372,16 @@ async function apply(ctx) {
|
|
|
7743
8372
|
cycleHint: "Click a tag to cycle its classification (On-demand → Disabled → Resident)",
|
|
7744
8373
|
notPreviewable: "This file is not a previewable text file",
|
|
7745
8374
|
previewClose: "Close",
|
|
8375
|
+
markdownCode: "Code",
|
|
8376
|
+
markdownWrap: "Wrap",
|
|
8377
|
+
markdownUnwrap: "Unwrap",
|
|
8378
|
+
markdownCopy: "Copy",
|
|
8379
|
+
markdownCopied: "Copied",
|
|
8380
|
+
markdownFootnotes: "Footnotes",
|
|
7746
8381
|
detailNotFound: "Tool detail not found",
|
|
7747
8382
|
cycleOverridden: "Classification not applied: {count} capability(ies) overridden by a higher-priority rule (e.g. a wildcard). Remove the matching wildcard rule and retry.",
|
|
7748
|
-
refresh: "Refresh",
|
|
7749
|
-
refreshing: "Refreshing…",
|
|
7750
8383
|
refreshFailed: "Refresh failed; see the log",
|
|
8384
|
+
mcpToolsPending: "\"{name}\" is registered, but its tools have not loaded yet. Check its connection and authentication, then refresh.",
|
|
7751
8385
|
retry: "Retry",
|
|
7752
8386
|
carrierFailureHint: "The request never reached the server: the browser connection to dsh is broken (\"Failed to fetch\" is a transport failure, so no server method ran). If dsh web was just restarted, hard-refresh the page (Ctrl/Cmd+Shift+R) and retry.",
|
|
7753
8387
|
registerCapability: "Register capability",
|
|
@@ -7781,20 +8415,24 @@ async function apply(ctx) {
|
|
|
7781
8415
|
skillAdopted: "Adopted: {path}",
|
|
7782
8416
|
skillSource: "Source: {source}",
|
|
7783
8417
|
skillSourceHint: "dsh found this skill in another root, not in a skill directory this plugin manages, so there is no Edit button.",
|
|
8418
|
+
skillFromPreset: "From preset {preset}",
|
|
8419
|
+
skillFromPresetHint: "This skill ships with an agent preset and is visible only to sessions that mount it. It cannot be adopted (adopting would link it into the global skill root, making it apply to every session).",
|
|
8420
|
+
githubImporting: "Fetching and validating the Skill from GitHub… It will register automatically when complete.",
|
|
8421
|
+
importing: "Importing…",
|
|
7784
8422
|
skillUnmanaged: "Not managed here",
|
|
7785
8423
|
sourceCustom: "custom skill dirs",
|
|
7786
8424
|
sourceBundled: "bundled with dsh",
|
|
7787
8425
|
adoptSkill: "Adopt",
|
|
7788
8426
|
adoptSkillHint: "Link it into dsh's skill root (default ~/.dsh/skills); the content is untouched, and it becomes editable and removable.",
|
|
7789
8427
|
adoptSkillTitle: "Adopt \"{name}\"?",
|
|
7790
|
-
skillDirHint: "
|
|
8428
|
+
skillDirHint: "Enter a local directory or a public GitHub HTTPS skill URL. Imports validate SKILL.md and keep only the selected skill directory. Git must be installed on the machine running dsh. The skill name comes from SKILL.md.",
|
|
7791
8429
|
serverName: "serverName",
|
|
7792
|
-
serverNameImmutable: "
|
|
8430
|
+
serverNameImmutable: "The name is locked after registration because tool IDs and permission rules use it.",
|
|
7793
8431
|
transport: "Transport",
|
|
7794
8432
|
transportStdio: "stdio (local subprocess)",
|
|
7795
8433
|
transportHttp: "streamable-http (HTTP MCP endpoint)",
|
|
7796
8434
|
transportHintStdio: "The client launches the MCP server as a subprocess and exchanges JSON-RPC messages over its standard input and output.",
|
|
7797
|
-
transportHintHttp: "
|
|
8435
|
+
transportHintHttp: "Connect through the MCP endpoint URL (supports POST and GET).",
|
|
7798
8436
|
command: "Command",
|
|
7799
8437
|
args: "Args (space separated)",
|
|
7800
8438
|
cwd: "Working directory",
|
|
@@ -7824,6 +8462,7 @@ async function apply(ctx) {
|
|
|
7824
8462
|
console.error("[capability-menu] $mount failed:", error);
|
|
7825
8463
|
}
|
|
7826
8464
|
const t = ctx.locale.bind(NS);
|
|
8465
|
+
installSettingsNavIcon(ctx, () => t("nav"));
|
|
7827
8466
|
const remote = () => {
|
|
7828
8467
|
try {
|
|
7829
8468
|
return ctx.get("remote.capabilityPolicy");
|
|
@@ -7832,10 +8471,44 @@ async function apply(ctx) {
|
|
|
7832
8471
|
return;
|
|
7833
8472
|
}
|
|
7834
8473
|
};
|
|
8474
|
+
/**
|
|
8475
|
+
* The two signals that say "the host catalog may have moved, and this page did
|
|
8476
|
+
* not ask": a settings document edit, and a carrier reconnect.
|
|
8477
|
+
*
|
|
8478
|
+
* `settings/document-updated` is one of the events dsh forwards to browsers
|
|
8479
|
+
* (so it arrives over the same validated channel as the remote calls) and it
|
|
8480
|
+
* fires for external edits too — composition files are edited outside the
|
|
8481
|
+
* browser, and nothing else on the wire announces that. `connection/reset` is
|
|
8482
|
+
* the client's own reconnect signal rather than a forwarded one, which is why
|
|
8483
|
+
* it is a plain local `ctx.on`; dsh's own settings sections listen to exactly
|
|
8484
|
+
* this pair, including the first connection, which closes the window where a
|
|
8485
|
+
* change lands between the initial read and the subscription.
|
|
8486
|
+
*
|
|
8487
|
+
* Neither subscription is fatal: the section falls back to its version poll.
|
|
8488
|
+
*/
|
|
8489
|
+
const subscribeSignals = (listener) => {
|
|
8490
|
+
const disposers = [];
|
|
8491
|
+
const forwarded = ctx.remote;
|
|
8492
|
+
try {
|
|
8493
|
+
if (typeof forwarded.$on !== "function") throw new Error("ctx.remote.$on is unavailable");
|
|
8494
|
+
disposers.push(forwarded.$on("settings/document-updated", () => listener()));
|
|
8495
|
+
} catch (error) {
|
|
8496
|
+
console.error("[capability-menu] settings/document-updated subscription failed:", error);
|
|
8497
|
+
}
|
|
8498
|
+
try {
|
|
8499
|
+
disposers.push(ctx.on("connection/reset", () => listener()));
|
|
8500
|
+
} catch (error) {
|
|
8501
|
+
console.error("[capability-menu] connection/reset subscription failed:", error);
|
|
8502
|
+
}
|
|
8503
|
+
return () => {
|
|
8504
|
+
for (const dispose of disposers) dispose();
|
|
8505
|
+
};
|
|
8506
|
+
};
|
|
7835
8507
|
const injected = () => {
|
|
7836
8508
|
return {
|
|
7837
8509
|
remote: remote(),
|
|
7838
8510
|
t,
|
|
8511
|
+
subscribeSignals,
|
|
7839
8512
|
...mountError !== void 0 ? { mountError } : {}
|
|
7840
8513
|
};
|
|
7841
8514
|
};
|