@burdenoff/microfe-vibecontrols 2026.625.3 → 2026.626.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/agents/AgentForm.js +129 -127
- package/dist/components/agents/AgentForm.js.map +1 -1
- package/dist/components/shared/InstallTabs.js +19 -7
- package/dist/components/shared/InstallTabs.js.map +1 -1
- package/dist/pages/ActionsPage.js +209 -204
- package/dist/pages/ActionsPage.js.map +1 -1
- package/dist/pages/AgentDetailsPage.js +678 -648
- package/dist/pages/AgentDetailsPage.js.map +1 -1
- package/dist/pages/AgentGraphPage.js +112 -108
- package/dist/pages/AgentGraphPage.js.map +1 -1
- package/dist/pages/AgentsPage.js +159 -155
- package/dist/pages/AgentsPage.js.map +1 -1
- package/dist/pages/AnalyticsPage.js +140 -136
- package/dist/pages/AnalyticsPage.js.map +1 -1
- package/dist/pages/CatalogDetailPage.js +249 -244
- package/dist/pages/CatalogDetailPage.js.map +1 -1
- package/dist/pages/CatalogPage.js +73 -68
- package/dist/pages/CatalogPage.js.map +1 -1
- package/dist/pages/ComponentDetailPage.js +195 -164
- package/dist/pages/ComponentDetailPage.js.map +1 -1
- package/dist/pages/ComponentsPage.js +80 -75
- package/dist/pages/ComponentsPage.js.map +1 -1
- package/dist/pages/EnvTopologyPage.js +100 -95
- package/dist/pages/EnvTopologyPage.js.map +1 -1
- package/dist/pages/HealthPage.js +331 -204
- package/dist/pages/HealthPage.js.map +1 -1
- package/dist/pages/LogsPage.js +54 -49
- package/dist/pages/LogsPage.js.map +1 -1
- package/dist/pages/OverviewPage.js +159 -129
- package/dist/pages/OverviewPage.js.map +1 -1
- package/dist/pages/PluginsCatalogPage.js +72 -78
- package/dist/pages/PluginsCatalogPage.js.map +1 -1
- package/dist/pages/SessionDetailsPage.js +517 -512
- package/dist/pages/SessionDetailsPage.js.map +1 -1
- package/dist/pages/SessionsPage.js +1084 -1079
- package/dist/pages/SessionsPage.js.map +1 -1
- package/dist/pages/SettingsPage.js +107 -103
- package/dist/pages/SettingsPage.js.map +1 -1
- package/dist/pages/TargetDetailsPage.js +131 -123
- package/dist/pages/TargetDetailsPage.js.map +1 -1
- package/dist/pages/TargetsPage.js +198 -194
- package/dist/pages/TargetsPage.js.map +1 -1
- package/dist/pages/TemplatesPage.js +77 -71
- package/dist/pages/TemplatesPage.js.map +1 -1
- package/dist/pages/VibeAuditPage.js +83 -78
- package/dist/pages/VibeAuditPage.js.map +1 -1
- package/dist/pages/VibeCalendarPage.js +109 -104
- package/dist/pages/VibeCalendarPage.js.map +1 -1
- package/dist/pages/VibeDeckPage.js +222 -217
- package/dist/pages/VibeDeckPage.js.map +1 -1
- package/dist/pages/VibeDetailsPage.js +532 -501
- package/dist/pages/VibeDetailsPage.js.map +1 -1
- package/dist/pages/VibesPage.js +137 -133
- package/dist/pages/VibesPage.js.map +1 -1
- package/dist/pages/WebhooksPage.js +329 -324
- package/dist/pages/WebhooksPage.js.map +1 -1
- package/dist/pages/ai/AIPage.js +149 -144
- package/dist/pages/ai/AIPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -15,25 +15,25 @@ import { notify as u } from "../lib/toast.js";
|
|
|
15
15
|
import { MeshGraphView as se } from "../components/graph/MeshGraphView.js";
|
|
16
16
|
import { MeshDeployWizard as ce } from "../components/graph/MeshDeployWizard.js";
|
|
17
17
|
import { MeshRequestPanel as le } from "../components/graph/MeshRequestPanel.js";
|
|
18
|
-
import { MeshNodeActions as
|
|
19
|
-
import { useCallback as
|
|
20
|
-
import { GitBranch as
|
|
21
|
-
import { usePermissions as
|
|
22
|
-
import { Fragment as
|
|
23
|
-
import { Button as
|
|
18
|
+
import { MeshNodeActions as d } from "../components/graph/MeshNodeActions.js";
|
|
19
|
+
import { useCallback as f, useMemo as p, useState as m } from "react";
|
|
20
|
+
import { GitBranch as ue, Loader2 as h, Network as g, Rocket as de, Send as fe, ShieldAlert as pe } from "lucide-react";
|
|
21
|
+
import { usePermissions as me } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
22
|
+
import { Fragment as he, jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
23
|
+
import { Button as y, PagePurpose as ge } from "@burdenoff/fe-libs/ui";
|
|
24
24
|
import { MotionPage as _e } from "@burdenoff/fe-libs/shared/motion";
|
|
25
25
|
//#region src/pages/AgentGraphPage.tsx
|
|
26
|
-
function
|
|
27
|
-
let { workspaceId:
|
|
26
|
+
function b() {
|
|
27
|
+
let { workspaceId: b, currentUser: ve } = te(), x = ne(), S = ee(), { hasPermission: ye, isLoading: be, permissions: C } = me(), [w, xe] = m(null), { agents: T } = a(), E = p(() => T.map((e) => ({
|
|
28
28
|
value: e.id,
|
|
29
|
-
label:
|
|
30
|
-
})), [
|
|
29
|
+
label: S("agentGraph.scopeAgentPrefix", "Agent: {{name}}", { name: e.name })
|
|
30
|
+
})), [T, S]), [D, O] = m(""), { data: k } = e({ skip: !b }), A = k?.agentConnectionConfig.permissions ?? [], j = k?.agentConnectionConfig.scopes ?? [], { data: M, previousData: Se, loading: Ce, error: N, refetch: P } = t({
|
|
31
31
|
variables: {
|
|
32
|
-
workspaceId:
|
|
33
|
-
rootAgentId:
|
|
32
|
+
workspaceId: b || "",
|
|
33
|
+
rootAgentId: w || void 0
|
|
34
34
|
},
|
|
35
|
-
skip: !
|
|
36
|
-
}), F =
|
|
35
|
+
skip: !b
|
|
36
|
+
}), F = M?.agentMeshGraph ?? Se?.agentMeshGraph, I = p(() => (F?.agents ?? []).map((e) => ({
|
|
37
37
|
id: e.id,
|
|
38
38
|
name: e.name,
|
|
39
39
|
hostname: e.hostname,
|
|
@@ -49,7 +49,7 @@ function y() {
|
|
|
49
49
|
type: String(e.target.type),
|
|
50
50
|
host: e.target.host ?? null
|
|
51
51
|
} : null
|
|
52
|
-
})), [F]), L =
|
|
52
|
+
})), [F]), L = p(() => (F?.meshLinks ?? []).map((e) => ({
|
|
53
53
|
id: e.id,
|
|
54
54
|
workspaceId: e.workspaceId,
|
|
55
55
|
status: e.status,
|
|
@@ -74,17 +74,17 @@ function y() {
|
|
|
74
74
|
host: e.destinationAgent.target.host ?? null
|
|
75
75
|
} : null
|
|
76
76
|
}
|
|
77
|
-
})), [F]), R =
|
|
77
|
+
})), [F]), R = Ce, we = R && !F, z = R && !!F, B = p(() => I.length > 0 ? I.map((e) => ({
|
|
78
78
|
id: e.id,
|
|
79
79
|
name: e.name,
|
|
80
80
|
hostname: e.hostname,
|
|
81
81
|
isActive: e.isActive
|
|
82
|
-
})) :
|
|
82
|
+
})) : T.map((e) => ({
|
|
83
83
|
id: e.id,
|
|
84
84
|
name: e.name,
|
|
85
85
|
hostname: e.hostname,
|
|
86
86
|
isActive: e.isActive
|
|
87
|
-
})), [I,
|
|
87
|
+
})), [I, T]), [V] = r(), [H] = n(), [U] = i(), [Te, W] = m(!1), [Ee, G] = m(null), [De, K] = m(!1), [Oe, q] = m(null), [J, Y] = m(null), ke = f(async (e, t, n) => {
|
|
88
88
|
await V({ variables: {
|
|
89
89
|
meshLinkId: e,
|
|
90
90
|
input: {
|
|
@@ -92,13 +92,13 @@ function y() {
|
|
|
92
92
|
scopes: n
|
|
93
93
|
}
|
|
94
94
|
} }), await P();
|
|
95
|
-
}, [P, V]), X =
|
|
95
|
+
}, [P, V]), X = w ?? B[0]?.id ?? null, Ae = f(async (e) => {
|
|
96
96
|
if (Y(null), X && e === X) {
|
|
97
|
-
|
|
97
|
+
x(`/agents/${e}?tab=terminal`);
|
|
98
98
|
return;
|
|
99
99
|
}
|
|
100
100
|
if (!X) {
|
|
101
|
-
u.error(
|
|
101
|
+
u.error(S("mesh.terminal.noRoot", "No reachable root agent to start a terminal from."));
|
|
102
102
|
return;
|
|
103
103
|
}
|
|
104
104
|
try {
|
|
@@ -106,27 +106,27 @@ function y() {
|
|
|
106
106
|
rootAgentId: X,
|
|
107
107
|
destinationAgentId: e,
|
|
108
108
|
input: { kind: re.Terminal }
|
|
109
|
-
} }),
|
|
109
|
+
} }), x(`/agents/${e}?tab=terminal`);
|
|
110
110
|
} catch {
|
|
111
|
-
u.info(
|
|
111
|
+
u.info(S("mesh.terminal.comingSoon", "Deep-terminal coming soon"));
|
|
112
112
|
}
|
|
113
113
|
}, [
|
|
114
114
|
X,
|
|
115
115
|
H,
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
]),
|
|
116
|
+
x,
|
|
117
|
+
S
|
|
118
|
+
]), je = f((e) => {
|
|
119
119
|
Y(null), q(e), K(!0);
|
|
120
|
-
}, []),
|
|
120
|
+
}, []), Me = f((e) => {
|
|
121
121
|
Y(null), G(e), W(!0);
|
|
122
|
-
}, []),
|
|
122
|
+
}, []), Z = f(async (e) => {
|
|
123
123
|
let t = I.find((t) => t.id === e)?.name ?? e;
|
|
124
|
-
u.info(
|
|
124
|
+
u.info(S("mesh.wake.starting", "Reconnecting {{name}} over SSH…", { name: t }));
|
|
125
125
|
try {
|
|
126
126
|
let { data: n } = await U({ variables: { agentId: e } }), r = n?.wakeMeshAgent;
|
|
127
|
-
r?.ok ? (u.success(
|
|
127
|
+
r?.ok ? (u.success(S("mesh.wake.success", "{{name}} is back online", { name: t })), await P()) : u.error(r?.error ?? S("mesh.wake.failed", "Could not wake {{name}} over SSH.", { name: t }));
|
|
128
128
|
} catch (e) {
|
|
129
|
-
u.error(e instanceof Error ? e.message :
|
|
129
|
+
u.error(e instanceof Error ? e.message : S("mesh.wake.failed", "Could not wake {{name}} over SSH.", { name: t }));
|
|
130
130
|
} finally {
|
|
131
131
|
Y(null);
|
|
132
132
|
}
|
|
@@ -134,168 +134,172 @@ function y() {
|
|
|
134
134
|
I,
|
|
135
135
|
U,
|
|
136
136
|
P,
|
|
137
|
-
|
|
138
|
-
]),
|
|
139
|
-
Y(null),
|
|
140
|
-
}, [
|
|
137
|
+
S
|
|
138
|
+
]), Ne = f((e) => {
|
|
139
|
+
Y(null), x(`/agents/${e}`);
|
|
140
|
+
}, [x]), Pe = f((e) => {
|
|
141
141
|
Y(e);
|
|
142
|
-
}, []), Q =
|
|
143
|
-
if (be) return /* @__PURE__ */
|
|
144
|
-
if (
|
|
145
|
-
icon: /* @__PURE__ */
|
|
146
|
-
title:
|
|
147
|
-
description:
|
|
142
|
+
}, []), Q = p(() => I.find((e) => e.id === J) ?? null, [I, J]);
|
|
143
|
+
if (be) return /* @__PURE__ */ _(s, { message: S("agentGraph.loadingPermissions", "Loading permissions...") });
|
|
144
|
+
if (C && (C.permissionStrings?.length ?? 0) > 0 && !ye(oe.AGENT_READ)) return /* @__PURE__ */ _(l, {
|
|
145
|
+
icon: /* @__PURE__ */ _(pe, { className: "size-8 text-text-secondary" }),
|
|
146
|
+
title: S("agentGraph.accessDeniedTitle", "Access Denied"),
|
|
147
|
+
description: S("agentGraph.accessDeniedDescription", "You don't have permission to access this page.")
|
|
148
148
|
});
|
|
149
149
|
let $ = I.length > 0;
|
|
150
|
-
return /* @__PURE__ */
|
|
150
|
+
return /* @__PURE__ */ v(_e, {
|
|
151
151
|
className: "flex h-full min-h-0 flex-col px-6 lg:px-8 pt-6",
|
|
152
152
|
children: [
|
|
153
|
-
/* @__PURE__ */
|
|
154
|
-
title:
|
|
155
|
-
description:
|
|
156
|
-
actions: /* @__PURE__ */
|
|
153
|
+
/* @__PURE__ */ _(o, {
|
|
154
|
+
title: S("nav.agentGraph", "Agent Mesh"),
|
|
155
|
+
description: S("agentGraph.meshDescription", "Visualize and manage the agent-to-agent mesh: deploy new agents, edit link permissions, and run requests across hops."),
|
|
156
|
+
actions: /* @__PURE__ */ v("div", {
|
|
157
157
|
className: "flex flex-wrap items-center gap-2",
|
|
158
|
-
children: [/* @__PURE__ */
|
|
158
|
+
children: [/* @__PURE__ */ v(y, {
|
|
159
159
|
type: "button",
|
|
160
160
|
variant: "outline",
|
|
161
161
|
onClick: () => {
|
|
162
162
|
K((e) => !e), q(null);
|
|
163
163
|
},
|
|
164
|
-
children: [/* @__PURE__ */
|
|
164
|
+
children: [/* @__PURE__ */ _(fe, {
|
|
165
165
|
className: "size-4",
|
|
166
166
|
"aria-hidden": "true"
|
|
167
|
-
}),
|
|
168
|
-
}), /* @__PURE__ */
|
|
167
|
+
}), S("mesh.requestPanelToggle", "Mesh request")]
|
|
168
|
+
}), /* @__PURE__ */ v(y, {
|
|
169
169
|
type: "button",
|
|
170
170
|
onClick: () => {
|
|
171
|
-
G(
|
|
171
|
+
G(w), W(!0);
|
|
172
172
|
},
|
|
173
173
|
disabled: B.length === 0,
|
|
174
|
-
children: [/* @__PURE__ */
|
|
174
|
+
children: [/* @__PURE__ */ _(de, {
|
|
175
175
|
className: "size-4",
|
|
176
176
|
"aria-hidden": "true"
|
|
177
|
-
}),
|
|
177
|
+
}), S("mesh.deployButton", "Deploy Agent")]
|
|
178
178
|
})]
|
|
179
179
|
})
|
|
180
180
|
}),
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
181
|
+
/* @__PURE__ */ _(ge, {
|
|
182
|
+
icon: /* @__PURE__ */ _(g, { className: "size-4" }),
|
|
183
|
+
children: S("agentGraph.purpose", "The Agent Mesh shows how your agents connect to one another and lets you manage those links. Use it to picture the topology, grant or revoke which agents may reach which, deploy a new agent into the mesh, and run a request that hops across agents — handy when work on one machine needs to trigger another.")
|
|
184
|
+
}),
|
|
185
|
+
we && /* @__PURE__ */ _(s, { message: S("agentGraph.loading", "Loading agent mesh...") }),
|
|
186
|
+
!R && N && !$ && /* @__PURE__ */ _(c, {
|
|
187
|
+
title: S("agentGraph.failedLoad", "Failed to load agent mesh"),
|
|
184
188
|
message: N.message
|
|
185
189
|
}),
|
|
186
|
-
!R && !N && !$ && !
|
|
187
|
-
icon: /* @__PURE__ */ g
|
|
188
|
-
title:
|
|
189
|
-
description:
|
|
190
|
-
action: /* @__PURE__ */
|
|
190
|
+
!R && !N && !$ && !D && /* @__PURE__ */ _(l, {
|
|
191
|
+
icon: /* @__PURE__ */ _(g, { className: "size-8 text-text-secondary" }),
|
|
192
|
+
title: S("agentGraph.noAgentsConnected", "No agents in the mesh"),
|
|
193
|
+
description: S("agentGraph.noAgentsDescription", "Deploy an agent to start building your agent mesh topology."),
|
|
194
|
+
action: /* @__PURE__ */ _(ie, { prompt: "What is the Agent Mesh in VibeControls and how do I deploy agents?" })
|
|
191
195
|
}),
|
|
192
|
-
|
|
196
|
+
De && /* @__PURE__ */ _("div", {
|
|
193
197
|
className: "mt-4",
|
|
194
|
-
children: /* @__PURE__ */
|
|
198
|
+
children: /* @__PURE__ */ _(le, {
|
|
195
199
|
rootAgentId: X,
|
|
196
200
|
destinations: I,
|
|
197
|
-
initialDestinationId:
|
|
201
|
+
initialDestinationId: Oe,
|
|
198
202
|
onClose: () => K(!1)
|
|
199
203
|
})
|
|
200
204
|
}),
|
|
201
|
-
($ ||
|
|
202
|
-
/* @__PURE__ */
|
|
205
|
+
($ || D || z) && /* @__PURE__ */ v(he, { children: [
|
|
206
|
+
/* @__PURE__ */ v("div", {
|
|
203
207
|
className: "mt-4 flex flex-wrap items-center justify-between gap-3",
|
|
204
|
-
children: [/* @__PURE__ */
|
|
208
|
+
children: [/* @__PURE__ */ v("div", {
|
|
205
209
|
className: "flex min-w-0 flex-wrap items-center gap-2",
|
|
206
210
|
children: [
|
|
207
|
-
/* @__PURE__ */
|
|
211
|
+
/* @__PURE__ */ _(ue, {
|
|
208
212
|
className: "size-4 shrink-0 text-text-muted",
|
|
209
213
|
"aria-hidden": "true"
|
|
210
214
|
}),
|
|
211
|
-
/* @__PURE__ */
|
|
215
|
+
/* @__PURE__ */ _("span", {
|
|
212
216
|
className: "text-sm font-medium text-text-secondary",
|
|
213
|
-
children:
|
|
217
|
+
children: S("agentGraph.meshCountSummary", "{{agents}} agents, {{links}} links", {
|
|
214
218
|
agents: I.length,
|
|
215
219
|
links: L.length
|
|
216
220
|
})
|
|
217
221
|
}),
|
|
218
|
-
z && /* @__PURE__ */
|
|
222
|
+
z && /* @__PURE__ */ v("span", {
|
|
219
223
|
role: "status",
|
|
220
224
|
"aria-live": "polite",
|
|
221
225
|
className: "inline-flex items-center gap-1.5 rounded-full border border-border-default bg-bg-surface px-2.5 py-1 text-xs font-medium text-text-secondary",
|
|
222
|
-
children: [/* @__PURE__ */
|
|
226
|
+
children: [/* @__PURE__ */ _(h, {
|
|
223
227
|
className: "size-3.5 animate-spin text-text-secondary",
|
|
224
228
|
"aria-hidden": "true"
|
|
225
|
-
}),
|
|
229
|
+
}), S("agentGraph.refreshing", "Refreshing graph...")]
|
|
226
230
|
})
|
|
227
231
|
]
|
|
228
|
-
}), /* @__PURE__ */
|
|
232
|
+
}), /* @__PURE__ */ v("div", {
|
|
229
233
|
className: "flex min-w-0 items-center gap-2",
|
|
230
|
-
children: [/* @__PURE__ */
|
|
234
|
+
children: [/* @__PURE__ */ _("span", {
|
|
231
235
|
className: "shrink-0 text-xs font-medium text-text-secondary",
|
|
232
|
-
children:
|
|
233
|
-
}), /* @__PURE__ */
|
|
234
|
-
value:
|
|
236
|
+
children: S("agentGraph.scopeLabel", "Scope:")
|
|
237
|
+
}), /* @__PURE__ */ _(ae, {
|
|
238
|
+
value: D,
|
|
235
239
|
onChange: (e) => {
|
|
236
|
-
|
|
240
|
+
O(e), xe(e || null);
|
|
237
241
|
},
|
|
238
|
-
options:
|
|
239
|
-
placeholder:
|
|
242
|
+
options: E,
|
|
243
|
+
placeholder: S("agentGraph.scopeAllAgents", "All agents"),
|
|
240
244
|
clearable: !0,
|
|
241
245
|
disabled: R,
|
|
242
246
|
dropdownAlign: "right"
|
|
243
247
|
})]
|
|
244
248
|
})]
|
|
245
249
|
}),
|
|
246
|
-
!R && N && /* @__PURE__ */
|
|
247
|
-
title:
|
|
250
|
+
!R && N && /* @__PURE__ */ _(c, {
|
|
251
|
+
title: S("agentGraph.failedRefresh", "Failed to refresh agent mesh"),
|
|
248
252
|
message: N.message
|
|
249
253
|
}),
|
|
250
|
-
!R && !$ &&
|
|
251
|
-
icon: /* @__PURE__ */ g
|
|
252
|
-
title:
|
|
253
|
-
description:
|
|
254
|
+
!R && !$ && D && /* @__PURE__ */ _(l, {
|
|
255
|
+
icon: /* @__PURE__ */ _(g, { className: "size-8 text-text-secondary" }),
|
|
256
|
+
title: S("agentGraph.noScopeResults", "No results for this scope"),
|
|
257
|
+
description: S("agentGraph.noScopeAgentDescription", "No agents match the selected scope. Clear the filter to view the full mesh.")
|
|
254
258
|
}),
|
|
255
|
-
($ || z) && /* @__PURE__ */
|
|
259
|
+
($ || z) && /* @__PURE__ */ v("div", {
|
|
256
260
|
className: "relative mt-4 min-h-[24rem] flex-1",
|
|
257
261
|
"aria-busy": z,
|
|
258
262
|
children: [
|
|
259
|
-
/* @__PURE__ */
|
|
263
|
+
/* @__PURE__ */ _(se, {
|
|
260
264
|
agents: I,
|
|
261
265
|
meshLinks: L,
|
|
262
266
|
currentUserId: ve?.id ?? null,
|
|
263
|
-
onUpdatePermissions:
|
|
264
|
-
availablePermissions:
|
|
265
|
-
availableScopes:
|
|
266
|
-
onNodeClick:
|
|
267
|
+
onUpdatePermissions: ke,
|
|
268
|
+
availablePermissions: A,
|
|
269
|
+
availableScopes: j,
|
|
270
|
+
onNodeClick: Pe,
|
|
267
271
|
fullHeight: !0
|
|
268
272
|
}),
|
|
269
|
-
Q && /* @__PURE__ */
|
|
273
|
+
Q && /* @__PURE__ */ _("div", {
|
|
270
274
|
className: "absolute right-4 top-4 z-20",
|
|
271
|
-
children: /* @__PURE__ */
|
|
275
|
+
children: /* @__PURE__ */ _(d, {
|
|
272
276
|
agent: Q,
|
|
273
|
-
onRunCommand:
|
|
274
|
-
onStartTerminal: (e) => void
|
|
275
|
-
onDeployFromHere:
|
|
276
|
-
onWakeViaSsh:
|
|
277
|
-
onOpenAgent:
|
|
277
|
+
onRunCommand: je,
|
|
278
|
+
onStartTerminal: (e) => void Ae(e),
|
|
279
|
+
onDeployFromHere: Me,
|
|
280
|
+
onWakeViaSsh: Z,
|
|
281
|
+
onOpenAgent: Ne,
|
|
278
282
|
onClose: () => Y(null)
|
|
279
283
|
})
|
|
280
284
|
}),
|
|
281
|
-
z && /* @__PURE__ */
|
|
285
|
+
z && /* @__PURE__ */ _("div", {
|
|
282
286
|
className: "absolute inset-0 z-10 flex items-center justify-center bg-bg-surface/60 backdrop-blur-[1px]",
|
|
283
|
-
children: /* @__PURE__ */
|
|
287
|
+
children: /* @__PURE__ */ v("div", {
|
|
284
288
|
role: "status",
|
|
285
289
|
className: "inline-flex items-center gap-2 rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-secondary shadow-[var(--shadow-elevation-2)]",
|
|
286
|
-
children: [/* @__PURE__ */
|
|
290
|
+
children: [/* @__PURE__ */ _(h, {
|
|
287
291
|
className: "size-4 animate-spin text-text-secondary",
|
|
288
292
|
"aria-hidden": "true"
|
|
289
|
-
}), /* @__PURE__ */
|
|
293
|
+
}), /* @__PURE__ */ _("span", { children: D ? S("agentGraph.loadingScope", "Applying selected scope...") : S("agentGraph.refreshing", "Refreshing graph...") })]
|
|
290
294
|
})
|
|
291
295
|
})
|
|
292
296
|
]
|
|
293
297
|
})
|
|
294
298
|
] }),
|
|
295
|
-
|
|
299
|
+
Te && /* @__PURE__ */ _(ce, {
|
|
296
300
|
sourceAgents: B,
|
|
297
|
-
initialSourceAgentId:
|
|
298
|
-
availablePermissions:
|
|
301
|
+
initialSourceAgentId: Ee,
|
|
302
|
+
availablePermissions: A,
|
|
299
303
|
onClose: () => W(!1),
|
|
300
304
|
onCompleted: () => {
|
|
301
305
|
P();
|
|
@@ -305,6 +309,6 @@ function y() {
|
|
|
305
309
|
});
|
|
306
310
|
}
|
|
307
311
|
//#endregion
|
|
308
|
-
export {
|
|
312
|
+
export { b as AgentGraphPage };
|
|
309
313
|
|
|
310
314
|
//# sourceMappingURL=AgentGraphPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentGraphPage.js","names":[],"sources":["../../src/pages/AgentGraphPage.tsx"],"sourcesContent":["/**\n * Agent Graph Page — Dedicated full-page view of the agent MESH graph.\n *\n * Agents are first-class nodes; AgentMeshLinks are the directed agent->agent\n * edges. Supports root-agent scoping, RBAC permission editing on links, a\n * \"Deploy Agent\" wizard, and an on-demand mesh request panel + per-node\n * quick-actions.\n */\n\nimport { useState, useMemo, useCallback } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { GitBranch, Loader2, Network, Rocket, Send, ShieldAlert } from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport {\n PageHeader,\n SearchableSelect,\n LoadingState,\n ErrorState,\n EmptyState,\n GetHelpButton,\n} from '@/components/shared';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { useTr } from '@/shared/hooks/useTr';\nimport { notify } from '@/lib/toast';\nimport { useVibeControls, useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\nimport {\n useGetAgentMeshGraphQuery,\n useUpdateMeshLinkPermissionsMutation,\n useStartMeshAgentSessionMutation,\n useWakeMeshAgentMutation,\n useAgentConnectionConfigQuery,\n} from '@/generated/wspace-operations';\nimport { MeshSessionKind } from '@/generated/wspace-types';\nimport type { GetAgentMeshGraphQuery } from '@/generated/wspace-operations';\nimport { MeshGraphView } from '@/components/graph/MeshGraphView';\nimport { MeshDeployWizard } from '@/components/graph/MeshDeployWizard';\nimport { MeshRequestPanel } from '@/components/graph/MeshRequestPanel';\nimport { MeshNodeActions } from '@/components/graph/MeshNodeActions';\nimport type { MeshAgentUi, MeshLinkUi } from '@/components/graph/meshTypes';\n\nexport function AgentGraphPage() {\n const { workspaceId, currentUser } = useVibeControls();\n const navigate = useVibeNavigate();\n const tr = useTr();\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n const [rootAgentId, setRootAgentId] = useState<string | null>(null);\n\n const { agents: cachedAgents } = useCachedInitialData();\n\n const scopeOptions = useMemo(\n () =>\n cachedAgents.map((a) => ({\n value: a.id,\n label: tr('agentGraph.scopeAgentPrefix', 'Agent: {{name}}', { name: a.name }),\n })),\n [cachedAgents, tr]\n );\n\n const [scopeValue, setScopeValue] = useState<string>('');\n\n const { data: connectionConfigData } = useAgentConnectionConfigQuery({\n skip: !workspaceId,\n });\n const availablePermissions = connectionConfigData?.agentConnectionConfig.permissions ?? [];\n const availableScopes = connectionConfigData?.agentConnectionConfig.scopes ?? [];\n\n const {\n data: graphData,\n previousData,\n loading,\n error,\n refetch,\n } = useGetAgentMeshGraphQuery({\n variables: {\n workspaceId: workspaceId || '',\n rootAgentId: rootAgentId || undefined,\n },\n skip: !workspaceId,\n });\n\n const resolvedMeshGraph: GetAgentMeshGraphQuery['agentMeshGraph'] | undefined =\n graphData?.agentMeshGraph ?? previousData?.agentMeshGraph;\n\n type MeshGraphAgent = GetAgentMeshGraphQuery['agentMeshGraph']['agents'][number];\n type MeshGraphLink = GetAgentMeshGraphQuery['agentMeshGraph']['meshLinks'][number];\n\n // Map generated shapes (enum target.type) into the plain UI shapes.\n const meshAgents = useMemo<MeshAgentUi[]>(\n () =>\n (resolvedMeshGraph?.agents ?? []).map((a: MeshGraphAgent) => ({\n id: a.id,\n name: a.name,\n hostname: a.hostname,\n platform: a.platform,\n version: a.version,\n isActive: a.isActive,\n ownerUserId: a.ownerUserId ?? null,\n targetId: a.targetId,\n tunnelStatus: a.tunnelStatus ?? null,\n target: a.target\n ? {\n id: a.target.id,\n name: a.target.name,\n type: String(a.target.type),\n host: a.target.host ?? null,\n }\n : null,\n })),\n [resolvedMeshGraph]\n );\n\n const meshLinks = useMemo<MeshLinkUi[]>(\n () =>\n (resolvedMeshGraph?.meshLinks ?? []).map((l: MeshGraphLink) => ({\n id: l.id,\n workspaceId: l.workspaceId,\n status: l.status,\n permissions: l.permissions,\n scopes: l.scopes,\n label: l.label ?? null,\n description: l.description ?? null,\n createdAt: l.createdAt,\n updatedAt: l.updatedAt,\n sourceAgent: { id: l.sourceAgent.id, name: l.sourceAgent.name },\n destinationAgent: {\n id: l.destinationAgent.id,\n name: l.destinationAgent.name,\n hostname: l.destinationAgent.hostname,\n target: l.destinationAgent.target\n ? {\n id: l.destinationAgent.target.id,\n name: l.destinationAgent.target.name,\n type: String(l.destinationAgent.target.type),\n host: l.destinationAgent.target.host ?? null,\n }\n : null,\n },\n })),\n [resolvedMeshGraph]\n );\n\n const isGraphLoading = loading;\n const isInitialLoad = isGraphLoading && !resolvedMeshGraph;\n const isRefreshingGraph = isGraphLoading && Boolean(resolvedMeshGraph);\n\n // Source agents available to act as deploy root / request root: prefer the\n // mesh agents (already scoped), fall back to the cached fleet.\n const sourceAgents = useMemo(\n () =>\n meshAgents.length > 0\n ? meshAgents.map((a) => ({\n id: a.id,\n name: a.name,\n hostname: a.hostname,\n isActive: a.isActive,\n }))\n : cachedAgents.map((a) => ({\n id: a.id,\n name: a.name,\n hostname: a.hostname,\n isActive: a.isActive,\n })),\n [meshAgents, cachedAgents]\n );\n\n const [updateMeshLinkPerms] = useUpdateMeshLinkPermissionsMutation();\n const [startMeshSession] = useStartMeshAgentSessionMutation();\n const [wakeMeshAgent] = useWakeMeshAgentMutation();\n\n // ── UI panels ──────────────────────────────────────────────────────────\n const [showDeployWizard, setShowDeployWizard] = useState(false);\n const [deploySeedSourceId, setDeploySeedSourceId] = useState<string | null>(null);\n const [showRequestPanel, setShowRequestPanel] = useState(false);\n const [requestDestinationId, setRequestDestinationId] = useState<string | null>(null);\n const [actionsForAgentId, setActionsForAgentId] = useState<string | null>(null);\n\n const handleUpdatePermissions = useCallback(\n async (meshLinkId: string, perms: string[], scopes: string[]) => {\n await updateMeshLinkPerms({\n variables: { meshLinkId, input: { permissions: perms, scopes } },\n });\n await refetch();\n },\n [refetch, updateMeshLinkPerms]\n );\n\n // The \"request root\" is the scoped root agent, else the first source agent.\n const requestRootAgentId = rootAgentId ?? sourceAgents[0]?.id ?? null;\n\n const handleStartTerminal = useCallback(\n async (agentId: string) => {\n setActionsForAgentId(null);\n // For a DIRECT / root agent, prefer the existing terminal flow.\n if (requestRootAgentId && agentId === requestRootAgentId) {\n navigate(`/agents/${agentId}?tab=terminal`);\n return;\n }\n if (!requestRootAgentId) {\n notify.error(\n tr('mesh.terminal.noRoot', 'No reachable root agent to start a terminal from.')\n );\n return;\n }\n try {\n await startMeshSession({\n variables: {\n rootAgentId: requestRootAgentId,\n destinationAgentId: agentId,\n input: { kind: MeshSessionKind.Terminal },\n },\n });\n // If the agent route ever lands, navigate; for now this rarely succeeds.\n navigate(`/agents/${agentId}?tab=terminal`);\n } catch {\n notify.info(tr('mesh.terminal.comingSoon', 'Deep-terminal coming soon'));\n }\n },\n [requestRootAgentId, startMeshSession, navigate, tr]\n );\n\n const handleRunCommand = useCallback((agentId: string) => {\n setActionsForAgentId(null);\n setRequestDestinationId(agentId);\n setShowRequestPanel(true);\n }, []);\n\n const handleDeployFromHere = useCallback((agentId: string) => {\n setActionsForAgentId(null);\n setDeploySeedSourceId(agentId);\n setShowDeployWizard(true);\n }, []);\n\n const handleWakeViaSsh = useCallback(\n async (agentId: string) => {\n const agentName = meshAgents.find((a) => a.id === agentId)?.name ?? agentId;\n notify.info(tr('mesh.wake.starting', 'Reconnecting {{name}} over SSH…', { name: agentName }));\n try {\n const { data } = await wakeMeshAgent({ variables: { agentId } });\n const result = data?.wakeMeshAgent;\n if (result?.ok) {\n notify.success(tr('mesh.wake.success', '{{name}} is back online', { name: agentName }));\n await refetch();\n } else {\n notify.error(\n result?.error ??\n tr('mesh.wake.failed', 'Could not wake {{name}} over SSH.', { name: agentName })\n );\n }\n } catch (err) {\n notify.error(\n err instanceof Error\n ? err.message\n : tr('mesh.wake.failed', 'Could not wake {{name}} over SSH.', { name: agentName })\n );\n } finally {\n setActionsForAgentId(null);\n }\n },\n [meshAgents, wakeMeshAgent, refetch, tr]\n );\n\n const handleOpenAgent = useCallback(\n (agentId: string) => {\n setActionsForAgentId(null);\n navigate(`/agents/${agentId}`);\n },\n [navigate]\n );\n\n const handleNodeClick = useCallback((agentId: string) => {\n setActionsForAgentId(agentId);\n }, []);\n\n const actionsAgent = useMemo(\n () => meshAgents.find((a) => a.id === actionsForAgentId) ?? null,\n [meshAgents, actionsForAgentId]\n );\n\n if (permissionsLoading)\n return <LoadingState message={tr('agentGraph.loadingPermissions', 'Loading permissions...')} />;\n if (\n permissions &&\n (permissions.permissionStrings?.length ?? 0) > 0 &&\n !hasPermission(VIBE_PERMISSIONS.AGENT_READ)\n )\n return (\n <EmptyState\n icon={<ShieldAlert className=\"size-8 text-text-secondary\" />}\n title={tr('agentGraph.accessDeniedTitle', 'Access Denied')}\n description={tr(\n 'agentGraph.accessDeniedDescription',\n \"You don't have permission to access this page.\"\n )}\n />\n );\n\n const hasAgents = meshAgents.length > 0;\n\n return (\n <MotionPage className=\"flex h-full min-h-0 flex-col px-6 lg:px-8 pt-6\">\n <PageHeader\n title={tr('nav.agentGraph', 'Agent Mesh')}\n description={tr(\n 'agentGraph.meshDescription',\n 'Visualize and manage the agent-to-agent mesh: deploy new agents, edit link permissions, and run requests across hops.'\n )}\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => {\n setShowRequestPanel((v) => !v);\n setRequestDestinationId(null);\n }}\n >\n <Send className=\"size-4\" aria-hidden=\"true\" />\n {tr('mesh.requestPanelToggle', 'Mesh request')}\n </Button>\n <Button\n type=\"button\"\n onClick={() => {\n setDeploySeedSourceId(rootAgentId);\n setShowDeployWizard(true);\n }}\n disabled={sourceAgents.length === 0}\n >\n <Rocket className=\"size-4\" aria-hidden=\"true\" />\n {tr('mesh.deployButton', 'Deploy Agent')}\n </Button>\n </div>\n }\n />\n\n {isInitialLoad && (\n <LoadingState message={tr('agentGraph.loading', 'Loading agent mesh...')} />\n )}\n\n {!isGraphLoading && error && !hasAgents && (\n <ErrorState\n title={tr('agentGraph.failedLoad', 'Failed to load agent mesh')}\n message={error.message}\n />\n )}\n\n {!isGraphLoading && !error && !hasAgents && !scopeValue && (\n <EmptyState\n icon={<Network className=\"size-8 text-text-secondary\" />}\n title={tr('agentGraph.noAgentsConnected', 'No agents in the mesh')}\n description={tr(\n 'agentGraph.noAgentsDescription',\n 'Deploy an agent to start building your agent mesh topology.'\n )}\n action={\n <GetHelpButton prompt=\"What is the Agent Mesh in VibeControls and how do I deploy agents?\" />\n }\n />\n )}\n\n {showRequestPanel && (\n <div className=\"mt-4\">\n <MeshRequestPanel\n rootAgentId={requestRootAgentId}\n destinations={meshAgents}\n initialDestinationId={requestDestinationId}\n onClose={() => setShowRequestPanel(false)}\n />\n </div>\n )}\n\n {(hasAgents || scopeValue || isRefreshingGraph) && (\n <>\n <div className=\"mt-4 flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex min-w-0 flex-wrap items-center gap-2\">\n <GitBranch className=\"size-4 shrink-0 text-text-muted\" aria-hidden=\"true\" />\n <span className=\"text-sm font-medium text-text-secondary\">\n {tr('agentGraph.meshCountSummary', '{{agents}} agents, {{links}} links', {\n agents: meshAgents.length,\n links: meshLinks.length,\n })}\n </span>\n {isRefreshingGraph && (\n <span\n role=\"status\"\n aria-live=\"polite\"\n className=\"inline-flex items-center gap-1.5 rounded-full border border-border-default bg-bg-surface px-2.5 py-1 text-xs font-medium text-text-secondary\"\n >\n <Loader2\n className=\"size-3.5 animate-spin text-text-secondary\"\n aria-hidden=\"true\"\n />\n {tr('agentGraph.refreshing', 'Refreshing graph...')}\n </span>\n )}\n </div>\n <div className=\"flex min-w-0 items-center gap-2\">\n <span className=\"shrink-0 text-xs font-medium text-text-secondary\">\n {tr('agentGraph.scopeLabel', 'Scope:')}\n </span>\n <SearchableSelect\n value={scopeValue}\n onChange={(v: string) => {\n setScopeValue(v);\n setRootAgentId(v || null);\n }}\n options={scopeOptions}\n placeholder={tr('agentGraph.scopeAllAgents', 'All agents')}\n clearable\n disabled={isGraphLoading}\n dropdownAlign=\"right\"\n />\n </div>\n </div>\n\n {!isGraphLoading && error && (\n <ErrorState\n title={tr('agentGraph.failedRefresh', 'Failed to refresh agent mesh')}\n message={error.message}\n />\n )}\n\n {!isGraphLoading && !hasAgents && scopeValue && (\n <EmptyState\n icon={<Network className=\"size-8 text-text-secondary\" />}\n title={tr('agentGraph.noScopeResults', 'No results for this scope')}\n description={tr(\n 'agentGraph.noScopeAgentDescription',\n 'No agents match the selected scope. Clear the filter to view the full mesh.'\n )}\n />\n )}\n\n {(hasAgents || isRefreshingGraph) && (\n <div className=\"relative mt-4 min-h-[24rem] flex-1\" aria-busy={isRefreshingGraph}>\n <MeshGraphView\n agents={meshAgents}\n meshLinks={meshLinks}\n currentUserId={currentUser?.id ?? null}\n onUpdatePermissions={handleUpdatePermissions}\n availablePermissions={availablePermissions}\n availableScopes={availableScopes}\n onNodeClick={handleNodeClick}\n fullHeight\n />\n\n {/* Node quick-actions popover */}\n {actionsAgent && (\n <div className=\"absolute right-4 top-4 z-20\">\n <MeshNodeActions\n agent={actionsAgent}\n onRunCommand={handleRunCommand}\n onStartTerminal={(id) => void handleStartTerminal(id)}\n onDeployFromHere={handleDeployFromHere}\n onWakeViaSsh={handleWakeViaSsh}\n onOpenAgent={handleOpenAgent}\n onClose={() => setActionsForAgentId(null)}\n />\n </div>\n )}\n\n {isRefreshingGraph && (\n <div className=\"absolute inset-0 z-10 flex items-center justify-center bg-bg-surface/60 backdrop-blur-[1px]\">\n <div\n role=\"status\"\n className=\"inline-flex items-center gap-2 rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-secondary shadow-[var(--shadow-elevation-2)]\"\n >\n <Loader2\n className=\"size-4 animate-spin text-text-secondary\"\n aria-hidden=\"true\"\n />\n <span>\n {scopeValue\n ? tr('agentGraph.loadingScope', 'Applying selected scope...')\n : tr('agentGraph.refreshing', 'Refreshing graph...')}\n </span>\n </div>\n </div>\n )}\n </div>\n )}\n </>\n )}\n\n {showDeployWizard && (\n <MeshDeployWizard\n sourceAgents={sourceAgents}\n initialSourceAgentId={deploySeedSourceId}\n availablePermissions={availablePermissions}\n onClose={() => setShowDeployWizard(false)}\n onCompleted={() => {\n void refetch();\n }}\n />\n )}\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AgentGraphPage.js","names":[],"sources":["../../src/pages/AgentGraphPage.tsx"],"sourcesContent":["/**\n * Agent Graph Page — Dedicated full-page view of the agent MESH graph.\n *\n * Agents are first-class nodes; AgentMeshLinks are the directed agent->agent\n * edges. Supports root-agent scoping, RBAC permission editing on links, a\n * \"Deploy Agent\" wizard, and an on-demand mesh request panel + per-node\n * quick-actions.\n */\n\nimport { useState, useMemo, useCallback } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { GitBranch, Loader2, Network, Rocket, Send, ShieldAlert } from 'lucide-react';\nimport { Button, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport {\n PageHeader,\n SearchableSelect,\n LoadingState,\n ErrorState,\n EmptyState,\n GetHelpButton,\n} from '@/components/shared';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { useTr } from '@/shared/hooks/useTr';\nimport { notify } from '@/lib/toast';\nimport { useVibeControls, useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\nimport {\n useGetAgentMeshGraphQuery,\n useUpdateMeshLinkPermissionsMutation,\n useStartMeshAgentSessionMutation,\n useWakeMeshAgentMutation,\n useAgentConnectionConfigQuery,\n} from '@/generated/wspace-operations';\nimport { MeshSessionKind } from '@/generated/wspace-types';\nimport type { GetAgentMeshGraphQuery } from '@/generated/wspace-operations';\nimport { MeshGraphView } from '@/components/graph/MeshGraphView';\nimport { MeshDeployWizard } from '@/components/graph/MeshDeployWizard';\nimport { MeshRequestPanel } from '@/components/graph/MeshRequestPanel';\nimport { MeshNodeActions } from '@/components/graph/MeshNodeActions';\nimport type { MeshAgentUi, MeshLinkUi } from '@/components/graph/meshTypes';\n\nexport function AgentGraphPage() {\n const { workspaceId, currentUser } = useVibeControls();\n const navigate = useVibeNavigate();\n const tr = useTr();\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n const [rootAgentId, setRootAgentId] = useState<string | null>(null);\n\n const { agents: cachedAgents } = useCachedInitialData();\n\n const scopeOptions = useMemo(\n () =>\n cachedAgents.map((a) => ({\n value: a.id,\n label: tr('agentGraph.scopeAgentPrefix', 'Agent: {{name}}', { name: a.name }),\n })),\n [cachedAgents, tr]\n );\n\n const [scopeValue, setScopeValue] = useState<string>('');\n\n const { data: connectionConfigData } = useAgentConnectionConfigQuery({\n skip: !workspaceId,\n });\n const availablePermissions = connectionConfigData?.agentConnectionConfig.permissions ?? [];\n const availableScopes = connectionConfigData?.agentConnectionConfig.scopes ?? [];\n\n const {\n data: graphData,\n previousData,\n loading,\n error,\n refetch,\n } = useGetAgentMeshGraphQuery({\n variables: {\n workspaceId: workspaceId || '',\n rootAgentId: rootAgentId || undefined,\n },\n skip: !workspaceId,\n });\n\n const resolvedMeshGraph: GetAgentMeshGraphQuery['agentMeshGraph'] | undefined =\n graphData?.agentMeshGraph ?? previousData?.agentMeshGraph;\n\n type MeshGraphAgent = GetAgentMeshGraphQuery['agentMeshGraph']['agents'][number];\n type MeshGraphLink = GetAgentMeshGraphQuery['agentMeshGraph']['meshLinks'][number];\n\n // Map generated shapes (enum target.type) into the plain UI shapes.\n const meshAgents = useMemo<MeshAgentUi[]>(\n () =>\n (resolvedMeshGraph?.agents ?? []).map((a: MeshGraphAgent) => ({\n id: a.id,\n name: a.name,\n hostname: a.hostname,\n platform: a.platform,\n version: a.version,\n isActive: a.isActive,\n ownerUserId: a.ownerUserId ?? null,\n targetId: a.targetId,\n tunnelStatus: a.tunnelStatus ?? null,\n target: a.target\n ? {\n id: a.target.id,\n name: a.target.name,\n type: String(a.target.type),\n host: a.target.host ?? null,\n }\n : null,\n })),\n [resolvedMeshGraph]\n );\n\n const meshLinks = useMemo<MeshLinkUi[]>(\n () =>\n (resolvedMeshGraph?.meshLinks ?? []).map((l: MeshGraphLink) => ({\n id: l.id,\n workspaceId: l.workspaceId,\n status: l.status,\n permissions: l.permissions,\n scopes: l.scopes,\n label: l.label ?? null,\n description: l.description ?? null,\n createdAt: l.createdAt,\n updatedAt: l.updatedAt,\n sourceAgent: { id: l.sourceAgent.id, name: l.sourceAgent.name },\n destinationAgent: {\n id: l.destinationAgent.id,\n name: l.destinationAgent.name,\n hostname: l.destinationAgent.hostname,\n target: l.destinationAgent.target\n ? {\n id: l.destinationAgent.target.id,\n name: l.destinationAgent.target.name,\n type: String(l.destinationAgent.target.type),\n host: l.destinationAgent.target.host ?? null,\n }\n : null,\n },\n })),\n [resolvedMeshGraph]\n );\n\n const isGraphLoading = loading;\n const isInitialLoad = isGraphLoading && !resolvedMeshGraph;\n const isRefreshingGraph = isGraphLoading && Boolean(resolvedMeshGraph);\n\n // Source agents available to act as deploy root / request root: prefer the\n // mesh agents (already scoped), fall back to the cached fleet.\n const sourceAgents = useMemo(\n () =>\n meshAgents.length > 0\n ? meshAgents.map((a) => ({\n id: a.id,\n name: a.name,\n hostname: a.hostname,\n isActive: a.isActive,\n }))\n : cachedAgents.map((a) => ({\n id: a.id,\n name: a.name,\n hostname: a.hostname,\n isActive: a.isActive,\n })),\n [meshAgents, cachedAgents]\n );\n\n const [updateMeshLinkPerms] = useUpdateMeshLinkPermissionsMutation();\n const [startMeshSession] = useStartMeshAgentSessionMutation();\n const [wakeMeshAgent] = useWakeMeshAgentMutation();\n\n // ── UI panels ──────────────────────────────────────────────────────────\n const [showDeployWizard, setShowDeployWizard] = useState(false);\n const [deploySeedSourceId, setDeploySeedSourceId] = useState<string | null>(null);\n const [showRequestPanel, setShowRequestPanel] = useState(false);\n const [requestDestinationId, setRequestDestinationId] = useState<string | null>(null);\n const [actionsForAgentId, setActionsForAgentId] = useState<string | null>(null);\n\n const handleUpdatePermissions = useCallback(\n async (meshLinkId: string, perms: string[], scopes: string[]) => {\n await updateMeshLinkPerms({\n variables: { meshLinkId, input: { permissions: perms, scopes } },\n });\n await refetch();\n },\n [refetch, updateMeshLinkPerms]\n );\n\n // The \"request root\" is the scoped root agent, else the first source agent.\n const requestRootAgentId = rootAgentId ?? sourceAgents[0]?.id ?? null;\n\n const handleStartTerminal = useCallback(\n async (agentId: string) => {\n setActionsForAgentId(null);\n // For a DIRECT / root agent, prefer the existing terminal flow.\n if (requestRootAgentId && agentId === requestRootAgentId) {\n navigate(`/agents/${agentId}?tab=terminal`);\n return;\n }\n if (!requestRootAgentId) {\n notify.error(\n tr('mesh.terminal.noRoot', 'No reachable root agent to start a terminal from.')\n );\n return;\n }\n try {\n await startMeshSession({\n variables: {\n rootAgentId: requestRootAgentId,\n destinationAgentId: agentId,\n input: { kind: MeshSessionKind.Terminal },\n },\n });\n // If the agent route ever lands, navigate; for now this rarely succeeds.\n navigate(`/agents/${agentId}?tab=terminal`);\n } catch {\n notify.info(tr('mesh.terminal.comingSoon', 'Deep-terminal coming soon'));\n }\n },\n [requestRootAgentId, startMeshSession, navigate, tr]\n );\n\n const handleRunCommand = useCallback((agentId: string) => {\n setActionsForAgentId(null);\n setRequestDestinationId(agentId);\n setShowRequestPanel(true);\n }, []);\n\n const handleDeployFromHere = useCallback((agentId: string) => {\n setActionsForAgentId(null);\n setDeploySeedSourceId(agentId);\n setShowDeployWizard(true);\n }, []);\n\n const handleWakeViaSsh = useCallback(\n async (agentId: string) => {\n const agentName = meshAgents.find((a) => a.id === agentId)?.name ?? agentId;\n notify.info(tr('mesh.wake.starting', 'Reconnecting {{name}} over SSH…', { name: agentName }));\n try {\n const { data } = await wakeMeshAgent({ variables: { agentId } });\n const result = data?.wakeMeshAgent;\n if (result?.ok) {\n notify.success(tr('mesh.wake.success', '{{name}} is back online', { name: agentName }));\n await refetch();\n } else {\n notify.error(\n result?.error ??\n tr('mesh.wake.failed', 'Could not wake {{name}} over SSH.', { name: agentName })\n );\n }\n } catch (err) {\n notify.error(\n err instanceof Error\n ? err.message\n : tr('mesh.wake.failed', 'Could not wake {{name}} over SSH.', { name: agentName })\n );\n } finally {\n setActionsForAgentId(null);\n }\n },\n [meshAgents, wakeMeshAgent, refetch, tr]\n );\n\n const handleOpenAgent = useCallback(\n (agentId: string) => {\n setActionsForAgentId(null);\n navigate(`/agents/${agentId}`);\n },\n [navigate]\n );\n\n const handleNodeClick = useCallback((agentId: string) => {\n setActionsForAgentId(agentId);\n }, []);\n\n const actionsAgent = useMemo(\n () => meshAgents.find((a) => a.id === actionsForAgentId) ?? null,\n [meshAgents, actionsForAgentId]\n );\n\n if (permissionsLoading)\n return <LoadingState message={tr('agentGraph.loadingPermissions', 'Loading permissions...')} />;\n if (\n permissions &&\n (permissions.permissionStrings?.length ?? 0) > 0 &&\n !hasPermission(VIBE_PERMISSIONS.AGENT_READ)\n )\n return (\n <EmptyState\n icon={<ShieldAlert className=\"size-8 text-text-secondary\" />}\n title={tr('agentGraph.accessDeniedTitle', 'Access Denied')}\n description={tr(\n 'agentGraph.accessDeniedDescription',\n \"You don't have permission to access this page.\"\n )}\n />\n );\n\n const hasAgents = meshAgents.length > 0;\n\n return (\n <MotionPage className=\"flex h-full min-h-0 flex-col px-6 lg:px-8 pt-6\">\n <PageHeader\n title={tr('nav.agentGraph', 'Agent Mesh')}\n description={tr(\n 'agentGraph.meshDescription',\n 'Visualize and manage the agent-to-agent mesh: deploy new agents, edit link permissions, and run requests across hops.'\n )}\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => {\n setShowRequestPanel((v) => !v);\n setRequestDestinationId(null);\n }}\n >\n <Send className=\"size-4\" aria-hidden=\"true\" />\n {tr('mesh.requestPanelToggle', 'Mesh request')}\n </Button>\n <Button\n type=\"button\"\n onClick={() => {\n setDeploySeedSourceId(rootAgentId);\n setShowDeployWizard(true);\n }}\n disabled={sourceAgents.length === 0}\n >\n <Rocket className=\"size-4\" aria-hidden=\"true\" />\n {tr('mesh.deployButton', 'Deploy Agent')}\n </Button>\n </div>\n }\n />\n\n <PagePurpose icon={<Network className=\"size-4\" />}>\n {tr(\n 'agentGraph.purpose',\n 'The Agent Mesh shows how your agents connect to one another and lets you manage those links. Use it to picture the topology, grant or revoke which agents may reach which, deploy a new agent into the mesh, and run a request that hops across agents — handy when work on one machine needs to trigger another.'\n )}\n </PagePurpose>\n\n {isInitialLoad && (\n <LoadingState message={tr('agentGraph.loading', 'Loading agent mesh...')} />\n )}\n\n {!isGraphLoading && error && !hasAgents && (\n <ErrorState\n title={tr('agentGraph.failedLoad', 'Failed to load agent mesh')}\n message={error.message}\n />\n )}\n\n {!isGraphLoading && !error && !hasAgents && !scopeValue && (\n <EmptyState\n icon={<Network className=\"size-8 text-text-secondary\" />}\n title={tr('agentGraph.noAgentsConnected', 'No agents in the mesh')}\n description={tr(\n 'agentGraph.noAgentsDescription',\n 'Deploy an agent to start building your agent mesh topology.'\n )}\n action={\n <GetHelpButton prompt=\"What is the Agent Mesh in VibeControls and how do I deploy agents?\" />\n }\n />\n )}\n\n {showRequestPanel && (\n <div className=\"mt-4\">\n <MeshRequestPanel\n rootAgentId={requestRootAgentId}\n destinations={meshAgents}\n initialDestinationId={requestDestinationId}\n onClose={() => setShowRequestPanel(false)}\n />\n </div>\n )}\n\n {(hasAgents || scopeValue || isRefreshingGraph) && (\n <>\n <div className=\"mt-4 flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex min-w-0 flex-wrap items-center gap-2\">\n <GitBranch className=\"size-4 shrink-0 text-text-muted\" aria-hidden=\"true\" />\n <span className=\"text-sm font-medium text-text-secondary\">\n {tr('agentGraph.meshCountSummary', '{{agents}} agents, {{links}} links', {\n agents: meshAgents.length,\n links: meshLinks.length,\n })}\n </span>\n {isRefreshingGraph && (\n <span\n role=\"status\"\n aria-live=\"polite\"\n className=\"inline-flex items-center gap-1.5 rounded-full border border-border-default bg-bg-surface px-2.5 py-1 text-xs font-medium text-text-secondary\"\n >\n <Loader2\n className=\"size-3.5 animate-spin text-text-secondary\"\n aria-hidden=\"true\"\n />\n {tr('agentGraph.refreshing', 'Refreshing graph...')}\n </span>\n )}\n </div>\n <div className=\"flex min-w-0 items-center gap-2\">\n <span className=\"shrink-0 text-xs font-medium text-text-secondary\">\n {tr('agentGraph.scopeLabel', 'Scope:')}\n </span>\n <SearchableSelect\n value={scopeValue}\n onChange={(v: string) => {\n setScopeValue(v);\n setRootAgentId(v || null);\n }}\n options={scopeOptions}\n placeholder={tr('agentGraph.scopeAllAgents', 'All agents')}\n clearable\n disabled={isGraphLoading}\n dropdownAlign=\"right\"\n />\n </div>\n </div>\n\n {!isGraphLoading && error && (\n <ErrorState\n title={tr('agentGraph.failedRefresh', 'Failed to refresh agent mesh')}\n message={error.message}\n />\n )}\n\n {!isGraphLoading && !hasAgents && scopeValue && (\n <EmptyState\n icon={<Network className=\"size-8 text-text-secondary\" />}\n title={tr('agentGraph.noScopeResults', 'No results for this scope')}\n description={tr(\n 'agentGraph.noScopeAgentDescription',\n 'No agents match the selected scope. Clear the filter to view the full mesh.'\n )}\n />\n )}\n\n {(hasAgents || isRefreshingGraph) && (\n <div className=\"relative mt-4 min-h-[24rem] flex-1\" aria-busy={isRefreshingGraph}>\n <MeshGraphView\n agents={meshAgents}\n meshLinks={meshLinks}\n currentUserId={currentUser?.id ?? null}\n onUpdatePermissions={handleUpdatePermissions}\n availablePermissions={availablePermissions}\n availableScopes={availableScopes}\n onNodeClick={handleNodeClick}\n fullHeight\n />\n\n {/* Node quick-actions popover */}\n {actionsAgent && (\n <div className=\"absolute right-4 top-4 z-20\">\n <MeshNodeActions\n agent={actionsAgent}\n onRunCommand={handleRunCommand}\n onStartTerminal={(id) => void handleStartTerminal(id)}\n onDeployFromHere={handleDeployFromHere}\n onWakeViaSsh={handleWakeViaSsh}\n onOpenAgent={handleOpenAgent}\n onClose={() => setActionsForAgentId(null)}\n />\n </div>\n )}\n\n {isRefreshingGraph && (\n <div className=\"absolute inset-0 z-10 flex items-center justify-center bg-bg-surface/60 backdrop-blur-[1px]\">\n <div\n role=\"status\"\n className=\"inline-flex items-center gap-2 rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-secondary shadow-[var(--shadow-elevation-2)]\"\n >\n <Loader2\n className=\"size-4 animate-spin text-text-secondary\"\n aria-hidden=\"true\"\n />\n <span>\n {scopeValue\n ? tr('agentGraph.loadingScope', 'Applying selected scope...')\n : tr('agentGraph.refreshing', 'Refreshing graph...')}\n </span>\n </div>\n </div>\n )}\n </div>\n )}\n </>\n )}\n\n {showDeployWizard && (\n <MeshDeployWizard\n sourceAgents={sourceAgents}\n initialSourceAgentId={deploySeedSourceId}\n availablePermissions={availablePermissions}\n onClose={() => setShowDeployWizard(false)}\n onCompleted={() => {\n void refetch();\n }}\n />\n )}\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|