weir-harness 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.capability-badge.js +2 -0
- package/lib/client.capability-manager.js +46 -179
- package/lib/client.capability-model.js +428 -3
- package/lib/client.file-lease-tool-model.js +189 -0
- package/lib/client.file-lease-tool-view.js +434 -0
- package/lib/client.js +105 -7
- package/lib/client.mcp-manager.js +563 -0
- package/package.json +1 -1
- package/skills/mcp-server-setup/SKILL.md +124 -0
- package/src/capabilities/apply-engine.js +18 -4
- package/src/capabilities/capability-remote.js +296 -45
- package/src/capabilities/initial-selection.js +3 -2
- package/src/capabilities/lifecycle-preload.js +69 -11
- package/src/capabilities/lifecycle-snapshot.js +127 -39
- package/src/capabilities/mcp-admission.js +2 -2
- package/src/capabilities/mcp-config.js +344 -0
- package/src/capabilities/mcp-credentials.js +180 -0
- package/src/capabilities/mcp-diagnostics.js +169 -0
- package/src/capabilities/mcp-drain.js +18 -4
- package/src/capabilities/mcp-facade.js +3 -3
- package/src/capabilities/mcp-gate.js +51 -52
- package/src/capabilities/mcp-install-tool.js +276 -0
- package/src/capabilities/mcp-manager.js +368 -142
- package/src/capabilities/mcp-mount.js +219 -79
- package/src/capabilities/mcp-operations.js +359 -0
- package/src/capabilities/mcp-process.js +43 -0
- package/src/capabilities/mcp-registry.js +74 -60
- package/src/capabilities/mcp-runtime.js +103 -0
- package/src/capabilities/read-payloads.js +8 -3
- package/src/capabilities/skill-applicability.js +185 -0
- package/src/capabilities/skill-builtin-migration.js +2 -1
- package/src/capabilities/skill-selection-plugin.js +161 -56
- package/src/file-lease/client.js +268 -35
- package/src/file-lease/index.js +4 -0
- package/src/file-lease/plugin.js +239 -15
- package/src/file-lease/tools.js +426 -38
- package/src/file-lease/watch.js +473 -0
- package/src/intent-gate/index.js +29 -12
- package/src/intent-gate/matcher.js +36 -2
- package/src/lease-broker/protocol.js +3 -2
- package/src/lease-broker/server.js +6 -3
- package/src/lease-broker/state.js +68 -11
- package/src/process/index.js +4 -2
- package/src/shared/audit.js +3 -0
- package/src/shared/child-scope.js +2 -0
- package/src/worktree/rules.js +1 -1
|
@@ -71,7 +71,7 @@ window.__ModuleLoader__.load({
|
|
|
71
71
|
const mcp = Array.isArray(listing?.mcpServers) ? listing.mcpServers : [];
|
|
72
72
|
return {
|
|
73
73
|
skills,
|
|
74
|
-
mcpManaged: mcp.filter((server) => server?.state
|
|
74
|
+
mcpManaged: mcp.filter((server) => server?.state !== "unmanaged"),
|
|
75
75
|
mcpUnmanaged: mcp.filter((server) => server?.state === "unmanaged"),
|
|
76
76
|
};
|
|
77
77
|
}
|
|
@@ -152,7 +152,13 @@ window.__ModuleLoader__.load({
|
|
|
152
152
|
next.dirty = !same(next.skills.map(skillEntryKeyOf), draft.applied.skills.map(skillEntryKeyOf)) || !same(next.mcpServers, draft.applied.mcpServers);
|
|
153
153
|
return next;
|
|
154
154
|
}
|
|
155
|
-
/** Preflight the complete draft against the unfiltered inventory. Strings are not identities.
|
|
155
|
+
/** Preflight the complete draft against the unfiltered inventory. Strings are not identities.
|
|
156
|
+
* Only a reference the CURRENT listing resolves to a parsed candidate can
|
|
157
|
+
* take part in a local same-name conflict: a preset entry rooted in another
|
|
158
|
+
* workspace (or otherwise unresolved here) has no applicable row, so it is
|
|
159
|
+
* not a second selection this session could apply and it never disables
|
|
160
|
+
* Apply. It stays in the draft untouched — the server decides whether it is
|
|
161
|
+
* inapplicable (skipped, warned) or genuinely unknown (hard failure). */
|
|
156
162
|
function skillDraftConflicts(draft, listing) {
|
|
157
163
|
const byName = new Map(), names = new Set();
|
|
158
164
|
for (const entry of Array.isArray(draft?.skills) ? draft.skills : []) {
|
|
@@ -162,8 +168,16 @@ window.__ModuleLoader__.load({
|
|
|
162
168
|
if (!byName.has(entry.name)) byName.set(entry.name, new Map());
|
|
163
169
|
byName.get(entry.name).set(key, entry);
|
|
164
170
|
}
|
|
165
|
-
const selected = [...byName].filter(([, group]) => group.size > 1).map(([name, group]) => ({ name, identities: [...group.values()] }));
|
|
166
171
|
const rows = Array.isArray(listing?.skills) ? listing.skills : null;
|
|
172
|
+
const resolvable = new Set();
|
|
173
|
+
for (const row of rows ?? []) {
|
|
174
|
+
if (row?.status !== "parsed") continue;
|
|
175
|
+
const key = skillIdentityKeyOf(row.identity);
|
|
176
|
+
if (key !== null) resolvable.add(key);
|
|
177
|
+
}
|
|
178
|
+
const selected = [...byName]
|
|
179
|
+
.map(([name, group]) => ({ name, identities: [...group].filter(([key]) => resolvable.has(key)).map(([, entry]) => entry) }))
|
|
180
|
+
.filter((item) => item.identities.length > 1);
|
|
167
181
|
const unresolved = [...names].map(name => {
|
|
168
182
|
if (!rows) return { name, state: "unavailable", candidates: [] };
|
|
169
183
|
const candidates = [...new Map(rows.filter(row => row?.status === "parsed" && row.name === name && skillIdentityKeyOf(row.identity) !== null)
|
|
@@ -552,6 +566,259 @@ window.__ModuleLoader__.load({
|
|
|
552
566
|
return `${base}.json`;
|
|
553
567
|
}
|
|
554
568
|
|
|
569
|
+
// ---- Managed MCP inventory, editor, preview and batch model ----
|
|
570
|
+
const MCP_STATES = new Set(["unknown", "registered", "mounted", "degraded", "cleanup-failed", "cleanup-unknown", "conflict"]);
|
|
571
|
+
const MCP_SERVER_NAME = /^[A-Za-z0-9_-]{1,32}$/;
|
|
572
|
+
const MCP_CREDENTIAL_REF = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
573
|
+
const MCP_DOCUMENT_LIMIT = 1024 * 1024;
|
|
574
|
+
|
|
575
|
+
function mcpStateOf(server) {
|
|
576
|
+
return MCP_STATES.has(server?.state) ? server.state : "unknown";
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function mcpListingAfterRead(current, incoming) {
|
|
580
|
+
if (incoming && incoming.error !== true) return incoming;
|
|
581
|
+
return current ? { ...current, stale: true, refreshError: true } : { error: true };
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
function mcpSecretRowsOf(config, kind) {
|
|
585
|
+
const refs = config?.secretRefs?.[kind] ?? {};
|
|
586
|
+
const literals = config?.client?.[kind] ?? {};
|
|
587
|
+
const keys = [...new Set([...Object.keys(refs), ...Object.keys(literals)])].sort();
|
|
588
|
+
return keys.map((key) => ({
|
|
589
|
+
key, action: "keep", source: refs[key] ? "ref" : "value",
|
|
590
|
+
ref: refs[key]?.ref ?? "", prefix: refs[key]?.prefix ?? "", value: "", configured: true,
|
|
591
|
+
}));
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
function mcpPublicArgsOf(config) {
|
|
595
|
+
const secret = new Set(Object.keys(config?.secretRefs?.args ?? {}).map(Number));
|
|
596
|
+
return (config?.client?.args ?? []).map((value, index) => secret.has(index) ? "" : value);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function mcpEditorFromServer(server, registryRevision = 0) {
|
|
600
|
+
const config = server?.config ?? {};
|
|
601
|
+
const client = config.client ?? {};
|
|
602
|
+
const transport = config.transport?.kind ?? client.transport ?? server?.transport ?? "stdio";
|
|
603
|
+
const reconnect = client.reconnect ?? {};
|
|
604
|
+
const legacySensitiveUrl = client.url === "[redacted-url]";
|
|
605
|
+
const secretUrl = config.secretRefs?.url;
|
|
606
|
+
return {
|
|
607
|
+
mode: server ? "edit" : "add",
|
|
608
|
+
identity: server?.identity ?? "", label: server?.label ?? "", serverName: client.serverName ?? server?.serverName ?? server?.identity ?? "",
|
|
609
|
+
originalServerName: client.serverName ?? server?.serverName ?? server?.identity ?? "",
|
|
610
|
+
transport,
|
|
611
|
+
command: client.command ?? "", argsText: JSON.stringify(mcpPublicArgsOf(config)), originalArgsText: server ? JSON.stringify(mcpPublicArgsOf(config)) : null, cwd: client.cwd ?? "",
|
|
612
|
+
url: legacySensitiveUrl ? "" : client.url ?? "", urlRef: secretUrl?.ref ?? "", urlPrefix: secretUrl?.prefix ?? "",
|
|
613
|
+
urlMode: secretUrl ? "ref" : legacySensitiveUrl ? "legacy" : "literal", urlAction: server ? "keep" : "replace",
|
|
614
|
+
envRows: mcpSecretRowsOf(config, "env"), headerRows: mcpSecretRowsOf(config, "headers"),
|
|
615
|
+
secretArgRows: Object.entries(config.secretRefs?.args ?? {}).map(([index, item]) => ({ index, action: "keep", source: "ref", ref: item.ref ?? "", prefix: item.prefix ?? "", value: "", configured: true })),
|
|
616
|
+
toolCallTimeoutMs: client.toolCallTimeoutMs == null ? "" : String(client.toolCallTimeoutMs),
|
|
617
|
+
maxInstructionBytes: client.maxInstructionBytes == null ? "" : String(client.maxInstructionBytes),
|
|
618
|
+
reconnectConfigured: Boolean(client.reconnect), reconnectEnabled: reconnect.enabled === true,
|
|
619
|
+
reconnectInitialDelayMs: reconnect.initialDelayMs == null ? "" : String(reconnect.initialDelayMs),
|
|
620
|
+
reconnectMaxDelayMs: reconnect.maxDelayMs == null ? "" : String(reconnect.maxDelayMs),
|
|
621
|
+
reconnectMaxAttempts: reconnect.maxAttempts == null ? "" : String(reconnect.maxAttempts),
|
|
622
|
+
registryRevision, targetFingerprint: server?.fingerprint ?? null,
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
function mcpParseArgs(text) {
|
|
627
|
+
const raw = String(text ?? "").trim();
|
|
628
|
+
if (!raw) return [];
|
|
629
|
+
if (!raw.startsWith("[")) return raw.split(/\s+/).filter(Boolean);
|
|
630
|
+
let parsed;
|
|
631
|
+
try { parsed = JSON.parse(raw); } catch { throw Object.assign(new Error("Arguments must be a JSON array or space-separated text"), { field: "args" }); }
|
|
632
|
+
if (!Array.isArray(parsed) || parsed.length > 128 || parsed.some((value) => typeof value !== "string")) throw Object.assign(new Error("Arguments must be at most 128 strings"), { field: "args" });
|
|
633
|
+
return parsed;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
function mcpSensitiveUrl(value) {
|
|
637
|
+
const text = String(value ?? "");
|
|
638
|
+
return /[?#]/.test(text) || /^https?:\/\/[^/\s]*@/i.test(text);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function mcpPositiveNumber(value, field, integer = false) {
|
|
642
|
+
if (value === "" || value == null) return undefined;
|
|
643
|
+
const parsed = Number(value);
|
|
644
|
+
if (!Number.isFinite(parsed) || parsed <= 0 || (integer && !Number.isSafeInteger(parsed))) throw Object.assign(new Error(`${field} must be a positive number`), { field });
|
|
645
|
+
return parsed;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function mcpApplySecretRows(rows, kind, refs, values) {
|
|
649
|
+
for (const row of rows ?? []) {
|
|
650
|
+
const key = String(kind === "args" ? row.index : row.key ?? "").trim();
|
|
651
|
+
if (!key || row.action === "keep") continue;
|
|
652
|
+
if (row.action === "remove") { values[kind][key] = { action: "remove" }; continue; }
|
|
653
|
+
if (row.source === "ref") refs[kind][key] = { ref: String(row.ref ?? "").trim(), ...(row.prefix ? { prefix: row.prefix } : {}) };
|
|
654
|
+
else if (String(row.value ?? "").length > 0) values[kind][key] = { action: "replace", value: row.value, ...(row.prefix ? { prefix: row.prefix } : row.prefixDirty === true ? { prefix: "" } : {}) };
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
function mcpConfigFromEditor(editor) {
|
|
659
|
+
const identity = String(editor.identity ?? "").trim();
|
|
660
|
+
const config = {
|
|
661
|
+
identity, label: String(editor.label ?? "").trim() || identity,
|
|
662
|
+
transport: editor.transport,
|
|
663
|
+
serverName: String(editor.serverName ?? "").trim() || identity,
|
|
664
|
+
};
|
|
665
|
+
for (const key of ["toolCallTimeoutMs", "maxInstructionBytes"]) {
|
|
666
|
+
const value = mcpPositiveNumber(editor[key], key, key === "maxInstructionBytes");
|
|
667
|
+
if (value !== undefined) config[key] = value;
|
|
668
|
+
}
|
|
669
|
+
if (editor.reconnectConfigured || editor.reconnectEnabled || editor.reconnectInitialDelayMs || editor.reconnectMaxDelayMs || editor.reconnectMaxAttempts) {
|
|
670
|
+
config.reconnect = { enabled: editor.reconnectEnabled === true };
|
|
671
|
+
for (const [field, target] of [["reconnectInitialDelayMs", "initialDelayMs"], ["reconnectMaxDelayMs", "maxDelayMs"], ["reconnectMaxAttempts", "maxAttempts"]]) {
|
|
672
|
+
const value = mcpPositiveNumber(editor[field], field, target === "maxAttempts");
|
|
673
|
+
if (value !== undefined) config.reconnect[target] = value;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
const secretRefs = {}, secretValues = {};
|
|
677
|
+
if (editor.transport === "stdio") {
|
|
678
|
+
config.command = String(editor.command ?? "").trim();
|
|
679
|
+
// An unchanged edit never resends the displayed (projected) argv: the
|
|
680
|
+
// Host keeps the stored arguments and their secret slots.
|
|
681
|
+
if (!(editor.mode === "edit" && editor.originalArgsText != null && editor.argsText === editor.originalArgsText)) config.args = mcpParseArgs(editor.argsText);
|
|
682
|
+
if (String(editor.cwd ?? "").length > 0) config.cwd = editor.cwd;
|
|
683
|
+
secretRefs.env = {}; secretValues.env = {};
|
|
684
|
+
mcpApplySecretRows(editor.envRows, "env", secretRefs, secretValues);
|
|
685
|
+
secretRefs.args = {}; secretValues.args = {};
|
|
686
|
+
mcpApplySecretRows(editor.secretArgRows, "args", secretRefs, secretValues);
|
|
687
|
+
} else {
|
|
688
|
+
secretRefs.headers = {}; secretValues.headers = {};
|
|
689
|
+
mcpApplySecretRows(editor.headerRows, "headers", secretRefs, secretValues);
|
|
690
|
+
if (editor.urlAction === "remove") secretValues.url = { action: "remove" };
|
|
691
|
+
else if (editor.urlAction === "replace" || editor.mode === "add") {
|
|
692
|
+
if (editor.urlMode === "ref") secretRefs.url = { ref: String(editor.urlRef ?? "").trim(), ...(editor.urlPrefix ? { prefix: editor.urlPrefix } : {}) };
|
|
693
|
+
else if (mcpSensitiveUrl(editor.url)) secretValues.url = { action: "replace", value: editor.url };
|
|
694
|
+
else config.url = String(editor.url ?? "").trim();
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
for (const kind of ["env", "headers", "args"]) {
|
|
698
|
+
if (secretRefs[kind] && Object.keys(secretRefs[kind]).length === 0) delete secretRefs[kind];
|
|
699
|
+
if (secretValues[kind] && Object.keys(secretValues[kind]).length === 0) delete secretValues[kind];
|
|
700
|
+
}
|
|
701
|
+
if (Object.keys(secretRefs).length > 0) config.secretRefs = secretRefs;
|
|
702
|
+
if (Object.values(secretValues).some((value) => Object.keys(value).length > 0) || secretValues.url) config.secretValues = secretValues;
|
|
703
|
+
return config;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
function mcpEditorHasSecrets(editor) {
|
|
707
|
+
const rows = [...(editor.envRows ?? []), ...(editor.headerRows ?? []), ...(editor.secretArgRows ?? [])];
|
|
708
|
+
if (rows.some((row) => row.configured || row.action === "remove" || (row.action !== "keep" && (row.source === "ref" || String(row.value ?? "").length > 0)))) return true;
|
|
709
|
+
return editor.urlMode === "ref" || editor.urlMode === "legacy" || mcpSensitiveUrl(editor.url);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
function mcpEditorErrorsOf(editor) {
|
|
713
|
+
const errors = {};
|
|
714
|
+
const identity = String(editor.identity ?? "").trim();
|
|
715
|
+
if (!identity) errors.identity = "required";
|
|
716
|
+
else if (!SEGMENT.test(identity)) errors.identity = "invalid";
|
|
717
|
+
const serverName = String(editor.serverName ?? "").trim() || identity;
|
|
718
|
+
if (!MCP_SERVER_NAME.test(serverName)) errors.serverName = "invalid";
|
|
719
|
+
try { mcpPositiveNumber(editor.toolCallTimeoutMs, "toolCallTimeoutMs"); } catch { errors.toolCallTimeoutMs = "invalid"; }
|
|
720
|
+
try { mcpPositiveNumber(editor.maxInstructionBytes, "maxInstructionBytes", true); } catch { errors.maxInstructionBytes = "invalid"; }
|
|
721
|
+
try {
|
|
722
|
+
const initial = mcpPositiveNumber(editor.reconnectInitialDelayMs, "reconnectInitialDelayMs");
|
|
723
|
+
const max = mcpPositiveNumber(editor.reconnectMaxDelayMs, "reconnectMaxDelayMs");
|
|
724
|
+
mcpPositiveNumber(editor.reconnectMaxAttempts, "reconnectMaxAttempts", true);
|
|
725
|
+
if ((initial ?? 500) > (max ?? 30000)) errors.reconnect = "invalid";
|
|
726
|
+
} catch { errors.reconnect = "invalid"; }
|
|
727
|
+
let argumentCount = null;
|
|
728
|
+
if (editor.transport === "stdio") {
|
|
729
|
+
if (!String(editor.command ?? "").trim()) errors.command = "required";
|
|
730
|
+
try { argumentCount = mcpParseArgs(editor.argsText).length; } catch { errors.args = "invalid"; }
|
|
731
|
+
} else if (editor.urlAction === "remove") errors.url = "required";
|
|
732
|
+
else if (editor.urlAction === "replace" || editor.mode === "add") {
|
|
733
|
+
if (editor.urlMode === "ref" && !MCP_CREDENTIAL_REF.test(String(editor.urlRef ?? "").trim())) errors.url = "invalid";
|
|
734
|
+
if (editor.urlMode !== "ref" && !String(editor.url ?? "").trim()) errors.url = "required";
|
|
735
|
+
}
|
|
736
|
+
for (const [field, rows] of [["env", editor.envRows], ["headers", editor.headerRows], ["args", editor.secretArgRows]]) {
|
|
737
|
+
if ((rows ?? []).length > 64 && field !== "args") errors[field] = "too-many";
|
|
738
|
+
const seen = new Set();
|
|
739
|
+
for (const [index, row] of (rows ?? []).entries()) {
|
|
740
|
+
const key = String(field === "args" ? row.index : row.key ?? "").trim();
|
|
741
|
+
if (!key && !row.value && !row.ref) continue;
|
|
742
|
+
if (!key && (row.action !== "keep" || row.value || row.ref)) errors[`${field}.${index}`] = "required";
|
|
743
|
+
if (seen.has(key)) errors[`${field}.${index}`] = "duplicate";
|
|
744
|
+
seen.add(key);
|
|
745
|
+
if (row.action !== "keep" && row.action !== "remove" && row.source === "ref" && !MCP_CREDENTIAL_REF.test(String(row.ref ?? "").trim())) errors[`${field}.${index}`] = "invalid-ref";
|
|
746
|
+
if (field === "args" && (!Number.isInteger(Number(key)) || Number(key) < 0 || Number(key) >= (argumentCount ?? 0))) errors[`${field}.${index}`] = "invalid-index";
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
return errors;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
function mcpPreviewSupportOf(preview, secretBearing) {
|
|
753
|
+
if (!secretBearing) return [];
|
|
754
|
+
const unsupported = [];
|
|
755
|
+
if (preview?.capabilities?.credentials === false) unsupported.push("credentials-unavailable");
|
|
756
|
+
if (preview?.capabilities?.ephemeral === false) unsupported.push("ephemeral-loader-unavailable");
|
|
757
|
+
return unsupported;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
function mcpPreviewHasSecrets(preview) {
|
|
761
|
+
return (preview?.plan ?? []).some((item) => {
|
|
762
|
+
const entry = item?.entry ?? {};
|
|
763
|
+
if (Object.keys(entry.secretRefs ?? {}).length > 0 || entry.transport?.ref === "http:credential-ref") return true;
|
|
764
|
+
if (Object.keys(entry.client?.env ?? {}).length > 0 || Object.keys(entry.client?.headers ?? {}).length > 0) return true;
|
|
765
|
+
return entry.client?.url === "[redacted-url]" || mcpSensitiveUrl(entry.client?.url);
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
function mcpDocumentHasSecrets(text) {
|
|
770
|
+
const guarded = mcpJsonGuard(text);
|
|
771
|
+
if (!guarded.ok) return false;
|
|
772
|
+
const servers = guarded.value.mcpServers ? Object.values(guarded.value.mcpServers) : [guarded.value];
|
|
773
|
+
return servers.some((server) => {
|
|
774
|
+
if (Object.keys(server?.secretRefs ?? {}).length > 0 || Object.keys(server?.secretValues ?? {}).length > 0) return true;
|
|
775
|
+
const client = server?.client ?? server;
|
|
776
|
+
if (Object.keys(client?.env ?? {}).length > 0 || Object.keys(client?.headers ?? {}).length > 0 || Object.keys(client?.secretArgs ?? {}).length > 0) return true;
|
|
777
|
+
if ((client?.args ?? []).some((value) => value && typeof value === "object" && typeof value.ref === "string")) return true;
|
|
778
|
+
return (client?.url && typeof client.url === "object" && typeof client.url.ref === "string") || mcpSensitiveUrl(client?.url);
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
function mcpUtf8Bytes(text) {
|
|
783
|
+
let bytes = 0;
|
|
784
|
+
const value = String(text ?? "");
|
|
785
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
786
|
+
const code = value.charCodeAt(index);
|
|
787
|
+
if (code < 0x80) bytes += 1;
|
|
788
|
+
else if (code < 0x800) bytes += 2;
|
|
789
|
+
else if (code >= 0xd800 && code <= 0xdbff && index + 1 < value.length && value.charCodeAt(index + 1) >= 0xdc00 && value.charCodeAt(index + 1) <= 0xdfff) { bytes += 4; index += 1; }
|
|
790
|
+
else bytes += 3;
|
|
791
|
+
}
|
|
792
|
+
return bytes;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
function mcpJsonGuard(text) {
|
|
796
|
+
if (mcpUtf8Bytes(text) > MCP_DOCUMENT_LIMIT) return { ok: false, code: "document-too-large" };
|
|
797
|
+
try {
|
|
798
|
+
const value = JSON.parse(text);
|
|
799
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return { ok: false, code: "invalid-document" };
|
|
800
|
+
return { ok: true, value };
|
|
801
|
+
} catch { return { ok: false, code: "invalid-json" }; }
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
function mcpDocumentEntryOf(text, identity) {
|
|
805
|
+
const guarded = mcpJsonGuard(text);
|
|
806
|
+
if (!guarded.ok) return null;
|
|
807
|
+
if (guarded.value.mcpServers) {
|
|
808
|
+
const entry = guarded.value.mcpServers[identity];
|
|
809
|
+
return entry && typeof entry === "object" && !Array.isArray(entry) ? { ...entry, identity: entry.identity ?? identity } : null;
|
|
810
|
+
}
|
|
811
|
+
const entry = guarded.value;
|
|
812
|
+
return (entry.identity ?? identity) === identity ? { ...entry, identity: entry.identity ?? identity } : null;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
function mcpBatchItemsOf(response) {
|
|
816
|
+
return (response?.items ?? []).map((item) => ({
|
|
817
|
+
...item, succeeded: item.status === "mounted", failed: item.status !== "mounted",
|
|
818
|
+
notAttempted: item.status === "not-attempted", saved: item.saved === true,
|
|
819
|
+
}));
|
|
820
|
+
}
|
|
821
|
+
|
|
555
822
|
// Locale data face (pure data, NO ctx calls — a chunk factory re-executes
|
|
556
823
|
// under HMR, so a chunk-local register would throw on the duplicate
|
|
557
824
|
// (namespace, locale) pair): the entry registers these under "settings.weir.capability"
|
|
@@ -607,6 +874,78 @@ window.__ModuleLoader__.load({
|
|
|
607
874
|
"capability.mcp.cancel": "Cancel",
|
|
608
875
|
"capability.mcp.errorRequired": "Required.",
|
|
609
876
|
"capability.mcp.errorIdentityInvalid": "Letters, digits, \"-\" and \"_\" only; start with a letter or digit.",
|
|
877
|
+
"capability.mcp.boundary": "MCP management could not be rendered. Other capability controls remain available.",
|
|
878
|
+
"capability.mcp.unavailableView": "MCP management is unavailable on this client.",
|
|
879
|
+
"capability.mcp.unavailableRegistry": "MCP registry unavailable: {status}. Last-good rows, when present, are stale.",
|
|
880
|
+
"capability.mcp.stale": "stale — last good inventory",
|
|
881
|
+
"capability.mcp.refresh": "Refresh",
|
|
882
|
+
"capability.mcp.pasteJson": "Paste JSON",
|
|
883
|
+
"capability.mcp.secretArgIndex": "argument index",
|
|
884
|
+
"capability.mcp.secretKey": "secret key",
|
|
885
|
+
"capability.mcp.secretAction": "secret action",
|
|
886
|
+
"capability.mcp.keep": "keep",
|
|
887
|
+
"capability.mcp.replace": "replace",
|
|
888
|
+
"capability.mcp.removeSecret": "remove",
|
|
889
|
+
"capability.mcp.secretSource": "secret source",
|
|
890
|
+
"capability.mcp.secretValueSource": "new value",
|
|
891
|
+
"capability.mcp.secretRefSource": "credential ref",
|
|
892
|
+
"capability.mcp.removeRow": "Remove row",
|
|
893
|
+
"capability.mcp.ref": "credential reference",
|
|
894
|
+
"capability.mcp.secretBlankKeep": "blank keeps the configured secret",
|
|
895
|
+
"capability.mcp.secretValue": "secret value",
|
|
896
|
+
"capability.mcp.prefix": "optional non-secret prefix",
|
|
897
|
+
"capability.mcp.addSecret": "+ Add secret/reference",
|
|
898
|
+
"capability.mcp.saved": "saved",
|
|
899
|
+
"capability.mcp.notAttempted": "not attempted",
|
|
900
|
+
"capability.mcp.defaultRemoved": "default removed",
|
|
901
|
+
"capability.mcp.discovery": "Discovery: {count} tool(s)",
|
|
902
|
+
"capability.mcp.nonAtomic": "Batch results are per server; no atomic rollback is claimed.",
|
|
903
|
+
"capability.mcp.planDigest": "Plan digest: {digest}",
|
|
904
|
+
"capability.mcp.editTitle": "Edit MCP server",
|
|
905
|
+
"capability.mcp.transport": "transport",
|
|
906
|
+
"capability.mcp.basic": "Basic",
|
|
907
|
+
"capability.mcp.advanced": "Advanced",
|
|
908
|
+
"capability.mcp.serverName": "public server name",
|
|
909
|
+
"capability.mcp.argsJson": "args (JSON array or space-separated)",
|
|
910
|
+
"capability.mcp.cwd": "working directory (optional)",
|
|
911
|
+
"capability.mcp.sensitiveConfigured": "Sensitive URL configured; its value is hidden.",
|
|
912
|
+
"capability.mcp.urlMode": "URL source",
|
|
913
|
+
"capability.mcp.urlLiteral": "URL",
|
|
914
|
+
"capability.mcp.urlRef": "URL credential reference",
|
|
915
|
+
"capability.mcp.url": "MCP URL",
|
|
916
|
+
"capability.mcp.namespacePending": "The public namespace changes; separate confirmation is required.",
|
|
917
|
+
"capability.mcp.preview": "Preview",
|
|
918
|
+
"capability.mcp.confirm": "Confirm",
|
|
919
|
+
"capability.mcp.test": "Test",
|
|
920
|
+
"capability.mcp.pasteTitle": "Paste MCP JSON (single or mcpServers batch)",
|
|
921
|
+
"capability.mcp.pasteLabel": "MCP JSON document",
|
|
922
|
+
"capability.mcp.tooLarge": "JSON exceeds the 1 MiB limit; no preview or write occurred.",
|
|
923
|
+
"capability.mcp.confirmBatch": "Confirm batch",
|
|
924
|
+
"capability.mcp.retryEdit": "Retry / edit",
|
|
925
|
+
"capability.mcp.selectSession": "Select {identity} for this session",
|
|
926
|
+
"capability.mcp.default": "default",
|
|
927
|
+
"capability.mcp.selected": "selected",
|
|
928
|
+
"capability.mcp.toolsUnknown": "Tools: unknown",
|
|
929
|
+
"capability.mcp.toolsCount": "Tools: {count}",
|
|
930
|
+
"capability.mcp.liveUnknown": "Live reconnect/process/stderr: unknown",
|
|
931
|
+
"capability.mcp.lastMount": "Last mount: {status} / generation {generation}",
|
|
932
|
+
"capability.mcp.lastTest": "Last test: {status}",
|
|
933
|
+
"capability.mcp.edit": "Edit",
|
|
934
|
+
"capability.mcp.restart": "Restart",
|
|
935
|
+
"capability.mcp.recover": "Recover",
|
|
936
|
+
"capability.mcp.defaultOff": "Disable default",
|
|
937
|
+
"capability.mcp.defaultOn": "Enable default",
|
|
938
|
+
"capability.mcp.remove": "Remove",
|
|
939
|
+
"capability.mcp.pending": "Pending…",
|
|
940
|
+
"capability.mcp.removeTitle": "Remove MCP server?",
|
|
941
|
+
"capability.mcp.namespaceTitle": "Change public namespace?",
|
|
942
|
+
"capability.mcp.batchTitle": "Install this MCP batch?",
|
|
943
|
+
"capability.mcp.removeConfirm": "Remove {identity}, close admission, await disposal, and remove its new-session default. Draft and accepted session selections are not rewritten.",
|
|
944
|
+
"capability.mcp.namespaceConfirm": "Change public serverName from {from} to {to}? Existing tool names and calls change after the strict replacement is ready.",
|
|
945
|
+
"capability.mcp.batchConfirm": "Probe and install {count} server(s) independently using digest {digest}. Results may be partial; there is no atomic rollback.",
|
|
946
|
+
"capability.mcp.removeConfirmButton": "Remove server",
|
|
947
|
+
"capability.mcp.credentials-unavailable": "Credentials are unavailable; secret-bearing MCP work is disabled. Use a secret-free configuration or enable DSH credentials.",
|
|
948
|
+
"capability.mcp.ephemeral-loader-unavailable": "Safe ephemeral mounting is unavailable; secret-bearing MCP work is disabled.",
|
|
610
949
|
"capability.diff.addSkills": "+{n} skills",
|
|
611
950
|
"capability.diff.removeSkills": "−{n} skills",
|
|
612
951
|
"capability.diff.addMcp": "+{n} MCP",
|
|
@@ -791,6 +1130,78 @@ window.__ModuleLoader__.load({
|
|
|
791
1130
|
"capability.mcp.cancel": "取消",
|
|
792
1131
|
"capability.mcp.errorRequired": "必填。",
|
|
793
1132
|
"capability.mcp.errorIdentityInvalid": "仅限字母、数字、\"-\"、\"_\",且以字母或数字开头。",
|
|
1133
|
+
"capability.mcp.boundary": "MCP 管理界面渲染失败;其他能力控件仍可使用。",
|
|
1134
|
+
"capability.mcp.unavailableView": "此客户端无法使用 MCP 管理。",
|
|
1135
|
+
"capability.mcp.unavailableRegistry": "MCP 注册表不可用:{status}。如有上次成功清单,现已标记为过期。",
|
|
1136
|
+
"capability.mcp.stale": "已过期——上次成功清单",
|
|
1137
|
+
"capability.mcp.refresh": "刷新",
|
|
1138
|
+
"capability.mcp.pasteJson": "粘贴 JSON",
|
|
1139
|
+
"capability.mcp.secretArgIndex": "参数索引",
|
|
1140
|
+
"capability.mcp.secretKey": "秘密键名",
|
|
1141
|
+
"capability.mcp.secretAction": "秘密操作",
|
|
1142
|
+
"capability.mcp.keep": "保留",
|
|
1143
|
+
"capability.mcp.replace": "替换",
|
|
1144
|
+
"capability.mcp.removeSecret": "移除",
|
|
1145
|
+
"capability.mcp.secretSource": "秘密来源",
|
|
1146
|
+
"capability.mcp.secretValueSource": "新值",
|
|
1147
|
+
"capability.mcp.secretRefSource": "凭据引用",
|
|
1148
|
+
"capability.mcp.removeRow": "移除此行",
|
|
1149
|
+
"capability.mcp.ref": "凭据引用",
|
|
1150
|
+
"capability.mcp.secretBlankKeep": "留空则保留已配置的秘密",
|
|
1151
|
+
"capability.mcp.secretValue": "秘密值",
|
|
1152
|
+
"capability.mcp.prefix": "可选非秘密前缀",
|
|
1153
|
+
"capability.mcp.addSecret": "+ 添加秘密/引用",
|
|
1154
|
+
"capability.mcp.saved": "已保存",
|
|
1155
|
+
"capability.mcp.notAttempted": "未尝试",
|
|
1156
|
+
"capability.mcp.defaultRemoved": "默认值已移除",
|
|
1157
|
+
"capability.mcp.discovery": "发现:{count} 个工具",
|
|
1158
|
+
"capability.mcp.nonAtomic": "批量结果按 server 分别报告;不承诺原子回滚。",
|
|
1159
|
+
"capability.mcp.planDigest": "计划摘要:{digest}",
|
|
1160
|
+
"capability.mcp.editTitle": "编辑 MCP server",
|
|
1161
|
+
"capability.mcp.transport": "传输方式",
|
|
1162
|
+
"capability.mcp.basic": "基本",
|
|
1163
|
+
"capability.mcp.advanced": "高级",
|
|
1164
|
+
"capability.mcp.serverName": "公开 server 名称",
|
|
1165
|
+
"capability.mcp.argsJson": "参数(JSON 数组或空格分隔)",
|
|
1166
|
+
"capability.mcp.cwd": "工作目录(可选)",
|
|
1167
|
+
"capability.mcp.sensitiveConfigured": "已配置敏感 URL;其值保持隐藏。",
|
|
1168
|
+
"capability.mcp.urlMode": "URL 来源",
|
|
1169
|
+
"capability.mcp.urlLiteral": "URL",
|
|
1170
|
+
"capability.mcp.urlRef": "URL 凭据引用",
|
|
1171
|
+
"capability.mcp.url": "MCP URL",
|
|
1172
|
+
"capability.mcp.namespacePending": "公开命名空间将改变;需要单独确认。",
|
|
1173
|
+
"capability.mcp.preview": "预览",
|
|
1174
|
+
"capability.mcp.confirm": "确认",
|
|
1175
|
+
"capability.mcp.test": "测试",
|
|
1176
|
+
"capability.mcp.pasteTitle": "粘贴 MCP JSON(单个或 mcpServers 批量)",
|
|
1177
|
+
"capability.mcp.pasteLabel": "MCP JSON 文档",
|
|
1178
|
+
"capability.mcp.tooLarge": "JSON 超过 1 MiB 上限;未预览或写入。",
|
|
1179
|
+
"capability.mcp.confirmBatch": "确认批量安装",
|
|
1180
|
+
"capability.mcp.retryEdit": "重试 / 编辑",
|
|
1181
|
+
"capability.mcp.selectSession": "为本会话选择 {identity}",
|
|
1182
|
+
"capability.mcp.default": "默认",
|
|
1183
|
+
"capability.mcp.selected": "已选择",
|
|
1184
|
+
"capability.mcp.toolsUnknown": "工具数:未知",
|
|
1185
|
+
"capability.mcp.toolsCount": "工具数:{count}",
|
|
1186
|
+
"capability.mcp.liveUnknown": "实时重连/进程/stderr:未知",
|
|
1187
|
+
"capability.mcp.lastMount": "上次挂载:{status} / generation {generation}",
|
|
1188
|
+
"capability.mcp.lastTest": "上次测试:{status}",
|
|
1189
|
+
"capability.mcp.edit": "编辑",
|
|
1190
|
+
"capability.mcp.restart": "重启",
|
|
1191
|
+
"capability.mcp.recover": "恢复残留",
|
|
1192
|
+
"capability.mcp.defaultOff": "取消默认",
|
|
1193
|
+
"capability.mcp.defaultOn": "设为默认",
|
|
1194
|
+
"capability.mcp.remove": "移除",
|
|
1195
|
+
"capability.mcp.pending": "处理中…",
|
|
1196
|
+
"capability.mcp.removeTitle": "移除 MCP server?",
|
|
1197
|
+
"capability.mcp.namespaceTitle": "更改公开命名空间?",
|
|
1198
|
+
"capability.mcp.batchTitle": "安装此 MCP 批次?",
|
|
1199
|
+
"capability.mcp.removeConfirm": "移除 {identity},关闭准入并等待清理,同时移除其新会话默认值。草稿和已接受的会话选择不会被改写。",
|
|
1200
|
+
"capability.mcp.namespaceConfirm": "将公开 serverName 从 {from} 改为 {to}?严格替换就绪后,现有工具名与调用将使用新命名空间。",
|
|
1201
|
+
"capability.mcp.batchConfirm": "使用摘要 {digest} 逐项探测并安装 {count} 个 server。结果可能部分成功;不进行原子回滚。",
|
|
1202
|
+
"capability.mcp.removeConfirmButton": "移除 server",
|
|
1203
|
+
"capability.mcp.credentials-unavailable": "凭据服务不可用;已禁用含秘密的 MCP 操作。请使用无秘密配置或启用 DSH credentials。",
|
|
1204
|
+
"capability.mcp.ephemeral-loader-unavailable": "安全临时挂载不可用;已禁用含秘密的 MCP 操作。",
|
|
794
1205
|
"capability.diff.addSkills": "+{n} 个技能",
|
|
795
1206
|
"capability.diff.removeSkills": "−{n} 个技能",
|
|
796
1207
|
"capability.diff.addMcp": "+{n} 个 MCP",
|
|
@@ -949,6 +1360,20 @@ window.__ModuleLoader__.load({
|
|
|
949
1360
|
exports.filterSkillRows = filterSkillRows;
|
|
950
1361
|
exports.draftDiffOf = draftDiffOf;
|
|
951
1362
|
exports.mcpAddErrorsOf = mcpAddErrorsOf;
|
|
1363
|
+
exports.mcpStateOf = mcpStateOf;
|
|
1364
|
+
exports.mcpListingAfterRead = mcpListingAfterRead;
|
|
1365
|
+
exports.mcpEditorFromServer = mcpEditorFromServer;
|
|
1366
|
+
exports.mcpConfigFromEditor = mcpConfigFromEditor;
|
|
1367
|
+
exports.mcpEditorHasSecrets = mcpEditorHasSecrets;
|
|
1368
|
+
exports.mcpEditorErrorsOf = mcpEditorErrorsOf;
|
|
1369
|
+
exports.mcpPreviewSupportOf = mcpPreviewSupportOf;
|
|
1370
|
+
exports.mcpPreviewHasSecrets = mcpPreviewHasSecrets;
|
|
1371
|
+
exports.mcpDocumentHasSecrets = mcpDocumentHasSecrets;
|
|
1372
|
+
exports.mcpSensitiveUrl = mcpSensitiveUrl;
|
|
1373
|
+
exports.mcpUtf8Bytes = mcpUtf8Bytes;
|
|
1374
|
+
exports.mcpJsonGuard = mcpJsonGuard;
|
|
1375
|
+
exports.mcpDocumentEntryOf = mcpDocumentEntryOf;
|
|
1376
|
+
exports.mcpBatchItemsOf = mcpBatchItemsOf;
|
|
952
1377
|
exports.draftFromPreset = draftFromPreset;
|
|
953
1378
|
exports.presetRowOf = presetRowOf;
|
|
954
1379
|
exports.groupPresets = groupPresets;
|