@burdenoff/microfe-vibecontrols 2026.529.2 → 2026.529.4
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/AgentCard.js +79 -86
- package/dist/components/agents/AgentCard.js.map +1 -1
- package/dist/components/agents/AgentList.js +95 -97
- package/dist/components/agents/AgentList.js.map +1 -1
- package/dist/components/agents/agentStatus.js +15 -0
- package/dist/components/agents/agentStatus.js.map +1 -0
- package/dist/components/sessions/TerminalIframe.js +22 -0
- package/dist/components/sessions/TerminalIframe.js.map +1 -0
- package/dist/components/sessions/TerminalPanel.js +343 -338
- package/dist/components/sessions/TerminalPanel.js.map +1 -1
- package/dist/components/shared/AgentSelector.js +69 -60
- package/dist/components/shared/AgentSelector.js.map +1 -1
- package/dist/hooks/useTerminalCapabilityUrl.js +55 -0
- package/dist/hooks/useTerminalCapabilityUrl.js.map +1 -0
- package/dist/pages/SessionsPage.js +300 -291
- package/dist/pages/SessionsPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
2
|
import { EmptyState as t } from "../shared/EmptyState.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { getAgentStatus as n } from "./agentStatus.js";
|
|
4
|
+
import { AgentStatusBadge as r } from "./AgentStatusBadge.js";
|
|
5
|
+
import { Clock as i, Link2Off as a, Play as o, Plus as s, Server as c, Settings as l, Square as u } from "lucide-react";
|
|
6
|
+
import { Fragment as d, jsx as f, jsxs as p } from "react/jsx-runtime";
|
|
6
7
|
//#region src/components/agents/AgentList.tsx
|
|
7
|
-
function p(e) {
|
|
8
|
-
return e.status ? e.status : e.isActive ? e.lastHeartbeat && Date.now() - new Date(e.lastHeartbeat).getTime() > 300 * 1e3 ? "DEGRADED" : "ACTIVE" : "OFFLINE";
|
|
9
|
-
}
|
|
10
8
|
function m(e) {
|
|
11
9
|
if (!e) return "Never";
|
|
12
10
|
let t = Date.now() - new Date(e).getTime();
|
|
@@ -17,8 +15,8 @@ var h = {
|
|
|
17
15
|
linux: "🐧 Linux",
|
|
18
16
|
windows: "🪟 Windows"
|
|
19
17
|
};
|
|
20
|
-
function g({ agent: e, onSelect: t, onStart:
|
|
21
|
-
return /* @__PURE__ */
|
|
18
|
+
function g({ agent: e, onSelect: t, onStart: s, onStop: d, onDisconnect: g, onSettings: _, selected: v = !1, onToggleSelect: y }) {
|
|
19
|
+
return /* @__PURE__ */ p("div", {
|
|
22
20
|
role: "button",
|
|
23
21
|
tabIndex: 0,
|
|
24
22
|
onKeyDown: (n) => {
|
|
@@ -27,124 +25,124 @@ function g({ agent: e, onSelect: t, onStart: o, onStop: u, onDisconnect: g, onSe
|
|
|
27
25
|
className: `relative p-4 border border-border-default rounded-lg bg-bg-surface hover:bg-bg-sunken/50 cursor-pointer transition-colors ${v ? "ring-2 ring-action-primary-bg" : ""}`,
|
|
28
26
|
onClick: () => t?.(e),
|
|
29
27
|
children: [
|
|
30
|
-
/* @__PURE__ */
|
|
28
|
+
/* @__PURE__ */ p("div", {
|
|
31
29
|
className: "flex items-start justify-between gap-3",
|
|
32
|
-
children: [/* @__PURE__ */
|
|
30
|
+
children: [/* @__PURE__ */ p("div", {
|
|
33
31
|
className: "flex items-start gap-3 min-w-0 flex-1",
|
|
34
32
|
children: [
|
|
35
|
-
y && /* @__PURE__ */
|
|
33
|
+
y && /* @__PURE__ */ f("button", {
|
|
36
34
|
type: "button",
|
|
37
35
|
onClick: (e) => {
|
|
38
36
|
e.stopPropagation(), y();
|
|
39
37
|
},
|
|
40
38
|
className: `size-5 min-w-[1.25rem] min-h-[1.25rem] rounded border transition-colors mt-0.5 ${v ? "bg-action-primary-bg border-action-primary-bg text-action-primary-text" : "border-border-default hover:border-border-strong bg-bg-surface"} flex items-center justify-center flex-shrink-0`,
|
|
41
|
-
children: v && /* @__PURE__ */
|
|
39
|
+
children: v && /* @__PURE__ */ f("svg", {
|
|
42
40
|
className: "size-3",
|
|
43
41
|
fill: "none",
|
|
44
42
|
viewBox: "0 0 24 24",
|
|
45
43
|
stroke: "currentColor",
|
|
46
44
|
strokeWidth: 3,
|
|
47
|
-
children: /* @__PURE__ */
|
|
45
|
+
children: /* @__PURE__ */ f("path", {
|
|
48
46
|
strokeLinecap: "round",
|
|
49
47
|
strokeLinejoin: "round",
|
|
50
48
|
d: "M5 13l4 4L19 7"
|
|
51
49
|
})
|
|
52
50
|
})
|
|
53
51
|
}),
|
|
54
|
-
/* @__PURE__ */
|
|
55
|
-
/* @__PURE__ */
|
|
52
|
+
/* @__PURE__ */ f(c, { className: "size-5 text-text-secondary flex-shrink-0 mt-0.5" }),
|
|
53
|
+
/* @__PURE__ */ p("div", {
|
|
56
54
|
className: "min-w-0 flex-1",
|
|
57
|
-
children: [/* @__PURE__ */
|
|
55
|
+
children: [/* @__PURE__ */ f("p", {
|
|
58
56
|
className: "text-sm font-medium text-text-primary truncate",
|
|
59
57
|
children: e.name
|
|
60
|
-
}), /* @__PURE__ */
|
|
58
|
+
}), /* @__PURE__ */ f("p", {
|
|
61
59
|
className: "text-xs text-text-muted truncate",
|
|
62
60
|
children: e.hostname || e.id.slice(0, 8)
|
|
63
61
|
})]
|
|
64
62
|
})
|
|
65
63
|
]
|
|
66
|
-
}), /* @__PURE__ */
|
|
64
|
+
}), /* @__PURE__ */ f(r, { status: n(e) })]
|
|
67
65
|
}),
|
|
68
|
-
/* @__PURE__ */
|
|
66
|
+
/* @__PURE__ */ p("div", {
|
|
69
67
|
className: "mt-3 flex flex-wrap items-center gap-x-4 gap-y-2 text-xs text-text-secondary",
|
|
70
68
|
children: [
|
|
71
|
-
/* @__PURE__ */
|
|
72
|
-
/* @__PURE__ */
|
|
73
|
-
/* @__PURE__ */
|
|
69
|
+
/* @__PURE__ */ f("span", { children: h[e.platform] || e.platform || "-" }),
|
|
70
|
+
/* @__PURE__ */ f("span", { children: e.architecture || "-" }),
|
|
71
|
+
/* @__PURE__ */ p("span", {
|
|
74
72
|
className: "flex items-center gap-1",
|
|
75
|
-
children: [/* @__PURE__ */
|
|
73
|
+
children: [/* @__PURE__ */ f(i, { className: "size-3" }), m(e.lastHeartbeat)]
|
|
76
74
|
})
|
|
77
75
|
]
|
|
78
76
|
}),
|
|
79
|
-
/* @__PURE__ */
|
|
77
|
+
/* @__PURE__ */ p("div", {
|
|
80
78
|
className: "mt-3 flex items-center gap-2 justify-end border-t border-border-subtle pt-3",
|
|
81
79
|
children: [
|
|
82
|
-
e.isActive &&
|
|
80
|
+
e.isActive && d && /* @__PURE__ */ f("button", {
|
|
83
81
|
type: "button",
|
|
84
82
|
onClick: (t) => {
|
|
85
|
-
t.stopPropagation(),
|
|
83
|
+
t.stopPropagation(), d(e);
|
|
86
84
|
},
|
|
87
85
|
className: "p-2 rounded hover:bg-bg-sunken transition-colors",
|
|
88
86
|
title: "Stop",
|
|
89
|
-
children: /* @__PURE__ */
|
|
87
|
+
children: /* @__PURE__ */ f(u, { className: "size-4 text-text-secondary" })
|
|
90
88
|
}),
|
|
91
|
-
!e.isActive &&
|
|
89
|
+
!e.isActive && s && /* @__PURE__ */ f("button", {
|
|
92
90
|
type: "button",
|
|
93
91
|
onClick: (t) => {
|
|
94
|
-
t.stopPropagation(),
|
|
92
|
+
t.stopPropagation(), s(e);
|
|
95
93
|
},
|
|
96
94
|
className: "p-2 rounded hover:bg-bg-sunken transition-colors",
|
|
97
95
|
title: "Start",
|
|
98
|
-
children: /* @__PURE__ */
|
|
96
|
+
children: /* @__PURE__ */ f(o, { className: "size-4 text-text-secondary" })
|
|
99
97
|
}),
|
|
100
98
|
g && [
|
|
101
99
|
"ACTIVE",
|
|
102
100
|
"STOPPED",
|
|
103
101
|
"OFFLINE"
|
|
104
|
-
].includes(
|
|
102
|
+
].includes(n(e)) && /* @__PURE__ */ f("button", {
|
|
105
103
|
type: "button",
|
|
106
104
|
onClick: (t) => {
|
|
107
105
|
t.stopPropagation(), g(e);
|
|
108
106
|
},
|
|
109
107
|
className: "p-2 rounded hover:bg-bg-sunken transition-colors",
|
|
110
|
-
title:
|
|
111
|
-
"aria-label":
|
|
112
|
-
children: /* @__PURE__ */
|
|
108
|
+
title: n(e) === "OFFLINE" ? "Clear Connection" : "Disconnect",
|
|
109
|
+
"aria-label": n(e) === "OFFLINE" ? "Clear Connection" : "Disconnect",
|
|
110
|
+
children: /* @__PURE__ */ f(a, { className: "size-4 text-action-danger-text" })
|
|
113
111
|
}),
|
|
114
|
-
_ && /* @__PURE__ */
|
|
112
|
+
_ && /* @__PURE__ */ f("button", {
|
|
115
113
|
type: "button",
|
|
116
114
|
onClick: (t) => {
|
|
117
115
|
t.stopPropagation(), _(e);
|
|
118
116
|
},
|
|
119
117
|
className: "p-2 rounded hover:bg-bg-sunken transition-colors",
|
|
120
118
|
title: "Settings",
|
|
121
|
-
children: /* @__PURE__ */
|
|
119
|
+
children: /* @__PURE__ */ f(l, { className: "size-4 text-text-secondary" })
|
|
122
120
|
})
|
|
123
121
|
]
|
|
124
122
|
})
|
|
125
123
|
]
|
|
126
124
|
});
|
|
127
125
|
}
|
|
128
|
-
function _({ agent: e, onSelect: t, onStart:
|
|
129
|
-
return /* @__PURE__ */
|
|
126
|
+
function _({ agent: e, onSelect: t, onStart: s, onStop: d, onDisconnect: g, onSettings: _, selected: v = !1, onToggleSelect: y }) {
|
|
127
|
+
return /* @__PURE__ */ p("tr", {
|
|
130
128
|
className: `relative border-b border-border-subtle hover:bg-bg-sunken/50 cursor-pointer transition-colors ${v ? "bg-action-primary-bg/5" : ""}`,
|
|
131
129
|
onClick: () => t?.(e),
|
|
132
130
|
children: [
|
|
133
|
-
y && /* @__PURE__ */
|
|
131
|
+
y && /* @__PURE__ */ f("td", {
|
|
134
132
|
className: "px-4 py-3 w-12",
|
|
135
|
-
children: /* @__PURE__ */
|
|
133
|
+
children: /* @__PURE__ */ f("button", {
|
|
136
134
|
type: "button",
|
|
137
135
|
onClick: (e) => {
|
|
138
136
|
e.stopPropagation(), y();
|
|
139
137
|
},
|
|
140
138
|
className: `size-5 min-w-[1.25rem] min-h-[1.25rem] rounded border transition-colors ${v ? "bg-action-primary-bg border-action-primary-bg text-action-primary-text" : "border-border-default hover:border-border-strong bg-bg-surface"} flex items-center justify-center`,
|
|
141
|
-
children: v && /* @__PURE__ */
|
|
139
|
+
children: v && /* @__PURE__ */ f("svg", {
|
|
142
140
|
className: "size-3",
|
|
143
141
|
fill: "none",
|
|
144
142
|
viewBox: "0 0 24 24",
|
|
145
143
|
stroke: "currentColor",
|
|
146
144
|
strokeWidth: 3,
|
|
147
|
-
children: /* @__PURE__ */
|
|
145
|
+
children: /* @__PURE__ */ f("path", {
|
|
148
146
|
strokeLinecap: "round",
|
|
149
147
|
strokeLinejoin: "round",
|
|
150
148
|
d: "M5 13l4 4L19 7"
|
|
@@ -152,92 +150,92 @@ function _({ agent: e, onSelect: t, onStart: o, onStop: u, onDisconnect: g, onSe
|
|
|
152
150
|
})
|
|
153
151
|
})
|
|
154
152
|
}),
|
|
155
|
-
/* @__PURE__ */
|
|
153
|
+
/* @__PURE__ */ f("td", {
|
|
156
154
|
className: "px-4 py-3",
|
|
157
|
-
children: /* @__PURE__ */
|
|
155
|
+
children: /* @__PURE__ */ p("div", {
|
|
158
156
|
className: "flex items-center gap-3 min-w-0",
|
|
159
|
-
children: [/* @__PURE__ */
|
|
157
|
+
children: [/* @__PURE__ */ f(c, { className: "size-4 text-text-secondary shrink-0" }), /* @__PURE__ */ p("div", {
|
|
160
158
|
className: "min-w-0",
|
|
161
|
-
children: [/* @__PURE__ */
|
|
159
|
+
children: [/* @__PURE__ */ f("p", {
|
|
162
160
|
className: "text-sm font-medium text-text-primary truncate",
|
|
163
161
|
children: e.name
|
|
164
|
-
}), /* @__PURE__ */
|
|
162
|
+
}), /* @__PURE__ */ f("p", {
|
|
165
163
|
className: "text-xs text-text-muted truncate",
|
|
166
164
|
children: e.hostname || e.id.slice(0, 8)
|
|
167
165
|
})]
|
|
168
166
|
})]
|
|
169
167
|
})
|
|
170
168
|
}),
|
|
171
|
-
/* @__PURE__ */
|
|
169
|
+
/* @__PURE__ */ f("td", {
|
|
172
170
|
className: "px-4 py-3",
|
|
173
|
-
children: /* @__PURE__ */
|
|
171
|
+
children: /* @__PURE__ */ f(r, { status: n(e) })
|
|
174
172
|
}),
|
|
175
|
-
/* @__PURE__ */
|
|
173
|
+
/* @__PURE__ */ f("td", {
|
|
176
174
|
className: "px-4 py-3",
|
|
177
|
-
children: /* @__PURE__ */
|
|
175
|
+
children: /* @__PURE__ */ f("span", {
|
|
178
176
|
className: "text-sm text-text-secondary",
|
|
179
177
|
children: h[e.platform] || e.platform || "-"
|
|
180
178
|
})
|
|
181
179
|
}),
|
|
182
|
-
/* @__PURE__ */
|
|
180
|
+
/* @__PURE__ */ f("td", {
|
|
183
181
|
className: "px-4 py-3",
|
|
184
|
-
children: /* @__PURE__ */
|
|
182
|
+
children: /* @__PURE__ */ f("span", {
|
|
185
183
|
className: "text-sm text-text-secondary",
|
|
186
184
|
children: e.architecture || "-"
|
|
187
185
|
})
|
|
188
186
|
}),
|
|
189
|
-
/* @__PURE__ */
|
|
187
|
+
/* @__PURE__ */ f("td", {
|
|
190
188
|
className: "px-4 py-3",
|
|
191
|
-
children: /* @__PURE__ */
|
|
189
|
+
children: /* @__PURE__ */ p("div", {
|
|
192
190
|
className: "flex items-center gap-1.5 text-xs text-text-muted",
|
|
193
|
-
children: [/* @__PURE__ */
|
|
191
|
+
children: [/* @__PURE__ */ f(i, { className: "size-3" }), m(e.lastHeartbeat)]
|
|
194
192
|
})
|
|
195
193
|
}),
|
|
196
|
-
/* @__PURE__ */
|
|
194
|
+
/* @__PURE__ */ f("td", {
|
|
197
195
|
className: "px-4 py-3",
|
|
198
|
-
children: /* @__PURE__ */
|
|
196
|
+
children: /* @__PURE__ */ p("div", {
|
|
199
197
|
className: "flex items-center gap-1 justify-end",
|
|
200
198
|
children: [
|
|
201
|
-
e.isActive &&
|
|
199
|
+
e.isActive && d && /* @__PURE__ */ f("button", {
|
|
202
200
|
type: "button",
|
|
203
201
|
onClick: (t) => {
|
|
204
|
-
t.stopPropagation(),
|
|
202
|
+
t.stopPropagation(), d(e);
|
|
205
203
|
},
|
|
206
204
|
className: "p-1.5 rounded hover:bg-bg-sunken transition-colors",
|
|
207
205
|
title: "Stop",
|
|
208
|
-
children: /* @__PURE__ */
|
|
206
|
+
children: /* @__PURE__ */ f(u, { className: "size-3.5 text-text-secondary" })
|
|
209
207
|
}),
|
|
210
|
-
!e.isActive &&
|
|
208
|
+
!e.isActive && s && /* @__PURE__ */ f("button", {
|
|
211
209
|
type: "button",
|
|
212
210
|
onClick: (t) => {
|
|
213
|
-
t.stopPropagation(),
|
|
211
|
+
t.stopPropagation(), s(e);
|
|
214
212
|
},
|
|
215
213
|
className: "p-1.5 rounded hover:bg-bg-sunken transition-colors",
|
|
216
214
|
title: "Start",
|
|
217
|
-
children: /* @__PURE__ */
|
|
215
|
+
children: /* @__PURE__ */ f(o, { className: "size-3.5 text-text-secondary" })
|
|
218
216
|
}),
|
|
219
217
|
g && [
|
|
220
218
|
"ACTIVE",
|
|
221
219
|
"STOPPED",
|
|
222
220
|
"OFFLINE"
|
|
223
|
-
].includes(
|
|
221
|
+
].includes(n(e)) && /* @__PURE__ */ f("button", {
|
|
224
222
|
type: "button",
|
|
225
223
|
onClick: (t) => {
|
|
226
224
|
t.stopPropagation(), g(e);
|
|
227
225
|
},
|
|
228
226
|
className: "p-1.5 rounded hover:bg-bg-sunken transition-colors",
|
|
229
|
-
title:
|
|
230
|
-
"aria-label":
|
|
231
|
-
children: /* @__PURE__ */
|
|
227
|
+
title: n(e) === "OFFLINE" ? "Clear Connection" : "Disconnect",
|
|
228
|
+
"aria-label": n(e) === "OFFLINE" ? "Clear Connection" : "Disconnect",
|
|
229
|
+
children: /* @__PURE__ */ f(a, { className: "size-3.5 text-action-danger-text" })
|
|
232
230
|
}),
|
|
233
|
-
_ && /* @__PURE__ */
|
|
231
|
+
_ && /* @__PURE__ */ f("button", {
|
|
234
232
|
type: "button",
|
|
235
233
|
onClick: (t) => {
|
|
236
234
|
t.stopPropagation(), _(e);
|
|
237
235
|
},
|
|
238
236
|
className: "p-1.5 rounded hover:bg-bg-sunken transition-colors",
|
|
239
237
|
title: "Settings",
|
|
240
|
-
children: /* @__PURE__ */
|
|
238
|
+
children: /* @__PURE__ */ f(l, { className: "size-3.5 text-text-secondary" })
|
|
241
239
|
})
|
|
242
240
|
]
|
|
243
241
|
})
|
|
@@ -245,60 +243,60 @@ function _({ agent: e, onSelect: t, onStart: o, onStop: u, onDisconnect: g, onSe
|
|
|
245
243
|
]
|
|
246
244
|
});
|
|
247
245
|
}
|
|
248
|
-
function v({ agents: n, onSelect: r, onStart: i, onStop: a, onDisconnect:
|
|
246
|
+
function v({ agents: n, onSelect: r, onStart: i, onStop: a, onDisconnect: o, onSettings: l, onCreateNew: u, isLoading: m, selectedIds: h, onToggleSelect: v, onSelectAll: y, onDeselectAll: b }) {
|
|
249
247
|
let x = e(), S = h && n.length > 0 && h.size === n.length;
|
|
250
|
-
return !m && n.length === 0 ? /* @__PURE__ */
|
|
251
|
-
icon: /* @__PURE__ */
|
|
248
|
+
return !m && n.length === 0 ? /* @__PURE__ */ f(t, {
|
|
249
|
+
icon: /* @__PURE__ */ f(c, { className: "size-8 text-text-secondary" }),
|
|
252
250
|
title: x("vibecontrols.agents.emptyTitle", "No agents configured"),
|
|
253
251
|
description: x("vibecontrols.agents.emptyDescription", "Agents are the bridge between VibeControls and your development environments. Install an agent to get started."),
|
|
254
|
-
action:
|
|
252
|
+
action: u && /* @__PURE__ */ p("button", {
|
|
255
253
|
type: "button",
|
|
256
|
-
onClick:
|
|
254
|
+
onClick: u,
|
|
257
255
|
className: "inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors",
|
|
258
|
-
children: [/* @__PURE__ */
|
|
256
|
+
children: [/* @__PURE__ */ f(s, { className: "size-4" }), x("vibecontrols.agents.addAgent", "Add Agent")]
|
|
259
257
|
})
|
|
260
|
-
}) : /* @__PURE__ */
|
|
258
|
+
}) : /* @__PURE__ */ p(d, { children: [/* @__PURE__ */ p("div", {
|
|
261
259
|
className: "sm:hidden space-y-3",
|
|
262
|
-
children: [v && n.length > 0 && /* @__PURE__ */
|
|
260
|
+
children: [v && n.length > 0 && /* @__PURE__ */ f("div", {
|
|
263
261
|
className: "flex justify-end",
|
|
264
|
-
children: /* @__PURE__ */
|
|
262
|
+
children: /* @__PURE__ */ f("button", {
|
|
265
263
|
type: "button",
|
|
266
264
|
onClick: () => S ? b?.() : y?.(),
|
|
267
265
|
className: "text-xs text-text-secondary hover:text-text-primary transition-colors",
|
|
268
266
|
children: S ? x("vibecontrols.agents.deselectAll", "Deselect All") : x("vibecontrols.agents.selectAll", "Select All")
|
|
269
267
|
})
|
|
270
|
-
}), n.map((e) => /* @__PURE__ */
|
|
268
|
+
}), n.map((e) => /* @__PURE__ */ f(g, {
|
|
271
269
|
agent: e,
|
|
272
270
|
onSelect: r,
|
|
273
271
|
onStart: i,
|
|
274
272
|
onStop: a,
|
|
275
|
-
onDisconnect:
|
|
273
|
+
onDisconnect: o,
|
|
276
274
|
onSettings: l,
|
|
277
275
|
selected: h?.has(e.id) ?? !1,
|
|
278
276
|
onToggleSelect: v ? () => v(e.id) : void 0
|
|
279
277
|
}, e.id))]
|
|
280
|
-
}), /* @__PURE__ */
|
|
278
|
+
}), /* @__PURE__ */ f("div", {
|
|
281
279
|
className: "hidden sm:block border border-border-default rounded-lg overflow-hidden",
|
|
282
|
-
children: /* @__PURE__ */
|
|
280
|
+
children: /* @__PURE__ */ f("div", {
|
|
283
281
|
className: "overflow-x-auto",
|
|
284
|
-
children: /* @__PURE__ */
|
|
282
|
+
children: /* @__PURE__ */ p("table", {
|
|
285
283
|
className: "w-full",
|
|
286
|
-
children: [/* @__PURE__ */
|
|
284
|
+
children: [/* @__PURE__ */ f("thead", { children: /* @__PURE__ */ p("tr", {
|
|
287
285
|
className: "bg-bg-sunken border-b border-border-default",
|
|
288
286
|
children: [
|
|
289
|
-
v && /* @__PURE__ */
|
|
287
|
+
v && /* @__PURE__ */ f("th", {
|
|
290
288
|
className: "px-4 py-2.5 w-12",
|
|
291
|
-
children: /* @__PURE__ */
|
|
289
|
+
children: /* @__PURE__ */ f("button", {
|
|
292
290
|
type: "button",
|
|
293
291
|
onClick: () => S ? b?.() : y?.(),
|
|
294
292
|
className: `size-5 min-w-[1.25rem] min-h-[1.25rem] rounded border transition-colors ${S ? "bg-action-primary-bg border-action-primary-bg text-action-primary-text" : "border-border-default hover:border-border-strong bg-bg-surface"} flex items-center justify-center`,
|
|
295
|
-
children: S && /* @__PURE__ */
|
|
293
|
+
children: S && /* @__PURE__ */ f("svg", {
|
|
296
294
|
className: "size-3",
|
|
297
295
|
fill: "none",
|
|
298
296
|
viewBox: "0 0 24 24",
|
|
299
297
|
stroke: "currentColor",
|
|
300
298
|
strokeWidth: 3,
|
|
301
|
-
children: /* @__PURE__ */
|
|
299
|
+
children: /* @__PURE__ */ f("path", {
|
|
302
300
|
strokeLinecap: "round",
|
|
303
301
|
strokeLinejoin: "round",
|
|
304
302
|
d: "M5 13l4 4L19 7"
|
|
@@ -306,37 +304,37 @@ function v({ agents: n, onSelect: r, onStart: i, onStop: a, onDisconnect: c, onS
|
|
|
306
304
|
})
|
|
307
305
|
})
|
|
308
306
|
}),
|
|
309
|
-
/* @__PURE__ */
|
|
307
|
+
/* @__PURE__ */ f("th", {
|
|
310
308
|
className: "px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider",
|
|
311
309
|
children: x("vibecontrols.agents.columnAgent", "Agent")
|
|
312
310
|
}),
|
|
313
|
-
/* @__PURE__ */
|
|
311
|
+
/* @__PURE__ */ f("th", {
|
|
314
312
|
className: "px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider",
|
|
315
313
|
children: x("vibecontrols.agents.columnStatus", "Status")
|
|
316
314
|
}),
|
|
317
|
-
/* @__PURE__ */
|
|
315
|
+
/* @__PURE__ */ f("th", {
|
|
318
316
|
className: "px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider",
|
|
319
317
|
children: x("vibecontrols.agents.columnPlatform", "Platform")
|
|
320
318
|
}),
|
|
321
|
-
/* @__PURE__ */
|
|
319
|
+
/* @__PURE__ */ f("th", {
|
|
322
320
|
className: "px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider",
|
|
323
321
|
children: x("vibecontrols.agents.columnArch", "Arch")
|
|
324
322
|
}),
|
|
325
|
-
/* @__PURE__ */
|
|
323
|
+
/* @__PURE__ */ f("th", {
|
|
326
324
|
className: "px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider",
|
|
327
325
|
children: x("vibecontrols.agents.columnLastHeartbeat", "Last Heartbeat")
|
|
328
326
|
}),
|
|
329
|
-
/* @__PURE__ */
|
|
327
|
+
/* @__PURE__ */ f("th", {
|
|
330
328
|
className: "px-4 py-2.5 text-right text-xs font-medium text-text-secondary uppercase tracking-wider",
|
|
331
329
|
children: x("vibecontrols.agents.columnActions", "Actions")
|
|
332
330
|
})
|
|
333
331
|
]
|
|
334
|
-
}) }), /* @__PURE__ */
|
|
332
|
+
}) }), /* @__PURE__ */ f("tbody", { children: n.map((e) => /* @__PURE__ */ f(_, {
|
|
335
333
|
agent: e,
|
|
336
334
|
onSelect: r,
|
|
337
335
|
onStart: i,
|
|
338
336
|
onStop: a,
|
|
339
|
-
onDisconnect:
|
|
337
|
+
onDisconnect: o,
|
|
340
338
|
onSettings: l,
|
|
341
339
|
selected: h?.has(e.id) ?? !1,
|
|
342
340
|
onToggleSelect: v ? () => v(e.id) : void 0
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentList.js","names":[],"sources":["../../../src/components/agents/AgentList.tsx"],"sourcesContent":["import type { Agent } from './types';\nimport { AgentStatusBadge } from './AgentStatusBadge';\nimport { EmptyState } from '../shared/EmptyState';\nimport { Server, Plus, Clock, Play, Square, Settings, Link2Off } from 'lucide-react';\nimport type { AgentStatus } from './types';\nimport { useTr } from '../../shared/hooks/useTr';\n\n/**\n * Derive agent status from agent properties\n */\nfunction getAgentStatus(agent: Agent): AgentStatus {\n if (agent.status) return agent.status;\n if (!agent.isActive) return 'OFFLINE';\n if (agent.lastHeartbeat) {\n const diff = Date.now() - new Date(agent.lastHeartbeat).getTime();\n if (diff > 5 * 60 * 1000) return 'DEGRADED';\n }\n return 'ACTIVE';\n}\n\n/**\n * Format a timestamp as a relative time string (e.g. \"5m ago\", \"2h ago\")\n */\nfunction formatLastHeartbeat(timestamp: string | null | undefined): string {\n if (!timestamp) return 'Never';\n const diff = Date.now() - new Date(timestamp).getTime();\n if (diff < 60000) return 'Just now';\n if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;\n if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;\n return `${Math.floor(diff / 86400000)}d ago`;\n}\n\ninterface AgentListProps {\n agents: Agent[];\n onSelect?: (agent: Agent) => void;\n onStart?: (agent: Agent) => void;\n onStop?: (agent: Agent) => void;\n onDisconnect?: (agent: Agent) => void;\n onSettings?: (agent: Agent) => void;\n onCreateNew?: () => void;\n isLoading?: boolean;\n selectedIds?: Set<string>;\n onToggleSelect?: (agentId: string) => void;\n onSelectAll?: () => void;\n onDeselectAll?: () => void;\n}\n\nconst platformLabels: Record<string, string> = {\n darwin: '🍎 macOS',\n linux: '🐧 Linux',\n windows: '🪟 Windows',\n};\n\n/**\n * Mobile card view for a single agent (used on small screens)\n */\nfunction AgentListCard({\n agent,\n onSelect,\n onStart,\n onStop,\n onDisconnect,\n onSettings,\n selected = false,\n onToggleSelect,\n}: {\n agent: Agent;\n onSelect?: (agent: Agent) => void;\n onStart?: (agent: Agent) => void;\n onStop?: (agent: Agent) => void;\n onDisconnect?: (agent: Agent) => void;\n onSettings?: (agent: Agent) => void;\n selected?: boolean;\n onToggleSelect?: () => void;\n}) {\n return (\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (() => onSelect?.(agent))();\n }\n }}\n className={`relative p-4 border border-border-default rounded-lg bg-bg-surface hover:bg-bg-sunken/50 cursor-pointer transition-colors ${\n selected ? 'ring-2 ring-action-primary-bg' : ''\n }`}\n onClick={() => onSelect?.(agent)}\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-start gap-3 min-w-0 flex-1\">\n {onToggleSelect && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onToggleSelect();\n }}\n className={`size-5 min-w-[1.25rem] min-h-[1.25rem] rounded border transition-colors mt-0.5 ${\n selected\n ? 'bg-action-primary-bg border-action-primary-bg text-action-primary-text'\n : 'border-border-default hover:border-border-strong bg-bg-surface'\n } flex items-center justify-center flex-shrink-0`}\n >\n {selected && (\n <svg\n className=\"size-3\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n strokeWidth={3}\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M5 13l4 4L19 7\" />\n </svg>\n )}\n </button>\n )}\n <Server className=\"size-5 text-text-secondary flex-shrink-0 mt-0.5\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium text-text-primary truncate\">{agent.name}</p>\n <p className=\"text-xs text-text-muted truncate\">\n {agent.hostname || agent.id.slice(0, 8)}\n </p>\n </div>\n </div>\n <AgentStatusBadge status={getAgentStatus(agent)} />\n </div>\n\n <div className=\"mt-3 flex flex-wrap items-center gap-x-4 gap-y-2 text-xs text-text-secondary\">\n <span>{platformLabels[agent.platform] || agent.platform || '-'}</span>\n <span>{agent.architecture || '-'}</span>\n <span className=\"flex items-center gap-1\">\n <Clock className=\"size-3\" />\n {formatLastHeartbeat(agent.lastHeartbeat)}\n </span>\n </div>\n\n <div className=\"mt-3 flex items-center gap-2 justify-end border-t border-border-subtle pt-3\">\n {agent.isActive && onStop && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onStop(agent);\n }}\n className=\"p-2 rounded hover:bg-bg-sunken transition-colors\"\n title=\"Stop\"\n >\n <Square className=\"size-4 text-text-secondary\" />\n </button>\n )}\n {!agent.isActive && onStart && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onStart(agent);\n }}\n className=\"p-2 rounded hover:bg-bg-sunken transition-colors\"\n title=\"Start\"\n >\n <Play className=\"size-4 text-text-secondary\" />\n </button>\n )}\n {onDisconnect &&\n (['ACTIVE', 'STOPPED', 'OFFLINE'] as AgentStatus[]).includes(getAgentStatus(agent)) && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onDisconnect(agent);\n }}\n className=\"p-2 rounded hover:bg-bg-sunken transition-colors\"\n title={getAgentStatus(agent) === 'OFFLINE' ? 'Clear Connection' : 'Disconnect'}\n aria-label={getAgentStatus(agent) === 'OFFLINE' ? 'Clear Connection' : 'Disconnect'}\n >\n <Link2Off className=\"size-4 text-action-danger-text\" />\n </button>\n )}\n {onSettings && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onSettings(agent);\n }}\n className=\"p-2 rounded hover:bg-bg-sunken transition-colors\"\n title=\"Settings\"\n >\n <Settings className=\"size-4 text-text-secondary\" />\n </button>\n )}\n </div>\n </div>\n );\n}\n\nfunction AgentRow({\n agent,\n onSelect,\n onStart,\n onStop,\n onDisconnect,\n onSettings,\n selected = false,\n onToggleSelect,\n}: {\n agent: Agent;\n onSelect?: (agent: Agent) => void;\n onStart?: (agent: Agent) => void;\n onStop?: (agent: Agent) => void;\n onDisconnect?: (agent: Agent) => void;\n onSettings?: (agent: Agent) => void;\n selected?: boolean;\n onToggleSelect?: () => void;\n}) {\n return (\n <tr\n className={`relative border-b border-border-subtle hover:bg-bg-sunken/50 cursor-pointer transition-colors ${\n selected ? 'bg-action-primary-bg/5' : ''\n }`}\n onClick={() => onSelect?.(agent)}\n >\n {onToggleSelect && (\n <td className=\"px-4 py-3 w-12\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onToggleSelect();\n }}\n className={`size-5 min-w-[1.25rem] min-h-[1.25rem] rounded border transition-colors ${\n selected\n ? 'bg-action-primary-bg border-action-primary-bg text-action-primary-text'\n : 'border-border-default hover:border-border-strong bg-bg-surface'\n } flex items-center justify-center`}\n >\n {selected && (\n <svg\n className=\"size-3\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n strokeWidth={3}\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M5 13l4 4L19 7\" />\n </svg>\n )}\n </button>\n </td>\n )}\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-3 min-w-0\">\n <Server className=\"size-4 text-text-secondary shrink-0\" />\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">{agent.name}</p>\n <p className=\"text-xs text-text-muted truncate\">\n {agent.hostname || agent.id.slice(0, 8)}\n </p>\n </div>\n </div>\n </td>\n <td className=\"px-4 py-3\">\n <AgentStatusBadge status={getAgentStatus(agent)} />\n </td>\n <td className=\"px-4 py-3\">\n <span className=\"text-sm text-text-secondary\">\n {platformLabels[agent.platform] || agent.platform || '-'}\n </span>\n </td>\n <td className=\"px-4 py-3\">\n <span className=\"text-sm text-text-secondary\">{agent.architecture || '-'}</span>\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-1.5 text-xs text-text-muted\">\n <Clock className=\"size-3\" />\n {formatLastHeartbeat(agent.lastHeartbeat)}\n </div>\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-1 justify-end\">\n {agent.isActive && onStop && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onStop(agent);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n title=\"Stop\"\n >\n <Square className=\"size-3.5 text-text-secondary\" />\n </button>\n )}\n {!agent.isActive && onStart && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onStart(agent);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n title=\"Start\"\n >\n <Play className=\"size-3.5 text-text-secondary\" />\n </button>\n )}\n {onDisconnect &&\n (['ACTIVE', 'STOPPED', 'OFFLINE'] as AgentStatus[]).includes(getAgentStatus(agent)) && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onDisconnect(agent);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n title={getAgentStatus(agent) === 'OFFLINE' ? 'Clear Connection' : 'Disconnect'}\n aria-label={getAgentStatus(agent) === 'OFFLINE' ? 'Clear Connection' : 'Disconnect'}\n >\n <Link2Off className=\"size-3.5 text-action-danger-text\" />\n </button>\n )}\n {onSettings && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onSettings(agent);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n title=\"Settings\"\n >\n <Settings className=\"size-3.5 text-text-secondary\" />\n </button>\n )}\n </div>\n </td>\n </tr>\n );\n}\n\nexport function AgentList({\n agents,\n onSelect,\n onStart,\n onStop,\n onDisconnect,\n onSettings,\n onCreateNew,\n isLoading,\n selectedIds,\n onToggleSelect,\n onSelectAll,\n onDeselectAll,\n}: AgentListProps) {\n const tr = useTr();\n const isAllSelected = selectedIds && agents.length > 0 && selectedIds.size === agents.length;\n if (!isLoading && agents.length === 0) {\n return (\n <EmptyState\n icon={<Server className=\"size-8 text-text-secondary\" />}\n title={tr('vibecontrols.agents.emptyTitle', 'No agents configured')}\n description={tr(\n 'vibecontrols.agents.emptyDescription',\n 'Agents are the bridge between VibeControls and your development environments. Install an agent to get started.'\n )}\n action={\n onCreateNew && (\n <button\n type=\"button\"\n onClick={onCreateNew}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\n {tr('vibecontrols.agents.addAgent', 'Add Agent')}\n </button>\n )\n }\n />\n );\n }\n\n return (\n <>\n {/* Mobile: Card view */}\n <div className=\"sm:hidden space-y-3\">\n {/* Select All button for mobile */}\n {onToggleSelect && agents.length > 0 && (\n <div className=\"flex justify-end\">\n <button\n type=\"button\"\n onClick={() => (isAllSelected ? onDeselectAll?.() : onSelectAll?.())}\n className=\"text-xs text-text-secondary hover:text-text-primary transition-colors\"\n >\n {isAllSelected\n ? tr('vibecontrols.agents.deselectAll', 'Deselect All')\n : tr('vibecontrols.agents.selectAll', 'Select All')}\n </button>\n </div>\n )}\n {agents.map((agent) => (\n <AgentListCard\n key={agent.id}\n agent={agent}\n onSelect={onSelect}\n onStart={onStart}\n onStop={onStop}\n onDisconnect={onDisconnect}\n onSettings={onSettings}\n selected={selectedIds?.has(agent.id) ?? false}\n onToggleSelect={onToggleSelect ? () => onToggleSelect(agent.id) : undefined}\n />\n ))}\n </div>\n\n {/* Desktop: Table view */}\n <div className=\"hidden sm:block border border-border-default rounded-lg overflow-hidden\">\n <div className=\"overflow-x-auto\">\n <table className=\"w-full\">\n <thead>\n <tr className=\"bg-bg-sunken border-b border-border-default\">\n {onToggleSelect && (\n <th className=\"px-4 py-2.5 w-12\">\n <button\n type=\"button\"\n onClick={() => (isAllSelected ? onDeselectAll?.() : onSelectAll?.())}\n className={`size-5 min-w-[1.25rem] min-h-[1.25rem] rounded border transition-colors ${\n isAllSelected\n ? 'bg-action-primary-bg border-action-primary-bg text-action-primary-text'\n : 'border-border-default hover:border-border-strong bg-bg-surface'\n } flex items-center justify-center`}\n >\n {isAllSelected && (\n <svg\n className=\"size-3\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n strokeWidth={3}\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M5 13l4 4L19 7\" />\n </svg>\n )}\n </button>\n </th>\n )}\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.agents.columnAgent', 'Agent')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.agents.columnStatus', 'Status')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.agents.columnPlatform', 'Platform')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.agents.columnArch', 'Arch')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.agents.columnLastHeartbeat', 'Last Heartbeat')}\n </th>\n <th className=\"px-4 py-2.5 text-right text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.agents.columnActions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody>\n {agents.map((agent) => (\n <AgentRow\n key={agent.id}\n agent={agent}\n onSelect={onSelect}\n onStart={onStart}\n onStop={onStop}\n onDisconnect={onDisconnect}\n onSettings={onSettings}\n selected={selectedIds?.has(agent.id) ?? false}\n onToggleSelect={onToggleSelect ? () => onToggleSelect(agent.id) : undefined}\n />\n ))}\n </tbody>\n </table>\n </div>\n </div>\n </>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AgentList.js","names":[],"sources":["../../../src/components/agents/AgentList.tsx"],"sourcesContent":["import type { Agent, AgentStatus } from './types';\nimport { AgentStatusBadge } from './AgentStatusBadge';\nimport { EmptyState } from '../shared/EmptyState';\nimport { Server, Plus, Clock, Play, Square, Settings, Link2Off } from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { getAgentStatus } from './agentStatus';\n\n/**\n * Format a timestamp as a relative time string (e.g. \"5m ago\", \"2h ago\")\n */\nfunction formatLastHeartbeat(timestamp: string | null | undefined): string {\n if (!timestamp) return 'Never';\n const diff = Date.now() - new Date(timestamp).getTime();\n if (diff < 60000) return 'Just now';\n if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;\n if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;\n return `${Math.floor(diff / 86400000)}d ago`;\n}\n\ninterface AgentListProps {\n agents: Agent[];\n onSelect?: (agent: Agent) => void;\n onStart?: (agent: Agent) => void;\n onStop?: (agent: Agent) => void;\n onDisconnect?: (agent: Agent) => void;\n onSettings?: (agent: Agent) => void;\n onCreateNew?: () => void;\n isLoading?: boolean;\n selectedIds?: Set<string>;\n onToggleSelect?: (agentId: string) => void;\n onSelectAll?: () => void;\n onDeselectAll?: () => void;\n}\n\nconst platformLabels: Record<string, string> = {\n darwin: '🍎 macOS',\n linux: '🐧 Linux',\n windows: '🪟 Windows',\n};\n\n/**\n * Mobile card view for a single agent (used on small screens)\n */\nfunction AgentListCard({\n agent,\n onSelect,\n onStart,\n onStop,\n onDisconnect,\n onSettings,\n selected = false,\n onToggleSelect,\n}: {\n agent: Agent;\n onSelect?: (agent: Agent) => void;\n onStart?: (agent: Agent) => void;\n onStop?: (agent: Agent) => void;\n onDisconnect?: (agent: Agent) => void;\n onSettings?: (agent: Agent) => void;\n selected?: boolean;\n onToggleSelect?: () => void;\n}) {\n return (\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (() => onSelect?.(agent))();\n }\n }}\n className={`relative p-4 border border-border-default rounded-lg bg-bg-surface hover:bg-bg-sunken/50 cursor-pointer transition-colors ${\n selected ? 'ring-2 ring-action-primary-bg' : ''\n }`}\n onClick={() => onSelect?.(agent)}\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-start gap-3 min-w-0 flex-1\">\n {onToggleSelect && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onToggleSelect();\n }}\n className={`size-5 min-w-[1.25rem] min-h-[1.25rem] rounded border transition-colors mt-0.5 ${\n selected\n ? 'bg-action-primary-bg border-action-primary-bg text-action-primary-text'\n : 'border-border-default hover:border-border-strong bg-bg-surface'\n } flex items-center justify-center flex-shrink-0`}\n >\n {selected && (\n <svg\n className=\"size-3\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n strokeWidth={3}\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M5 13l4 4L19 7\" />\n </svg>\n )}\n </button>\n )}\n <Server className=\"size-5 text-text-secondary flex-shrink-0 mt-0.5\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium text-text-primary truncate\">{agent.name}</p>\n <p className=\"text-xs text-text-muted truncate\">\n {agent.hostname || agent.id.slice(0, 8)}\n </p>\n </div>\n </div>\n <AgentStatusBadge status={getAgentStatus(agent)} />\n </div>\n\n <div className=\"mt-3 flex flex-wrap items-center gap-x-4 gap-y-2 text-xs text-text-secondary\">\n <span>{platformLabels[agent.platform] || agent.platform || '-'}</span>\n <span>{agent.architecture || '-'}</span>\n <span className=\"flex items-center gap-1\">\n <Clock className=\"size-3\" />\n {formatLastHeartbeat(agent.lastHeartbeat)}\n </span>\n </div>\n\n <div className=\"mt-3 flex items-center gap-2 justify-end border-t border-border-subtle pt-3\">\n {agent.isActive && onStop && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onStop(agent);\n }}\n className=\"p-2 rounded hover:bg-bg-sunken transition-colors\"\n title=\"Stop\"\n >\n <Square className=\"size-4 text-text-secondary\" />\n </button>\n )}\n {!agent.isActive && onStart && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onStart(agent);\n }}\n className=\"p-2 rounded hover:bg-bg-sunken transition-colors\"\n title=\"Start\"\n >\n <Play className=\"size-4 text-text-secondary\" />\n </button>\n )}\n {onDisconnect &&\n (['ACTIVE', 'STOPPED', 'OFFLINE'] as AgentStatus[]).includes(getAgentStatus(agent)) && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onDisconnect(agent);\n }}\n className=\"p-2 rounded hover:bg-bg-sunken transition-colors\"\n title={getAgentStatus(agent) === 'OFFLINE' ? 'Clear Connection' : 'Disconnect'}\n aria-label={getAgentStatus(agent) === 'OFFLINE' ? 'Clear Connection' : 'Disconnect'}\n >\n <Link2Off className=\"size-4 text-action-danger-text\" />\n </button>\n )}\n {onSettings && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onSettings(agent);\n }}\n className=\"p-2 rounded hover:bg-bg-sunken transition-colors\"\n title=\"Settings\"\n >\n <Settings className=\"size-4 text-text-secondary\" />\n </button>\n )}\n </div>\n </div>\n );\n}\n\nfunction AgentRow({\n agent,\n onSelect,\n onStart,\n onStop,\n onDisconnect,\n onSettings,\n selected = false,\n onToggleSelect,\n}: {\n agent: Agent;\n onSelect?: (agent: Agent) => void;\n onStart?: (agent: Agent) => void;\n onStop?: (agent: Agent) => void;\n onDisconnect?: (agent: Agent) => void;\n onSettings?: (agent: Agent) => void;\n selected?: boolean;\n onToggleSelect?: () => void;\n}) {\n return (\n <tr\n className={`relative border-b border-border-subtle hover:bg-bg-sunken/50 cursor-pointer transition-colors ${\n selected ? 'bg-action-primary-bg/5' : ''\n }`}\n onClick={() => onSelect?.(agent)}\n >\n {onToggleSelect && (\n <td className=\"px-4 py-3 w-12\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onToggleSelect();\n }}\n className={`size-5 min-w-[1.25rem] min-h-[1.25rem] rounded border transition-colors ${\n selected\n ? 'bg-action-primary-bg border-action-primary-bg text-action-primary-text'\n : 'border-border-default hover:border-border-strong bg-bg-surface'\n } flex items-center justify-center`}\n >\n {selected && (\n <svg\n className=\"size-3\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n strokeWidth={3}\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M5 13l4 4L19 7\" />\n </svg>\n )}\n </button>\n </td>\n )}\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-3 min-w-0\">\n <Server className=\"size-4 text-text-secondary shrink-0\" />\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">{agent.name}</p>\n <p className=\"text-xs text-text-muted truncate\">\n {agent.hostname || agent.id.slice(0, 8)}\n </p>\n </div>\n </div>\n </td>\n <td className=\"px-4 py-3\">\n <AgentStatusBadge status={getAgentStatus(agent)} />\n </td>\n <td className=\"px-4 py-3\">\n <span className=\"text-sm text-text-secondary\">\n {platformLabels[agent.platform] || agent.platform || '-'}\n </span>\n </td>\n <td className=\"px-4 py-3\">\n <span className=\"text-sm text-text-secondary\">{agent.architecture || '-'}</span>\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-1.5 text-xs text-text-muted\">\n <Clock className=\"size-3\" />\n {formatLastHeartbeat(agent.lastHeartbeat)}\n </div>\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-1 justify-end\">\n {agent.isActive && onStop && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onStop(agent);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n title=\"Stop\"\n >\n <Square className=\"size-3.5 text-text-secondary\" />\n </button>\n )}\n {!agent.isActive && onStart && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onStart(agent);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n title=\"Start\"\n >\n <Play className=\"size-3.5 text-text-secondary\" />\n </button>\n )}\n {onDisconnect &&\n (['ACTIVE', 'STOPPED', 'OFFLINE'] as AgentStatus[]).includes(getAgentStatus(agent)) && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onDisconnect(agent);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n title={getAgentStatus(agent) === 'OFFLINE' ? 'Clear Connection' : 'Disconnect'}\n aria-label={getAgentStatus(agent) === 'OFFLINE' ? 'Clear Connection' : 'Disconnect'}\n >\n <Link2Off className=\"size-3.5 text-action-danger-text\" />\n </button>\n )}\n {onSettings && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onSettings(agent);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n title=\"Settings\"\n >\n <Settings className=\"size-3.5 text-text-secondary\" />\n </button>\n )}\n </div>\n </td>\n </tr>\n );\n}\n\nexport function AgentList({\n agents,\n onSelect,\n onStart,\n onStop,\n onDisconnect,\n onSettings,\n onCreateNew,\n isLoading,\n selectedIds,\n onToggleSelect,\n onSelectAll,\n onDeselectAll,\n}: AgentListProps) {\n const tr = useTr();\n const isAllSelected = selectedIds && agents.length > 0 && selectedIds.size === agents.length;\n if (!isLoading && agents.length === 0) {\n return (\n <EmptyState\n icon={<Server className=\"size-8 text-text-secondary\" />}\n title={tr('vibecontrols.agents.emptyTitle', 'No agents configured')}\n description={tr(\n 'vibecontrols.agents.emptyDescription',\n 'Agents are the bridge between VibeControls and your development environments. Install an agent to get started.'\n )}\n action={\n onCreateNew && (\n <button\n type=\"button\"\n onClick={onCreateNew}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\n {tr('vibecontrols.agents.addAgent', 'Add Agent')}\n </button>\n )\n }\n />\n );\n }\n\n return (\n <>\n {/* Mobile: Card view */}\n <div className=\"sm:hidden space-y-3\">\n {/* Select All button for mobile */}\n {onToggleSelect && agents.length > 0 && (\n <div className=\"flex justify-end\">\n <button\n type=\"button\"\n onClick={() => (isAllSelected ? onDeselectAll?.() : onSelectAll?.())}\n className=\"text-xs text-text-secondary hover:text-text-primary transition-colors\"\n >\n {isAllSelected\n ? tr('vibecontrols.agents.deselectAll', 'Deselect All')\n : tr('vibecontrols.agents.selectAll', 'Select All')}\n </button>\n </div>\n )}\n {agents.map((agent) => (\n <AgentListCard\n key={agent.id}\n agent={agent}\n onSelect={onSelect}\n onStart={onStart}\n onStop={onStop}\n onDisconnect={onDisconnect}\n onSettings={onSettings}\n selected={selectedIds?.has(agent.id) ?? false}\n onToggleSelect={onToggleSelect ? () => onToggleSelect(agent.id) : undefined}\n />\n ))}\n </div>\n\n {/* Desktop: Table view */}\n <div className=\"hidden sm:block border border-border-default rounded-lg overflow-hidden\">\n <div className=\"overflow-x-auto\">\n <table className=\"w-full\">\n <thead>\n <tr className=\"bg-bg-sunken border-b border-border-default\">\n {onToggleSelect && (\n <th className=\"px-4 py-2.5 w-12\">\n <button\n type=\"button\"\n onClick={() => (isAllSelected ? onDeselectAll?.() : onSelectAll?.())}\n className={`size-5 min-w-[1.25rem] min-h-[1.25rem] rounded border transition-colors ${\n isAllSelected\n ? 'bg-action-primary-bg border-action-primary-bg text-action-primary-text'\n : 'border-border-default hover:border-border-strong bg-bg-surface'\n } flex items-center justify-center`}\n >\n {isAllSelected && (\n <svg\n className=\"size-3\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n strokeWidth={3}\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M5 13l4 4L19 7\" />\n </svg>\n )}\n </button>\n </th>\n )}\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.agents.columnAgent', 'Agent')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.agents.columnStatus', 'Status')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.agents.columnPlatform', 'Platform')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.agents.columnArch', 'Arch')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.agents.columnLastHeartbeat', 'Last Heartbeat')}\n </th>\n <th className=\"px-4 py-2.5 text-right text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.agents.columnActions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody>\n {agents.map((agent) => (\n <AgentRow\n key={agent.id}\n agent={agent}\n onSelect={onSelect}\n onStart={onStart}\n onStop={onStop}\n onDisconnect={onDisconnect}\n onSettings={onSettings}\n selected={selectedIds?.has(agent.id) ?? false}\n onToggleSelect={onToggleSelect ? () => onToggleSelect(agent.id) : undefined}\n />\n ))}\n </tbody>\n </table>\n </div>\n </div>\n </>\n );\n}\n"],"mappings":";;;;;;;AAUA,SAAS,EAAoB,GAA8C;AACzE,KAAI,CAAC,EAAW,QAAO;CACvB,IAAM,IAAO,KAAK,KAAK,GAAG,IAAI,KAAK,EAAU,CAAC,SAAS;AAIvD,QAHI,IAAO,MAAc,aACrB,IAAO,OAAgB,GAAG,KAAK,MAAM,IAAO,IAAM,CAAC,SACnD,IAAO,QAAiB,GAAG,KAAK,MAAM,IAAO,KAAQ,CAAC,SACnD,GAAG,KAAK,MAAM,IAAO,MAAS,CAAC;;AAkBxC,IAAM,IAAyC;CAC7C,QAAQ;CACR,OAAO;CACP,SAAS;CACV;AAKD,SAAS,EAAc,EACrB,UACA,aACA,YACA,WACA,iBACA,eACA,cAAW,IACX,qBAUC;AACD,QACE,kBAAC,OAAD;EACE,MAAK;EACL,UAAU;EACV,YAAY,MAAM;AAChB,IAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,SACjC,EAAE,gBAAgB,EACX,IAAW,EAAM;;EAG5B,WAAW,6HACT,IAAW,kCAAkC;EAE/C,eAAe,IAAW,EAAM;YAZlC;GAcE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACG,KACC,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,MAAM;AAEd,QADA,EAAE,iBAAiB,EACnB,GAAgB;;OAElB,WAAW,kFACT,IACI,2EACA,iEACL;iBAEA,KACC,kBAAC,OAAD;QACE,WAAU;QACV,MAAK;QACL,SAAQ;QACR,QAAO;QACP,aAAa;kBAEb,kBAAC,QAAD;SAAM,eAAc;SAAQ,gBAAe;SAAQ,GAAE;SAAmB,CAAA;QACpE,CAAA;OAED,CAAA;MAEX,kBAAC,GAAD,EAAQ,WAAU,mDAAoD,CAAA;MACtE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBAAkD,EAAM;QAAS,CAAA,EAC9E,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAM,YAAY,EAAM,GAAG,MAAM,GAAG,EAAE;QACrC,CAAA,CACA;;MACF;QACN,kBAAC,GAAD,EAAkB,QAAQ,EAAe,EAAM,EAAI,CAAA,CAC/C;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,QAAD,EAAA,UAAO,EAAe,EAAM,aAAa,EAAM,YAAY,KAAW,CAAA;KACtE,kBAAC,QAAD,EAAA,UAAO,EAAM,gBAAgB,KAAW,CAAA;KACxC,kBAAC,QAAD;MAAM,WAAU;gBAAhB,CACE,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA,EAC3B,EAAoB,EAAM,cAAc,CACpC;;KACH;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACG,EAAM,YAAY,KACjB,kBAAC,UAAD;MACE,MAAK;MACL,UAAU,MAAM;AAEd,OADA,EAAE,iBAAiB,EACnB,EAAO,EAAM;;MAEf,WAAU;MACV,OAAM;gBAEN,kBAAC,GAAD,EAAQ,WAAU,8BAA+B,CAAA;MAC1C,CAAA;KAEV,CAAC,EAAM,YAAY,KAClB,kBAAC,UAAD;MACE,MAAK;MACL,UAAU,MAAM;AAEd,OADA,EAAE,iBAAiB,EACnB,EAAQ,EAAM;;MAEhB,WAAU;MACV,OAAM;gBAEN,kBAAC,GAAD,EAAM,WAAU,8BAA+B,CAAA;MACxC,CAAA;KAEV,KACE;MAAC;MAAU;MAAW;MAAU,CAAmB,SAAS,EAAe,EAAM,CAAC,IACjF,kBAAC,UAAD;MACE,MAAK;MACL,UAAU,MAAM;AAEd,OADA,EAAE,iBAAiB,EACnB,EAAa,EAAM;;MAErB,WAAU;MACV,OAAO,EAAe,EAAM,KAAK,YAAY,qBAAqB;MAClE,cAAY,EAAe,EAAM,KAAK,YAAY,qBAAqB;gBAEvE,kBAAC,GAAD,EAAU,WAAU,kCAAmC,CAAA;MAChD,CAAA;KAEZ,KACC,kBAAC,UAAD;MACE,MAAK;MACL,UAAU,MAAM;AAEd,OADA,EAAE,iBAAiB,EACnB,EAAW,EAAM;;MAEnB,WAAU;MACV,OAAM;gBAEN,kBAAC,GAAD,EAAU,WAAU,8BAA+B,CAAA;MAC5C,CAAA;KAEP;;GACF;;;AAIV,SAAS,EAAS,EAChB,UACA,aACA,YACA,WACA,iBACA,eACA,cAAW,IACX,qBAUC;AACD,QACE,kBAAC,MAAD;EACE,WAAW,iGACT,IAAW,2BAA2B;EAExC,eAAe,IAAW,EAAM;YAJlC;GAMG,KACC,kBAAC,MAAD;IAAI,WAAU;cACZ,kBAAC,UAAD;KACE,MAAK;KACL,UAAU,MAAM;AAEd,MADA,EAAE,iBAAiB,EACnB,GAAgB;;KAElB,WAAW,2EACT,IACI,2EACA,iEACL;eAEA,KACC,kBAAC,OAAD;MACE,WAAU;MACV,MAAK;MACL,SAAQ;MACR,QAAO;MACP,aAAa;gBAEb,kBAAC,QAAD;OAAM,eAAc;OAAQ,gBAAe;OAAQ,GAAE;OAAmB,CAAA;MACpE,CAAA;KAED,CAAA;IACN,CAAA;GAEP,kBAAC,MAAD;IAAI,WAAU;cACZ,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,uCAAwC,CAAA,EAC1D,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBAAkD,EAAM;OAAS,CAAA,EAC9E,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAM,YAAY,EAAM,GAAG,MAAM,GAAG,EAAE;OACrC,CAAA,CACA;QACF;;IACH,CAAA;GACL,kBAAC,MAAD;IAAI,WAAU;cACZ,kBAAC,GAAD,EAAkB,QAAQ,EAAe,EAAM,EAAI,CAAA;IAChD,CAAA;GACL,kBAAC,MAAD;IAAI,WAAU;cACZ,kBAAC,QAAD;KAAM,WAAU;eACb,EAAe,EAAM,aAAa,EAAM,YAAY;KAChD,CAAA;IACJ,CAAA;GACL,kBAAC,MAAD;IAAI,WAAU;cACZ,kBAAC,QAAD;KAAM,WAAU;eAA+B,EAAM,gBAAgB;KAAW,CAAA;IAC7E,CAAA;GACL,kBAAC,MAAD;IAAI,WAAU;cACZ,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA,EAC3B,EAAoB,EAAM,cAAc,CACrC;;IACH,CAAA;GACL,kBAAC,MAAD;IAAI,WAAU;cACZ,kBAAC,OAAD;KAAK,WAAU;eAAf;MACG,EAAM,YAAY,KACjB,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,MAAM;AAEd,QADA,EAAE,iBAAiB,EACnB,EAAO,EAAM;;OAEf,WAAU;OACV,OAAM;iBAEN,kBAAC,GAAD,EAAQ,WAAU,gCAAiC,CAAA;OAC5C,CAAA;MAEV,CAAC,EAAM,YAAY,KAClB,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,MAAM;AAEd,QADA,EAAE,iBAAiB,EACnB,EAAQ,EAAM;;OAEhB,WAAU;OACV,OAAM;iBAEN,kBAAC,GAAD,EAAM,WAAU,gCAAiC,CAAA;OAC1C,CAAA;MAEV,KACE;OAAC;OAAU;OAAW;OAAU,CAAmB,SAAS,EAAe,EAAM,CAAC,IACjF,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,MAAM;AAEd,QADA,EAAE,iBAAiB,EACnB,EAAa,EAAM;;OAErB,WAAU;OACV,OAAO,EAAe,EAAM,KAAK,YAAY,qBAAqB;OAClE,cAAY,EAAe,EAAM,KAAK,YAAY,qBAAqB;iBAEvE,kBAAC,GAAD,EAAU,WAAU,oCAAqC,CAAA;OAClD,CAAA;MAEZ,KACC,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,MAAM;AAEd,QADA,EAAE,iBAAiB,EACnB,EAAW,EAAM;;OAEnB,WAAU;OACV,OAAM;iBAEN,kBAAC,GAAD,EAAU,WAAU,gCAAiC,CAAA;OAC9C,CAAA;MAEP;;IACH,CAAA;GACF;;;AAIT,SAAgB,EAAU,EACxB,WACA,aACA,YACA,WACA,iBACA,eACA,gBACA,cACA,gBACA,mBACA,gBACA,oBACiB;CACjB,IAAM,IAAK,GAAO,EACZ,IAAgB,KAAe,EAAO,SAAS,KAAK,EAAY,SAAS,EAAO;AA0BtF,QAzBI,CAAC,KAAa,EAAO,WAAW,IAEhC,kBAAC,GAAD;EACE,MAAM,kBAAC,GAAD,EAAQ,WAAU,8BAA+B,CAAA;EACvD,OAAO,EAAG,kCAAkC,uBAAuB;EACnE,aAAa,EACX,wCACA,iHACD;EACD,QACE,KACE,kBAAC,UAAD;GACE,MAAK;GACL,SAAS;GACT,WAAU;aAHZ,CAKE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA,EAC1B,EAAG,gCAAgC,YAAY,CACzC;;EAGb,CAAA,GAKJ,kBAAA,GAAA,EAAA,UAAA,CAEE,kBAAC,OAAD;EAAK,WAAU;YAAf,CAEG,KAAkB,EAAO,SAAS,KACjC,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,UAAD;IACE,MAAK;IACL,eAAgB,IAAgB,KAAiB,GAAG,KAAe;IACnE,WAAU;cAET,IACG,EAAG,mCAAmC,eAAe,GACrD,EAAG,iCAAiC,aAAa;IAC9C,CAAA;GACL,CAAA,EAEP,EAAO,KAAK,MACX,kBAAC,GAAD;GAES;GACG;GACD;GACD;GACM;GACF;GACZ,UAAU,GAAa,IAAI,EAAM,GAAG,IAAI;GACxC,gBAAgB,UAAuB,EAAe,EAAM,GAAG,GAAG,KAAA;GAClE,EATK,EAAM,GASX,CACF,CACE;KAGN,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,SAAD;IAAO,WAAU;cAAjB,CACE,kBAAC,SAAD,EAAA,UACE,kBAAC,MAAD;KAAI,WAAU;eAAd;MACG,KACC,kBAAC,MAAD;OAAI,WAAU;iBACZ,kBAAC,UAAD;QACE,MAAK;QACL,eAAgB,IAAgB,KAAiB,GAAG,KAAe;QACnE,WAAW,2EACT,IACI,2EACA,iEACL;kBAEA,KACC,kBAAC,OAAD;SACE,WAAU;SACV,MAAK;SACL,SAAQ;SACR,QAAO;SACP,aAAa;mBAEb,kBAAC,QAAD;UAAM,eAAc;UAAQ,gBAAe;UAAQ,GAAE;UAAmB,CAAA;SACpE,CAAA;QAED,CAAA;OACN,CAAA;MAEP,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,mCAAmC,QAAQ;OAC5C,CAAA;MACL,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,oCAAoC,SAAS;OAC9C,CAAA;MACL,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,sCAAsC,WAAW;OAClD,CAAA;MACL,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,kCAAkC,OAAO;OAC1C,CAAA;MACL,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,2CAA2C,iBAAiB;OAC7D,CAAA;MACL,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,qCAAqC,UAAU;OAChD,CAAA;MACF;QACC,CAAA,EACR,kBAAC,SAAD,EAAA,UACG,EAAO,KAAK,MACX,kBAAC,GAAD;KAES;KACG;KACD;KACD;KACM;KACF;KACZ,UAAU,GAAa,IAAI,EAAM,GAAG,IAAI;KACxC,gBAAgB,UAAuB,EAAe,EAAM,GAAG,GAAG,KAAA;KAClE,EATK,EAAM,GASX,CACF,EACI,CAAA,CACF;;GACJ,CAAA;EACF,CAAA,CACL,EAAA,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/components/agents/agentStatus.ts
|
|
2
|
+
var e = 300 * 1e3;
|
|
3
|
+
function t(t) {
|
|
4
|
+
if (t.status) return t.status;
|
|
5
|
+
if (t.isActive === !1) return "OFFLINE";
|
|
6
|
+
if (t.lastHeartbeat) {
|
|
7
|
+
let n = t.lastHeartbeat instanceof Date ? t.lastHeartbeat.getTime() : new Date(t.lastHeartbeat).getTime();
|
|
8
|
+
if (Number.isFinite(n) && Date.now() - n > e) return "DEGRADED";
|
|
9
|
+
}
|
|
10
|
+
return "ACTIVE";
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { t as getAgentStatus };
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=agentStatus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentStatus.js","names":[],"sources":["../../../src/components/agents/agentStatus.ts"],"sourcesContent":["/**\n * Shared agent-status derivation.\n *\n * Returns the live status of an agent given the fields available in the\n * common `VibecontrolsAgent` GraphQL fragment. Centralised here so every\n * consumer (AgentList, AgentCard, SessionsPage picker, AgentSelector,\n * future ones) speaks the same vocabulary — previously each page\n * derived its own, and the inconsistency manifested as the AgentsPage\n * showing one status while SessionsPage showed another.\n *\n * Precedence:\n * 1. `agent.status` — wins when present (future BE-side derived field).\n * 2. `agent.isActive === false` — OFFLINE (admin disable or stale-sweep).\n * 3. `agent.lastHeartbeat` older than 5 min — DEGRADED (active flag is\n * sticky-true until next sweep, so we surface the staleness here\n * rather than waiting for the periodic job).\n * 4. Default — ACTIVE.\n *\n * Keep in sync with the svc-side `markStaleAgentsInactive` threshold in\n * `agent.service.ts` (`STALE_HEARTBEAT_THRESHOLD_MS = 5 * 60 * 1000`).\n */\nimport type { AgentStatus } from './types';\n\nconst STALE_HEARTBEAT_MS = 5 * 60 * 1000;\n\nexport interface AgentStatusInput {\n status?: AgentStatus | string | null;\n isActive?: boolean | null;\n lastHeartbeat?: string | Date | null;\n}\n\nexport function getAgentStatus(agent: AgentStatusInput): AgentStatus {\n if (agent.status) return agent.status as AgentStatus;\n if (agent.isActive === false) return 'OFFLINE';\n if (agent.lastHeartbeat) {\n const last =\n agent.lastHeartbeat instanceof Date\n ? agent.lastHeartbeat.getTime()\n : new Date(agent.lastHeartbeat).getTime();\n if (Number.isFinite(last) && Date.now() - last > STALE_HEARTBEAT_MS) {\n return 'DEGRADED';\n }\n }\n return 'ACTIVE';\n}\n\n/** True if the agent is reachable enough to start a session against. */\nexport function isAgentSessionReady(agent: AgentStatusInput): boolean {\n return getAgentStatus(agent) === 'ACTIVE';\n}\n"],"mappings":";AAuBA,IAAM,IAAqB,MAAS;AAQpC,SAAgB,EAAe,GAAsC;AACnE,KAAI,EAAM,OAAQ,QAAO,EAAM;AAC/B,KAAI,EAAM,aAAa,GAAO,QAAO;AACrC,KAAI,EAAM,eAAe;EACvB,IAAM,IACJ,EAAM,yBAAyB,OAC3B,EAAM,cAAc,SAAS,GAC7B,IAAI,KAAK,EAAM,cAAc,CAAC,SAAS;AAC7C,MAAI,OAAO,SAAS,EAAK,IAAI,KAAK,KAAK,GAAG,IAAO,EAC/C,QAAO;;AAGX,QAAO"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useTerminalCapabilityUrl as e } from "../../hooks/useTerminalCapabilityUrl.js";
|
|
2
|
+
import { forwardRef as t } from "react";
|
|
3
|
+
import { Fragment as n, jsx as r } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/sessions/TerminalIframe.tsx
|
|
5
|
+
var i = t(function({ sessionId: t, agentId: i, baseTtydUrl: a, title: o, className: s, allow: c, onLoad: l, onError: u, fallback: d }, f) {
|
|
6
|
+
let { url: p, remint: m } = e(i, t, a);
|
|
7
|
+
return p ? /* @__PURE__ */ r("iframe", {
|
|
8
|
+
ref: f,
|
|
9
|
+
src: p,
|
|
10
|
+
title: o,
|
|
11
|
+
className: s,
|
|
12
|
+
allow: c,
|
|
13
|
+
onLoad: l,
|
|
14
|
+
onError: () => {
|
|
15
|
+
m(), u?.();
|
|
16
|
+
}
|
|
17
|
+
}) : /* @__PURE__ */ r(n, { children: d ?? null });
|
|
18
|
+
});
|
|
19
|
+
//#endregion
|
|
20
|
+
export { i as TerminalIframe };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=TerminalIframe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TerminalIframe.js","names":[],"sources":["../../../src/components/sessions/TerminalIframe.tsx"],"sourcesContent":["/**\n * TerminalIframe — renders the ttyd iframe with an F3-compliant\n * capability token appended as a `#vt=<token>` URL fragment.\n *\n * Each tab in the sessions terminal panel needs its own capability mint\n * (the iframe-token is scoped to `/terminal/<sessionId>`). React hooks\n * can't be called in a `tabs.map(...)` loop, so the iframe rendering is\n * extracted here and the hook runs per component instance.\n *\n * Stale browser tabs and reopened sessions hit a 10-minute cookie TTL\n * inside the agent; `onError` triggers `remint()` which re-fetches a\n * fresh token and re-iframes.\n */\nimport { type ReactNode, forwardRef } from 'react';\n\nimport { useTerminalCapabilityUrl } from '@/hooks/useTerminalCapabilityUrl';\n\nexport interface TerminalIframeProps {\n /** Session id — used as resourceId for the capability mint. */\n sessionId: string;\n /** Agent id owning this session — required for the mint mutation. */\n agentId: string | null | undefined;\n /** Bare ttyd tunnel URL from `session.ttydUrl`. Hook strips any legacy ?apiKey=. */\n baseTtydUrl: string | null | undefined;\n /** Title for accessibility. */\n title: string;\n /** Optional className for the iframe. */\n className?: string;\n /** Permissions-Policy allow attribute (clipboard). */\n allow?: string;\n /** Called on iframe load. Use to flip connection status to connected. */\n onLoad?: () => void;\n /** Called on iframe error. We re-mint internally + bubble the event. */\n onError?: () => void;\n /** Slot for fallback content when no URL is available yet. */\n fallback?: ReactNode;\n}\n\nexport const TerminalIframe = forwardRef<HTMLIFrameElement, TerminalIframeProps>(\n function TerminalIframe(\n { sessionId, agentId, baseTtydUrl, title, className, allow, onLoad, onError, fallback },\n ref\n ) {\n const { url, remint } = useTerminalCapabilityUrl(agentId, sessionId, baseTtydUrl);\n\n if (!url) {\n return <>{fallback ?? null}</>;\n }\n\n return (\n <iframe\n ref={ref}\n src={url}\n title={title}\n className={className}\n allow={allow}\n onLoad={onLoad}\n onError={() => {\n // Re-mint on any iframe error — covers both 401 (cookie expired\n // mid-session) and network blips.\n remint();\n onError?.();\n }}\n />\n );\n }\n);\n"],"mappings":";;;;AAsCA,IAAa,IAAiB,EAC5B,SACE,EAAE,cAAW,YAAS,gBAAa,UAAO,cAAW,UAAO,WAAQ,YAAS,eAC7E,GACA;CACA,IAAM,EAAE,QAAK,cAAW,EAAyB,GAAS,GAAW,EAAY;AAMjF,QAJK,IAKH,kBAAC,UAAD;EACO;EACL,KAAK;EACE;EACI;EACJ;EACC;EACR,eAAe;AAIb,GADA,GAAQ,EACR,KAAW;;EAEb,CAAA,GAjBK,kBAAA,GAAA,EAAA,UAAG,KAAY,MAAQ,CAAA;EAoBnC"}
|