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
|
@@ -233,6 +233,8 @@ window.__ModuleLoader__.load({
|
|
|
233
233
|
// would sit on the perpetual loading surface (12.2).
|
|
234
234
|
fetchListing: props.fetchListing, fetchConditions: props.fetchConditions,
|
|
235
235
|
fetchReceipt: props.fetchReceipt, applySelection: props.applySelection, mcpAdd: props.mcpAdd,
|
|
236
|
+
mcpPreview: props.mcpPreview, mcpMutate: props.mcpMutate, mcpBatch: props.mcpBatch,
|
|
237
|
+
McpManagerView: props.McpManagerView, McpManagerBoundary: props.McpManagerBoundary,
|
|
236
238
|
// The preset/workspace-default verbs and the presets-chunk
|
|
237
239
|
// loader travel the same prop bridge (D3); an old composition
|
|
238
240
|
// root leaves them undefined and the view degrades to its
|
|
@@ -541,46 +541,55 @@ window.__ModuleLoader__.load({
|
|
|
541
541
|
const [draft, setDraft] = react.useState(null);
|
|
542
542
|
const [receiptState, setReceiptState] = react.useState(null);
|
|
543
543
|
const receiptRequest = react.useRef(0);
|
|
544
|
+
const listingRequest = react.useRef(0);
|
|
544
545
|
const applying = react.useRef(false);
|
|
545
546
|
const [commit, setCommit] = react.useState(null);
|
|
546
|
-
// Managed MCP add form (8.1/8.2): per-field errors are derived by the
|
|
547
|
-
// pure model (mcpAddErrorsOf) and shown after the first submit
|
|
548
|
-
// attempt; the hook lives with the others — a hook after an early
|
|
549
|
-
// return crashes with React #310.
|
|
550
|
-
const [addForm, setAddForm] = react.useState({ open: false, identity: "", label: "", command: "", args: "", showErrors: false, error: null, busy: false });
|
|
551
547
|
|
|
552
|
-
const refreshReceipt = () => {
|
|
553
|
-
if (typeof props.fetchReceipt !== "function") return;
|
|
548
|
+
const refreshReceipt = async () => {
|
|
549
|
+
if (typeof props.fetchReceipt !== "function") return null;
|
|
554
550
|
const request = ++receiptRequest.current;
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
551
|
+
try {
|
|
552
|
+
const receipt = await props.fetchReceipt(props.sessionId);
|
|
553
|
+
if (request === receiptRequest.current) {
|
|
554
|
+
setReceiptState(model.badgeStateOf(receipt, null));
|
|
555
|
+
setDraft(current => model.draftAfterRefresh(current, receipt));
|
|
556
|
+
}
|
|
557
|
+
return receipt;
|
|
558
|
+
} catch {
|
|
560
559
|
if (request === receiptRequest.current) {
|
|
561
560
|
setReceiptState(null);
|
|
562
561
|
setDraft(current => model.draftAfterRefresh(current, null));
|
|
563
562
|
}
|
|
564
|
-
|
|
563
|
+
return null;
|
|
564
|
+
}
|
|
565
565
|
};
|
|
566
|
-
const
|
|
567
|
-
if (typeof props.fetchListing
|
|
568
|
-
|
|
566
|
+
const refreshListing = async () => {
|
|
567
|
+
if (typeof props.fetchListing !== "function") return null;
|
|
568
|
+
const request = ++listingRequest.current;
|
|
569
|
+
try {
|
|
570
|
+
const data = await props.fetchListing(props.sessionId);
|
|
571
|
+
const folded = model.mcpListingAfterRead(listing, data ?? { error: true });
|
|
572
|
+
if (request === listingRequest.current) setListing(folded);
|
|
573
|
+
return folded;
|
|
574
|
+
} catch {
|
|
575
|
+
const folded = model.mcpListingAfterRead(listing, { error: true });
|
|
576
|
+
if (request === listingRequest.current) setListing(folded);
|
|
577
|
+
return folded;
|
|
569
578
|
}
|
|
570
|
-
|
|
579
|
+
};
|
|
580
|
+
const refreshAll = async () => {
|
|
581
|
+
const [freshListing] = await Promise.all([refreshListing(), refreshReceipt()]);
|
|
582
|
+
return { listing: freshListing };
|
|
571
583
|
};
|
|
572
584
|
react.useEffect(() => {
|
|
573
585
|
let alive = true;
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
props.fetchListing(props.sessionId).then((data) => { if (alive) setListing(data ?? { error: true }); }).catch(() => { if (alive) setListing({ error: true }); });
|
|
578
|
-
}
|
|
586
|
+
setListing(null);
|
|
587
|
+
setConditions(null);
|
|
588
|
+
void refreshAll();
|
|
579
589
|
if (typeof props.fetchConditions === "function") {
|
|
580
590
|
props.fetchConditions(props.sessionId).then((data) => { if (alive) setConditions(data); }).catch(() => {});
|
|
581
591
|
}
|
|
582
|
-
|
|
583
|
-
return () => { alive = false; ++receiptRequest.current; };
|
|
592
|
+
return () => { alive = false; ++receiptRequest.current; ++listingRequest.current; };
|
|
584
593
|
}, [props.sessionId]);
|
|
585
594
|
const toggle = (kind, name) => {
|
|
586
595
|
if (draft && commit?.phase !== "submitting") { setDraft(model.draftToggle(draft, kind, name)); setCommit(null); }
|
|
@@ -623,31 +632,6 @@ window.__ModuleLoader__.load({
|
|
|
623
632
|
setDraft(model.draftFromPreset(document, draft, resolveName));
|
|
624
633
|
setCommit(null);
|
|
625
634
|
};
|
|
626
|
-
// Managed MCP add form (8.1/8.2): registers a stdio server and mounts
|
|
627
|
-
// it immediately via /capabilities mcp-add. Client-side per-field
|
|
628
|
-
// validation first; the server's own status still surfaces verbatim.
|
|
629
|
-
const addErrors = model.mcpAddErrorsOf(addForm);
|
|
630
|
-
const submitAdd = () => {
|
|
631
|
-
if (typeof props.mcpAdd !== "function" || addForm.busy) return;
|
|
632
|
-
if (Object.keys(addErrors).length > 0) {
|
|
633
|
-
setAddForm({ ...addForm, showErrors: true, error: null });
|
|
634
|
-
return;
|
|
635
|
-
}
|
|
636
|
-
setAddForm({ ...addForm, busy: true, error: null });
|
|
637
|
-
props.mcpAdd(props.sessionId, {
|
|
638
|
-
identity: addForm.identity.trim(),
|
|
639
|
-
label: addForm.label.trim() || addForm.identity.trim(),
|
|
640
|
-
command: addForm.command.trim(),
|
|
641
|
-
args: addForm.args.trim() ? addForm.args.trim().split(/\s+/) : [],
|
|
642
|
-
}).then((result) => {
|
|
643
|
-
if (result && result.status === "registered") {
|
|
644
|
-
setAddForm({ open: false, identity: "", label: "", command: "", args: "", showErrors: false, error: null, busy: false });
|
|
645
|
-
refreshAll();
|
|
646
|
-
} else {
|
|
647
|
-
setAddForm({ ...addForm, busy: false, error: (result && result.status) || "add failed" });
|
|
648
|
-
}
|
|
649
|
-
}).catch((cause) => setAddForm({ ...addForm, busy: false, error: String(cause) }));
|
|
650
|
-
};
|
|
651
635
|
const frameStyle = props.shell === "sidebar" ? S.frame.sidebar : S.frame.popover;
|
|
652
636
|
const conditionState = model.managerConditionState(conditions?.conditions ?? conditions);
|
|
653
637
|
// Loading: row-height skeleton blocks (D1) with the loading text kept
|
|
@@ -734,61 +718,8 @@ window.__ModuleLoader__.load({
|
|
|
734
718
|
.replace("{name}", item.name).replace("{sources}", item.candidates.map(sourceOf).join(", "))),
|
|
735
719
|
...unknown.map(item => t("capability.unknownLegacyName", "{name}: no matching Skill in the current listing. Refresh the listing or remove this legacy name.").replace("{name}", item.name))
|
|
736
720
|
];
|
|
737
|
-
const draftHas = (kind, name) => draft !== null && draft[kind].includes(name);
|
|
738
721
|
const selectionDisabled = draft === null || commit?.phase === "submitting";
|
|
739
|
-
const
|
|
740
|
-
? t("capability.mcp.errorIdentityInvalid", "Letters, digits, \"-\" and \"_\" only; start with a letter or digit.")
|
|
741
|
-
: t("capability.mcp.errorRequired", "Required.");
|
|
742
|
-
const addField = (key, placeholder, errorCode) => react_jsx_runtime.jsxs("div", {
|
|
743
|
-
children: [
|
|
744
|
-
react_jsx_runtime.jsx("input", {
|
|
745
|
-
style: S.form.input,
|
|
746
|
-
placeholder,
|
|
747
|
-
value: addForm[key],
|
|
748
|
-
"aria-invalid": errorCode ? true : undefined,
|
|
749
|
-
...ring,
|
|
750
|
-
onChange: (event) => setAddForm({ ...addForm, [key]: event.target.value })
|
|
751
|
-
}),
|
|
752
|
-
errorCode ? react_jsx_runtime.jsx("div", { style: S.form.error, children: fieldError(errorCode) }) : null
|
|
753
|
-
]
|
|
754
|
-
}, key);
|
|
755
|
-
const addBlock = tab === "mcp" ? react_jsx_runtime.jsxs("div", { style: S.form.wrap, children: [
|
|
756
|
-
!addForm.open
|
|
757
|
-
? react_jsx_runtime.jsx("button", {
|
|
758
|
-
type: "button",
|
|
759
|
-
style: S.form.addButton,
|
|
760
|
-
...ring,
|
|
761
|
-
...hover(HOVER_BG, "none"),
|
|
762
|
-
onClick: () => setAddForm({ ...addForm, open: true, showErrors: false, error: null }),
|
|
763
|
-
children: t("capability.mcp.add", "+ Add managed MCP server")
|
|
764
|
-
})
|
|
765
|
-
: react_jsx_runtime.jsxs("div", { style: S.form.box, children: [
|
|
766
|
-
react_jsx_runtime.jsx("div", { style: S.form.title, children: t("capability.mcp.addTitle", "Add managed MCP server") }),
|
|
767
|
-
addField("identity", t("capability.mcp.identity", "identity (e.g. my-docs)"), addForm.showErrors ? addErrors.identity : undefined),
|
|
768
|
-
addField("label", t("capability.mcp.label", "label (display name, optional)"), undefined),
|
|
769
|
-
addField("command", t("capability.mcp.command", "command (e.g. npx)"), addForm.showErrors ? addErrors.command : undefined),
|
|
770
|
-
addField("args", t("capability.mcp.args", "args, space-separated (optional)"), undefined),
|
|
771
|
-
react_jsx_runtime.jsxs("div", { style: S.form.actions, children: [
|
|
772
|
-
react_jsx_runtime.jsx("button", {
|
|
773
|
-
type: "button",
|
|
774
|
-
style: addForm.busy ? { ...S.form.primary, ...S.footer.disabled } : S.form.primary,
|
|
775
|
-
...ring,
|
|
776
|
-
disabled: addForm.busy,
|
|
777
|
-
onClick: submitAdd,
|
|
778
|
-
children: addForm.busy ? t("capability.mcp.adding", "Adding…") : t("capability.mcp.addConfirm", "Add")
|
|
779
|
-
}),
|
|
780
|
-
react_jsx_runtime.jsx("button", {
|
|
781
|
-
type: "button",
|
|
782
|
-
style: S.form.ghost,
|
|
783
|
-
...ring,
|
|
784
|
-
...hover(HOVER_BG, "none"),
|
|
785
|
-
onClick: () => setAddForm({ ...addForm, open: false, showErrors: false, error: null }),
|
|
786
|
-
children: t("capability.mcp.cancel", "Cancel")
|
|
787
|
-
}),
|
|
788
|
-
addForm.error ? react_jsx_runtime.jsx("span", { style: { ...S.tag.base, ...S.tag.warn }, children: String(addForm.error) }) : null
|
|
789
|
-
] })
|
|
790
|
-
] })
|
|
791
|
-
] }) : null;
|
|
722
|
+
const addBlock = null;
|
|
792
723
|
const skillRow = (skill, index) => {
|
|
793
724
|
const selected = skillKeys.has(model.skillEntryKeyOf(skill.identity ?? skill.name));
|
|
794
725
|
const selectedConflict = selectedKeys.has(model.skillEntryKeyOf(skill.identity));
|
|
@@ -820,45 +751,6 @@ window.__ModuleLoader__.load({
|
|
|
820
751
|
]
|
|
821
752
|
}, `${model.skillIdentityKeyOf(skill.identity) ?? `${skill.scopeKey}:${skill.name}`}:${index}`);
|
|
822
753
|
};
|
|
823
|
-
const mcpRow = (server) => {
|
|
824
|
-
const name = server.identity ?? server.serverName ?? "unknown";
|
|
825
|
-
const selected = draftHas("mcpServers", name);
|
|
826
|
-
const rest = selected ? tint(ACCENT, 8) : "transparent";
|
|
827
|
-
const mounted = server.state === "mounted";
|
|
828
|
-
return react_jsx_runtime.jsxs("label", {
|
|
829
|
-
style: { ...S.row.base, background: rest },
|
|
830
|
-
...ring,
|
|
831
|
-
...hover(selected ? tint(ACCENT, 12) : HOVER_BG, rest),
|
|
832
|
-
children: [
|
|
833
|
-
react_jsx_runtime.jsx("input", {
|
|
834
|
-
type: "checkbox",
|
|
835
|
-
style: S.row.checkbox,
|
|
836
|
-
checked: selected,
|
|
837
|
-
disabled: selectionDisabled,
|
|
838
|
-
"aria-label": name,
|
|
839
|
-
onChange: () => toggle("mcpServers", name)
|
|
840
|
-
}),
|
|
841
|
-
react_jsx_runtime.jsxs("span", { style: { ...S.row.nameLine, flex: 1 }, children: [
|
|
842
|
-
react_jsx_runtime.jsx("span", { style: S.row.name, title: name, children: name }),
|
|
843
|
-
react_jsx_runtime.jsx("span", {
|
|
844
|
-
style: { ...S.tag.base, ...(mounted ? S.tag.ok : S.tag.neutral) },
|
|
845
|
-
children: mounted ? t("capability.mcp.stateMounted", "mounted") : t("capability.mcp.stateRegistered", "registered")
|
|
846
|
-
})
|
|
847
|
-
] })
|
|
848
|
-
]
|
|
849
|
-
}, name);
|
|
850
|
-
};
|
|
851
|
-
const unmanagedRow = (server) => {
|
|
852
|
-
const name = server.serverName ?? "unknown";
|
|
853
|
-
return react_jsx_runtime.jsxs("div", {
|
|
854
|
-
style: { ...S.row.base, alignItems: "center", cursor: "default" },
|
|
855
|
-
title: t("capability.mcp.unmanagedHint", "Configured outside Weir; the selection cannot govern it."),
|
|
856
|
-
children: [
|
|
857
|
-
react_jsx_runtime.jsx("span", { style: { ...S.row.name, flex: 1 }, children: name }),
|
|
858
|
-
react_jsx_runtime.jsx("span", { style: { ...S.tag.base, ...S.tag.neutral }, children: t("capability.unmanaged", "unmanaged") })
|
|
859
|
-
]
|
|
860
|
-
}, name);
|
|
861
|
-
};
|
|
862
754
|
const legacyActions = [...ambiguous, ...unknown].map(item => react_jsx_runtime.jsxs("div", { style: S.list.card, children: [
|
|
863
755
|
react_jsx_runtime.jsx("div", { style: S.form.title, children: t("capability.unresolvedNameTitle", "Choose a source for legacy name {name}").replace("{name}", item.name) }),
|
|
864
756
|
react_jsx_runtime.jsxs("div", { style: S.form.actions, children: [
|
|
@@ -883,43 +775,18 @@ window.__ModuleLoader__.load({
|
|
|
883
775
|
}, group.key))), ...legacyActions]
|
|
884
776
|
})
|
|
885
777
|
: tab === "mcp"
|
|
886
|
-
?
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
}),
|
|
899
|
-
...partition.mcpManaged.map(mcpRow)
|
|
900
|
-
]
|
|
901
|
-
}, "managed")
|
|
902
|
-
: null,
|
|
903
|
-
partition.mcpUnmanaged.length > 0
|
|
904
|
-
? react_jsx_runtime.jsxs("div", {
|
|
905
|
-
style: S.list.card,
|
|
906
|
-
children: [
|
|
907
|
-
react_jsx_runtime.jsxs("div", {
|
|
908
|
-
style: S.list.cardHead,
|
|
909
|
-
children: [
|
|
910
|
-
react_jsx_runtime.jsx("span", { children: t("capability.mcp.groupUnmanaged", "Unmanaged") }),
|
|
911
|
-
react_jsx_runtime.jsx("span", { style: S.list.count, children: String(partition.mcpUnmanaged.length) })
|
|
912
|
-
]
|
|
913
|
-
}),
|
|
914
|
-
...partition.mcpUnmanaged.map(unmanagedRow)
|
|
915
|
-
]
|
|
916
|
-
}, "unmanaged")
|
|
917
|
-
: null,
|
|
918
|
-
partition.mcpManaged.length === 0 && partition.mcpUnmanaged.length === 0
|
|
919
|
-
? react_jsx_runtime.jsx(StatePanel, { icon: "blocks", title: t("capability.mcp.none", "No MCP servers configured.") })
|
|
920
|
-
: null
|
|
921
|
-
]
|
|
922
|
-
})
|
|
778
|
+
? (() => {
|
|
779
|
+
if (typeof props.McpManagerView !== "function") return react_jsx_runtime.jsx(StatePanel, { icon: "warn", color: WARN_TEXT, title: t("capability.mcp.unavailableView", "MCP management is unavailable on this client.") });
|
|
780
|
+
const content = react_jsx_runtime.jsx(props.McpManagerView, {
|
|
781
|
+
sessionId: props.sessionId, model, t, listing,
|
|
782
|
+
managed: partition.mcpManaged, unmanaged: partition.mcpUnmanaged,
|
|
783
|
+
draft, toggle, selectionDisabled, refreshAll,
|
|
784
|
+
mcpPreview: props.mcpPreview, mcpMutate: props.mcpMutate, mcpBatch: props.mcpBatch,
|
|
785
|
+
});
|
|
786
|
+
return typeof props.McpManagerBoundary === "function"
|
|
787
|
+
? react_jsx_runtime.jsx(props.McpManagerBoundary, { t, children: content })
|
|
788
|
+
: content;
|
|
789
|
+
})()
|
|
923
790
|
: react_jsx_runtime.jsx(LazyCapabilityPresets, {
|
|
924
791
|
sessionId: props.sessionId,
|
|
925
792
|
model,
|