@burdenoff/microfe-vibecontrols 2026.526.1 → 2026.526.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ai/PluginHarnessPicker.js +64 -65
- package/dist/components/ai/PluginHarnessPicker.js.map +1 -1
- package/dist/components/shared/PluginGate.js +70 -71
- package/dist/components/shared/PluginGate.js.map +1 -1
- package/dist/generated/wspace-operations.js +4 -4
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/hooks/usePluginAvailability.js +59 -83
- package/dist/hooks/usePluginAvailability.js.map +1 -1
- package/dist/pages/AgentDetailsPage.js +486 -508
- package/dist/pages/AgentDetailsPage.js.map +1 -1
- package/dist/pages/VibeDetailsPage.js +457 -457
- package/dist/pages/VibeDetailsPage.js.map +1 -1
- package/dist/pages/ai/AIPage.js +10 -10
- package/dist/pages/ai/AISessionDetailPage.js +4 -4
- package/dist/pages/ai/AgentManagerPage.js +11 -11
- package/dist/pages/ai/AgentManagerPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,119 +1,118 @@
|
|
|
1
1
|
import { useRemoveAgentPluginMutation as e } from "../../generated/wspace-operations.js";
|
|
2
2
|
import { Card as t } from "../shared/Card.js";
|
|
3
3
|
import { usePluginAvailability as n } from "../../hooks/usePluginAvailability.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { Fragment as h, jsx as g, jsxs as _ } from "react/jsx-runtime";
|
|
4
|
+
import { getPluginMeta as r } from "../../constants/pluginCatalog.js";
|
|
5
|
+
import { useCallback as i, useEffect as a, useRef as o, useState as s } from "react";
|
|
6
|
+
import { Check as c, CheckCircle2 as l, Download as u, Loader2 as d, Puzzle as f, Trash2 as p } from "lucide-react";
|
|
7
|
+
import { Fragment as m, jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
9
8
|
//#region src/components/ai/PluginHarnessPicker.tsx
|
|
10
|
-
function
|
|
11
|
-
let {
|
|
12
|
-
|
|
13
|
-
let e =
|
|
9
|
+
function _({ packages: _, agentId: v, title: y, description: b, onChange: x }) {
|
|
10
|
+
let { installed: S, installing: C, install: w, refetch: T, loading: E, error: D } = n(_, v), [O] = e(), [k, A] = s(null), [j, M] = s({}), [N, P] = s({}), F = o(/* @__PURE__ */ new Map());
|
|
11
|
+
a(() => {
|
|
12
|
+
let e = F.current;
|
|
14
13
|
return () => {
|
|
15
14
|
for (let t of e.values()) clearTimeout(t);
|
|
16
15
|
e.clear();
|
|
17
16
|
};
|
|
18
17
|
}, []);
|
|
19
|
-
let
|
|
20
|
-
if (await
|
|
21
|
-
|
|
18
|
+
let I = i(async (e) => {
|
|
19
|
+
if (await w(e)) {
|
|
20
|
+
P((t) => ({
|
|
22
21
|
...t,
|
|
23
22
|
[e]: !0
|
|
24
23
|
}));
|
|
25
|
-
let t =
|
|
24
|
+
let t = F.current.get(e);
|
|
26
25
|
t && clearTimeout(t);
|
|
27
26
|
let n = setTimeout(() => {
|
|
28
|
-
|
|
27
|
+
P((t) => {
|
|
29
28
|
if (!t[e]) return t;
|
|
30
29
|
let n = { ...t };
|
|
31
30
|
return delete n[e], n;
|
|
32
|
-
}),
|
|
31
|
+
}), F.current.delete(e);
|
|
33
32
|
}, 1500);
|
|
34
|
-
|
|
33
|
+
F.current.set(e, n), x?.();
|
|
35
34
|
}
|
|
36
|
-
}, [
|
|
37
|
-
if (
|
|
38
|
-
|
|
35
|
+
}, [w, x]), L = i(async (e) => {
|
|
36
|
+
if (v) {
|
|
37
|
+
A(e), M((t) => ({
|
|
39
38
|
...t,
|
|
40
39
|
[e]: ""
|
|
41
40
|
}));
|
|
42
41
|
try {
|
|
43
|
-
let { data: t } = await
|
|
44
|
-
agentId:
|
|
42
|
+
let { data: t } = await O({ variables: {
|
|
43
|
+
agentId: v,
|
|
45
44
|
packageName: e
|
|
46
45
|
} });
|
|
47
46
|
if (!t?.removeAgentPlugin.success) {
|
|
48
|
-
|
|
47
|
+
M((n) => ({
|
|
49
48
|
...n,
|
|
50
49
|
[e]: t?.removeAgentPlugin.error || "Remove failed"
|
|
51
50
|
}));
|
|
52
51
|
return;
|
|
53
52
|
}
|
|
54
|
-
await
|
|
53
|
+
await T(), x?.();
|
|
55
54
|
} catch (t) {
|
|
56
|
-
|
|
55
|
+
M((n) => ({
|
|
57
56
|
...n,
|
|
58
57
|
[e]: t instanceof Error ? t.message : String(t)
|
|
59
58
|
}));
|
|
60
59
|
} finally {
|
|
61
|
-
|
|
60
|
+
A(null);
|
|
62
61
|
}
|
|
63
62
|
}
|
|
64
63
|
}, [
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
v,
|
|
65
|
+
O,
|
|
66
|
+
T,
|
|
67
|
+
x
|
|
69
68
|
]);
|
|
70
|
-
return
|
|
69
|
+
return _.length === 0 ? null : /* @__PURE__ */ g("section", {
|
|
71
70
|
className: "flex flex-col gap-3",
|
|
72
71
|
"data-testid": "plugin-harness-picker",
|
|
73
72
|
children: [
|
|
74
|
-
(
|
|
73
|
+
(y || b) && /* @__PURE__ */ g("div", { children: [y && /* @__PURE__ */ h("h3", {
|
|
75
74
|
className: "text-base font-semibold text-text-primary",
|
|
76
|
-
children:
|
|
77
|
-
}),
|
|
75
|
+
children: y
|
|
76
|
+
}), b && /* @__PURE__ */ h("p", {
|
|
78
77
|
className: "text-sm text-text-secondary mt-1",
|
|
79
|
-
children:
|
|
78
|
+
children: b
|
|
80
79
|
})] }),
|
|
81
|
-
|
|
80
|
+
D && /* @__PURE__ */ h("div", {
|
|
82
81
|
role: "alert",
|
|
83
82
|
className: "rounded-md border border-status-error-border bg-status-error-bg px-3 py-2 text-xs text-status-error-text",
|
|
84
|
-
children:
|
|
83
|
+
children: D
|
|
85
84
|
}),
|
|
86
|
-
/* @__PURE__ */
|
|
85
|
+
/* @__PURE__ */ h("div", {
|
|
87
86
|
className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3",
|
|
88
|
-
children:
|
|
89
|
-
let n =
|
|
90
|
-
return /* @__PURE__ */
|
|
87
|
+
children: _.map((e) => {
|
|
88
|
+
let n = r(e), i = !!S[e], a = C === e, o = k === e, s = a || o || E, _ = !!N[e], y = j[e];
|
|
89
|
+
return /* @__PURE__ */ g(t, {
|
|
91
90
|
padding: "md",
|
|
92
91
|
className: "flex flex-col gap-3",
|
|
93
92
|
children: [
|
|
94
|
-
/* @__PURE__ */
|
|
93
|
+
/* @__PURE__ */ g("div", {
|
|
95
94
|
className: "flex items-start gap-3",
|
|
96
|
-
children: [/* @__PURE__ */
|
|
95
|
+
children: [/* @__PURE__ */ h("div", {
|
|
97
96
|
className: "flex-shrink-0 w-9 h-9 rounded-md bg-bg-sunken flex items-center justify-center",
|
|
98
|
-
children: /* @__PURE__ */
|
|
99
|
-
}), /* @__PURE__ */
|
|
97
|
+
children: /* @__PURE__ */ h(f, { className: "w-4 h-4 text-text-secondary" })
|
|
98
|
+
}), /* @__PURE__ */ g("div", {
|
|
100
99
|
className: "min-w-0 flex-1",
|
|
101
100
|
children: [
|
|
102
|
-
/* @__PURE__ */
|
|
101
|
+
/* @__PURE__ */ g("div", {
|
|
103
102
|
className: "flex items-start gap-2 flex-wrap",
|
|
104
|
-
children: [/* @__PURE__ */
|
|
103
|
+
children: [/* @__PURE__ */ h("h4", {
|
|
105
104
|
className: "font-medium text-text-primary break-words max-w-[100ch]",
|
|
106
105
|
children: n.displayName
|
|
107
|
-
}),
|
|
106
|
+
}), i && /* @__PURE__ */ g("span", {
|
|
108
107
|
className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs rounded bg-status-success-bg text-status-success-text shrink-0",
|
|
109
|
-
children: [/* @__PURE__ */
|
|
108
|
+
children: [/* @__PURE__ */ h(c, { className: "w-3 h-3" }), " installed"]
|
|
110
109
|
})]
|
|
111
110
|
}),
|
|
112
|
-
/* @__PURE__ */
|
|
111
|
+
/* @__PURE__ */ h("p", {
|
|
113
112
|
className: "text-sm text-text-secondary mt-0.5",
|
|
114
113
|
children: n.description
|
|
115
114
|
}),
|
|
116
|
-
/* @__PURE__ */
|
|
115
|
+
/* @__PURE__ */ h("p", {
|
|
117
116
|
className: "text-[10px] font-mono text-text-placeholder mt-1 break-all",
|
|
118
117
|
title: e,
|
|
119
118
|
children: e
|
|
@@ -121,41 +120,41 @@ function v({ packages: v, agentId: y, title: b, description: x, onChange: S }) {
|
|
|
121
120
|
]
|
|
122
121
|
})]
|
|
123
122
|
}),
|
|
124
|
-
/* @__PURE__ */
|
|
123
|
+
/* @__PURE__ */ h("div", {
|
|
125
124
|
className: "flex items-center gap-2",
|
|
126
|
-
children:
|
|
125
|
+
children: i && _ ? /* @__PURE__ */ g("button", {
|
|
127
126
|
type: "button",
|
|
128
127
|
disabled: !0,
|
|
129
128
|
className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md bg-status-success-bg text-status-success-text border border-status-success-border cursor-default",
|
|
130
129
|
"data-testid": `plugin-installed-${e}`,
|
|
131
130
|
"aria-live": "polite",
|
|
132
|
-
children: [/* @__PURE__ */
|
|
133
|
-
}) :
|
|
131
|
+
children: [/* @__PURE__ */ h(l, { className: "w-3.5 h-3.5" }), "Installed"]
|
|
132
|
+
}) : i ? /* @__PURE__ */ g("button", {
|
|
134
133
|
type: "button",
|
|
135
|
-
disabled: s || !
|
|
136
|
-
onClick: () =>
|
|
134
|
+
disabled: s || !v,
|
|
135
|
+
onClick: () => L(e),
|
|
137
136
|
className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md border border-border-default text-text-secondary hover:border-status-error-border hover:text-status-error-text transition disabled:opacity-60 disabled:cursor-not-allowed",
|
|
138
137
|
"data-testid": `plugin-remove-${e}`,
|
|
139
|
-
children: [o ? /* @__PURE__ */
|
|
140
|
-
}) : /* @__PURE__ */
|
|
138
|
+
children: [o ? /* @__PURE__ */ h(d, { className: "w-3.5 h-3.5 animate-spin" }) : /* @__PURE__ */ h(p, { className: "w-3.5 h-3.5" }), "Remove"]
|
|
139
|
+
}) : /* @__PURE__ */ h("button", {
|
|
141
140
|
type: "button",
|
|
142
|
-
disabled: s || !
|
|
143
|
-
onClick: () =>
|
|
141
|
+
disabled: s || !v,
|
|
142
|
+
onClick: () => I(e),
|
|
144
143
|
className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition disabled:opacity-60 disabled:cursor-not-allowed",
|
|
145
144
|
"data-testid": `plugin-install-${e}`,
|
|
146
145
|
"aria-busy": a,
|
|
147
|
-
children: a ? /* @__PURE__ */
|
|
146
|
+
children: a ? /* @__PURE__ */ g(m, { children: [/* @__PURE__ */ h(d, { className: "w-3.5 h-3.5 animate-spin" }), "Installing…"] }) : /* @__PURE__ */ g(m, { children: [/* @__PURE__ */ h(u, { className: "w-3.5 h-3.5" }), "Install"] })
|
|
148
147
|
})
|
|
149
148
|
}),
|
|
150
|
-
|
|
149
|
+
y && /* @__PURE__ */ h("p", {
|
|
151
150
|
className: "text-xs text-status-error-text",
|
|
152
|
-
children:
|
|
151
|
+
children: y
|
|
153
152
|
})
|
|
154
153
|
]
|
|
155
154
|
}, e);
|
|
156
155
|
})
|
|
157
156
|
}),
|
|
158
|
-
!
|
|
157
|
+
!v && /* @__PURE__ */ h("p", {
|
|
159
158
|
className: "text-xs text-text-muted",
|
|
160
159
|
children: "Select an agent to manage plugins."
|
|
161
160
|
})
|
|
@@ -163,6 +162,6 @@ function v({ packages: v, agentId: y, title: b, description: x, onChange: S }) {
|
|
|
163
162
|
});
|
|
164
163
|
}
|
|
165
164
|
//#endregion
|
|
166
|
-
export {
|
|
165
|
+
export { _ as PluginHarnessPicker };
|
|
167
166
|
|
|
168
167
|
//# sourceMappingURL=PluginHarnessPicker.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PluginHarnessPicker.js","names":[],"sources":["../../../src/components/ai/PluginHarnessPicker.tsx"],"sourcesContent":["/**\n * <PluginHarnessPicker />\n *\n * Grid of installable VibeControls agent plugins with per-card\n * Install / Installed / Remove state. Driven by the existing\n * `usePluginAvailability` hook (same code path as `<PluginGate>`), so\n * the Plugins tab on the Agent details page and this picker stay in\n * sync automatically.\n *\n * The default use-case is the AI workbench: once the base\n * `@vibecontrols/vibe-plugin-ai` plugin is installed, this picker lists\n * every AI provider harness (claude, codex, copilot, gemini, …) so the\n * user can add / remove harnesses without leaving the AI page.\n *\n * The component is feature-neutral — pass any `packages` array (e.g.\n * session providers, tunnel providers) to reuse the same UI.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { Check, CheckCircle2, Download, Loader2, Puzzle, Trash2 } from 'lucide-react';\nimport { useRemoveAgentPluginMutation } from '@/generated/wspace-operations';\nimport { usePluginAvailability } from '@/hooks/usePluginAvailability';\nimport { useActiveAgentProfile } from '@/hooks/useActiveAgentProfile';\nimport { getPluginMeta } from '@/constants/pluginCatalog';\nimport { Card } from '../shared/Card';\n\nexport interface PluginHarnessPickerProps {\n /** Package names to offer. Metadata is resolved via `getPluginMeta`. */\n packages: readonly string[];\n /** Active agent id. When null, the picker renders in a disabled state. */\n agentId: string | null;\n /** Optional section heading (rendered above the grid). */\n title?: string;\n /** Optional section description. */\n description?: string;\n /** Fired after a successful install or remove so parents can refetch. */\n onChange?: () => void;\n}\n\nexport function PluginHarnessPicker({\n packages,\n agentId,\n title,\n description,\n onChange,\n}: PluginHarnessPickerProps) {\n const { active: activeProfile } = useActiveAgentProfile(agentId);\n const { installed, installing, install, refetch, loading, error } = usePluginAvailability(\n packages,\n agentId,\n activeProfile\n );\n const [removePlugin] = useRemoveAgentPluginMutation();\n const [removingPkg, setRemovingPkg] = useState<string | null>(null);\n const [removeError, setRemoveError] = useState<Record<string, string>>({});\n // Per-package \"just installed\" pulse — flips on for ~1.5s after a\n // successful install so the button shows a clear success affordance\n // (checkmark + \"Installed\") before the card transitions to its\n // installed/Remove state. Without this the button silently flips from\n // spinner to Remove and users miss the success signal.\n const [justInstalled, setJustInstalled] = useState<Record<string, boolean>>({});\n const justInstalledTimers = useRef<Map<string, ReturnType<typeof setTimeout>>>(new Map());\n\n useEffect(() => {\n const timers = justInstalledTimers.current;\n return () => {\n for (const t of timers.values()) clearTimeout(t);\n timers.clear();\n };\n }, []);\n\n const handleInstall = useCallback(\n async (pkg: string) => {\n const ok = await install(pkg);\n if (ok) {\n setJustInstalled((prev) => ({ ...prev, [pkg]: true }));\n const existing = justInstalledTimers.current.get(pkg);\n if (existing) clearTimeout(existing);\n const timer = setTimeout(() => {\n setJustInstalled((prev) => {\n if (!prev[pkg]) return prev;\n const next = { ...prev };\n delete next[pkg];\n return next;\n });\n justInstalledTimers.current.delete(pkg);\n }, 1500);\n justInstalledTimers.current.set(pkg, timer);\n onChange?.();\n }\n },\n [install, onChange]\n );\n\n const handleRemove = useCallback(\n async (pkg: string) => {\n if (!agentId) return;\n setRemovingPkg(pkg);\n setRemoveError((e) => ({ ...e, [pkg]: '' }));\n try {\n const { data } = await removePlugin({\n variables: { agentId, packageName: pkg },\n });\n if (!data?.removeAgentPlugin.success) {\n setRemoveError((e) => ({\n ...e,\n [pkg]: data?.removeAgentPlugin.error || 'Remove failed',\n }));\n return;\n }\n await refetch();\n onChange?.();\n } catch (e) {\n setRemoveError((err) => ({\n ...err,\n [pkg]: e instanceof Error ? e.message : String(e),\n }));\n } finally {\n setRemovingPkg(null);\n }\n },\n [agentId, removePlugin, refetch, onChange]\n );\n\n if (packages.length === 0) return null;\n\n return (\n <section className=\"flex flex-col gap-3\" data-testid=\"plugin-harness-picker\">\n {(title || description) && (\n <div>\n {title && <h3 className=\"text-base font-semibold text-text-primary\">{title}</h3>}\n {description && <p className=\"text-sm text-text-secondary mt-1\">{description}</p>}\n </div>\n )}\n {error && (\n <div\n role=\"alert\"\n className=\"rounded-md border border-status-error-border bg-status-error-bg px-3 py-2 text-xs text-status-error-text\"\n >\n {error}\n </div>\n )}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3\">\n {packages.map((pkg) => {\n const meta = getPluginMeta(pkg);\n const isInstalled = !!installed[pkg];\n const isInstalling = installing === pkg;\n const isRemoving = removingPkg === pkg;\n const isBusy = isInstalling || isRemoving || loading;\n const showJustInstalled = !!justInstalled[pkg];\n const err = removeError[pkg];\n return (\n <Card key={pkg} padding=\"md\" className=\"flex flex-col gap-3\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex-shrink-0 w-9 h-9 rounded-md bg-bg-sunken flex items-center justify-center\">\n <Puzzle className=\"w-4 h-4 text-text-secondary\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-start gap-2 flex-wrap\">\n {/*\n Plugin display name must wrap freely up to a\n reasonable hard cap so the column never blows up\n the modal layout. break-words allows wrapping on\n arbitrary characters when a name has no spaces;\n max-w bound corresponds to ~100 char-cells (a\n generous upper bound on real-world display names).\n */}\n <h4 className=\"font-medium text-text-primary break-words max-w-[100ch]\">\n {meta.displayName}\n </h4>\n {isInstalled && (\n <span className=\"inline-flex items-center gap-1 px-2 py-0.5 text-xs rounded bg-status-success-bg text-status-success-text shrink-0\">\n <Check className=\"w-3 h-3\" /> installed\n </span>\n )}\n </div>\n <p className=\"text-sm text-text-secondary mt-0.5\">{meta.description}</p>\n <p\n className=\"text-[10px] font-mono text-text-placeholder mt-1 break-all\"\n title={pkg}\n >\n {pkg}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n {isInstalled && showJustInstalled ? (\n <button\n type=\"button\"\n disabled\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md bg-status-success-bg text-status-success-text border border-status-success-border cursor-default\"\n data-testid={`plugin-installed-${pkg}`}\n aria-live=\"polite\"\n >\n <CheckCircle2 className=\"w-3.5 h-3.5\" />\n Installed\n </button>\n ) : isInstalled ? (\n <button\n type=\"button\"\n disabled={isBusy || !agentId}\n onClick={() => handleRemove(pkg)}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md border border-border-default text-text-secondary hover:border-status-error-border hover:text-status-error-text transition disabled:opacity-60 disabled:cursor-not-allowed\"\n data-testid={`plugin-remove-${pkg}`}\n >\n {isRemoving ? (\n <Loader2 className=\"w-3.5 h-3.5 animate-spin\" />\n ) : (\n <Trash2 className=\"w-3.5 h-3.5\" />\n )}\n Remove\n </button>\n ) : (\n <button\n type=\"button\"\n disabled={isBusy || !agentId}\n onClick={() => handleInstall(pkg)}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition disabled:opacity-60 disabled:cursor-not-allowed\"\n data-testid={`plugin-install-${pkg}`}\n aria-busy={isInstalling}\n >\n {isInstalling ? (\n <>\n <Loader2 className=\"w-3.5 h-3.5 animate-spin\" />\n Installing…\n </>\n ) : (\n <>\n <Download className=\"w-3.5 h-3.5\" />\n Install\n </>\n )}\n </button>\n )}\n </div>\n {err && <p className=\"text-xs text-status-error-text\">{err}</p>}\n </Card>\n );\n })}\n </div>\n {!agentId && <p className=\"text-xs text-text-muted\">Select an agent to manage plugins.</p>}\n </section>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PluginHarnessPicker.js","names":[],"sources":["../../../src/components/ai/PluginHarnessPicker.tsx"],"sourcesContent":["/**\n * <PluginHarnessPicker />\n *\n * Grid of installable VibeControls agent plugins with per-card\n * Install / Installed / Remove state. Driven by the existing\n * `usePluginAvailability` hook (same code path as `<PluginGate>`), so\n * the Plugins tab on the Agent details page and this picker stay in\n * sync automatically.\n *\n * The default use-case is the AI workbench: once the base\n * `@vibecontrols/vibe-plugin-ai` plugin is installed, this picker lists\n * every AI provider harness (claude, codex, copilot, gemini, …) so the\n * user can add / remove harnesses without leaving the AI page.\n *\n * The component is feature-neutral — pass any `packages` array (e.g.\n * session providers, tunnel providers) to reuse the same UI.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { Check, CheckCircle2, Download, Loader2, Puzzle, Trash2 } from 'lucide-react';\nimport { useRemoveAgentPluginMutation } from '@/generated/wspace-operations';\nimport { usePluginAvailability } from '@/hooks/usePluginAvailability';\nimport { getPluginMeta } from '@/constants/pluginCatalog';\nimport { Card } from '../shared/Card';\n\nexport interface PluginHarnessPickerProps {\n /** Package names to offer. Metadata is resolved via `getPluginMeta`. */\n packages: readonly string[];\n /** Active agent id. When null, the picker renders in a disabled state. */\n agentId: string | null;\n /** Optional section heading (rendered above the grid). */\n title?: string;\n /** Optional section description. */\n description?: string;\n /** Fired after a successful install or remove so parents can refetch. */\n onChange?: () => void;\n}\n\nexport function PluginHarnessPicker({\n packages,\n agentId,\n title,\n description,\n onChange,\n}: PluginHarnessPickerProps) {\n // Plugins are agent-wide (BOFF-2612); no profile threading needed.\n const { installed, installing, install, refetch, loading, error } = usePluginAvailability(\n packages,\n agentId\n );\n const [removePlugin] = useRemoveAgentPluginMutation();\n const [removingPkg, setRemovingPkg] = useState<string | null>(null);\n const [removeError, setRemoveError] = useState<Record<string, string>>({});\n // Per-package \"just installed\" pulse — flips on for ~1.5s after a\n // successful install so the button shows a clear success affordance\n // (checkmark + \"Installed\") before the card transitions to its\n // installed/Remove state. Without this the button silently flips from\n // spinner to Remove and users miss the success signal.\n const [justInstalled, setJustInstalled] = useState<Record<string, boolean>>({});\n const justInstalledTimers = useRef<Map<string, ReturnType<typeof setTimeout>>>(new Map());\n\n useEffect(() => {\n const timers = justInstalledTimers.current;\n return () => {\n for (const t of timers.values()) clearTimeout(t);\n timers.clear();\n };\n }, []);\n\n const handleInstall = useCallback(\n async (pkg: string) => {\n const ok = await install(pkg);\n if (ok) {\n setJustInstalled((prev) => ({ ...prev, [pkg]: true }));\n const existing = justInstalledTimers.current.get(pkg);\n if (existing) clearTimeout(existing);\n const timer = setTimeout(() => {\n setJustInstalled((prev) => {\n if (!prev[pkg]) return prev;\n const next = { ...prev };\n delete next[pkg];\n return next;\n });\n justInstalledTimers.current.delete(pkg);\n }, 1500);\n justInstalledTimers.current.set(pkg, timer);\n onChange?.();\n }\n },\n [install, onChange]\n );\n\n const handleRemove = useCallback(\n async (pkg: string) => {\n if (!agentId) return;\n setRemovingPkg(pkg);\n setRemoveError((e) => ({ ...e, [pkg]: '' }));\n try {\n const { data } = await removePlugin({\n variables: { agentId, packageName: pkg },\n });\n if (!data?.removeAgentPlugin.success) {\n setRemoveError((e) => ({\n ...e,\n [pkg]: data?.removeAgentPlugin.error || 'Remove failed',\n }));\n return;\n }\n await refetch();\n onChange?.();\n } catch (e) {\n setRemoveError((err) => ({\n ...err,\n [pkg]: e instanceof Error ? e.message : String(e),\n }));\n } finally {\n setRemovingPkg(null);\n }\n },\n [agentId, removePlugin, refetch, onChange]\n );\n\n if (packages.length === 0) return null;\n\n return (\n <section className=\"flex flex-col gap-3\" data-testid=\"plugin-harness-picker\">\n {(title || description) && (\n <div>\n {title && <h3 className=\"text-base font-semibold text-text-primary\">{title}</h3>}\n {description && <p className=\"text-sm text-text-secondary mt-1\">{description}</p>}\n </div>\n )}\n {error && (\n <div\n role=\"alert\"\n className=\"rounded-md border border-status-error-border bg-status-error-bg px-3 py-2 text-xs text-status-error-text\"\n >\n {error}\n </div>\n )}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3\">\n {packages.map((pkg) => {\n const meta = getPluginMeta(pkg);\n const isInstalled = !!installed[pkg];\n const isInstalling = installing === pkg;\n const isRemoving = removingPkg === pkg;\n const isBusy = isInstalling || isRemoving || loading;\n const showJustInstalled = !!justInstalled[pkg];\n const err = removeError[pkg];\n return (\n <Card key={pkg} padding=\"md\" className=\"flex flex-col gap-3\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex-shrink-0 w-9 h-9 rounded-md bg-bg-sunken flex items-center justify-center\">\n <Puzzle className=\"w-4 h-4 text-text-secondary\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-start gap-2 flex-wrap\">\n {/*\n Plugin display name must wrap freely up to a\n reasonable hard cap so the column never blows up\n the modal layout. break-words allows wrapping on\n arbitrary characters when a name has no spaces;\n max-w bound corresponds to ~100 char-cells (a\n generous upper bound on real-world display names).\n */}\n <h4 className=\"font-medium text-text-primary break-words max-w-[100ch]\">\n {meta.displayName}\n </h4>\n {isInstalled && (\n <span className=\"inline-flex items-center gap-1 px-2 py-0.5 text-xs rounded bg-status-success-bg text-status-success-text shrink-0\">\n <Check className=\"w-3 h-3\" /> installed\n </span>\n )}\n </div>\n <p className=\"text-sm text-text-secondary mt-0.5\">{meta.description}</p>\n <p\n className=\"text-[10px] font-mono text-text-placeholder mt-1 break-all\"\n title={pkg}\n >\n {pkg}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n {isInstalled && showJustInstalled ? (\n <button\n type=\"button\"\n disabled\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md bg-status-success-bg text-status-success-text border border-status-success-border cursor-default\"\n data-testid={`plugin-installed-${pkg}`}\n aria-live=\"polite\"\n >\n <CheckCircle2 className=\"w-3.5 h-3.5\" />\n Installed\n </button>\n ) : isInstalled ? (\n <button\n type=\"button\"\n disabled={isBusy || !agentId}\n onClick={() => handleRemove(pkg)}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md border border-border-default text-text-secondary hover:border-status-error-border hover:text-status-error-text transition disabled:opacity-60 disabled:cursor-not-allowed\"\n data-testid={`plugin-remove-${pkg}`}\n >\n {isRemoving ? (\n <Loader2 className=\"w-3.5 h-3.5 animate-spin\" />\n ) : (\n <Trash2 className=\"w-3.5 h-3.5\" />\n )}\n Remove\n </button>\n ) : (\n <button\n type=\"button\"\n disabled={isBusy || !agentId}\n onClick={() => handleInstall(pkg)}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition disabled:opacity-60 disabled:cursor-not-allowed\"\n data-testid={`plugin-install-${pkg}`}\n aria-busy={isInstalling}\n >\n {isInstalling ? (\n <>\n <Loader2 className=\"w-3.5 h-3.5 animate-spin\" />\n Installing…\n </>\n ) : (\n <>\n <Download className=\"w-3.5 h-3.5\" />\n Install\n </>\n )}\n </button>\n )}\n </div>\n {err && <p className=\"text-xs text-status-error-text\">{err}</p>}\n </Card>\n );\n })}\n </div>\n {!agentId && <p className=\"text-xs text-text-muted\">Select an agent to manage plugins.</p>}\n </section>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { useVibeNavigate as e } from "../../providers/VibeControlsProvider.js";
|
|
2
2
|
import { QuotaExhaustedDialog as t } from "./QuotaExhaustedDialog.js";
|
|
3
3
|
import { usePluginAvailability as n } from "../../hooks/usePluginAvailability.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { Fragment as m, jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
4
|
+
import { getPluginMeta as r } from "../../constants/pluginCatalog.js";
|
|
5
|
+
import { useCallback as i, useMemo as a } from "react";
|
|
6
|
+
import { AlertCircle as o, Clock as s, Download as c, Loader2 as l, Puzzle as u, RefreshCw as d, Server as f } from "lucide-react";
|
|
7
|
+
import { Fragment as p, jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
9
8
|
//#region src/components/shared/PluginGate.tsx
|
|
10
|
-
function
|
|
9
|
+
function g(e) {
|
|
11
10
|
return e.requirements && e.requirements.length > 0 ? e.requirements.map((e) => ({
|
|
12
11
|
packages: e.packages,
|
|
13
12
|
mode: e.mode ?? "all",
|
|
@@ -22,50 +21,50 @@ function _(e) {
|
|
|
22
21
|
description: e.description
|
|
23
22
|
}] : [];
|
|
24
23
|
}
|
|
25
|
-
function
|
|
26
|
-
let { agentId:
|
|
24
|
+
function _(_) {
|
|
25
|
+
let { agentId: v, compact: y = !1, onInstalled: b, children: x } = _, S = a(() => g(_), [_]), { loading: C, initialLoading: w, installed: T, installing: E, install: D, error: O, errorIsAgentState: k, quotaExhausted: A, clearQuotaExhausted: j, refetch: M } = n(a(() => {
|
|
27
26
|
let e = /* @__PURE__ */ new Set();
|
|
28
|
-
for (let t of
|
|
27
|
+
for (let t of S) for (let n of t.packages) e.add(n);
|
|
29
28
|
return [...e];
|
|
30
|
-
}, [
|
|
31
|
-
await
|
|
32
|
-
}, [
|
|
33
|
-
return
|
|
34
|
-
className: `rounded-lg border border-border-default bg-bg-surface ${
|
|
29
|
+
}, [S]), v), N = e(), P = a(() => S.map((e) => e.mode === "any" ? e.packages.some((e) => !!T[e]) : e.packages.every((e) => !!T[e])), [S, T]), F = S.length > 0 && P.every(Boolean), I = i(async (e) => {
|
|
30
|
+
await D(e) && b?.();
|
|
31
|
+
}, [D, b]);
|
|
32
|
+
return v ? F ? /* @__PURE__ */ m(p, { children: x }) : w ? /* @__PURE__ */ m("div", {
|
|
33
|
+
className: `rounded-lg border border-border-default bg-bg-surface ${y ? "p-4" : "p-6"}`,
|
|
35
34
|
"data-testid": "plugin-gate-initial-loading",
|
|
36
|
-
children: /* @__PURE__ */
|
|
35
|
+
children: /* @__PURE__ */ h("div", {
|
|
37
36
|
className: "flex items-center gap-2 text-sm text-text-secondary",
|
|
38
|
-
children: [/* @__PURE__ */
|
|
37
|
+
children: [/* @__PURE__ */ m(l, { className: "h-4 w-4 animate-spin" }), "Checking plugin availability on the agent…"]
|
|
39
38
|
})
|
|
40
|
-
}) :
|
|
41
|
-
className: `rounded-lg border border-border-default bg-bg-surface ${
|
|
42
|
-
children: /* @__PURE__ */
|
|
39
|
+
}) : k ? /* @__PURE__ */ m("div", {
|
|
40
|
+
className: `rounded-lg border border-border-default bg-bg-surface ${y ? "p-4" : "p-6"}`,
|
|
41
|
+
children: /* @__PURE__ */ h("div", {
|
|
43
42
|
className: "flex items-start gap-3",
|
|
44
|
-
children: [/* @__PURE__ */
|
|
43
|
+
children: [/* @__PURE__ */ m(s, { className: "h-5 w-5 text-status-warning-text shrink-0 mt-0.5" }), /* @__PURE__ */ h("div", {
|
|
45
44
|
className: "flex-1 min-w-0 space-y-2",
|
|
46
45
|
children: [
|
|
47
|
-
/* @__PURE__ */
|
|
46
|
+
/* @__PURE__ */ m("p", {
|
|
48
47
|
className: "text-sm font-semibold text-text-primary",
|
|
49
48
|
children: "Agent is not ready yet"
|
|
50
49
|
}),
|
|
51
|
-
/* @__PURE__ */
|
|
50
|
+
/* @__PURE__ */ m("p", {
|
|
52
51
|
className: "text-sm text-text-secondary",
|
|
53
52
|
children: "Your agent is still starting up or waiting for gateway credentials. Plugin install is only available once the agent reaches the ready state."
|
|
54
53
|
}),
|
|
55
|
-
/* @__PURE__ */
|
|
54
|
+
/* @__PURE__ */ m("p", {
|
|
56
55
|
className: "text-xs font-mono text-text-placeholder break-all",
|
|
57
|
-
children:
|
|
56
|
+
children: O
|
|
58
57
|
}),
|
|
59
|
-
/* @__PURE__ */
|
|
58
|
+
/* @__PURE__ */ h("div", {
|
|
60
59
|
className: "flex flex-wrap items-center gap-2 pt-1",
|
|
61
|
-
children: [/* @__PURE__ */
|
|
62
|
-
onClick: () => void
|
|
63
|
-
disabled:
|
|
60
|
+
children: [/* @__PURE__ */ h("button", {
|
|
61
|
+
onClick: () => void M(),
|
|
62
|
+
disabled: C,
|
|
64
63
|
className: "inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50",
|
|
65
64
|
"data-testid": "plugin-gate-retry",
|
|
66
|
-
children: [/* @__PURE__ */
|
|
67
|
-
}), /* @__PURE__ */
|
|
68
|
-
onClick: () =>
|
|
65
|
+
children: [/* @__PURE__ */ m(d, { className: `h-3.5 w-3.5 ${C ? "animate-spin" : ""}` }), "Retry"]
|
|
66
|
+
}), /* @__PURE__ */ m("button", {
|
|
67
|
+
onClick: () => N("/agents"),
|
|
69
68
|
className: "rounded-md border border-border-default px-3 py-1.5 text-xs font-medium text-text-secondary hover:text-text-primary hover:border-border-strong transition-colors",
|
|
70
69
|
children: "Manage agents"
|
|
71
70
|
})]
|
|
@@ -73,102 +72,102 @@ function v(v) {
|
|
|
73
72
|
]
|
|
74
73
|
})]
|
|
75
74
|
})
|
|
76
|
-
}) : /* @__PURE__ */
|
|
75
|
+
}) : /* @__PURE__ */ h("div", {
|
|
77
76
|
className: "space-y-3",
|
|
78
|
-
children: [
|
|
79
|
-
if (
|
|
80
|
-
let n = (e.mode, e.packages.filter((e) => !
|
|
81
|
-
return /* @__PURE__ */
|
|
82
|
-
className: `rounded-lg border border-border-default bg-bg-surface ${
|
|
83
|
-
children: /* @__PURE__ */
|
|
77
|
+
children: [S.map((e, t) => {
|
|
78
|
+
if (P[t]) return null;
|
|
79
|
+
let n = (e.mode, e.packages.filter((e) => !T[e])), i = e.mode === "any" ? "Install a plugin to continue" : e.packages.length === 1 ? "Plugin required" : "Plugins required", a = e.mode === "any" ? "Pick any one of the plugins below. Once installed, this surface will render automatically." : e.packages.length === 1 ? "This surface needs the following agent plugin installed." : "This surface needs the following agent plugins installed.";
|
|
80
|
+
return /* @__PURE__ */ m("div", {
|
|
81
|
+
className: `rounded-lg border border-border-default bg-bg-surface ${y ? "p-4" : "p-6"}`,
|
|
82
|
+
children: /* @__PURE__ */ h("div", {
|
|
84
83
|
className: "flex items-start gap-3",
|
|
85
|
-
children: [/* @__PURE__ */
|
|
84
|
+
children: [/* @__PURE__ */ m(u, { className: "h-5 w-5 text-action-primary-bg shrink-0 mt-0.5" }), /* @__PURE__ */ h("div", {
|
|
86
85
|
className: "flex-1 min-w-0",
|
|
87
86
|
children: [
|
|
88
|
-
e.eyebrow && /* @__PURE__ */
|
|
87
|
+
e.eyebrow && /* @__PURE__ */ m("p", {
|
|
89
88
|
className: "text-xs uppercase tracking-wide text-text-secondary mb-1",
|
|
90
89
|
children: e.eyebrow
|
|
91
90
|
}),
|
|
92
|
-
/* @__PURE__ */
|
|
91
|
+
/* @__PURE__ */ m("p", {
|
|
93
92
|
className: "text-sm font-semibold text-text-primary",
|
|
94
|
-
children: e.title ??
|
|
93
|
+
children: e.title ?? i
|
|
95
94
|
}),
|
|
96
|
-
/* @__PURE__ */
|
|
95
|
+
/* @__PURE__ */ m("p", {
|
|
97
96
|
className: "mt-1 text-sm text-text-secondary",
|
|
98
97
|
children: e.description ?? a
|
|
99
98
|
}),
|
|
100
|
-
|
|
99
|
+
O && /* @__PURE__ */ h("div", {
|
|
101
100
|
role: "alert",
|
|
102
101
|
className: "mt-3 flex items-start gap-2 rounded-md border border-status-error-border bg-status-error-bg px-3 py-2 text-xs text-status-error-text",
|
|
103
|
-
children: [/* @__PURE__ */
|
|
102
|
+
children: [/* @__PURE__ */ m(o, { className: "h-3.5 w-3.5 mt-0.5 shrink-0" }), /* @__PURE__ */ m("span", { children: O })]
|
|
104
103
|
}),
|
|
105
|
-
/* @__PURE__ */
|
|
104
|
+
/* @__PURE__ */ m("ul", {
|
|
106
105
|
className: "mt-4 space-y-2",
|
|
107
106
|
children: n.map((e) => {
|
|
108
|
-
let t =
|
|
109
|
-
return /* @__PURE__ */
|
|
107
|
+
let t = r(e), n = E === e, i = E !== null;
|
|
108
|
+
return /* @__PURE__ */ h("li", {
|
|
110
109
|
className: "flex items-center justify-between gap-3 rounded-md border border-border-default bg-bg-sunken px-3 py-2",
|
|
111
|
-
children: [/* @__PURE__ */
|
|
110
|
+
children: [/* @__PURE__ */ h("div", {
|
|
112
111
|
className: "min-w-0",
|
|
113
112
|
children: [
|
|
114
|
-
/* @__PURE__ */
|
|
113
|
+
/* @__PURE__ */ m("p", {
|
|
115
114
|
className: "text-sm font-medium text-text-primary truncate",
|
|
116
115
|
children: t.displayName
|
|
117
116
|
}),
|
|
118
|
-
/* @__PURE__ */
|
|
117
|
+
/* @__PURE__ */ m("p", {
|
|
119
118
|
className: "text-xs text-text-secondary truncate",
|
|
120
119
|
children: t.description
|
|
121
120
|
}),
|
|
122
|
-
/* @__PURE__ */
|
|
121
|
+
/* @__PURE__ */ m("p", {
|
|
123
122
|
className: "text-[10px] font-mono text-text-placeholder truncate",
|
|
124
123
|
title: e,
|
|
125
124
|
children: e
|
|
126
125
|
})
|
|
127
126
|
]
|
|
128
|
-
}), /* @__PURE__ */
|
|
129
|
-
onClick: () => void
|
|
130
|
-
disabled:
|
|
127
|
+
}), /* @__PURE__ */ m("button", {
|
|
128
|
+
onClick: () => void I(e),
|
|
129
|
+
disabled: i || C,
|
|
131
130
|
className: "inline-flex items-center gap-1.5 whitespace-nowrap rounded-md bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
|
|
132
131
|
"data-testid": `plugin-install-${e}`,
|
|
133
|
-
children: n ? /* @__PURE__ */
|
|
132
|
+
children: n ? /* @__PURE__ */ h(p, { children: [/* @__PURE__ */ m(l, { className: "h-3.5 w-3.5 animate-spin" }), "Installing…"] }) : /* @__PURE__ */ h(p, { children: [/* @__PURE__ */ m(c, { className: "h-3.5 w-3.5" }), "Install"] })
|
|
134
133
|
})]
|
|
135
134
|
}, e);
|
|
136
135
|
})
|
|
137
136
|
}),
|
|
138
|
-
|
|
137
|
+
C && n.length === 0 && /* @__PURE__ */ h("div", {
|
|
139
138
|
className: "mt-4 flex items-center gap-2 text-xs text-text-secondary",
|
|
140
|
-
children: [/* @__PURE__ */
|
|
139
|
+
children: [/* @__PURE__ */ m(l, { className: "h-3.5 w-3.5 animate-spin" }), "Checking plugins…"]
|
|
141
140
|
})
|
|
142
141
|
]
|
|
143
142
|
})]
|
|
144
143
|
})
|
|
145
144
|
}, t);
|
|
146
|
-
}), /* @__PURE__ */
|
|
147
|
-
isOpen:
|
|
148
|
-
onClose:
|
|
145
|
+
}), /* @__PURE__ */ m(t, {
|
|
146
|
+
isOpen: A,
|
|
147
|
+
onClose: j,
|
|
149
148
|
onConfirm: () => {
|
|
150
|
-
|
|
149
|
+
j(), N("/billing");
|
|
151
150
|
},
|
|
152
151
|
title: "Plugin quota reached",
|
|
153
152
|
message: "You've used all plugin install quota on your current plan. Upgrade to install more plugins."
|
|
154
153
|
})]
|
|
155
|
-
}) : /* @__PURE__ */
|
|
156
|
-
className: `rounded-lg border border-border-default bg-bg-surface ${
|
|
157
|
-
children: /* @__PURE__ */
|
|
154
|
+
}) : /* @__PURE__ */ m("div", {
|
|
155
|
+
className: `rounded-lg border border-border-default bg-bg-surface ${y ? "p-4" : "p-6"}`,
|
|
156
|
+
children: /* @__PURE__ */ h("div", {
|
|
158
157
|
className: "flex items-start gap-3",
|
|
159
|
-
children: [/* @__PURE__ */
|
|
158
|
+
children: [/* @__PURE__ */ m(f, { className: "h-5 w-5 text-text-secondary shrink-0 mt-0.5" }), /* @__PURE__ */ h("div", {
|
|
160
159
|
className: "flex-1 space-y-2",
|
|
161
160
|
children: [
|
|
162
|
-
/* @__PURE__ */
|
|
161
|
+
/* @__PURE__ */ m("p", {
|
|
163
162
|
className: "text-sm font-semibold text-text-primary",
|
|
164
163
|
children: "No active agent"
|
|
165
164
|
}),
|
|
166
|
-
/* @__PURE__ */
|
|
165
|
+
/* @__PURE__ */ m("p", {
|
|
167
166
|
className: "text-sm text-text-secondary",
|
|
168
167
|
children: "This surface needs an active vibecontrols agent with a tunnel URL and API key."
|
|
169
168
|
}),
|
|
170
|
-
/* @__PURE__ */
|
|
171
|
-
onClick: () =>
|
|
169
|
+
/* @__PURE__ */ m("button", {
|
|
170
|
+
onClick: () => N("/agents"),
|
|
172
171
|
className: "px-3 py-1.5 text-xs font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors",
|
|
173
172
|
children: "Go to Agents"
|
|
174
173
|
})
|
|
@@ -178,6 +177,6 @@ function v(v) {
|
|
|
178
177
|
});
|
|
179
178
|
}
|
|
180
179
|
//#endregion
|
|
181
|
-
export {
|
|
180
|
+
export { _ as PluginGate };
|
|
182
181
|
|
|
183
182
|
//# sourceMappingURL=PluginGate.js.map
|