@daweifu/capability-menu 0.1.1 → 0.1.3
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 +238 -0
- package/README.md +127 -63
- package/cordis.patch.yml +37 -46
- package/lib/client.d.ts +224 -19
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +1796 -226
- package/lib/constants.js +33 -0
- package/lib/constants.js.map +1 -0
- package/lib/index.js +1 -1
- package/lib/invoke.js +68 -96
- package/lib/invoke.js.map +1 -1
- package/lib/locations.js +541 -0
- package/lib/locations.js.map +1 -0
- package/lib/patch-file.js +192 -0
- package/lib/patch-file.js.map +1 -0
- package/lib/policy.js +422 -94
- package/lib/policy.js.map +1 -1
- package/lib/registry.js +410 -152
- package/lib/registry.js.map +1 -1
- package/lib/search.js +26 -9
- package/lib/search.js.map +1 -1
- package/lib/server/remote.js +219 -3
- package/lib/server/remote.js.map +1 -1
- package/lib/types/constants.d.ts +33 -0
- package/lib/types/constants.d.ts.map +1 -0
- package/lib/types/index.d.ts +1 -1
- package/lib/types/invoke.d.ts +7 -13
- package/lib/types/invoke.d.ts.map +1 -1
- package/lib/types/locations.d.ts +142 -0
- package/lib/types/locations.d.ts.map +1 -0
- package/lib/types/patch-file.d.ts +46 -0
- package/lib/types/patch-file.d.ts.map +1 -0
- package/lib/types/policy.d.ts +115 -53
- package/lib/types/policy.d.ts.map +1 -1
- package/lib/types/registry.d.ts +94 -25
- package/lib/types/registry.d.ts.map +1 -1
- package/lib/types/search.d.ts.map +1 -1
- package/lib/types/server/remote.d.ts +68 -2
- package/lib/types/server/remote.d.ts.map +1 -1
- package/package.json +23 -26
- package/lib/invariant.js +0 -22
- package/lib/invariant.js.map +0 -1
- package/lib/types/invariant.d.ts +0 -16
- package/lib/types/invariant.d.ts.map +0 -1
package/lib/client.js
CHANGED
|
@@ -284,7 +284,7 @@ function prefixIssues(path, issues) {
|
|
|
284
284
|
return iss;
|
|
285
285
|
});
|
|
286
286
|
}
|
|
287
|
-
function unwrapMessage(message) {
|
|
287
|
+
function unwrapMessage$1(message) {
|
|
288
288
|
return typeof message === "string" ? message : message?.message;
|
|
289
289
|
}
|
|
290
290
|
function attachSchema(issues, start, inst) {
|
|
@@ -299,7 +299,7 @@ function finalizeIssue(iss, ctx, config) {
|
|
|
299
299
|
else iss.schema = iss.inst;
|
|
300
300
|
}
|
|
301
301
|
const schemaError = iss.schema !== iss.inst ? iss.schema?._zod.def?.error : void 0;
|
|
302
|
-
const message = iss.message ? iss.message : unwrapMessage(iss.inst?._zod.def?.error?.(iss)) ?? unwrapMessage(schemaError?.(iss)) ?? unwrapMessage(ctx?.error?.(iss)) ?? unwrapMessage(config.customError?.(iss)) ?? unwrapMessage(config.localeError?.(iss)) ?? "Invalid input";
|
|
302
|
+
const message = iss.message ? iss.message : unwrapMessage$1(iss.inst?._zod.def?.error?.(iss)) ?? unwrapMessage$1(schemaError?.(iss)) ?? unwrapMessage$1(ctx?.error?.(iss)) ?? unwrapMessage$1(config.customError?.(iss)) ?? unwrapMessage$1(config.localeError?.(iss)) ?? "Invalid input";
|
|
303
303
|
const { inst: _inst, schema: _schema, continue: _continue, input: _input, ...rest } = iss;
|
|
304
304
|
rest.path ?? (rest.path = []);
|
|
305
305
|
rest.message = message;
|
|
@@ -5190,15 +5190,60 @@ function superRefine(fn, params) {
|
|
|
5190
5190
|
* namespace and exports a `TYPERT_REMOTE` contribution that the browser
|
|
5191
5191
|
* plugin mounts via `ctx.remote.$mount(...)`.
|
|
5192
5192
|
*/
|
|
5193
|
+
const stringRecord$schema = record(string(), string());
|
|
5194
|
+
const mcpLocation$schema = object({
|
|
5195
|
+
id: string().readonly(),
|
|
5196
|
+
serverName: string().readonly(),
|
|
5197
|
+
transport: union([literal("stdio"), literal("streamable-http")]).readonly(),
|
|
5198
|
+
disabled: boolean().readonly(),
|
|
5199
|
+
command: string().optional().readonly(),
|
|
5200
|
+
args: array(string()).optional().readonly(),
|
|
5201
|
+
env: stringRecord$schema.optional().readonly(),
|
|
5202
|
+
cwd: string().optional().readonly(),
|
|
5203
|
+
url: string().optional().readonly(),
|
|
5204
|
+
headers: stringRecord$schema.optional().readonly(),
|
|
5205
|
+
toolCallTimeoutMs: number().optional().readonly()
|
|
5206
|
+
});
|
|
5207
|
+
const skillLocation$schema = object({
|
|
5208
|
+
name: string().readonly(),
|
|
5209
|
+
path: string().readonly(),
|
|
5210
|
+
linked: boolean().readonly(),
|
|
5211
|
+
valid: boolean().readonly(),
|
|
5212
|
+
root: union([literal("user"), literal("project")]).readonly(),
|
|
5213
|
+
entryDir: string().readonly(),
|
|
5214
|
+
target: string().optional().readonly()
|
|
5215
|
+
});
|
|
5216
|
+
const mcpInput$schema = object({
|
|
5217
|
+
serverName: string(),
|
|
5218
|
+
transport: union([literal("stdio"), literal("streamable-http")]),
|
|
5219
|
+
command: string().optional(),
|
|
5220
|
+
args: array(string()).optional(),
|
|
5221
|
+
env: stringRecord$schema.optional(),
|
|
5222
|
+
cwd: string().optional(),
|
|
5223
|
+
url: string().optional(),
|
|
5224
|
+
headers: stringRecord$schema.optional(),
|
|
5225
|
+
toolCallTimeoutMs: number().optional()
|
|
5226
|
+
});
|
|
5227
|
+
const mcpUpdateInput$schema = object({
|
|
5228
|
+
transport: union([literal("stdio"), literal("streamable-http")]),
|
|
5229
|
+
command: string().optional(),
|
|
5230
|
+
args: array(string()).optional(),
|
|
5231
|
+
env: stringRecord$schema.optional(),
|
|
5232
|
+
cwd: string().optional(),
|
|
5233
|
+
url: string().optional(),
|
|
5234
|
+
headers: stringRecord$schema.optional(),
|
|
5235
|
+
toolCallTimeoutMs: number().optional()
|
|
5236
|
+
});
|
|
5193
5237
|
const capabilityRow$schema = object({
|
|
5194
5238
|
id: string().readonly(),
|
|
5195
5239
|
kind: union([literal("tool"), literal("skill")]).readonly(),
|
|
5196
5240
|
name: string().readonly(),
|
|
5197
5241
|
server: string().optional().readonly(),
|
|
5242
|
+
source: string().optional().readonly(),
|
|
5198
5243
|
class: union([
|
|
5199
|
-
literal("
|
|
5200
|
-
literal("
|
|
5201
|
-
literal("
|
|
5244
|
+
literal("resident"),
|
|
5245
|
+
literal("on-demand"),
|
|
5246
|
+
literal("disabled")
|
|
5202
5247
|
]).readonly(),
|
|
5203
5248
|
classLabel: string().optional().readonly(),
|
|
5204
5249
|
mandatory: boolean().readonly()
|
|
@@ -5207,6 +5252,14 @@ const skillFileEntry$schema = object({
|
|
|
5207
5252
|
name: string().readonly(),
|
|
5208
5253
|
type: union([literal("file"), literal("directory")]).readonly()
|
|
5209
5254
|
});
|
|
5255
|
+
const catalogDocs$schema = object({
|
|
5256
|
+
policyYaml: string().readonly(),
|
|
5257
|
+
catalog: object({
|
|
5258
|
+
path: string().readonly(),
|
|
5259
|
+
content: string().readonly()
|
|
5260
|
+
}).optional().readonly(),
|
|
5261
|
+
catalogMissing: union([literal("disabled"), literal("read-failed")]).optional().readonly()
|
|
5262
|
+
});
|
|
5210
5263
|
const toolDetail$schema = object({
|
|
5211
5264
|
id: string().readonly(),
|
|
5212
5265
|
kind: union([literal("tool"), literal("skill")]).readonly(),
|
|
@@ -5219,7 +5272,8 @@ const toolDetail$schema = object({
|
|
|
5219
5272
|
origin: object({
|
|
5220
5273
|
provider: string().readonly(),
|
|
5221
5274
|
serverName: string().optional().readonly(),
|
|
5222
|
-
path: string().optional().readonly()
|
|
5275
|
+
path: string().optional().readonly(),
|
|
5276
|
+
source: string().optional().readonly()
|
|
5223
5277
|
}).readonly(),
|
|
5224
5278
|
tags: array(string()).readonly(),
|
|
5225
5279
|
stats: object({
|
|
@@ -5247,7 +5301,7 @@ const TYPERT_REMOTE = {
|
|
|
5247
5301
|
},
|
|
5248
5302
|
sourceLocation: {
|
|
5249
5303
|
file: "src/server/remote.ts",
|
|
5250
|
-
line:
|
|
5304
|
+
line: 61,
|
|
5251
5305
|
column: 3
|
|
5252
5306
|
}
|
|
5253
5307
|
},
|
|
@@ -5274,7 +5328,7 @@ const TYPERT_REMOTE = {
|
|
|
5274
5328
|
},
|
|
5275
5329
|
sourceLocation: {
|
|
5276
5330
|
file: "src/server/remote.ts",
|
|
5277
|
-
line:
|
|
5331
|
+
line: 67,
|
|
5278
5332
|
column: 3
|
|
5279
5333
|
}
|
|
5280
5334
|
},
|
|
@@ -5292,7 +5346,25 @@ const TYPERT_REMOTE = {
|
|
|
5292
5346
|
},
|
|
5293
5347
|
sourceLocation: {
|
|
5294
5348
|
file: "src/server/remote.ts",
|
|
5295
|
-
line:
|
|
5349
|
+
line: 73,
|
|
5350
|
+
column: 3
|
|
5351
|
+
}
|
|
5352
|
+
},
|
|
5353
|
+
{
|
|
5354
|
+
id: "@daweifu/capability-menu#capabilityPolicy/refresh",
|
|
5355
|
+
service: "capabilityPolicy",
|
|
5356
|
+
namespace: "capabilityPolicy",
|
|
5357
|
+
method: "refresh",
|
|
5358
|
+
invocation: { kind: "direct" },
|
|
5359
|
+
parameters: [],
|
|
5360
|
+
result: {
|
|
5361
|
+
mode: "strict",
|
|
5362
|
+
typeSymbol: "void",
|
|
5363
|
+
schema: _undefined()
|
|
5364
|
+
},
|
|
5365
|
+
sourceLocation: {
|
|
5366
|
+
file: "src/server/remote.ts",
|
|
5367
|
+
line: 84,
|
|
5296
5368
|
column: 3
|
|
5297
5369
|
}
|
|
5298
5370
|
},
|
|
@@ -5329,7 +5401,7 @@ const TYPERT_REMOTE = {
|
|
|
5329
5401
|
},
|
|
5330
5402
|
sourceLocation: {
|
|
5331
5403
|
file: "src/server/remote.ts",
|
|
5332
|
-
line:
|
|
5404
|
+
line: 85,
|
|
5333
5405
|
column: 3
|
|
5334
5406
|
}
|
|
5335
5407
|
},
|
|
@@ -5365,7 +5437,7 @@ const TYPERT_REMOTE = {
|
|
|
5365
5437
|
},
|
|
5366
5438
|
sourceLocation: {
|
|
5367
5439
|
file: "src/server/remote.ts",
|
|
5368
|
-
line:
|
|
5440
|
+
line: 91,
|
|
5369
5441
|
column: 3
|
|
5370
5442
|
}
|
|
5371
5443
|
},
|
|
@@ -5392,51 +5464,940 @@ const TYPERT_REMOTE = {
|
|
|
5392
5464
|
},
|
|
5393
5465
|
sourceLocation: {
|
|
5394
5466
|
file: "src/server/remote.ts",
|
|
5395
|
-
line:
|
|
5467
|
+
line: 79,
|
|
5396
5468
|
column: 3
|
|
5397
5469
|
}
|
|
5470
|
+
},
|
|
5471
|
+
{
|
|
5472
|
+
id: "@daweifu/capability-menu#capabilityPolicy/getCatalogDocs",
|
|
5473
|
+
service: "capabilityPolicy",
|
|
5474
|
+
namespace: "capabilityPolicy",
|
|
5475
|
+
method: "getCatalogDocs",
|
|
5476
|
+
invocation: { kind: "direct" },
|
|
5477
|
+
parameters: [],
|
|
5478
|
+
result: {
|
|
5479
|
+
mode: "strict",
|
|
5480
|
+
typeSymbol: "@daweifu/capability-menu#CatalogDocs",
|
|
5481
|
+
schema: catalogDocs$schema
|
|
5482
|
+
},
|
|
5483
|
+
sourceLocation: {
|
|
5484
|
+
file: "src/server/remote.ts",
|
|
5485
|
+
line: 104,
|
|
5486
|
+
column: 3
|
|
5487
|
+
}
|
|
5488
|
+
},
|
|
5489
|
+
{
|
|
5490
|
+
id: "@daweifu/capability-menu#capabilityPolicy/listLocations",
|
|
5491
|
+
service: "capabilityPolicy",
|
|
5492
|
+
namespace: "capabilityPolicy",
|
|
5493
|
+
method: "listLocations",
|
|
5494
|
+
invocation: { kind: "direct" },
|
|
5495
|
+
parameters: [],
|
|
5496
|
+
result: {
|
|
5497
|
+
mode: "strict",
|
|
5498
|
+
typeSymbol: "@daweifu/capability-menu#McpLocation[]",
|
|
5499
|
+
schema: array(mcpLocation$schema)
|
|
5500
|
+
},
|
|
5501
|
+
sourceLocation: {
|
|
5502
|
+
file: "src/server/remote.ts",
|
|
5503
|
+
line: 193,
|
|
5504
|
+
column: 3
|
|
5505
|
+
}
|
|
5506
|
+
},
|
|
5507
|
+
{
|
|
5508
|
+
id: "@daweifu/capability-menu#capabilityPolicy/addLocation",
|
|
5509
|
+
service: "capabilityPolicy",
|
|
5510
|
+
namespace: "capabilityPolicy",
|
|
5511
|
+
method: "addLocation",
|
|
5512
|
+
invocation: { kind: "direct" },
|
|
5513
|
+
parameters: [{
|
|
5514
|
+
name: "input",
|
|
5515
|
+
wire: "input",
|
|
5516
|
+
source: "json",
|
|
5517
|
+
codec: {
|
|
5518
|
+
mode: "strict",
|
|
5519
|
+
typeSymbol: "@daweifu/capability-menu#McpInput",
|
|
5520
|
+
schema: mcpInput$schema
|
|
5521
|
+
}
|
|
5522
|
+
}],
|
|
5523
|
+
result: {
|
|
5524
|
+
mode: "strict",
|
|
5525
|
+
typeSymbol: "string",
|
|
5526
|
+
schema: string()
|
|
5527
|
+
},
|
|
5528
|
+
sourceLocation: {
|
|
5529
|
+
file: "src/server/remote.ts",
|
|
5530
|
+
line: 199,
|
|
5531
|
+
column: 3
|
|
5532
|
+
}
|
|
5533
|
+
},
|
|
5534
|
+
{
|
|
5535
|
+
id: "@daweifu/capability-menu#capabilityPolicy/removeLocation",
|
|
5536
|
+
service: "capabilityPolicy",
|
|
5537
|
+
namespace: "capabilityPolicy",
|
|
5538
|
+
method: "removeLocation",
|
|
5539
|
+
invocation: { kind: "direct" },
|
|
5540
|
+
parameters: [{
|
|
5541
|
+
name: "id",
|
|
5542
|
+
wire: "id",
|
|
5543
|
+
source: "json",
|
|
5544
|
+
codec: {
|
|
5545
|
+
mode: "strict",
|
|
5546
|
+
typeSymbol: "string",
|
|
5547
|
+
schema: string()
|
|
5548
|
+
}
|
|
5549
|
+
}],
|
|
5550
|
+
result: {
|
|
5551
|
+
mode: "strict",
|
|
5552
|
+
typeSymbol: "boolean",
|
|
5553
|
+
schema: boolean()
|
|
5554
|
+
},
|
|
5555
|
+
sourceLocation: {
|
|
5556
|
+
file: "src/server/remote.ts",
|
|
5557
|
+
line: 205,
|
|
5558
|
+
column: 3
|
|
5559
|
+
}
|
|
5560
|
+
},
|
|
5561
|
+
{
|
|
5562
|
+
id: "@daweifu/capability-menu#capabilityPolicy/updateLocation",
|
|
5563
|
+
service: "capabilityPolicy",
|
|
5564
|
+
namespace: "capabilityPolicy",
|
|
5565
|
+
method: "updateLocation",
|
|
5566
|
+
invocation: { kind: "direct" },
|
|
5567
|
+
parameters: [{
|
|
5568
|
+
name: "id",
|
|
5569
|
+
wire: "id",
|
|
5570
|
+
source: "json",
|
|
5571
|
+
codec: {
|
|
5572
|
+
mode: "strict",
|
|
5573
|
+
typeSymbol: "string",
|
|
5574
|
+
schema: string()
|
|
5575
|
+
}
|
|
5576
|
+
}, {
|
|
5577
|
+
name: "input",
|
|
5578
|
+
wire: "input",
|
|
5579
|
+
source: "json",
|
|
5580
|
+
codec: {
|
|
5581
|
+
mode: "strict",
|
|
5582
|
+
typeSymbol: "@daweifu/capability-menu#McpUpdateInput",
|
|
5583
|
+
schema: mcpUpdateInput$schema
|
|
5584
|
+
}
|
|
5585
|
+
}],
|
|
5586
|
+
result: {
|
|
5587
|
+
mode: "strict",
|
|
5588
|
+
typeSymbol: "boolean",
|
|
5589
|
+
schema: boolean()
|
|
5590
|
+
},
|
|
5591
|
+
sourceLocation: {
|
|
5592
|
+
file: "src/server/remote.ts",
|
|
5593
|
+
line: 211,
|
|
5594
|
+
column: 3
|
|
5595
|
+
}
|
|
5596
|
+
},
|
|
5597
|
+
{
|
|
5598
|
+
id: "@daweifu/capability-menu#capabilityPolicy/listSkillLocations",
|
|
5599
|
+
service: "capabilityPolicy",
|
|
5600
|
+
namespace: "capabilityPolicy",
|
|
5601
|
+
method: "listSkillLocations",
|
|
5602
|
+
invocation: { kind: "direct" },
|
|
5603
|
+
parameters: [],
|
|
5604
|
+
result: {
|
|
5605
|
+
mode: "strict",
|
|
5606
|
+
typeSymbol: "@daweifu/capability-menu#SkillLocation[]",
|
|
5607
|
+
schema: array(skillLocation$schema)
|
|
5608
|
+
},
|
|
5609
|
+
sourceLocation: {
|
|
5610
|
+
file: "src/server/remote.ts",
|
|
5611
|
+
line: 229,
|
|
5612
|
+
column: 3
|
|
5613
|
+
}
|
|
5614
|
+
},
|
|
5615
|
+
{
|
|
5616
|
+
id: "@daweifu/capability-menu#capabilityPolicy/addSkillLocation",
|
|
5617
|
+
service: "capabilityPolicy",
|
|
5618
|
+
namespace: "capabilityPolicy",
|
|
5619
|
+
method: "addSkillLocation",
|
|
5620
|
+
invocation: { kind: "direct" },
|
|
5621
|
+
parameters: [{
|
|
5622
|
+
name: "dir",
|
|
5623
|
+
wire: "dir",
|
|
5624
|
+
source: "json",
|
|
5625
|
+
codec: {
|
|
5626
|
+
mode: "strict",
|
|
5627
|
+
typeSymbol: "string",
|
|
5628
|
+
schema: string()
|
|
5629
|
+
}
|
|
5630
|
+
}, {
|
|
5631
|
+
name: "projectPath",
|
|
5632
|
+
wire: "projectPath",
|
|
5633
|
+
source: "json",
|
|
5634
|
+
acceptsUndefined: true,
|
|
5635
|
+
codec: {
|
|
5636
|
+
mode: "strict",
|
|
5637
|
+
typeSymbol: "string",
|
|
5638
|
+
schema: string().optional()
|
|
5639
|
+
}
|
|
5640
|
+
}],
|
|
5641
|
+
result: {
|
|
5642
|
+
mode: "strict",
|
|
5643
|
+
typeSymbol: "string",
|
|
5644
|
+
schema: string()
|
|
5645
|
+
},
|
|
5646
|
+
sourceLocation: {
|
|
5647
|
+
file: "src/server/remote.ts",
|
|
5648
|
+
line: 252,
|
|
5649
|
+
column: 3
|
|
5650
|
+
}
|
|
5651
|
+
},
|
|
5652
|
+
{
|
|
5653
|
+
id: "@daweifu/capability-menu#capabilityPolicy/removeSkillLocation",
|
|
5654
|
+
service: "capabilityPolicy",
|
|
5655
|
+
namespace: "capabilityPolicy",
|
|
5656
|
+
method: "removeSkillLocation",
|
|
5657
|
+
invocation: { kind: "direct" },
|
|
5658
|
+
parameters: [{
|
|
5659
|
+
name: "name",
|
|
5660
|
+
wire: "name",
|
|
5661
|
+
source: "json",
|
|
5662
|
+
codec: {
|
|
5663
|
+
mode: "strict",
|
|
5664
|
+
typeSymbol: "string",
|
|
5665
|
+
schema: string()
|
|
5666
|
+
}
|
|
5667
|
+
}, {
|
|
5668
|
+
name: "entryDir",
|
|
5669
|
+
wire: "entryDir",
|
|
5670
|
+
source: "json",
|
|
5671
|
+
acceptsUndefined: true,
|
|
5672
|
+
codec: {
|
|
5673
|
+
mode: "strict",
|
|
5674
|
+
typeSymbol: "string",
|
|
5675
|
+
schema: string().optional()
|
|
5676
|
+
}
|
|
5677
|
+
}],
|
|
5678
|
+
result: {
|
|
5679
|
+
mode: "strict",
|
|
5680
|
+
typeSymbol: "boolean",
|
|
5681
|
+
schema: boolean()
|
|
5682
|
+
},
|
|
5683
|
+
sourceLocation: {
|
|
5684
|
+
file: "src/server/remote.ts",
|
|
5685
|
+
line: 258,
|
|
5686
|
+
column: 3
|
|
5687
|
+
}
|
|
5688
|
+
},
|
|
5689
|
+
{
|
|
5690
|
+
id: "@daweifu/capability-menu#capabilityPolicy/adoptSkillLocation",
|
|
5691
|
+
service: "capabilityPolicy",
|
|
5692
|
+
namespace: "capabilityPolicy",
|
|
5693
|
+
method: "adoptSkillLocation",
|
|
5694
|
+
invocation: { kind: "direct" },
|
|
5695
|
+
parameters: [{
|
|
5696
|
+
name: "name",
|
|
5697
|
+
wire: "name",
|
|
5698
|
+
source: "json",
|
|
5699
|
+
codec: {
|
|
5700
|
+
mode: "strict",
|
|
5701
|
+
typeSymbol: "string",
|
|
5702
|
+
schema: string()
|
|
5703
|
+
}
|
|
5704
|
+
}],
|
|
5705
|
+
result: {
|
|
5706
|
+
mode: "strict",
|
|
5707
|
+
typeSymbol: "string",
|
|
5708
|
+
schema: string()
|
|
5709
|
+
},
|
|
5710
|
+
sourceLocation: {
|
|
5711
|
+
file: "src/server/remote.ts",
|
|
5712
|
+
line: 259,
|
|
5713
|
+
column: 3
|
|
5714
|
+
}
|
|
5715
|
+
},
|
|
5716
|
+
{
|
|
5717
|
+
id: "@daweifu/capability-menu#capabilityPolicy/updateSkillLocation",
|
|
5718
|
+
service: "capabilityPolicy",
|
|
5719
|
+
namespace: "capabilityPolicy",
|
|
5720
|
+
method: "updateSkillLocation",
|
|
5721
|
+
invocation: { kind: "direct" },
|
|
5722
|
+
parameters: [
|
|
5723
|
+
{
|
|
5724
|
+
name: "name",
|
|
5725
|
+
wire: "name",
|
|
5726
|
+
source: "json",
|
|
5727
|
+
codec: {
|
|
5728
|
+
mode: "strict",
|
|
5729
|
+
typeSymbol: "string",
|
|
5730
|
+
schema: string()
|
|
5731
|
+
}
|
|
5732
|
+
},
|
|
5733
|
+
{
|
|
5734
|
+
name: "dir",
|
|
5735
|
+
wire: "dir",
|
|
5736
|
+
source: "json",
|
|
5737
|
+
codec: {
|
|
5738
|
+
mode: "strict",
|
|
5739
|
+
typeSymbol: "string",
|
|
5740
|
+
schema: string()
|
|
5741
|
+
}
|
|
5742
|
+
},
|
|
5743
|
+
{
|
|
5744
|
+
name: "entryDir",
|
|
5745
|
+
wire: "entryDir",
|
|
5746
|
+
source: "json",
|
|
5747
|
+
acceptsUndefined: true,
|
|
5748
|
+
codec: {
|
|
5749
|
+
mode: "strict",
|
|
5750
|
+
typeSymbol: "string",
|
|
5751
|
+
schema: string().optional()
|
|
5752
|
+
}
|
|
5753
|
+
}
|
|
5754
|
+
],
|
|
5755
|
+
result: {
|
|
5756
|
+
mode: "strict",
|
|
5757
|
+
typeSymbol: "boolean",
|
|
5758
|
+
schema: boolean()
|
|
5759
|
+
},
|
|
5760
|
+
sourceLocation: {
|
|
5761
|
+
file: "src/server/remote.ts",
|
|
5762
|
+
line: 264,
|
|
5763
|
+
column: 3
|
|
5764
|
+
}
|
|
5765
|
+
}
|
|
5766
|
+
]
|
|
5767
|
+
};
|
|
5768
|
+
//#endregion
|
|
5769
|
+
//#region src/client/store.ts
|
|
5770
|
+
/** Unwrap a RemoteResult-like, throwing a readable error on failure. */
|
|
5771
|
+
function unwrap(result, what) {
|
|
5772
|
+
if (!result.ok) throw new Error(`${what} failed: ${result.error.code}: ${result.error.message}`);
|
|
5773
|
+
return result.value;
|
|
5774
|
+
}
|
|
5775
|
+
/**
|
|
5776
|
+
* Unwrap a RemoteResult-like for a user-facing mutation.
|
|
5777
|
+
*
|
|
5778
|
+
* The policy service's messages are already written for the person at the
|
|
5779
|
+
* dialog (Chinese, naming the offending path), so the RPC name and error code
|
|
5780
|
+
* that `unwrap` adds for diagnostics are dropped here: they would surface as
|
|
5781
|
+
* `capabilityPolicy.addSkillLocation failed: <code>: 目录中没有 SKILL.md:…`
|
|
5782
|
+
* in a dialog that is otherwise plain Chinese.
|
|
5783
|
+
*/
|
|
5784
|
+
function unwrapMessage(result) {
|
|
5785
|
+
if (!result.ok) throw new Error(result.error.message);
|
|
5786
|
+
return result.value;
|
|
5787
|
+
}
|
|
5788
|
+
/**
|
|
5789
|
+
* The last successful snapshot, at module scope.
|
|
5790
|
+
*
|
|
5791
|
+
* The section is remounted every time the settings tab is opened, and a cold
|
|
5792
|
+
* `classifyAll` can be slow once per page load (the plugin's mount awaits a full
|
|
5793
|
+
* catalog enumeration before the surface has data). Keeping the previous rows
|
|
5794
|
+
* lets a remount paint them immediately and revalidate behind the user instead
|
|
5795
|
+
* of showing a loading placeholder. This is a view cache only — every read is
|
|
5796
|
+
* still followed by an authoritative one.
|
|
5797
|
+
*/
|
|
5798
|
+
let lastSnapshot;
|
|
5799
|
+
/** The previously loaded snapshot, when this page session has one. */
|
|
5800
|
+
function cachedSnapshot() {
|
|
5801
|
+
return lastSnapshot;
|
|
5802
|
+
}
|
|
5803
|
+
/** Load the classification list from the remote, remembering it for remounts. */
|
|
5804
|
+
async function loadSnapshot(remote) {
|
|
5805
|
+
lastSnapshot = { rows: unwrap(await remote.classifyAll(), "capabilityPolicy.classifyAll") };
|
|
5806
|
+
return lastSnapshot;
|
|
5807
|
+
}
|
|
5808
|
+
//#endregion
|
|
5809
|
+
//#region src/client/LocationModal.tsx
|
|
5810
|
+
/**
|
|
5811
|
+
* 能力来源弹窗:注册与编辑 MCP 服务器 / Skill 目录。
|
|
5812
|
+
*
|
|
5813
|
+
* Two modes, one form implementation:
|
|
5814
|
+
*
|
|
5815
|
+
* - **注册** — opened from the header's 注册能力 button. Carries the two
|
|
5816
|
+
* sub-tabs (MCP servers / skill directories) and opens on the one matching
|
|
5817
|
+
* the main tab the user is on, so one button is context-sensitive. No list of
|
|
5818
|
+
* what is already registered: that lives on the entries themselves, behind
|
|
5819
|
+
* the per-row 编辑 buttons, where it is actually actionable.
|
|
5820
|
+
* - **编辑** — opened from a row's 编辑 button with the entry prefilled.
|
|
5821
|
+
*
|
|
5822
|
+
* Only 注册 / 保存 / 移除 are offered. Temporarily silencing a source is the
|
|
5823
|
+
* exposure policy's job (classify the capability as Disabled), so there is no
|
|
5824
|
+
* second enable switch here.
|
|
5825
|
+
*/
|
|
5826
|
+
const CSS_ID$1 = "capability-menu-location-css";
|
|
5827
|
+
const CSS$1 = `
|
|
5828
|
+
.lm-block{display:flex;flex-direction:column;gap:8px}
|
|
5829
|
+
.lm-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}
|
|
5830
|
+
.lm-btn:hover{border-color:var(--dsw-alias-border-l3);background:var(--dsw-alias-interactive-bg-hover)}
|
|
5831
|
+
.lm-btn--danger:hover{color:#b3261e;border-color:#b3261e}
|
|
5832
|
+
.lm-btn:disabled{opacity:.55;cursor:default}
|
|
5833
|
+
/* Each field is its own two-column grid with a fixed label track.
|
|
5834
|
+
A single form-wide grid looked tidier but was fragile: it needed
|
|
5835
|
+
.lm-field{display:contents}, which turned every hint into a free-floating grid
|
|
5836
|
+
item, and one extra half-width item pushed the following labels into column 2 and
|
|
5837
|
+
their inputs into column 1 — which is exactly how the streamable-http layout got
|
|
5838
|
+
scrambled (that branch has an odd number of items). Per-field grids mean a hint
|
|
5839
|
+
can only ever affect its own field, and a fixed label track (rather than
|
|
5840
|
+
min-width, which sizes per row) keeps every label column identical. */
|
|
5841
|
+
.lm-form{display:flex;flex-direction:column;gap:6px}
|
|
5842
|
+
.lm-field{--lm-label-w:104px;display:grid;grid-template-columns:var(--lm-label-w) 1fr;gap:2px 8px;align-items:start}
|
|
5843
|
+
.lm-label{grid-column:1;min-width:0;overflow-wrap:anywhere;font-size:12px;line-height:20px;color:var(--dsw-alias-label-tertiary)}
|
|
5844
|
+
/* width:100% must come with border-box: the field carries a 1px border plus 8px of
|
|
5845
|
+
padding, which would otherwise overflow the column and clip. */
|
|
5846
|
+
.lm-input{grid-column:2;width:100%;box-sizing:border-box;min-width:0;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;background:var(--dsw-alias-bg-layer-1);color:inherit;font:inherit;font-size:12px;line-height:20px;padding:2px 8px}
|
|
5847
|
+
.lm-input:disabled{opacity:.6}
|
|
5848
|
+
textarea.lm-input{resize:vertical;font-family:var(--dsw-font-markdown-code-block-font-family)}
|
|
5849
|
+
/* Lives inside its field, in the content column: it lines up with the box it
|
|
5850
|
+
annotates and takes the caption colour so it does not read as another label. */
|
|
5851
|
+
.lm-hint{grid-column:2;margin:0;font-size:11px;line-height:16px;color:var(--dsw-alias-label-caption)}
|
|
5852
|
+
.lm-error{margin:0;font-size:12px;color:#b3261e;word-break:break-word}
|
|
5853
|
+
.lm-actions{display:flex;gap:8px;justify-content:flex-end;padding:4px 0 0}
|
|
5854
|
+
/* Removal is irreversible, so it takes over the action row for one confirmation
|
|
5855
|
+
step: the message states the consequence, then the buttons act. */
|
|
5856
|
+
.lm-confirm{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:8px 10px;border:1px solid #b3261e;border-radius:6px}
|
|
5857
|
+
.lm-confirm-msg{flex:1 1 auto;margin:0;font-size:12px;line-height:18px;word-break:break-word}
|
|
5858
|
+
.lm-confirm-actions{display:flex;gap:8px;flex-shrink:0}
|
|
5859
|
+
/* The modal shell itself carries no padding (it is shared with the read-only
|
|
5860
|
+
catalog viewer, whose body supplies its own); the form supplies it here. */
|
|
5861
|
+
.lm-body{padding:14px 16px;overflow:auto;flex:1 1 auto;min-height:0}
|
|
5862
|
+
`;
|
|
5863
|
+
if (typeof document !== "undefined" && document.querySelector(`style[data-css-id="${CSS_ID$1}"]`) === null) {
|
|
5864
|
+
const tag = document.createElement("style");
|
|
5865
|
+
tag.dataset.cssId = CSS_ID$1;
|
|
5866
|
+
tag.textContent = CSS$1;
|
|
5867
|
+
document.head.appendChild(tag);
|
|
5868
|
+
}
|
|
5869
|
+
/** Render a key/value map as `Key: Value` lines. */
|
|
5870
|
+
function toLines(record) {
|
|
5871
|
+
if (record === void 0) return "";
|
|
5872
|
+
return Object.entries(record).map(([key, value]) => `${key}: ${value}`).join("\n");
|
|
5873
|
+
}
|
|
5874
|
+
/** Parse `Key: Value` lines into a map; blank lines and lines without `:` are skipped. */
|
|
5875
|
+
function fromLines(text) {
|
|
5876
|
+
const out = {};
|
|
5877
|
+
for (const line of text.split("\n")) {
|
|
5878
|
+
const trimmed = line.trim();
|
|
5879
|
+
if (trimmed.length === 0) continue;
|
|
5880
|
+
const at = trimmed.indexOf(":");
|
|
5881
|
+
if (at <= 0) continue;
|
|
5882
|
+
const key = trimmed.slice(0, at).trim();
|
|
5883
|
+
const value = trimmed.slice(at + 1).trim();
|
|
5884
|
+
if (key.length > 0) out[key] = value;
|
|
5885
|
+
}
|
|
5886
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
5887
|
+
}
|
|
5888
|
+
function LocationModal(props) {
|
|
5889
|
+
const { remote, t, defaultKind = "mcp", editMcp, editSkill, onClose, onChanged } = props;
|
|
5890
|
+
const editing = editMcp !== void 0 || editSkill !== void 0;
|
|
5891
|
+
const [subTab, setSubTab] = (0, react.useState)(editSkill !== void 0 ? "skill" : editMcp !== void 0 ? "mcp" : defaultKind);
|
|
5892
|
+
const [error, setError] = (0, react.useState)(null);
|
|
5893
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
5894
|
+
/**
|
|
5895
|
+
* Which action is waiting for confirmation, if any. Two need one: 移除, and
|
|
5896
|
+
* 保存 on an entry that is a real directory — repointing unlinks the entry
|
|
5897
|
+
* first, which recursively deletes a real directory's contents.
|
|
5898
|
+
*/
|
|
5899
|
+
const [confirm, setConfirm] = (0, react.useState)(null);
|
|
5900
|
+
const [serverName, setServerName] = (0, react.useState)(editMcp?.serverName ?? "");
|
|
5901
|
+
const [transport, setTransport] = (0, react.useState)(editMcp?.transport ?? "streamable-http");
|
|
5902
|
+
const [command, setCommand] = (0, react.useState)(editMcp?.command ?? "");
|
|
5903
|
+
const [args, setArgs] = (0, react.useState)((editMcp?.args ?? []).join(" "));
|
|
5904
|
+
const [cwd, setCwd] = (0, react.useState)(editMcp?.cwd ?? "");
|
|
5905
|
+
const [env, setEnv] = (0, react.useState)(toLines(editMcp?.env));
|
|
5906
|
+
const [url, setUrl] = (0, react.useState)(editMcp?.url ?? "");
|
|
5907
|
+
const [headers, setHeaders] = (0, react.useState)(toLines(editMcp?.headers));
|
|
5908
|
+
const [timeoutSec, setTimeoutSec] = (0, react.useState)(editMcp?.toolCallTimeoutMs === void 0 ? "" : String(Math.round(editMcp.toolCallTimeoutMs / 1e3)));
|
|
5909
|
+
const [skillDir, setSkillDir] = (0, react.useState)(editSkill?.target ?? editSkill?.path ?? "");
|
|
5910
|
+
const [skillRoot, setSkillRoot] = (0, react.useState)(editSkill?.root ?? "user");
|
|
5911
|
+
/** A path inside the project; the server derives the project root from it. */
|
|
5912
|
+
const [projectPath, setProjectPath] = (0, react.useState)("");
|
|
5913
|
+
/**
|
|
5914
|
+
* What the operator sees this entry called. They got here from a row named by
|
|
5915
|
+
* the skill's declared name, so the messages use that and not the entry's own
|
|
5916
|
+
* name — which is only ever handed back to the server, since that is what
|
|
5917
|
+
* addresses the symlink on disk.
|
|
5918
|
+
*/
|
|
5919
|
+
const skillLabel = editSkill?.skillName ?? editSkill?.name ?? "";
|
|
5920
|
+
const title = (0, react.useMemo)(() => {
|
|
5921
|
+
if (editMcp !== void 0) return t("editMcp");
|
|
5922
|
+
if (editSkill !== void 0) return t("editSkillNamed", { name: skillLabel });
|
|
5923
|
+
return t("registerCapability");
|
|
5924
|
+
}, [
|
|
5925
|
+
editMcp,
|
|
5926
|
+
editSkill,
|
|
5927
|
+
skillLabel,
|
|
5928
|
+
t
|
|
5929
|
+
]);
|
|
5930
|
+
/** Run a mutation; on success close and ask the parent to re-pull. */
|
|
5931
|
+
const submit = (0, react.useCallback)(async (action, noticeFor) => {
|
|
5932
|
+
if (busy) return;
|
|
5933
|
+
setBusy(true);
|
|
5934
|
+
try {
|
|
5935
|
+
const changed = await action();
|
|
5936
|
+
if (changed === false) throw new Error(t("entryNotFound"));
|
|
5937
|
+
setError(null);
|
|
5938
|
+
onChanged(noticeFor?.(changed));
|
|
5939
|
+
onClose();
|
|
5940
|
+
} catch (e) {
|
|
5941
|
+
setError(String(e));
|
|
5942
|
+
} finally {
|
|
5943
|
+
setBusy(false);
|
|
5944
|
+
}
|
|
5945
|
+
}, [
|
|
5946
|
+
busy,
|
|
5947
|
+
onChanged,
|
|
5948
|
+
onClose,
|
|
5949
|
+
t
|
|
5950
|
+
]);
|
|
5951
|
+
const submitMcp = (0, react.useCallback)(() => {
|
|
5952
|
+
const trimmedTimeout = timeoutSec.trim();
|
|
5953
|
+
const timeoutMs = trimmedTimeout.length === 0 ? void 0 : Math.round(Number(trimmedTimeout) * 1e3);
|
|
5954
|
+
if (timeoutMs !== void 0 && !Number.isFinite(timeoutMs)) {
|
|
5955
|
+
setError(t("timeoutInvalid"));
|
|
5956
|
+
return;
|
|
5957
|
+
}
|
|
5958
|
+
const parsedEnv = fromLines(env);
|
|
5959
|
+
const parsedHeaders = fromLines(headers);
|
|
5960
|
+
const common = {
|
|
5961
|
+
transport,
|
|
5962
|
+
...transport === "stdio" ? {
|
|
5963
|
+
command: command.trim(),
|
|
5964
|
+
...args.trim().length > 0 ? { args: args.trim().split(/\s+/) } : {},
|
|
5965
|
+
...parsedEnv !== void 0 ? { env: parsedEnv } : {},
|
|
5966
|
+
...cwd.trim().length > 0 ? { cwd: cwd.trim() } : {}
|
|
5967
|
+
} : {
|
|
5968
|
+
url: url.trim(),
|
|
5969
|
+
...parsedHeaders !== void 0 ? { headers: parsedHeaders } : {}
|
|
5970
|
+
},
|
|
5971
|
+
...timeoutMs !== void 0 ? { toolCallTimeoutMs: timeoutMs } : {}
|
|
5972
|
+
};
|
|
5973
|
+
submit(async () => editMcp !== void 0 ? unwrapMessage(await remote.updateLocation(editMcp.id, common)) : unwrapMessage(await remote.addLocation({
|
|
5974
|
+
serverName: serverName.trim(),
|
|
5975
|
+
...common
|
|
5976
|
+
})));
|
|
5977
|
+
}, [
|
|
5978
|
+
submit,
|
|
5979
|
+
remote,
|
|
5980
|
+
editMcp,
|
|
5981
|
+
serverName,
|
|
5982
|
+
transport,
|
|
5983
|
+
command,
|
|
5984
|
+
args,
|
|
5985
|
+
cwd,
|
|
5986
|
+
env,
|
|
5987
|
+
url,
|
|
5988
|
+
headers,
|
|
5989
|
+
timeoutSec,
|
|
5990
|
+
t
|
|
5991
|
+
]);
|
|
5992
|
+
const submitSkill = (0, react.useCallback)(() => {
|
|
5993
|
+
const asked = skillRoot === "project" ? projectPath.trim() : void 0;
|
|
5994
|
+
submit(async () => editSkill !== void 0 ? unwrapMessage(await remote.updateSkillLocation(editSkill.name, skillDir.trim(), editSkill.entryDir)) : unwrapMessage(await remote.addSkillLocation(skillDir.trim(), asked)), (result) => editSkill !== void 0 ? t("skillRepointed", { dir: skillDir.trim() }) : t("skillRegisteredAt", { path: String(result) }));
|
|
5995
|
+
}, [
|
|
5996
|
+
submit,
|
|
5997
|
+
remote,
|
|
5998
|
+
editSkill,
|
|
5999
|
+
skillDir,
|
|
6000
|
+
skillRoot,
|
|
6001
|
+
projectPath,
|
|
6002
|
+
t
|
|
6003
|
+
]);
|
|
6004
|
+
const remove = (0, react.useCallback)(() => {
|
|
6005
|
+
if (editMcp !== void 0) {
|
|
6006
|
+
submit(async () => unwrapMessage(await remote.removeLocation(editMcp.id)));
|
|
6007
|
+
return;
|
|
5398
6008
|
}
|
|
5399
|
-
|
|
5400
|
-
}
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
6009
|
+
if (editSkill !== void 0) submit(async () => unwrapMessage(await remote.removeSkillLocation(editSkill.name, editSkill.entryDir)));
|
|
6010
|
+
}, [
|
|
6011
|
+
submit,
|
|
6012
|
+
remote,
|
|
6013
|
+
editMcp,
|
|
6014
|
+
editSkill
|
|
6015
|
+
]);
|
|
6016
|
+
/**
|
|
6017
|
+
* What removal actually costs, which differs by target: an MCP row only loses
|
|
6018
|
+
* its config, a linked skill only loses its symlink — but `removeSkill`
|
|
6019
|
+
* recursively deletes a *real* directory in the skill root, so that case has to
|
|
6020
|
+
* say so before the click, not after.
|
|
6021
|
+
*/
|
|
6022
|
+
const removalWarning = (0, react.useMemo)(() => {
|
|
6023
|
+
if (editMcp !== void 0) return t("confirmRemoveMcp", { name: editMcp.serverName });
|
|
6024
|
+
if (editSkill === void 0) return "";
|
|
6025
|
+
return editSkill.linked ? t("confirmRemoveSkillLink", { name: skillLabel }) : t("confirmRemoveSkillDir", { name: skillLabel });
|
|
6026
|
+
}, [
|
|
6027
|
+
editMcp,
|
|
6028
|
+
editSkill,
|
|
6029
|
+
skillLabel,
|
|
6030
|
+
t
|
|
6031
|
+
]);
|
|
6032
|
+
/** Saving needs a confirmation only when it would delete a real directory. */
|
|
6033
|
+
const needsSaveConfirm = editSkill !== void 0 && !editSkill.linked;
|
|
6034
|
+
/** The action row, or the confirmation step that has replaced it. */
|
|
6035
|
+
const renderActions = (onSubmit) => confirm !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6036
|
+
className: "lm-confirm",
|
|
6037
|
+
role: "alert",
|
|
6038
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6039
|
+
className: "lm-confirm-msg",
|
|
6040
|
+
children: confirm === "save" ? t("saveConfirmRealDir", { name: skillLabel }) : removalWarning
|
|
6041
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6042
|
+
className: "lm-confirm-actions",
|
|
6043
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6044
|
+
type: "button",
|
|
6045
|
+
className: "lm-btn",
|
|
6046
|
+
disabled: busy,
|
|
6047
|
+
onClick: () => setConfirm(null),
|
|
6048
|
+
children: t("cancel")
|
|
6049
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6050
|
+
type: "button",
|
|
6051
|
+
className: "lm-btn lm-btn--danger",
|
|
6052
|
+
disabled: busy,
|
|
6053
|
+
onClick: () => {
|
|
6054
|
+
if (confirm === "save") onSubmit();
|
|
6055
|
+
else remove();
|
|
6056
|
+
},
|
|
6057
|
+
children: confirm === "save" ? t("confirmSaveAnyway") : t("confirmRemove")
|
|
6058
|
+
})]
|
|
6059
|
+
})]
|
|
6060
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6061
|
+
className: "lm-actions",
|
|
6062
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6063
|
+
type: "button",
|
|
6064
|
+
className: "lm-btn",
|
|
6065
|
+
disabled: busy,
|
|
6066
|
+
onClick: () => {
|
|
6067
|
+
if (needsSaveConfirm) setConfirm("save");
|
|
6068
|
+
else onSubmit();
|
|
6069
|
+
},
|
|
6070
|
+
children: editing ? t("save") : t("register")
|
|
6071
|
+
}), editing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6072
|
+
type: "button",
|
|
6073
|
+
className: "lm-btn lm-btn--danger",
|
|
6074
|
+
disabled: busy,
|
|
6075
|
+
onClick: () => setConfirm("remove"),
|
|
6076
|
+
children: t("remove")
|
|
6077
|
+
})]
|
|
6078
|
+
});
|
|
6079
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6080
|
+
className: "mc-preview-mask",
|
|
6081
|
+
onClick: onClose,
|
|
6082
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6083
|
+
className: "mc-preview",
|
|
6084
|
+
onClick: (e) => e.stopPropagation(),
|
|
6085
|
+
children: [
|
|
6086
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6087
|
+
className: "mc-preview-head",
|
|
6088
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6089
|
+
className: "mc-preview-title",
|
|
6090
|
+
children: title
|
|
6091
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6092
|
+
type: "button",
|
|
6093
|
+
className: "mc-preview-close",
|
|
6094
|
+
onClick: onClose,
|
|
6095
|
+
children: t("previewClose")
|
|
6096
|
+
})]
|
|
6097
|
+
}),
|
|
6098
|
+
!editing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6099
|
+
className: "mc-subtabs mc-catalog-tabs",
|
|
6100
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6101
|
+
className: "mc-tab-group",
|
|
6102
|
+
role: "tablist",
|
|
6103
|
+
"aria-label": t("registerCapability"),
|
|
6104
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6105
|
+
type: "button",
|
|
6106
|
+
role: "tab",
|
|
6107
|
+
className: "mc-tab",
|
|
6108
|
+
"aria-selected": subTab === "mcp",
|
|
6109
|
+
"data-active": subTab === "mcp" ? "true" : void 0,
|
|
6110
|
+
onClick: () => setSubTab("mcp"),
|
|
6111
|
+
children: t("mcpServers")
|
|
6112
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6113
|
+
type: "button",
|
|
6114
|
+
role: "tab",
|
|
6115
|
+
className: "mc-tab",
|
|
6116
|
+
"aria-selected": subTab === "skill",
|
|
6117
|
+
"data-active": subTab === "skill" ? "true" : void 0,
|
|
6118
|
+
onClick: () => setSubTab("skill"),
|
|
6119
|
+
children: t("skillDirs")
|
|
6120
|
+
})]
|
|
6121
|
+
})
|
|
6122
|
+
}),
|
|
6123
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6124
|
+
className: "lm-body",
|
|
6125
|
+
children: [error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6126
|
+
className: "lm-error",
|
|
6127
|
+
children: error
|
|
6128
|
+
}), subTab === "mcp" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6129
|
+
className: "lm-block",
|
|
6130
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6131
|
+
className: "lm-form",
|
|
6132
|
+
children: [
|
|
6133
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6134
|
+
className: "lm-field",
|
|
6135
|
+
children: [
|
|
6136
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6137
|
+
className: "lm-label",
|
|
6138
|
+
children: t("transport")
|
|
6139
|
+
}),
|
|
6140
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
6141
|
+
className: "lm-input",
|
|
6142
|
+
value: transport,
|
|
6143
|
+
onChange: (e) => setTransport(e.target.value === "stdio" ? "stdio" : "streamable-http"),
|
|
6144
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
6145
|
+
value: "stdio",
|
|
6146
|
+
children: t("transportStdio")
|
|
6147
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
6148
|
+
value: "streamable-http",
|
|
6149
|
+
children: t("transportHttp")
|
|
6150
|
+
})]
|
|
6151
|
+
}),
|
|
6152
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6153
|
+
className: "lm-hint",
|
|
6154
|
+
children: transport === "stdio" ? t("transportHintStdio") : t("transportHintHttp")
|
|
6155
|
+
})
|
|
6156
|
+
]
|
|
6157
|
+
}),
|
|
6158
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6159
|
+
className: "lm-field",
|
|
6160
|
+
children: [
|
|
6161
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6162
|
+
className: "lm-label",
|
|
6163
|
+
children: t("serverName")
|
|
6164
|
+
}),
|
|
6165
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6166
|
+
className: "lm-input",
|
|
6167
|
+
value: serverName,
|
|
6168
|
+
disabled: editMcp !== void 0,
|
|
6169
|
+
onChange: (e) => setServerName(e.target.value)
|
|
6170
|
+
}),
|
|
6171
|
+
editMcp !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6172
|
+
className: "lm-hint",
|
|
6173
|
+
children: t("serverNameImmutable")
|
|
6174
|
+
})
|
|
6175
|
+
]
|
|
6176
|
+
}),
|
|
6177
|
+
transport === "stdio" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
6178
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6179
|
+
className: "lm-field",
|
|
6180
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6181
|
+
className: "lm-label",
|
|
6182
|
+
children: t("command")
|
|
6183
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6184
|
+
className: "lm-input",
|
|
6185
|
+
value: command,
|
|
6186
|
+
onChange: (e) => setCommand(e.target.value)
|
|
6187
|
+
})]
|
|
6188
|
+
}),
|
|
6189
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6190
|
+
className: "lm-field",
|
|
6191
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6192
|
+
className: "lm-label",
|
|
6193
|
+
children: t("args")
|
|
6194
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6195
|
+
className: "lm-input",
|
|
6196
|
+
value: args,
|
|
6197
|
+
onChange: (e) => setArgs(e.target.value)
|
|
6198
|
+
})]
|
|
6199
|
+
}),
|
|
6200
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6201
|
+
className: "lm-field",
|
|
6202
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6203
|
+
className: "lm-label",
|
|
6204
|
+
children: t("cwd")
|
|
6205
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6206
|
+
className: "lm-input",
|
|
6207
|
+
value: cwd,
|
|
6208
|
+
onChange: (e) => setCwd(e.target.value)
|
|
6209
|
+
})]
|
|
6210
|
+
}),
|
|
6211
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6212
|
+
className: "lm-field",
|
|
6213
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6214
|
+
className: "lm-label",
|
|
6215
|
+
children: t("env")
|
|
6216
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
6217
|
+
className: "lm-input",
|
|
6218
|
+
rows: 3,
|
|
6219
|
+
value: env,
|
|
6220
|
+
onChange: (e) => setEnv(e.target.value)
|
|
6221
|
+
})]
|
|
6222
|
+
})
|
|
6223
|
+
] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6224
|
+
className: "lm-field",
|
|
6225
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6226
|
+
className: "lm-label",
|
|
6227
|
+
children: t("url")
|
|
6228
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6229
|
+
className: "lm-input",
|
|
6230
|
+
value: url,
|
|
6231
|
+
onChange: (e) => setUrl(e.target.value)
|
|
6232
|
+
})]
|
|
6233
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6234
|
+
className: "lm-field",
|
|
6235
|
+
children: [
|
|
6236
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6237
|
+
className: "lm-label",
|
|
6238
|
+
children: t("headers")
|
|
6239
|
+
}),
|
|
6240
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
6241
|
+
className: "lm-input",
|
|
6242
|
+
rows: 4,
|
|
6243
|
+
value: headers,
|
|
6244
|
+
onChange: (e) => setHeaders(e.target.value)
|
|
6245
|
+
}),
|
|
6246
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6247
|
+
className: "lm-hint",
|
|
6248
|
+
children: t("headersHint")
|
|
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
|
+
]
|
|
6339
|
+
})
|
|
6340
|
+
})]
|
|
6341
|
+
})
|
|
6342
|
+
]
|
|
6343
|
+
})
|
|
6344
|
+
});
|
|
5411
6345
|
}
|
|
6346
|
+
/**
|
|
6347
|
+
* dsh's source labels for a project's own skill roots — the `project`-prefixed
|
|
6348
|
+
* providers of `@deepseek-ai/dsh-skill-filesystem`, which scan
|
|
6349
|
+
* `<projectRoot>/.dsh/skills` and `<projectRoot>/.agents/skills`. Every other
|
|
6350
|
+
* label (`user-dsh`, `user-agents`, `bundled`, `custom`, `runtime`) is a
|
|
6351
|
+
* user-level root. Shared so the server's location manager and the browser's
|
|
6352
|
+
* 全局 / 项目 grouping agree on what "project scoped" means.
|
|
6353
|
+
*/
|
|
6354
|
+
const PROJECT_SKILL_SOURCES = /* @__PURE__ */ new Set(["project-dsh", "project-agents"]);
|
|
6355
|
+
/**
|
|
6356
|
+
* Source labels for user-level skill roots whose entries this plugin can adopt:
|
|
6357
|
+
* linking the skill's own directory into the user root (where the entry becomes
|
|
6358
|
+
* editable) without touching its content. `user-dsh` is excluded because that
|
|
6359
|
+
* root is already managed, and `bundled` because a packaged skill should not be
|
|
6360
|
+
* shadowed by a user copy.
|
|
6361
|
+
*/
|
|
6362
|
+
const ADOPTABLE_SKILL_SOURCES = /* @__PURE__ */ new Set(["user-agents", "custom"]);
|
|
5412
6363
|
//#endregion
|
|
5413
6364
|
//#region src/client/CapabilitySection.tsx
|
|
5414
6365
|
/**
|
|
5415
6366
|
* ⚠️ VERIFIED AGAINST REAL rc.8 CLIENT API.
|
|
5416
6367
|
*
|
|
5417
6368
|
* React section for the 能力菜单 settings tab. Follows the real dsh client
|
|
5418
|
-
* pattern (see `dsh-client-ui-settings-plugins`): two tabs (
|
|
6369
|
+
* pattern (see `dsh-client-ui-settings-plugins`): two tabs (工具 / Skills)
|
|
5419
6370
|
* under one heading, each listing capabilities with a clickable class chip.
|
|
5420
6371
|
*
|
|
5421
6372
|
* Layout:
|
|
5422
|
-
* - summary strip → per-class counts for the active tab
|
|
5423
|
-
*
|
|
5424
|
-
* -
|
|
6373
|
+
* - summary strip → per-class counts for the active tab (Skills tab counts
|
|
6374
|
+
* its active 全局/项目 sub-tab)
|
|
6375
|
+
* - tabs → Tools | Skills (plugins-tab chrome)
|
|
6376
|
+
* - Tools tab → grouped by server, collapsible disclosure rows; the
|
|
5425
6377
|
* per-class count chip and each tool's class chip are
|
|
5426
|
-
* clickable to cycle
|
|
5427
|
-
*
|
|
5428
|
-
*
|
|
6378
|
+
* clickable to cycle Resident → On-demand → Disabled.
|
|
6379
|
+
* Harness-native tools (no real MCP server) share the
|
|
6380
|
+
* reserved `built-in` group, shown as 「系统内置」.
|
|
6381
|
+
* - Skills tab → sub-tabs 全局技能 / 项目技能 (always visible), each
|
|
6382
|
+
* with flat skill rows carrying the same clickable class
|
|
6383
|
+
* chip plus a directory tree / file preview
|
|
5429
6384
|
*/
|
|
5430
6385
|
const CLASS_KEYS = [
|
|
5431
|
-
"
|
|
5432
|
-
"
|
|
5433
|
-
"
|
|
6386
|
+
"resident",
|
|
6387
|
+
"on-demand",
|
|
6388
|
+
"disabled"
|
|
5434
6389
|
];
|
|
5435
|
-
/** Click-cycle order
|
|
6390
|
+
/** Click-cycle order on machine values (displayed as On-demand → Disabled → Resident). */
|
|
5436
6391
|
const NEXT_CLASS = {
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
6392
|
+
"on-demand": "disabled",
|
|
6393
|
+
disabled: "resident",
|
|
6394
|
+
resident: "on-demand"
|
|
6395
|
+
};
|
|
6396
|
+
/** i18n keys for the short display labels, keyed by the machine class value. */
|
|
6397
|
+
const CLASS_SHORT_KEYS = {
|
|
6398
|
+
resident: "residentShort",
|
|
6399
|
+
"on-demand": "onDemandShort",
|
|
6400
|
+
disabled: "disabledShort"
|
|
5440
6401
|
};
|
|
5441
6402
|
/** Scoped stylesheet: injected once at module scope, like every official bundle. */
|
|
5442
6403
|
const CSS_ID = "capability-menu-section-css";
|
|
@@ -5444,31 +6405,52 @@ const CSS = `
|
|
|
5444
6405
|
.mc-section{display:flex;flex-direction:column;gap:12px;color:var(--dsw-alias-label-primary)}
|
|
5445
6406
|
.mc-heading{margin:0;font-size:18px;font-weight:600}
|
|
5446
6407
|
.mc-desc{margin:0;color:var(--dsw-alias-label-tertiary);font-size:13px}
|
|
6408
|
+
/* 说明行右侧放只读文档入口:把它从头部的计数行挪出来,计数多(Tools 常驻 ·
|
|
6409
|
+
167)时那一行不再因此折行。 */
|
|
6410
|
+
.mc-desc-row{display:flex;align-items:center;justify-content:space-between;gap:12px}
|
|
6411
|
+
.mc-desc-row .mc-desc{flex:1 1 auto;min-width:0}
|
|
5447
6412
|
.mc-summary{display:flex;gap:12px;flex-wrap:wrap;justify-content:flex-end;align-items:center;padding-bottom:8px}
|
|
6413
|
+
.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
|
+
.mc-catalog-btn:hover{border-color:var(--dsw-alias-border-l3);background:var(--dsw-alias-interactive-bg-hover)}
|
|
6415
|
+
.mc-catalog-tabs{padding:8px 16px 0}
|
|
6416
|
+
.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}
|
|
5448
6417
|
.mc-chip{display:inline-flex;align-items:center;gap:6px;font-size:12px;line-height:20px;white-space:nowrap}
|
|
5449
|
-
/*
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
.mc-dot
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
6418
|
+
/* 三态圆点:常驻=实心、按需=半实心、禁用=圆环+斜杠(禁行标志)。
|
|
6419
|
+
形状区分之外仍保留色盲友好(蓝-黄轴):冷蓝=常驻、暖琥珀=按需、中性灰=禁用。
|
|
6420
|
+
三个状态都用内联 SVG 做 mask 绘制,保证 10px 下也是矢量正圆(避免 CSS
|
|
6421
|
+
border-radius 小尺寸的方圆变形);禁用不用空心圆:在「禁用 · 0」这类计数旁,
|
|
6422
|
+
空心圆容易被误读成数字 0。 */
|
|
6423
|
+
.mc-dot{position:relative;width:13px;height:13px;border-radius:50%;flex:none;box-sizing:border-box}
|
|
6424
|
+
.mc-dot--resident{--mc-dot:#527a9c;background:var(--mc-dot);-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Ccircle cx='6' cy='6' r='5.3' fill='%23000'/%3E%3C/svg%3E") center/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Ccircle cx='6' cy='6' r='5.3' fill='%23000'/%3E%3C/svg%3E") center/contain no-repeat}
|
|
6425
|
+
.mc-dot--on-demand{--mc-dot:#a57c33;background:var(--mc-dot);-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Ccircle cx='6' cy='6' r='4.6' fill='none' stroke='%23000' stroke-width='1.4'/%3E%3Cpath d='M1.4 6 A4.6 4.6 0 0 1 10.6 6 Z' fill='%23000'/%3E%3C/svg%3E") center/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Ccircle cx='6' cy='6' r='4.6' fill='none' stroke='%23000' stroke-width='1.4'/%3E%3Cpath d='M1.4 6 A4.6 4.6 0 0 1 10.6 6 Z' fill='%23000'/%3E%3C/svg%3E") center/contain no-repeat}
|
|
6426
|
+
.mc-dot--disabled{--mc-dot:#7e7477;background:var(--mc-dot);-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Ccircle cx='6' cy='6' r='4.6' fill='none' stroke='%23000' stroke-width='1.4'/%3E%3Cline x1='3' y1='9' x2='9' y2='3' stroke='%23000' stroke-width='1.4' stroke-linecap='round'/%3E%3C/svg%3E") center/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Ccircle cx='6' cy='6' r='4.6' fill='none' stroke='%23000' stroke-width='1.4'/%3E%3Cline x1='3' y1='9' x2='9' y2='3' stroke='%23000' stroke-width='1.4' stroke-linecap='round'/%3E%3C/svg%3E") center/contain no-repeat}
|
|
6427
|
+
body[data-ds-dark-theme] .mc-dot--resident{--mc-dot:#96b6d1}
|
|
6428
|
+
body[data-ds-dark-theme] .mc-dot--on-demand{--mc-dot:#d4b26b}
|
|
6429
|
+
body[data-ds-dark-theme] .mc-dot--disabled{--mc-dot:#b8abad}
|
|
5458
6430
|
/* 同上色系(低饱和灰调):仅文字着色,不加背景,浅/深主题各一档。 */
|
|
5459
|
-
.mc-chip--
|
|
5460
|
-
.mc-chip--
|
|
5461
|
-
.mc-chip--
|
|
5462
|
-
body[data-ds-dark-theme] .mc-chip--
|
|
5463
|
-
body[data-ds-dark-theme] .mc-chip--
|
|
5464
|
-
body[data-ds-dark-theme] .mc-chip--
|
|
6431
|
+
.mc-chip--resident{color:#527a9c}
|
|
6432
|
+
.mc-chip--on-demand{color:#a57c33}
|
|
6433
|
+
.mc-chip--disabled{color:#7e7477}
|
|
6434
|
+
body[data-ds-dark-theme] .mc-chip--resident{color:#96b6d1}
|
|
6435
|
+
body[data-ds-dark-theme] .mc-chip--on-demand{color:#d4b26b}
|
|
6436
|
+
body[data-ds-dark-theme] .mc-chip--disabled{color:#b8abad}
|
|
5465
6437
|
.mc-tabs{border-bottom:1px solid var(--dsw-alias-border-l2);display:flex;align-items:flex-end;justify-content:space-between;gap:22px}
|
|
6438
|
+
/* Skills sub-tab bar (全局技能/项目技能): same underline chrome, no right-side summary. */
|
|
6439
|
+
.mc-subtabs{border-bottom:1px solid var(--dsw-alias-border-l2);display:flex;align-items:flex-end;gap:22px}
|
|
5466
6440
|
.mc-tab-group{display:flex;align-items:flex-end;gap:22px}
|
|
5467
6441
|
.mc-tab{color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:0;padding:7px 1px 9px;font-size:13px;line-height:20px;position:relative}
|
|
5468
6442
|
.mc-tab:hover,.mc-tab[data-active=true]{color:var(--dsw-alias-label-primary)}
|
|
5469
6443
|
.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}
|
|
5470
6444
|
.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}
|
|
5471
6445
|
.mc-panel{min-width:0;padding-top:12px}
|
|
6446
|
+
/* Skills panel: its first row is the 全局技能/项目技能 sub-tab bar, which sits
|
|
6447
|
+
directly under the main tab bar. It is a second level of the same control, so
|
|
6448
|
+
the two bars have to read as one stack: no panel padding at all, and the
|
|
6449
|
+
sub-tabs keep a token top padding instead of the 7px they share with the
|
|
6450
|
+
primary tabs. Together the label sits ~13px under the main label rather than
|
|
6451
|
+
~20px, with the main bar's rule between them. */
|
|
6452
|
+
.mc-panel--tight{padding-top:0}
|
|
6453
|
+
.mc-panel--tight .mc-subtabs .mc-tab{padding-top:4px}
|
|
5472
6454
|
.mc-panel-inner{display:flex;flex-direction:column;gap:14px}
|
|
5473
6455
|
.mc-group{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;overflow:hidden}
|
|
5474
6456
|
.mc-group-header{box-sizing:border-box;display:flex;align-items:center;gap:10px;width:100%;min-width:0;padding:10px 12px;background:var(--dsw-alias-bg-layer-1);border:0;color:inherit;font:inherit;text-align:left;cursor:pointer}
|
|
@@ -5483,12 +6465,17 @@ body[data-ds-dark-theme] .mc-chip--blocked{color:#b8abad}
|
|
|
5483
6465
|
.mc-count{font-size:11px;line-height:18px;padding:0 8px;border-radius:999px;border:1px solid transparent;font-family:inherit;cursor:pointer;display:inline-flex;align-items:center;gap:5px;white-space:nowrap}
|
|
5484
6466
|
.mc-count:hover{border-color:var(--dsw-alias-border-l3)}
|
|
5485
6467
|
.mc-count:disabled{cursor:default;opacity:.6}
|
|
5486
|
-
.mc-count--
|
|
5487
|
-
.mc-count--
|
|
5488
|
-
.mc-count--
|
|
5489
|
-
body[data-ds-dark-theme] .mc-count--
|
|
5490
|
-
body[data-ds-dark-theme] .mc-count--
|
|
5491
|
-
body[data-ds-dark-theme] .mc-count--
|
|
6468
|
+
.mc-count--resident{color:#527a9c}
|
|
6469
|
+
.mc-count--on-demand{color:#a57c33}
|
|
6470
|
+
.mc-count--disabled{color:#7e7477}
|
|
6471
|
+
body[data-ds-dark-theme] .mc-count--resident{color:#96b6d1}
|
|
6472
|
+
body[data-ds-dark-theme] .mc-count--on-demand{color:#d4b26b}
|
|
6473
|
+
body[data-ds-dark-theme] .mc-count--disabled{color:#b8abad}
|
|
6474
|
+
/* 「编辑」是动作,不是分类计数:沿用 chip 的尺寸以对齐,但用次要文字色、hover
|
|
6475
|
+
才变蓝加下划线。裸用 .mc-count 时会继承 .mc-section 的 label-primary(浅色
|
|
6476
|
+
主题下近黑),夹在着色的计数 chip 旁会读成一枚没有圆点的黑色 chip。 */
|
|
6477
|
+
.mc-count--action{color:var(--dsw-alias-label-secondary)}
|
|
6478
|
+
.mc-count--action:hover{color:var(--dsw-alias-state-business-primary);text-decoration:underline}
|
|
5492
6479
|
.mc-tools{border-top:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base)}
|
|
5493
6480
|
.mc-tool{display:flex;align-items:center;gap:10px;padding:7px 12px 7px 26px;font-size:13px;line-height:20px;cursor:pointer}
|
|
5494
6481
|
.mc-tool:hover{background:var(--dsw-alias-interactive-bg-hover)}
|
|
@@ -5500,12 +6487,26 @@ body[data-ds-dark-theme] .mc-count--blocked{color:#b8abad}
|
|
|
5500
6487
|
.mc-tag{font-size:11px;line-height:18px;padding:0 8px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary)}
|
|
5501
6488
|
.mc-empty{padding:16px;text-align:center;font-size:13px;color:var(--dsw-alias-label-tertiary);border:1px dashed var(--dsw-alias-border-l2);border-radius:8px}
|
|
5502
6489
|
.mc-error{padding:12px;border:1px solid var(--dsw-alias-state-error-primary);border-radius:8px;color:var(--dsw-alias-state-error-primary);font-size:13px}
|
|
5503
|
-
.mc-error
|
|
6490
|
+
.mc-error p{margin:0}
|
|
6491
|
+
.mc-error p.mc-error-hint{margin-top:8px;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}
|
|
6492
|
+
.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}
|
|
5504
6494
|
.mc-skill{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;overflow:hidden;background:var(--dsw-alias-bg-layer-1)}
|
|
5505
6495
|
.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}
|
|
5506
6496
|
.mc-skill-row:hover{background:var(--dsw-alias-interactive-bg-hover)}
|
|
5507
6497
|
.mc-skill-name{font-weight:600;font-size:14px;line-height:20px;flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
5508
6498
|
.mc-skill-meta{margin-left:auto;display:flex;align-items:center;gap:8px;flex:0 1 auto;min-width:0}
|
|
6499
|
+
/* A row has no room for prose: let a long source label truncate instead of
|
|
6500
|
+
pushing the row's buttons past the right edge. */
|
|
6501
|
+
.mc-source{font-size:11px;line-height:18px;color:var(--dsw-alias-label-caption);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;flex:0 1 auto}
|
|
6502
|
+
/* One sentence to confirm a row action: the shared modal chrome, sized down. */
|
|
6503
|
+
.mc-confirm-dialog{width:min(420px,calc(100vw - 48px))}
|
|
6504
|
+
.mc-confirm-body{display:flex;flex-direction:column;gap:12px;padding:14px 16px;font-size:13px;line-height:20px;color:var(--dsw-alias-label-secondary)}
|
|
6505
|
+
.mc-confirm-body p{margin:0}
|
|
6506
|
+
/* A directory has no spaces to break at, and the modal is where it can show in
|
|
6507
|
+
full: wrap it instead of truncating it the way a row's 来源 slot must. */
|
|
6508
|
+
.mc-confirm-path{font-family:var(--dsw-font-markdown-code-block-font-family);word-break:break-all}
|
|
6509
|
+
.mc-confirm-actions{display:flex;justify-content:flex-end;gap:8px}
|
|
5509
6510
|
.mc-skill-body{border-top:1px solid var(--dsw-alias-border-l1);padding:8px 12px 12px}
|
|
5510
6511
|
.mc-tree{display:flex;flex-direction:column;gap:2px;font-size:13px;line-height:20px}
|
|
5511
6512
|
.mc-tree-row{display:flex;align-items:center;gap:8px;padding:3px 4px;border-radius:6px;cursor:pointer;min-width:0}
|
|
@@ -5520,7 +6521,7 @@ body[data-ds-dark-theme] .mc-count--blocked{color:#b8abad}
|
|
|
5520
6521
|
.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}
|
|
5521
6522
|
.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}
|
|
5522
6523
|
.mc-preview-close:hover{background:var(--dsw-alias-interactive-bg-hover)}
|
|
5523
|
-
.mc-preview-body{overflow:auto;padding:14px 16px;font-family:var(--dsw-font-markdown-code-block-font-family);font-size:12px;line-height:20px;white-space:pre-wrap;word-break:break-all;color:var(--dsw-alias-label-primary)}
|
|
6524
|
+
.mc-preview-body{overflow:auto;flex:1 1 auto;min-height:0;padding:14px 16px;font-family:var(--dsw-font-markdown-code-block-font-family);font-size:12px;line-height:20px;white-space:pre-wrap;word-break:break-all;color:var(--dsw-alias-label-primary)}
|
|
5524
6525
|
.mc-preview-hint{padding:16px;text-align:center;font-size:13px;color:var(--dsw-alias-label-tertiary)}
|
|
5525
6526
|
`;
|
|
5526
6527
|
if (typeof document !== "undefined" && document.querySelector(`style[data-css-id="${CSS_ID}"]`) === null) {
|
|
@@ -5529,6 +6530,31 @@ if (typeof document !== "undefined" && document.querySelector(`style[data-css-id
|
|
|
5529
6530
|
tag.textContent = CSS;
|
|
5530
6531
|
document.head.appendChild(tag);
|
|
5531
6532
|
}
|
|
6533
|
+
/**
|
|
6534
|
+
* dsh's skill source labels, shown on rows this plugin cannot edit — so "why is
|
|
6535
|
+
* there no 编辑 button" has a visible answer. Labels that name one directory are
|
|
6536
|
+
* shown as that path; the rest name a category and go through the dictionary, so
|
|
6537
|
+
* none of dsh's internal labels (`custom`, `bundled`, …) reach the operator raw.
|
|
6538
|
+
*/
|
|
6539
|
+
const SOURCE_PATHS = {
|
|
6540
|
+
"user-dsh": "~/.dsh/skills",
|
|
6541
|
+
"user-agents": "~/.agents/skills",
|
|
6542
|
+
"project-dsh": "<project>/.dsh/skills",
|
|
6543
|
+
"project-agents": "<project>/.agents/skills"
|
|
6544
|
+
};
|
|
6545
|
+
/** Source labels that name no single directory; shown via `t`. */
|
|
6546
|
+
const SOURCE_LABEL_KEYS = {
|
|
6547
|
+
"custom": "sourceCustom",
|
|
6548
|
+
"bundled": "sourceBundled"
|
|
6549
|
+
};
|
|
6550
|
+
/** How to name a skill's source root on a row we cannot manage. */
|
|
6551
|
+
function sourceLabel(source, t) {
|
|
6552
|
+
if (source === void 0) return t("skillUnmanaged");
|
|
6553
|
+
const path = SOURCE_PATHS[source];
|
|
6554
|
+
if (path !== void 0) return path;
|
|
6555
|
+
const key = SOURCE_LABEL_KEYS[source];
|
|
6556
|
+
return key === void 0 ? source : t(key);
|
|
6557
|
+
}
|
|
5532
6558
|
function groupRows(rows) {
|
|
5533
6559
|
const byServer = /* @__PURE__ */ new Map();
|
|
5534
6560
|
const skills = [];
|
|
@@ -5537,7 +6563,7 @@ function groupRows(rows) {
|
|
|
5537
6563
|
skills.push(row);
|
|
5538
6564
|
continue;
|
|
5539
6565
|
}
|
|
5540
|
-
const server = row.server ?? "
|
|
6566
|
+
const server = row.server ?? "built-in";
|
|
5541
6567
|
const list = byServer.get(server);
|
|
5542
6568
|
if (list === void 0) byServer.set(server, [row]);
|
|
5543
6569
|
else list.push(row);
|
|
@@ -5554,9 +6580,16 @@ function countByClass(rows, cls) {
|
|
|
5554
6580
|
return rows.reduce((n, row) => row.class === cls ? n + 1 : n, 0);
|
|
5555
6581
|
}
|
|
5556
6582
|
function CapabilitySection(props) {
|
|
5557
|
-
const { remote, t, mountError
|
|
5558
|
-
const [state, setState] = (0, react.useState)(
|
|
6583
|
+
const { remote, t, mountError } = props;
|
|
6584
|
+
const [state, setState] = (0, react.useState)(() => {
|
|
6585
|
+
const cached = cachedSnapshot();
|
|
6586
|
+
return cached === void 0 ? { status: "loading" } : {
|
|
6587
|
+
status: "ready",
|
|
6588
|
+
snapshot: cached
|
|
6589
|
+
};
|
|
6590
|
+
});
|
|
5559
6591
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
6592
|
+
const [notice, setNotice] = (0, react.useState)(null);
|
|
5560
6593
|
const [openServers, setOpenServers] = (0, react.useState)(/* @__PURE__ */ new Set());
|
|
5561
6594
|
const [activeTab, setActiveTab] = (0, react.useState)("tools");
|
|
5562
6595
|
const reload = (0, react.useCallback)(async () => {
|
|
@@ -5573,15 +6606,41 @@ function CapabilitySection(props) {
|
|
|
5573
6606
|
snapshot
|
|
5574
6607
|
})));
|
|
5575
6608
|
} catch (e) {
|
|
5576
|
-
setState({
|
|
6609
|
+
setState((prev) => prev.status === "ready" ? prev : {
|
|
5577
6610
|
status: "error",
|
|
5578
6611
|
message: String(e)
|
|
5579
6612
|
});
|
|
6613
|
+
setNotice(String(e));
|
|
5580
6614
|
}
|
|
5581
6615
|
}, [remote, mountError]);
|
|
5582
6616
|
(0, react.useEffect)(() => {
|
|
5583
6617
|
reload();
|
|
5584
6618
|
}, [reload]);
|
|
6619
|
+
const [refreshing, setRefreshing] = (0, react.useState)(false);
|
|
6620
|
+
/**
|
|
6621
|
+
* Rebuild the host catalog, then re-read the classification list. Used after
|
|
6622
|
+
* registering a new source so the list reflects it without waiting for the
|
|
6623
|
+
* scheduler's debounce window.
|
|
6624
|
+
*/
|
|
6625
|
+
const refreshCatalog = (0, react.useCallback)(async () => {
|
|
6626
|
+
if (refreshing) return;
|
|
6627
|
+
setRefreshing(true);
|
|
6628
|
+
try {
|
|
6629
|
+
if (remote !== void 0) unwrap(await remote.refresh(), "capabilityPolicy.refresh");
|
|
6630
|
+
await reload();
|
|
6631
|
+
setNotice(null);
|
|
6632
|
+
} catch (e) {
|
|
6633
|
+
console.error("[capability-menu] refresh failed:", e);
|
|
6634
|
+
setNotice(t("refreshFailed"));
|
|
6635
|
+
} finally {
|
|
6636
|
+
setRefreshing(false);
|
|
6637
|
+
}
|
|
6638
|
+
}, [
|
|
6639
|
+
refreshing,
|
|
6640
|
+
remote,
|
|
6641
|
+
reload,
|
|
6642
|
+
t
|
|
6643
|
+
]);
|
|
5585
6644
|
const toggleServer = (0, react.useCallback)((server) => {
|
|
5586
6645
|
setOpenServers((prev) => {
|
|
5587
6646
|
const next = new Set(prev);
|
|
@@ -5594,29 +6653,42 @@ function CapabilitySection(props) {
|
|
|
5594
6653
|
const cycleClass = (0, react.useCallback)(async (ids, kind) => {
|
|
5595
6654
|
if (ids.length === 0 || busy) return;
|
|
5596
6655
|
setBusy(true);
|
|
6656
|
+
const from = ids.map((id) => state.status === "ready" ? state.snapshot.rows.find((r) => r.id === id)?.class : void 0).find((c) => c !== void 0) ?? "on-demand";
|
|
6657
|
+
const to = NEXT_CLASS[from];
|
|
6658
|
+
const moved = new Set(ids);
|
|
6659
|
+
setState((prev) => prev.status === "ready" ? {
|
|
6660
|
+
status: "ready",
|
|
6661
|
+
snapshot: { rows: prev.snapshot.rows.map((row) => moved.has(row.id) ? {
|
|
6662
|
+
...row,
|
|
6663
|
+
class: to,
|
|
6664
|
+
classLabel: void 0
|
|
6665
|
+
} : row) }
|
|
6666
|
+
} : prev);
|
|
5597
6667
|
try {
|
|
5598
6668
|
const config = unwrap(await remote.getConfig(), "capabilityPolicy.getConfig");
|
|
5599
6669
|
const key = kind === "skill" ? "skills" : "tools";
|
|
5600
6670
|
const set = config[key];
|
|
5601
6671
|
const lists = {
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
6672
|
+
resident: [],
|
|
6673
|
+
"on-demand": [],
|
|
6674
|
+
disabled: []
|
|
5605
6675
|
};
|
|
5606
6676
|
for (const cls of CLASS_KEYS) {
|
|
5607
6677
|
const raw = set && typeof set === "object" && !Array.isArray(set) ? set[cls] : void 0;
|
|
5608
6678
|
lists[cls] = Array.isArray(raw) ? raw.filter((x) => typeof x === "string") : [];
|
|
5609
6679
|
}
|
|
5610
|
-
const from = ids.map((id) => state.status === "ready" ? state.snapshot.rows.find((r) => r.id === id)?.class : void 0).find((c) => c !== void 0) ?? "progressive";
|
|
5611
|
-
const to = NEXT_CLASS[from];
|
|
5612
6680
|
for (const cls of CLASS_KEYS) lists[cls] = lists[cls].filter((id) => !ids.includes(id));
|
|
5613
6681
|
lists[to] = [...lists[to], ...ids];
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
6682
|
+
const nextLists = {};
|
|
6683
|
+
for (const cls of CLASS_KEYS) nextLists[cls] = lists[cls];
|
|
6684
|
+
unwrap(await remote.updateConfig({ [key]: nextLists }), "capabilityPolicy.updateConfig");
|
|
6685
|
+
const next = await loadSnapshot(remote);
|
|
6686
|
+
setState({
|
|
6687
|
+
status: "ready",
|
|
6688
|
+
snapshot: next
|
|
6689
|
+
});
|
|
6690
|
+
const overridden = ids.filter((id) => next.rows.find((r) => r.id === id)?.class !== to);
|
|
6691
|
+
setNotice(overridden.length > 0 ? t("cycleOverridden", { count: overridden.length }) : null);
|
|
5620
6692
|
} catch (e) {
|
|
5621
6693
|
setState({
|
|
5622
6694
|
status: "error",
|
|
@@ -5627,9 +6699,9 @@ function CapabilitySection(props) {
|
|
|
5627
6699
|
}
|
|
5628
6700
|
}, [
|
|
5629
6701
|
remote,
|
|
5630
|
-
reload,
|
|
5631
6702
|
busy,
|
|
5632
|
-
state
|
|
6703
|
+
state,
|
|
6704
|
+
t
|
|
5633
6705
|
]);
|
|
5634
6706
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
5635
6707
|
className: "mc-section",
|
|
@@ -5640,7 +6712,26 @@ function CapabilitySection(props) {
|
|
|
5640
6712
|
}),
|
|
5641
6713
|
state.status === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5642
6714
|
className: "mc-error",
|
|
5643
|
-
children: [
|
|
6715
|
+
children: [
|
|
6716
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: state.message }),
|
|
6717
|
+
state.message.includes("Failed to fetch") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6718
|
+
className: "mc-error-hint",
|
|
6719
|
+
children: t("carrierFailureHint")
|
|
6720
|
+
}),
|
|
6721
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6722
|
+
className: "mc-error-actions",
|
|
6723
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6724
|
+
type: "button",
|
|
6725
|
+
className: "mc-catalog-btn",
|
|
6726
|
+
onClick: () => void reload(),
|
|
6727
|
+
children: t("retry")
|
|
6728
|
+
})
|
|
6729
|
+
})
|
|
6730
|
+
]
|
|
6731
|
+
}),
|
|
6732
|
+
notice !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6733
|
+
className: "mc-notice",
|
|
6734
|
+
children: notice
|
|
5644
6735
|
}),
|
|
5645
6736
|
state.status === "ready" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReadyBody, {
|
|
5646
6737
|
remote,
|
|
@@ -5651,15 +6742,21 @@ function CapabilitySection(props) {
|
|
|
5651
6742
|
t,
|
|
5652
6743
|
onTabChange: setActiveTab,
|
|
5653
6744
|
onToggleServer: toggleServer,
|
|
5654
|
-
onCycle: cycleClass
|
|
6745
|
+
onCycle: cycleClass,
|
|
6746
|
+
onRefresh: () => void refreshCatalog(),
|
|
6747
|
+
refreshing,
|
|
6748
|
+
onNotice: setNotice
|
|
5655
6749
|
})
|
|
5656
6750
|
]
|
|
5657
6751
|
});
|
|
5658
6752
|
}
|
|
5659
6753
|
function ReadyBody(props) {
|
|
5660
|
-
const { remote, snapshot, openServers, busy, activeTab, t, onTabChange, onToggleServer, onCycle } = props;
|
|
6754
|
+
const { remote, snapshot, openServers, busy, activeTab, t, onTabChange, onToggleServer, onCycle, onRefresh, refreshing, onNotice } = props;
|
|
5661
6755
|
const { servers, skills } = groupRows(snapshot.rows);
|
|
5662
|
-
const
|
|
6756
|
+
const projectSkills = skills.filter((skill) => PROJECT_SKILL_SOURCES.has(skill.source ?? ""));
|
|
6757
|
+
const globalSkills = skills.filter((skill) => !PROJECT_SKILL_SOURCES.has(skill.source ?? ""));
|
|
6758
|
+
const [skillTab, setSkillTab] = (0, react.useState)("global");
|
|
6759
|
+
const statRows = activeTab === "tools" ? snapshot.rows.filter((r) => r.kind === "tool") : skillTab === "project" ? projectSkills : globalSkills;
|
|
5663
6760
|
const summary = CLASS_KEYS.map((cls) => ({
|
|
5664
6761
|
cls,
|
|
5665
6762
|
count: countByClass(statRows, cls)
|
|
@@ -5691,14 +6788,128 @@ function ReadyBody(props) {
|
|
|
5691
6788
|
});
|
|
5692
6789
|
}
|
|
5693
6790
|
}, [remote, t]);
|
|
6791
|
+
/** 能力目录弹层状态:查看三档策略配置 + 按需能力目录文件。 */
|
|
6792
|
+
const [catalogDocs, setCatalogDocs] = (0, react.useState)(null);
|
|
6793
|
+
const [catalogTab, setCatalogTab] = (0, react.useState)("policy");
|
|
6794
|
+
/** 注册能力 modal; its default sub-tab follows `activeTab`. */
|
|
6795
|
+
const [registerOpen, setRegisterOpen] = (0, react.useState)(false);
|
|
6796
|
+
/** 编辑 target; at most one is set. */
|
|
6797
|
+
const [editMcp, setEditMcp] = (0, react.useState)(void 0);
|
|
6798
|
+
const [editSkill, setEditSkill] = (0, react.useState)(void 0);
|
|
6799
|
+
/**
|
|
6800
|
+
* Skill directories registered under the skill root. Only those can be
|
|
6801
|
+
* edited: a project or bundled skill has no row to repoint, so its row gets
|
|
6802
|
+
* no 编辑 button rather than a button that always fails.
|
|
6803
|
+
*/
|
|
6804
|
+
const [editableSkills, setEditableSkills] = (0, react.useState)(/* @__PURE__ */ new Set());
|
|
6805
|
+
const skillSignature = skills.map((skill) => `${skill.id}\u0000${skill.source ?? ""}`).join("");
|
|
6806
|
+
(0, react.useEffect)(() => {
|
|
6807
|
+
let cancelled = false;
|
|
6808
|
+
(async () => {
|
|
6809
|
+
try {
|
|
6810
|
+
const rows = unwrap(await remote.listSkillLocations(), "capabilityPolicy.listSkillLocations");
|
|
6811
|
+
if (!cancelled) setEditableSkills(new Set(rows.map((row) => row.skillName ?? row.name)));
|
|
6812
|
+
} catch {}
|
|
6813
|
+
})();
|
|
6814
|
+
return () => {
|
|
6815
|
+
cancelled = true;
|
|
6816
|
+
};
|
|
6817
|
+
}, [remote, skillSignature]);
|
|
6818
|
+
/** Open the 编辑 form for the MCP server behind a Tools-tab group header. */
|
|
6819
|
+
const openMcpEdit = (0, react.useCallback)(async (serverName) => {
|
|
6820
|
+
try {
|
|
6821
|
+
const found = unwrap(await remote.listLocations(), "capabilityPolicy.listLocations").find((row) => row.serverName === serverName);
|
|
6822
|
+
if (found === void 0) {
|
|
6823
|
+
onNotice(t("notEditable"));
|
|
6824
|
+
return;
|
|
6825
|
+
}
|
|
6826
|
+
setEditMcp(found);
|
|
6827
|
+
} catch (e) {
|
|
6828
|
+
onNotice(String(e));
|
|
6829
|
+
}
|
|
6830
|
+
}, [
|
|
6831
|
+
remote,
|
|
6832
|
+
t,
|
|
6833
|
+
onNotice
|
|
6834
|
+
]);
|
|
6835
|
+
/**
|
|
6836
|
+
* Adopt a skill that dsh discovered from a user-level root this plugin reads
|
|
6837
|
+
* but does not manage: link its own directory into the user root so it becomes
|
|
6838
|
+
* an editable entry. The content is untouched.
|
|
6839
|
+
*/
|
|
6840
|
+
const adoptSkill = (0, react.useCallback)(async (name) => {
|
|
6841
|
+
try {
|
|
6842
|
+
const entry = unwrap(await remote.adoptSkillLocation(name), "capabilityPolicy.adoptSkillLocation");
|
|
6843
|
+
onNotice(t("skillAdopted", { path: entry }));
|
|
6844
|
+
onRefresh();
|
|
6845
|
+
} catch (e) {
|
|
6846
|
+
onNotice(String(e));
|
|
6847
|
+
}
|
|
6848
|
+
}, [
|
|
6849
|
+
remote,
|
|
6850
|
+
t,
|
|
6851
|
+
onNotice,
|
|
6852
|
+
onRefresh
|
|
6853
|
+
]);
|
|
6854
|
+
/**
|
|
6855
|
+
* Open the 编辑 form for a managed skill entry. `name` is the skill's declared
|
|
6856
|
+
* name, which is what the row and the panel address it by — an entry is
|
|
6857
|
+
* matched on that rather than on its own, possibly different, name. A bare
|
|
6858
|
+
* name can exist in both the user root and a project root, so the row's source
|
|
6859
|
+
* decides which entry the click meant; without it we fall back to the first
|
|
6860
|
+
* match.
|
|
6861
|
+
*/
|
|
6862
|
+
const openSkillEdit = (0, react.useCallback)(async (name, source) => {
|
|
6863
|
+
try {
|
|
6864
|
+
const rows = unwrap(await remote.listSkillLocations(), "capabilityPolicy.listSkillLocations");
|
|
6865
|
+
const wanted = PROJECT_SKILL_SOURCES.has(source ?? "") ? "project" : "user";
|
|
6866
|
+
const named = rows.filter((row) => (row.skillName ?? row.name) === name);
|
|
6867
|
+
const found = named.find((row) => row.root === wanted) ?? named[0];
|
|
6868
|
+
if (found === void 0) {
|
|
6869
|
+
onNotice(t("notEditable"));
|
|
6870
|
+
return;
|
|
6871
|
+
}
|
|
6872
|
+
setEditSkill(found);
|
|
6873
|
+
} catch (e) {
|
|
6874
|
+
onNotice(String(e));
|
|
6875
|
+
}
|
|
6876
|
+
}, [
|
|
6877
|
+
remote,
|
|
6878
|
+
t,
|
|
6879
|
+
onNotice
|
|
6880
|
+
]);
|
|
6881
|
+
/** Fetch and show the two read-only catalog documents. */
|
|
6882
|
+
const openCatalogDocs = (0, react.useCallback)(async () => {
|
|
6883
|
+
setCatalogDocs({ status: "loading" });
|
|
6884
|
+
try {
|
|
6885
|
+
const docs = unwrap(await remote.getCatalogDocs(), "capabilityPolicy.getCatalogDocs");
|
|
6886
|
+
setCatalogDocs({
|
|
6887
|
+
status: "ready",
|
|
6888
|
+
docs
|
|
6889
|
+
});
|
|
6890
|
+
} catch (error) {
|
|
6891
|
+
setCatalogDocs({
|
|
6892
|
+
status: "error",
|
|
6893
|
+
message: String(error)
|
|
6894
|
+
});
|
|
6895
|
+
}
|
|
6896
|
+
}, [remote]);
|
|
5694
6897
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
5695
6898
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
5696
6899
|
className: "mc-heading",
|
|
5697
6900
|
children: t("title")
|
|
5698
6901
|
}),
|
|
5699
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
5700
|
-
className: "mc-desc",
|
|
5701
|
-
children:
|
|
6902
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6903
|
+
className: "mc-desc-row",
|
|
6904
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6905
|
+
className: "mc-desc",
|
|
6906
|
+
children: t("desc")
|
|
6907
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6908
|
+
type: "button",
|
|
6909
|
+
className: "mc-catalog-btn",
|
|
6910
|
+
onClick: () => void openCatalogDocs(),
|
|
6911
|
+
children: t("viewCatalog")
|
|
6912
|
+
})]
|
|
5702
6913
|
}),
|
|
5703
6914
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5704
6915
|
className: "mc-tabs",
|
|
@@ -5723,20 +6934,35 @@ function ReadyBody(props) {
|
|
|
5723
6934
|
onClick: () => onTabChange("skills"),
|
|
5724
6935
|
children: t("skillsGroup")
|
|
5725
6936
|
})]
|
|
5726
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.
|
|
6937
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5727
6938
|
className: "mc-summary",
|
|
5728
|
-
children:
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
6939
|
+
children: [
|
|
6940
|
+
summary.map(({ cls, count }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
6941
|
+
className: `mc-chip mc-chip--${cls}`,
|
|
6942
|
+
children: [
|
|
6943
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6944
|
+
className: `mc-dot mc-dot--${cls}`,
|
|
6945
|
+
"aria-hidden": "true"
|
|
6946
|
+
}),
|
|
6947
|
+
t(CLASS_SHORT_KEYS[cls]),
|
|
6948
|
+
" · ",
|
|
6949
|
+
count
|
|
6950
|
+
]
|
|
6951
|
+
}, cls)),
|
|
6952
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6953
|
+
type: "button",
|
|
6954
|
+
className: "mc-catalog-btn",
|
|
6955
|
+
onClick: () => onRefresh(),
|
|
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
|
+
]
|
|
5740
6966
|
})]
|
|
5741
6967
|
}),
|
|
5742
6968
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -5775,15 +7001,15 @@ function ReadyBody(props) {
|
|
|
5775
7001
|
}),
|
|
5776
7002
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5777
7003
|
className: "mc-server-name",
|
|
5778
|
-
children: server
|
|
7004
|
+
children: server === "built-in" ? t("builtInGroup") : server
|
|
5779
7005
|
}),
|
|
5780
7006
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5781
7007
|
className: "mc-server-count",
|
|
5782
7008
|
children: t("toolCount", { count: tools.length })
|
|
5783
7009
|
}),
|
|
5784
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
7010
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5785
7011
|
className: "mc-server-meta",
|
|
5786
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7012
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5787
7013
|
className: "mc-counts",
|
|
5788
7014
|
children: counts.filter(({ count }) => count > 0).map(({ cls, count }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5789
7015
|
type: "button",
|
|
@@ -5799,12 +7025,21 @@ function ReadyBody(props) {
|
|
|
5799
7025
|
className: `mc-dot mc-dot--${cls}`,
|
|
5800
7026
|
"aria-hidden": "true"
|
|
5801
7027
|
}),
|
|
5802
|
-
t(
|
|
7028
|
+
t(CLASS_SHORT_KEYS[cls]),
|
|
5803
7029
|
" ",
|
|
5804
7030
|
count
|
|
5805
7031
|
]
|
|
5806
7032
|
}, cls))
|
|
5807
|
-
})
|
|
7033
|
+
}), server !== "built-in" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7034
|
+
type: "button",
|
|
7035
|
+
className: "mc-count mc-count--action",
|
|
7036
|
+
disabled: busy,
|
|
7037
|
+
onClick: (e) => {
|
|
7038
|
+
e.stopPropagation();
|
|
7039
|
+
openMcpEdit(server);
|
|
7040
|
+
},
|
|
7041
|
+
children: t("edit")
|
|
7042
|
+
})]
|
|
5808
7043
|
})
|
|
5809
7044
|
]
|
|
5810
7045
|
}), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -5855,19 +7090,61 @@ function ReadyBody(props) {
|
|
|
5855
7090
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5856
7091
|
role: "tabpanel",
|
|
5857
7092
|
hidden: activeTab !== "skills",
|
|
5858
|
-
className: "mc-panel",
|
|
7093
|
+
className: "mc-panel mc-panel--tight",
|
|
5859
7094
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5860
7095
|
className: "mc-panel-inner",
|
|
5861
7096
|
children: skills.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5862
7097
|
className: "mc-empty",
|
|
5863
7098
|
children: t("emptySkills")
|
|
5864
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
5865
|
-
|
|
7099
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7100
|
+
className: "mc-subtabs",
|
|
7101
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7102
|
+
className: "mc-tab-group",
|
|
7103
|
+
role: "tablist",
|
|
7104
|
+
"aria-label": t("skillsGroup"),
|
|
7105
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7106
|
+
type: "button",
|
|
7107
|
+
role: "tab",
|
|
7108
|
+
className: "mc-tab",
|
|
7109
|
+
"aria-selected": skillTab === "global",
|
|
7110
|
+
"data-active": skillTab === "global" ? "true" : void 0,
|
|
7111
|
+
onClick: () => setSkillTab("global"),
|
|
7112
|
+
children: t("globalSkills")
|
|
7113
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7114
|
+
type: "button",
|
|
7115
|
+
role: "tab",
|
|
7116
|
+
className: "mc-tab",
|
|
7117
|
+
"aria-selected": skillTab === "project",
|
|
7118
|
+
"data-active": skillTab === "project" ? "true" : void 0,
|
|
7119
|
+
onClick: () => setSkillTab("project"),
|
|
7120
|
+
children: t("projectSkills")
|
|
7121
|
+
})]
|
|
7122
|
+
})
|
|
7123
|
+
}), skillTab === "global" ? globalSkills.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillList, {
|
|
7124
|
+
skills: globalSkills,
|
|
5866
7125
|
remote,
|
|
5867
7126
|
busy,
|
|
5868
7127
|
t,
|
|
5869
|
-
onCycle
|
|
5870
|
-
|
|
7128
|
+
onCycle,
|
|
7129
|
+
editableSkills,
|
|
7130
|
+
onEditSkill: openSkillEdit,
|
|
7131
|
+
onAdoptSkill: adoptSkill
|
|
7132
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
7133
|
+
className: "mc-empty",
|
|
7134
|
+
children: t("emptyGlobalSkills")
|
|
7135
|
+
}) : projectSkills.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillList, {
|
|
7136
|
+
skills: projectSkills,
|
|
7137
|
+
remote,
|
|
7138
|
+
busy,
|
|
7139
|
+
t,
|
|
7140
|
+
onCycle,
|
|
7141
|
+
editableSkills,
|
|
7142
|
+
onEditSkill: openSkillEdit,
|
|
7143
|
+
onAdoptSkill: adoptSkill
|
|
7144
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
7145
|
+
className: "mc-empty",
|
|
7146
|
+
children: t("emptyProjectSkills")
|
|
7147
|
+
})] })
|
|
5871
7148
|
})
|
|
5872
7149
|
}),
|
|
5873
7150
|
toolDetail !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -5910,11 +7187,99 @@ function ReadyBody(props) {
|
|
|
5910
7187
|
})
|
|
5911
7188
|
]
|
|
5912
7189
|
})
|
|
7190
|
+
}),
|
|
7191
|
+
(registerOpen || editMcp !== void 0 || editSkill !== void 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LocationModal, {
|
|
7192
|
+
remote,
|
|
7193
|
+
t,
|
|
7194
|
+
defaultKind: activeTab === "skills" ? "skill" : "mcp",
|
|
7195
|
+
...editMcp !== void 0 ? { editMcp } : {},
|
|
7196
|
+
...editSkill !== void 0 ? { editSkill } : {},
|
|
7197
|
+
onClose: () => {
|
|
7198
|
+
setRegisterOpen(false);
|
|
7199
|
+
setEditMcp(void 0);
|
|
7200
|
+
setEditSkill(void 0);
|
|
7201
|
+
},
|
|
7202
|
+
onChanged: (notice) => {
|
|
7203
|
+
onRefresh();
|
|
7204
|
+
if (notice !== void 0) onNotice(notice);
|
|
7205
|
+
}
|
|
7206
|
+
}),
|
|
7207
|
+
catalogDocs !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7208
|
+
className: "mc-preview-mask",
|
|
7209
|
+
onClick: () => setCatalogDocs(null),
|
|
7210
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7211
|
+
className: "mc-preview",
|
|
7212
|
+
onClick: (e) => e.stopPropagation(),
|
|
7213
|
+
children: [
|
|
7214
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7215
|
+
className: "mc-preview-head",
|
|
7216
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7217
|
+
className: "mc-preview-title",
|
|
7218
|
+
children: t("viewCatalog")
|
|
7219
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7220
|
+
type: "button",
|
|
7221
|
+
className: "mc-preview-close",
|
|
7222
|
+
onClick: () => setCatalogDocs(null),
|
|
7223
|
+
children: t("previewClose")
|
|
7224
|
+
})]
|
|
7225
|
+
}),
|
|
7226
|
+
catalogDocs.status === "loading" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7227
|
+
className: "mc-preview-hint",
|
|
7228
|
+
children: "…"
|
|
7229
|
+
}),
|
|
7230
|
+
catalogDocs.status === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7231
|
+
className: "mc-preview-hint",
|
|
7232
|
+
children: catalogDocs.message
|
|
7233
|
+
}),
|
|
7234
|
+
catalogDocs.status === "ready" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7235
|
+
className: "mc-subtabs mc-catalog-tabs",
|
|
7236
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7237
|
+
className: "mc-tab-group",
|
|
7238
|
+
role: "tablist",
|
|
7239
|
+
"aria-label": t("viewCatalog"),
|
|
7240
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7241
|
+
type: "button",
|
|
7242
|
+
role: "tab",
|
|
7243
|
+
className: "mc-tab",
|
|
7244
|
+
"aria-selected": catalogTab === "policy",
|
|
7245
|
+
"data-active": catalogTab === "policy" ? "true" : void 0,
|
|
7246
|
+
onClick: () => setCatalogTab("policy"),
|
|
7247
|
+
children: t("catalogPolicy")
|
|
7248
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7249
|
+
type: "button",
|
|
7250
|
+
role: "tab",
|
|
7251
|
+
className: "mc-tab",
|
|
7252
|
+
"aria-selected": catalogTab === "catalog",
|
|
7253
|
+
"data-active": catalogTab === "catalog" ? "true" : void 0,
|
|
7254
|
+
onClick: () => setCatalogTab("catalog"),
|
|
7255
|
+
children: t("catalogOnDemand")
|
|
7256
|
+
})]
|
|
7257
|
+
})
|
|
7258
|
+
}), catalogTab === "policy" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
7259
|
+
className: "mc-catalog-path",
|
|
7260
|
+
children: t("catalogPolicyNote")
|
|
7261
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
7262
|
+
className: "mc-preview-body",
|
|
7263
|
+
children: catalogDocs.docs.policyYaml
|
|
7264
|
+
})] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [catalogDocs.docs.catalog !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
7265
|
+
className: "mc-catalog-path",
|
|
7266
|
+
children: catalogDocs.docs.catalog.path
|
|
7267
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
7268
|
+
className: "mc-catalog-path",
|
|
7269
|
+
children: t("catalogOnDemand")
|
|
7270
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
7271
|
+
className: "mc-preview-body",
|
|
7272
|
+
children: catalogDocs.docs.catalog?.content ?? (catalogDocs.docs.catalogMissing === "disabled" ? t("catalogDisabled") : t("catalogUnreadable"))
|
|
7273
|
+
})] })] })
|
|
7274
|
+
]
|
|
7275
|
+
})
|
|
5913
7276
|
})
|
|
5914
7277
|
] });
|
|
5915
7278
|
}
|
|
5916
7279
|
function SkillList(props) {
|
|
5917
|
-
const { skills, remote, busy, t, onCycle } = props;
|
|
7280
|
+
const { skills, remote, busy, t, onCycle, editableSkills, onEditSkill, onAdoptSkill } = props;
|
|
7281
|
+
/** Skill id whose 纳入管理 is waiting for a confirmation click. */
|
|
7282
|
+
const [confirmAdopt, setConfirmAdopt] = (0, react.useState)(null);
|
|
5918
7283
|
const [openSkill, setOpenSkill] = (0, react.useState)(null);
|
|
5919
7284
|
const [tree, setTree] = (0, react.useState)({
|
|
5920
7285
|
open: {},
|
|
@@ -6083,89 +7448,165 @@ function SkillList(props) {
|
|
|
6083
7448
|
}, relPath);
|
|
6084
7449
|
});
|
|
6085
7450
|
};
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
"
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
7451
|
+
/** The row the 纳入管理 confirmation is for, so the dialog can name it and
|
|
7452
|
+
show the directory the click will link. */
|
|
7453
|
+
const confirmAdoptRow = confirmAdopt === null ? void 0 : skills.find((row) => row.id === confirmAdopt);
|
|
7454
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
7455
|
+
skills.map((skill) => {
|
|
7456
|
+
const open = openSkill === skill.id;
|
|
7457
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7458
|
+
className: "mc-skill",
|
|
7459
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7460
|
+
className: "mc-skill-row",
|
|
7461
|
+
role: "button",
|
|
7462
|
+
tabIndex: 0,
|
|
7463
|
+
"aria-expanded": open,
|
|
7464
|
+
onClick: () => void toggleSkill(skill.id),
|
|
7465
|
+
onKeyDown: (e) => {
|
|
7466
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
7467
|
+
e.preventDefault();
|
|
7468
|
+
toggleSkill(skill.id);
|
|
7469
|
+
}
|
|
7470
|
+
},
|
|
7471
|
+
children: [
|
|
7472
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTriangleRightFill14, {
|
|
7473
|
+
size: 12,
|
|
7474
|
+
className: `mc-chevron${open ? " mc-chevron--open" : ""}`
|
|
7475
|
+
}),
|
|
7476
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7477
|
+
className: "mc-skill-name",
|
|
7478
|
+
children: skill.name
|
|
7479
|
+
}),
|
|
7480
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
7481
|
+
className: "mc-skill-meta",
|
|
7482
|
+
children: [
|
|
7483
|
+
skill.mandatory && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7484
|
+
className: "mc-tag",
|
|
7485
|
+
children: t("mandatory")
|
|
7486
|
+
}),
|
|
7487
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
7488
|
+
type: "button",
|
|
7489
|
+
className: `mc-count mc-count--${skill.class}`,
|
|
7490
|
+
disabled: busy || skill.mandatory,
|
|
7491
|
+
title: skill.classLabel,
|
|
7492
|
+
onClick: (e) => {
|
|
7493
|
+
e.stopPropagation();
|
|
7494
|
+
onCycle([skill.id], "skill");
|
|
7495
|
+
},
|
|
7496
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7497
|
+
className: `mc-dot mc-dot--${skill.class}`,
|
|
7498
|
+
"aria-hidden": "true"
|
|
7499
|
+
}), t(CLASS_SHORT_KEYS[skill.class])]
|
|
7500
|
+
}),
|
|
7501
|
+
!editableSkills.has(skill.id) && (ADOPTABLE_SKILL_SOURCES.has(skill.source ?? "") ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7502
|
+
type: "button",
|
|
7503
|
+
className: "mc-count mc-count--action",
|
|
7504
|
+
disabled: busy,
|
|
7505
|
+
title: t("adoptSkillHint"),
|
|
7506
|
+
onClick: (e) => {
|
|
7507
|
+
e.stopPropagation();
|
|
7508
|
+
setConfirmAdopt(skill.id);
|
|
7509
|
+
},
|
|
7510
|
+
children: t("adoptSkill")
|
|
7511
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7512
|
+
className: "mc-source",
|
|
7513
|
+
title: t("skillSourceHint"),
|
|
7514
|
+
children: t("skillSource", { source: sourceLabel(skill.source, t) })
|
|
7515
|
+
})),
|
|
7516
|
+
editableSkills.has(skill.id) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7517
|
+
type: "button",
|
|
7518
|
+
className: "mc-count mc-count--action",
|
|
7519
|
+
disabled: busy,
|
|
7520
|
+
onClick: (e) => {
|
|
7521
|
+
e.stopPropagation();
|
|
7522
|
+
onEditSkill(skill.id, skill.source);
|
|
7523
|
+
},
|
|
7524
|
+
children: t("edit")
|
|
7525
|
+
})
|
|
7526
|
+
]
|
|
7527
|
+
})
|
|
7528
|
+
]
|
|
7529
|
+
}), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7530
|
+
className: "mc-skill-body",
|
|
7531
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7532
|
+
className: "mc-tree",
|
|
7533
|
+
children: renderEntries(tree.dirs[`${skill.id}:`], skill.id, "") ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7534
|
+
className: "mc-tree-row",
|
|
7535
|
+
children: "…"
|
|
7536
|
+
})
|
|
6130
7537
|
})
|
|
6131
|
-
]
|
|
6132
|
-
}
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
7538
|
+
})]
|
|
7539
|
+
}, skill.id);
|
|
7540
|
+
}),
|
|
7541
|
+
confirmAdopt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7542
|
+
className: "mc-preview-mask",
|
|
7543
|
+
onClick: () => setConfirmAdopt(null),
|
|
7544
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7545
|
+
className: "mc-preview mc-confirm-dialog",
|
|
7546
|
+
onClick: (e) => e.stopPropagation(),
|
|
7547
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7548
|
+
className: "mc-preview-head",
|
|
7549
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7550
|
+
className: "mc-preview-title",
|
|
7551
|
+
children: t("adoptSkillTitle", { name: confirmAdoptRow?.name ?? confirmAdopt })
|
|
6139
7552
|
})
|
|
6140
|
-
})
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
7553
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7554
|
+
className: "mc-confirm-body",
|
|
7555
|
+
children: [
|
|
7556
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("adoptSkillHint") }),
|
|
7557
|
+
confirmAdoptRow !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
7558
|
+
className: "mc-confirm-path",
|
|
7559
|
+
children: t("skillSource", { source: confirmAdoptRow.path ?? sourceLabel(confirmAdoptRow.source, t) })
|
|
7560
|
+
}),
|
|
7561
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7562
|
+
className: "mc-confirm-actions",
|
|
7563
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7564
|
+
type: "button",
|
|
7565
|
+
className: "mc-catalog-btn",
|
|
7566
|
+
onClick: () => setConfirmAdopt(null),
|
|
7567
|
+
children: t("cancel")
|
|
7568
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7569
|
+
type: "button",
|
|
7570
|
+
className: "mc-catalog-btn",
|
|
7571
|
+
onClick: () => {
|
|
7572
|
+
const id = confirmAdopt;
|
|
7573
|
+
setConfirmAdopt(null);
|
|
7574
|
+
onAdoptSkill(id);
|
|
7575
|
+
},
|
|
7576
|
+
children: t("adoptSkill")
|
|
7577
|
+
})]
|
|
7578
|
+
})
|
|
7579
|
+
]
|
|
6159
7580
|
})]
|
|
6160
|
-
})
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
7581
|
+
})
|
|
7582
|
+
}),
|
|
7583
|
+
preview !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7584
|
+
className: "mc-preview-mask",
|
|
7585
|
+
onClick: () => setPreview(null),
|
|
7586
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7587
|
+
className: "mc-preview",
|
|
7588
|
+
onClick: (e) => e.stopPropagation(),
|
|
7589
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7590
|
+
className: "mc-preview-head",
|
|
7591
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7592
|
+
className: "mc-preview-title",
|
|
7593
|
+
children: preview.relPath
|
|
7594
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7595
|
+
type: "button",
|
|
7596
|
+
className: "mc-preview-close",
|
|
7597
|
+
onClick: () => setPreview(null),
|
|
7598
|
+
children: t("previewClose")
|
|
7599
|
+
})]
|
|
7600
|
+
}), preview.content !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
7601
|
+
className: "mc-preview-body",
|
|
7602
|
+
children: preview.content
|
|
7603
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7604
|
+
className: "mc-preview-hint",
|
|
7605
|
+
children: preview.error ?? "…"
|
|
7606
|
+
})]
|
|
7607
|
+
})
|
|
6167
7608
|
})
|
|
6168
|
-
|
|
7609
|
+
] });
|
|
6169
7610
|
}
|
|
6170
7611
|
//#endregion
|
|
6171
7612
|
//#region src/client/index.ts
|
|
@@ -6185,54 +7626,190 @@ async function apply(ctx) {
|
|
|
6185
7626
|
const zh = {
|
|
6186
7627
|
nav: "能力菜单",
|
|
6187
7628
|
title: "能力菜单",
|
|
6188
|
-
desc: "
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
kind: "类型",
|
|
6193
|
-
class: "分类",
|
|
7629
|
+
desc: "管理工具与技能的 常驻 / 按需 / 禁用 三档分类。",
|
|
7630
|
+
resident: "Resident(常驻上下文)",
|
|
7631
|
+
"on-demand": "On-demand(按需发现)",
|
|
7632
|
+
disabled: "Disabled(禁用)",
|
|
6194
7633
|
tool: "tool",
|
|
6195
7634
|
skill: "skill",
|
|
6196
7635
|
mandatory: "meta",
|
|
6197
|
-
|
|
6198
|
-
toolsGroup: "MCP tools",
|
|
7636
|
+
toolsGroup: "Tools",
|
|
6199
7637
|
skillsGroup: "Skills",
|
|
6200
|
-
|
|
7638
|
+
builtInGroup: "系统内置",
|
|
7639
|
+
globalSkills: "全局技能",
|
|
7640
|
+
projectSkills: "项目技能",
|
|
7641
|
+
emptyGlobalSkills: "暂无全局技能",
|
|
7642
|
+
emptyProjectSkills: "暂无项目技能",
|
|
7643
|
+
emptyTools: "暂无工具",
|
|
6201
7644
|
emptySkills: "暂无 Skill",
|
|
6202
7645
|
toolCount: "{count} 个工具",
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
7646
|
+
residentShort: "常驻",
|
|
7647
|
+
onDemandShort: "按需",
|
|
7648
|
+
disabledShort: "禁用",
|
|
6206
7649
|
cycleHint: "点击标签切换分类(按需 → 禁用 → 常驻)",
|
|
6207
7650
|
notPreviewable: "该文件不是可预览的文本文件",
|
|
6208
7651
|
previewClose: "关闭",
|
|
6209
|
-
detailNotFound: "未找到该工具的详情"
|
|
7652
|
+
detailNotFound: "未找到该工具的详情",
|
|
7653
|
+
cycleOverridden: "分类未生效:{count} 个能力被更高优先级规则覆盖(如通配规则),可移除对应通配规则后重试",
|
|
7654
|
+
refresh: "刷新",
|
|
7655
|
+
refreshing: "刷新中…",
|
|
7656
|
+
refreshFailed: "刷新失败,请查看日志",
|
|
7657
|
+
retry: "重试",
|
|
7658
|
+
carrierFailureHint: "请求未到达服务端:浏览器与 dsh 之间的连接已断开(Failed to fetch 属传输层失败,服务端方法未执行)。若刚重启过 dsh web,请先硬刷新页面(Ctrl/Cmd+Shift+R)再重试。",
|
|
7659
|
+
registerCapability: "注册能力",
|
|
7660
|
+
editMcp: "编辑 MCP 服务器",
|
|
7661
|
+
editSkillNamed: "编辑 Skill 目录:{name}",
|
|
7662
|
+
edit: "编辑",
|
|
7663
|
+
save: "保存",
|
|
7664
|
+
remove: "移除",
|
|
7665
|
+
cancel: "取消",
|
|
7666
|
+
confirmRemove: "确认移除",
|
|
7667
|
+
confirmRemoveMcp: "移除 MCP 服务器「{name}」?其 patch 文件中的配置行(含 headers、环境变量)将被删除,mcp__{name}__* 工具随之消失。",
|
|
7668
|
+
confirmRemoveSkillLink: "移除 Skill「{name}」?仅删除 ~/.dsh/skills 下的软链,源目录不受影响。",
|
|
7669
|
+
confirmRemoveSkillDir: "移除 Skill「{name}」?该项位于 ~/.dsh/skills 下且非软链,将连同文件递归删除,且不可恢复。",
|
|
7670
|
+
confirmSaveAnyway: "仍然保存",
|
|
7671
|
+
saveConfirmRealDir: "「{name}」这个条目是真实目录、不是软链:保存会先把它连同文件递归删除,再改为指向新路径,且不可恢复。",
|
|
7672
|
+
register: "注册",
|
|
7673
|
+
notEditable: "该项未在注册表中(可能是项目技能或内置技能),无法编辑。",
|
|
7674
|
+
entryNotFound: "目标已不存在,可能已被移除。",
|
|
7675
|
+
mcpServers: "MCP 服务器",
|
|
7676
|
+
skillDirs: "Skill 目录",
|
|
7677
|
+
skillDirPath: "技能来源路径",
|
|
7678
|
+
skillRoot: "技能位置",
|
|
7679
|
+
skillRootUser: "全局技能(~/.dsh/skills)",
|
|
7680
|
+
skillRootProject: "项目技能(<项目根>/.dsh/skills)",
|
|
7681
|
+
skillRootHintUser: "所有会话都能看到该技能。",
|
|
7682
|
+
skillRootHintProject: "只对 cwd 落在该项目内的会话可见。",
|
|
7683
|
+
skillProjectPath: "目标项目路径",
|
|
7684
|
+
skillProjectPathHint: "项目内任意一个已存在的路径即可;项目根按 dsh 的规则确定(向上找最近的 .git),技能写进 <项目根>/.dsh/skills。",
|
|
7685
|
+
skillRegisteredAt: "已注册到 {path}",
|
|
7686
|
+
skillRepointed: "已改为指向 {dir}",
|
|
7687
|
+
skillAdopted: "已纳入管理:{path}",
|
|
7688
|
+
skillSource: "来源:{source}",
|
|
7689
|
+
skillSourceHint: "该技能由 dsh 从其它根发现,不在本插件管理的技能目录里,因此没有「编辑」。",
|
|
7690
|
+
skillUnmanaged: "未纳入管理",
|
|
7691
|
+
sourceCustom: "自定义技能目录",
|
|
7692
|
+
sourceBundled: "随 dsh 预置",
|
|
7693
|
+
adoptSkill: "纳入管理",
|
|
7694
|
+
adoptSkillHint: "把它软链进 dsh 的技能根目录(默认 ~/.dsh/skills),内容不动;之后就能编辑与移除了。",
|
|
7695
|
+
adoptSkillTitle: "纳入管理「{name}」?",
|
|
7696
|
+
skillDirHint: "目录内需有 SKILL.md(按 dsh 的 skill 格式校验)。技能名以 SKILL.md 里声明的为准,目录名只决定注册后的软链名,两者不同也可以。",
|
|
7697
|
+
serverName: "serverName",
|
|
7698
|
+
serverNameImmutable: "serverName 不可修改:它构成工具名前缀 mcp__<serverName>__<tool>,并已被既有会话历史与权限规则引用;修改后这些记录将不再匹配。",
|
|
7699
|
+
transport: "传输方式",
|
|
7700
|
+
transportStdio: "stdio(本地子进程)",
|
|
7701
|
+
transportHttp: "streamable-http(HTTP MCP 端点)",
|
|
7702
|
+
transportHintStdio: "由客户端将该 MCP server 作为子进程启动,JSON-RPC 报文经标准输入 / 输出交换。",
|
|
7703
|
+
transportHintHttp: "该 MCP server 作为独立进程运行,可服务多个客户端连接;客户端经单个支持 POST / GET 的 HTTP 端点(MCP endpoint)与其通信。",
|
|
7704
|
+
command: "命令",
|
|
7705
|
+
args: "参数(空格分隔)",
|
|
7706
|
+
cwd: "工作目录",
|
|
7707
|
+
env: "环境变量",
|
|
7708
|
+
url: "URL",
|
|
7709
|
+
headers: "请求头",
|
|
7710
|
+
headersHint: "每行一个 Header,格式 Key: Value;Bearer token 等认证凭据在此填写。",
|
|
7711
|
+
timeout: "超时(秒)",
|
|
7712
|
+
timeoutInvalid: "超时须为数字,单位为秒。",
|
|
7713
|
+
viewCatalog: "策略与目录",
|
|
7714
|
+
catalogPolicy: "三档策略配置",
|
|
7715
|
+
catalogOnDemand: "按需能力目录",
|
|
7716
|
+
catalogPolicyNote: "实时生成;点选改动会在停手后自动写回 cordis.patch.yml(未列出规则的能力默认常驻)",
|
|
7717
|
+
catalogDisabled: "按需能力目录未启用(catalogFile 为空)",
|
|
7718
|
+
catalogUnreadable: "按需能力目录文件读取失败"
|
|
6210
7719
|
};
|
|
6211
7720
|
const en = {
|
|
6212
|
-
nav: "Capability
|
|
6213
|
-
title: "Capability
|
|
6214
|
-
desc: "Manage the Resident / On-demand /
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
kind: "kind",
|
|
6219
|
-
class: "class",
|
|
7721
|
+
nav: "Capability Management",
|
|
7722
|
+
title: "Capability Management",
|
|
7723
|
+
desc: "Manage the Resident / On-demand / Disabled classification of tools and skills.",
|
|
7724
|
+
resident: "Resident",
|
|
7725
|
+
"on-demand": "On-demand",
|
|
7726
|
+
disabled: "Disabled",
|
|
6220
7727
|
tool: "tool",
|
|
6221
7728
|
skill: "skill",
|
|
6222
7729
|
mandatory: "meta",
|
|
6223
|
-
|
|
6224
|
-
toolsGroup: "MCP tools",
|
|
7730
|
+
toolsGroup: "Tools",
|
|
6225
7731
|
skillsGroup: "Skills",
|
|
6226
|
-
|
|
7732
|
+
builtInGroup: "System built-in",
|
|
7733
|
+
globalSkills: "Global skills",
|
|
7734
|
+
projectSkills: "Project skills",
|
|
7735
|
+
emptyGlobalSkills: "No global skills",
|
|
7736
|
+
emptyProjectSkills: "No project skills",
|
|
7737
|
+
emptyTools: "No tools",
|
|
6227
7738
|
emptySkills: "No skills",
|
|
6228
7739
|
toolCount: "{count} tools",
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
cycleHint: "Click a tag to cycle its classification (On-demand →
|
|
7740
|
+
residentShort: "Resident",
|
|
7741
|
+
onDemandShort: "On-demand",
|
|
7742
|
+
disabledShort: "Disabled",
|
|
7743
|
+
cycleHint: "Click a tag to cycle its classification (On-demand → Disabled → Resident)",
|
|
6233
7744
|
notPreviewable: "This file is not a previewable text file",
|
|
6234
7745
|
previewClose: "Close",
|
|
6235
|
-
detailNotFound: "Tool detail not found"
|
|
7746
|
+
detailNotFound: "Tool detail not found",
|
|
7747
|
+
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
|
+
refreshFailed: "Refresh failed; see the log",
|
|
7751
|
+
retry: "Retry",
|
|
7752
|
+
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
|
+
registerCapability: "Register capability",
|
|
7754
|
+
editMcp: "Edit MCP server",
|
|
7755
|
+
editSkillNamed: "Edit skill directory: {name}",
|
|
7756
|
+
edit: "Edit",
|
|
7757
|
+
save: "Save",
|
|
7758
|
+
remove: "Remove",
|
|
7759
|
+
cancel: "Cancel",
|
|
7760
|
+
confirmRemove: "Confirm removal",
|
|
7761
|
+
confirmRemoveMcp: "Remove the MCP server \"{name}\"? Its config row in the patch file (headers and environment included) is deleted, and the mcp__{name}__* tools disappear with it.",
|
|
7762
|
+
confirmRemoveSkillLink: "Unregister the skill \"{name}\"? Only the symlink under ~/.dsh/skills is removed; the source directory is left untouched.",
|
|
7763
|
+
confirmRemoveSkillDir: "Remove the skill \"{name}\"? It sits under ~/.dsh/skills and is not a symlink, so it will be deleted recursively with all of its files. This cannot be undone.",
|
|
7764
|
+
confirmSaveAnyway: "Save anyway",
|
|
7765
|
+
saveConfirmRealDir: "The entry \"{name}\" is a real directory, not a symlink: saving deletes it and its files recursively before repointing it, and that cannot be undone.",
|
|
7766
|
+
register: "Register",
|
|
7767
|
+
notEditable: "This entry is not in the registry (a project or bundled skill), so it cannot be edited.",
|
|
7768
|
+
entryNotFound: "The target no longer exists; it may have been removed.",
|
|
7769
|
+
mcpServers: "MCP servers",
|
|
7770
|
+
skillDirs: "Skill directories",
|
|
7771
|
+
skillDirPath: "Skill source path",
|
|
7772
|
+
skillRoot: "Skill location",
|
|
7773
|
+
skillRootUser: "Global (~/.dsh/skills)",
|
|
7774
|
+
skillRootProject: "Project (<projectRoot>/.dsh/skills)",
|
|
7775
|
+
skillRootHintUser: "Visible to every session.",
|
|
7776
|
+
skillRootHintProject: "Visible only to sessions whose cwd sits inside that project.",
|
|
7777
|
+
skillProjectPath: "Target project path",
|
|
7778
|
+
skillProjectPathHint: "Any existing path inside the project; the project root is derived the way dsh derives it (nearest .git above it) and the skill is written under <projectRoot>/.dsh/skills.",
|
|
7779
|
+
skillRegisteredAt: "Registered at {path}",
|
|
7780
|
+
skillRepointed: "Repointed to {dir}",
|
|
7781
|
+
skillAdopted: "Adopted: {path}",
|
|
7782
|
+
skillSource: "Source: {source}",
|
|
7783
|
+
skillSourceHint: "dsh found this skill in another root, not in a skill directory this plugin manages, so there is no Edit button.",
|
|
7784
|
+
skillUnmanaged: "Not managed here",
|
|
7785
|
+
sourceCustom: "custom skill dirs",
|
|
7786
|
+
sourceBundled: "bundled with dsh",
|
|
7787
|
+
adoptSkill: "Adopt",
|
|
7788
|
+
adoptSkillHint: "Link it into dsh's skill root (default ~/.dsh/skills); the content is untouched, and it becomes editable and removable.",
|
|
7789
|
+
adoptSkillTitle: "Adopt \"{name}\"?",
|
|
7790
|
+
skillDirHint: "The directory must hold a SKILL.md in dsh's skill format. The skill name is the one declared in SKILL.md; the directory name only names the link this registers, so the two may differ.",
|
|
7791
|
+
serverName: "serverName",
|
|
7792
|
+
serverNameImmutable: "serverName is immutable: it forms the tool name prefix mcp__<serverName>__<tool> and is referenced by existing session history and permission rules, which stop matching once it changes.",
|
|
7793
|
+
transport: "Transport",
|
|
7794
|
+
transportStdio: "stdio (local subprocess)",
|
|
7795
|
+
transportHttp: "streamable-http (HTTP MCP endpoint)",
|
|
7796
|
+
transportHintStdio: "The client launches the MCP server as a subprocess and exchanges JSON-RPC messages over its standard input and output.",
|
|
7797
|
+
transportHintHttp: "The server runs as an independent process that can handle multiple client connections; the client reaches it over a single HTTP endpoint (the MCP endpoint) supporting POST and GET.",
|
|
7798
|
+
command: "Command",
|
|
7799
|
+
args: "Args (space separated)",
|
|
7800
|
+
cwd: "Working directory",
|
|
7801
|
+
env: "Environment",
|
|
7802
|
+
url: "URL",
|
|
7803
|
+
headers: "Headers",
|
|
7804
|
+
headersHint: "One header per line as Key: Value; credentials such as a Bearer token go here.",
|
|
7805
|
+
timeout: "Timeout (seconds)",
|
|
7806
|
+
timeoutInvalid: "Timeout must be a number of seconds.",
|
|
7807
|
+
viewCatalog: "Policy & catalog",
|
|
7808
|
+
catalogPolicy: "Policy (effective)",
|
|
7809
|
+
catalogOnDemand: "On-demand catalog",
|
|
7810
|
+
catalogPolicyNote: "Generated live from the effective policy; clicked changes are written back to cordis.patch.yml once you stop. Capabilities without a rule default to Resident.",
|
|
7811
|
+
catalogDisabled: "On-demand catalog emission is disabled (catalogFile is empty).",
|
|
7812
|
+
catalogUnreadable: "Failed to read the on-demand catalog file."
|
|
6236
7813
|
};
|
|
6237
7814
|
ctx.effect(() => ctx.locale.register(NS, {
|
|
6238
7815
|
zh,
|
|
@@ -6256,17 +7833,10 @@ async function apply(ctx) {
|
|
|
6256
7833
|
}
|
|
6257
7834
|
};
|
|
6258
7835
|
const injected = () => {
|
|
6259
|
-
const namespace = remote();
|
|
6260
|
-
const remoteKeys = namespace == null ? void 0 : Object.keys(namespace).filter((k) => [
|
|
6261
|
-
"getConfig",
|
|
6262
|
-
"updateConfig",
|
|
6263
|
-
"classifyAll"
|
|
6264
|
-
].includes(k)).join(",");
|
|
6265
7836
|
return {
|
|
6266
|
-
remote:
|
|
7837
|
+
remote: remote(),
|
|
6267
7838
|
t,
|
|
6268
|
-
...mountError !== void 0 ? { mountError } : {}
|
|
6269
|
-
...remoteKeys !== void 0 ? { remoteKeys } : {}
|
|
7839
|
+
...mountError !== void 0 ? { mountError } : {}
|
|
6270
7840
|
};
|
|
6271
7841
|
};
|
|
6272
7842
|
ctx.slots.inject("settings.section", () => ctx.slots.register({
|