@burdenoff/microfe-vibecontrols 2026.527.4 → 2026.527.6
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 +85 -76
- package/dist/components/agents/AgentCard.js.map +1 -1
- package/dist/components/ai/AIPlanPanel.js +45 -33
- package/dist/components/ai/AIPlanPanel.js.map +1 -1
- package/dist/pages/AgentDetailsPage.js +263 -255
- package/dist/pages/AgentDetailsPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
import { AgentStatusBadge as e } from "./AgentStatusBadge.js";
|
|
2
2
|
import { useState as t } from "react";
|
|
3
3
|
import { useI18n as n } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
4
|
-
import { Clock as r, Cpu as i, Globe as a, Link as o, MoreVertical as s, Play as c,
|
|
5
|
-
import { AnimatePresence as
|
|
6
|
-
import { jsx as
|
|
4
|
+
import { Clock as r, Cpu as i, Globe as a, Link as o, MoreVertical as s, Play as c, RefreshCw as l, Server as u, Settings as d, Square as f } from "lucide-react";
|
|
5
|
+
import { AnimatePresence as p, motion as m } from "framer-motion";
|
|
6
|
+
import { jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
7
7
|
//#region src/components/agents/AgentCard.tsx
|
|
8
|
-
var
|
|
9
|
-
let { t:
|
|
10
|
-
let n =
|
|
8
|
+
var _ = ({ agent: _, onSelect: v, onConfigure: y, onSettings: b, onStart: x, onStop: S, onDisconnect: C, onEditTunnelUrl: w, selected: T = !1, onToggleSelect: E, className: D = "", healthErrorMessage: O }) => {
|
|
9
|
+
let { t: k } = n(), A = (e, t) => {
|
|
10
|
+
let n = k(e);
|
|
11
11
|
return n === e ? t : n;
|
|
12
|
-
},
|
|
13
|
-
if (
|
|
14
|
-
if (!
|
|
15
|
-
if (
|
|
16
|
-
let e = new Date(
|
|
12
|
+
}, j = y || b, [M, N] = t(!1), P = (() => {
|
|
13
|
+
if (_.status) return _.status;
|
|
14
|
+
if (!_.isActive) return "OFFLINE";
|
|
15
|
+
if (_.lastHeartbeat) {
|
|
16
|
+
let e = new Date(_.lastHeartbeat).getTime();
|
|
17
17
|
if (Date.now() - e > 300 * 1e3) return "DEGRADED";
|
|
18
18
|
}
|
|
19
19
|
return "ACTIVE";
|
|
20
|
-
})(),
|
|
21
|
-
if (!e) return
|
|
20
|
+
})(), F = (e) => {
|
|
21
|
+
if (!e) return A("agentsPage.never", "Never");
|
|
22
22
|
let t = Date.now() - new Date(e).getTime();
|
|
23
|
-
return t < 6e4 ?
|
|
24
|
-
},
|
|
23
|
+
return t < 6e4 ? A("agentsPage.justNow", "Just now") : t < 36e5 ? `${Math.floor(t / 6e4)}m ago` : t < 864e5 ? `${Math.floor(t / 36e5)}h ago` : `${Math.floor(t / 864e5)}d ago`;
|
|
24
|
+
}, I = {
|
|
25
25
|
darwin: "🍎",
|
|
26
26
|
linux: "🐧",
|
|
27
27
|
windows: "🪟"
|
|
28
|
-
}[
|
|
29
|
-
return /* @__PURE__ */
|
|
28
|
+
}[_.platform || ""] || "💻";
|
|
29
|
+
return /* @__PURE__ */ g(m.div, {
|
|
30
30
|
whileHover: {
|
|
31
31
|
y: -2,
|
|
32
32
|
transition: {
|
|
@@ -40,68 +40,68 @@ var g = ({ agent: g, onSelect: _, onConfigure: v, onSettings: y, onStart: b, onS
|
|
|
40
40
|
}
|
|
41
41
|
},
|
|
42
42
|
whileTap: { scale: .98 },
|
|
43
|
-
className: `bg-bg-surface border rounded-lg p-4 h-full hover:border-border-strong hover:shadow-md transition-all cursor-pointer ${
|
|
44
|
-
onClick: () =>
|
|
43
|
+
className: `bg-bg-surface border rounded-lg p-4 h-full hover:border-border-strong hover:shadow-md transition-all cursor-pointer ${T ? "border-action-primary-bg ring-1 ring-action-primary-bg/30" : "border-border-default"} ${D}`,
|
|
44
|
+
onClick: () => v?.(_),
|
|
45
45
|
children: [
|
|
46
|
-
/* @__PURE__ */
|
|
46
|
+
/* @__PURE__ */ g("div", {
|
|
47
47
|
className: "flex items-start justify-between gap-2",
|
|
48
|
-
children: [/* @__PURE__ */
|
|
48
|
+
children: [/* @__PURE__ */ g("div", {
|
|
49
49
|
className: "flex items-start gap-2 sm:gap-3 min-w-0 flex-1",
|
|
50
50
|
children: [
|
|
51
|
-
|
|
51
|
+
E && /* @__PURE__ */ h("button", {
|
|
52
52
|
onClick: (e) => {
|
|
53
|
-
e.stopPropagation(),
|
|
53
|
+
e.stopPropagation(), E();
|
|
54
54
|
},
|
|
55
|
-
className: `flex-shrink-0 w-5 h-5 min-w-[1.25rem] min-h-[1.25rem] rounded border transition-colors mt-0.5 ${
|
|
56
|
-
children:
|
|
55
|
+
className: `flex-shrink-0 w-5 h-5 min-w-[1.25rem] min-h-[1.25rem] rounded border transition-colors mt-0.5 ${T ? "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`,
|
|
56
|
+
children: T && /* @__PURE__ */ h("svg", {
|
|
57
57
|
className: "w-3 h-3",
|
|
58
58
|
fill: "none",
|
|
59
59
|
viewBox: "0 0 24 24",
|
|
60
60
|
stroke: "currentColor",
|
|
61
61
|
strokeWidth: 3,
|
|
62
|
-
children: /* @__PURE__ */
|
|
62
|
+
children: /* @__PURE__ */ h("path", {
|
|
63
63
|
strokeLinecap: "round",
|
|
64
64
|
strokeLinejoin: "round",
|
|
65
65
|
d: "M5 13l4 4L19 7"
|
|
66
66
|
})
|
|
67
67
|
})
|
|
68
68
|
}),
|
|
69
|
-
/* @__PURE__ */
|
|
69
|
+
/* @__PURE__ */ h("div", {
|
|
70
70
|
className: "flex-shrink-0 w-8 h-8 sm:w-10 sm:h-10 rounded-lg bg-bg-sunken flex items-center justify-center",
|
|
71
|
-
children: /* @__PURE__ */
|
|
71
|
+
children: /* @__PURE__ */ h(u, { className: "w-4 h-4 sm:w-5 sm:h-5 text-text-secondary" })
|
|
72
72
|
}),
|
|
73
|
-
/* @__PURE__ */
|
|
73
|
+
/* @__PURE__ */ g("div", {
|
|
74
74
|
className: "min-w-0 flex-1",
|
|
75
|
-
children: [/* @__PURE__ */
|
|
75
|
+
children: [/* @__PURE__ */ g("div", {
|
|
76
76
|
className: "flex items-center gap-1.5 sm:gap-2 min-w-0",
|
|
77
|
-
children: [/* @__PURE__ */
|
|
77
|
+
children: [/* @__PURE__ */ h("h3", {
|
|
78
78
|
className: "font-semibold text-text-primary truncate min-w-0 max-w-[120px] sm:max-w-none text-sm sm:text-base",
|
|
79
|
-
children:
|
|
80
|
-
}), /* @__PURE__ */
|
|
79
|
+
children: _.name
|
|
80
|
+
}), /* @__PURE__ */ h("span", {
|
|
81
81
|
className: "text-base sm:text-lg flex-shrink-0",
|
|
82
|
-
children:
|
|
82
|
+
children: I
|
|
83
83
|
})]
|
|
84
|
-
}), /* @__PURE__ */
|
|
84
|
+
}), /* @__PURE__ */ h("p", {
|
|
85
85
|
className: "text-xs sm:text-sm text-text-secondary truncate",
|
|
86
|
-
children:
|
|
86
|
+
children: _.hostname || `ID: ${_.id}`
|
|
87
87
|
})]
|
|
88
88
|
})
|
|
89
89
|
]
|
|
90
|
-
}), /* @__PURE__ */
|
|
90
|
+
}), /* @__PURE__ */ g("div", {
|
|
91
91
|
className: "flex items-center gap-1 sm:gap-2 flex-shrink-0",
|
|
92
|
-
children: [/* @__PURE__ */
|
|
93
|
-
status:
|
|
94
|
-
reason:
|
|
95
|
-
}), (
|
|
92
|
+
children: [/* @__PURE__ */ h(e, {
|
|
93
|
+
status: P,
|
|
94
|
+
reason: O ?? null
|
|
95
|
+
}), (j || x || S) && /* @__PURE__ */ g("div", {
|
|
96
96
|
className: "relative",
|
|
97
|
-
children: [/* @__PURE__ */
|
|
97
|
+
children: [/* @__PURE__ */ h("button", {
|
|
98
98
|
onClick: (e) => {
|
|
99
|
-
e.stopPropagation(),
|
|
99
|
+
e.stopPropagation(), N(!M);
|
|
100
100
|
},
|
|
101
101
|
className: "p-1 sm:p-1.5 rounded hover:bg-bg-sunken transition-colors flex items-center justify-center",
|
|
102
102
|
"aria-label": "Agent actions menu",
|
|
103
|
-
children: /* @__PURE__ */
|
|
104
|
-
}), /* @__PURE__ */
|
|
103
|
+
children: /* @__PURE__ */ h(s, { className: "w-4 h-4 text-text-primary opacity-70 hover:opacity-100" })
|
|
104
|
+
}), /* @__PURE__ */ h(p, { children: M && /* @__PURE__ */ g(m.div, {
|
|
105
105
|
initial: {
|
|
106
106
|
opacity: 0,
|
|
107
107
|
scale: .95,
|
|
@@ -120,93 +120,102 @@ var g = ({ agent: g, onSelect: _, onConfigure: v, onSettings: y, onStart: b, onS
|
|
|
120
120
|
transition: { duration: .15 },
|
|
121
121
|
className: "absolute right-0 mt-1 w-40 bg-bg-surface border border-border-default rounded-lg shadow-lg z-10",
|
|
122
122
|
children: [
|
|
123
|
-
|
|
123
|
+
P === "ACTIVE" && S && /* @__PURE__ */ g(m.button, {
|
|
124
|
+
whileTap: { scale: .97 },
|
|
125
|
+
onClick: (e) => {
|
|
126
|
+
e.stopPropagation(), S(_), N(!1);
|
|
127
|
+
},
|
|
128
|
+
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg",
|
|
129
|
+
children: [/* @__PURE__ */ h(f, { className: "w-4 h-4" }), A("agentsPage.stopAgent", "Stop Agent")]
|
|
130
|
+
}),
|
|
131
|
+
(P === "STOPPED" || P === "OFFLINE") && x && /* @__PURE__ */ g(m.button, {
|
|
124
132
|
whileTap: { scale: .97 },
|
|
125
133
|
onClick: (e) => {
|
|
126
|
-
e.stopPropagation(), x(
|
|
134
|
+
e.stopPropagation(), x(_), N(!1);
|
|
127
135
|
},
|
|
128
136
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg",
|
|
129
|
-
children: [/* @__PURE__ */
|
|
137
|
+
children: [/* @__PURE__ */ h(c, { className: "w-4 h-4" }), A("agentsPage.startAgent", "Start Agent")]
|
|
130
138
|
}),
|
|
131
|
-
|
|
139
|
+
P === "DEGRADED" && x && /* @__PURE__ */ g(m.button, {
|
|
132
140
|
whileTap: { scale: .97 },
|
|
133
141
|
onClick: (e) => {
|
|
134
|
-
e.stopPropagation(),
|
|
142
|
+
e.stopPropagation(), x(_), N(!1);
|
|
135
143
|
},
|
|
136
144
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg",
|
|
137
|
-
|
|
145
|
+
title: O ? `Retry lifecycle/start — last error: ${O}` : "Retry lifecycle/start",
|
|
146
|
+
children: [/* @__PURE__ */ h(l, { className: "w-4 h-4" }), A("agentsPage.reconnectAgent", "Reconnect")]
|
|
138
147
|
}),
|
|
139
|
-
(
|
|
148
|
+
(P === "ACTIVE" || P === "STOPPED" || P === "OFFLINE" || P === "DEGRADED") && C && /* @__PURE__ */ g(m.button, {
|
|
140
149
|
whileTap: { scale: .97 },
|
|
141
150
|
onClick: (e) => {
|
|
142
|
-
e.stopPropagation(),
|
|
151
|
+
e.stopPropagation(), C(_), N(!1);
|
|
143
152
|
},
|
|
144
153
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-action-danger-text hover:bg-bg-sunken",
|
|
145
|
-
children: [/* @__PURE__ */
|
|
154
|
+
children: [/* @__PURE__ */ h(f, { className: "w-4 h-4" }), P === "OFFLINE" ? "Clear Connection" : "Disconnect"]
|
|
146
155
|
}),
|
|
147
|
-
|
|
156
|
+
w && /* @__PURE__ */ g(m.button, {
|
|
148
157
|
whileTap: { scale: .97 },
|
|
149
158
|
onClick: (e) => {
|
|
150
|
-
e.stopPropagation(),
|
|
159
|
+
e.stopPropagation(), w(_), N(!1);
|
|
151
160
|
},
|
|
152
161
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken",
|
|
153
|
-
children: [/* @__PURE__ */
|
|
162
|
+
children: [/* @__PURE__ */ h(o, { className: "w-4 h-4" }), "Edit Tunnel"]
|
|
154
163
|
}),
|
|
155
|
-
|
|
164
|
+
j && /* @__PURE__ */ g(m.button, {
|
|
156
165
|
whileTap: { scale: .97 },
|
|
157
166
|
onClick: (e) => {
|
|
158
|
-
e.stopPropagation(),
|
|
167
|
+
e.stopPropagation(), j(_), N(!1);
|
|
159
168
|
},
|
|
160
169
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-b-lg",
|
|
161
|
-
children: [/* @__PURE__ */
|
|
170
|
+
children: [/* @__PURE__ */ h(d, { className: "w-4 h-4" }), "Settings"]
|
|
162
171
|
})
|
|
163
172
|
]
|
|
164
173
|
}) })]
|
|
165
174
|
})]
|
|
166
175
|
})]
|
|
167
176
|
}),
|
|
168
|
-
/* @__PURE__ */
|
|
177
|
+
/* @__PURE__ */ g("div", {
|
|
169
178
|
className: "mt-4 grid grid-cols-2 gap-3 text-sm",
|
|
170
|
-
children: [/* @__PURE__ */
|
|
179
|
+
children: [/* @__PURE__ */ g("div", {
|
|
171
180
|
className: "flex items-center gap-2 text-text-secondary",
|
|
172
|
-
children: [/* @__PURE__ */
|
|
173
|
-
}),
|
|
181
|
+
children: [/* @__PURE__ */ h(r, { className: "w-4 h-4" }), /* @__PURE__ */ h("span", { children: F(_.lastHeartbeat) })]
|
|
182
|
+
}), _.architecture && /* @__PURE__ */ g("div", {
|
|
174
183
|
className: "flex items-center gap-2 text-text-secondary",
|
|
175
|
-
children: [/* @__PURE__ */
|
|
184
|
+
children: [/* @__PURE__ */ h(i, { className: "w-4 h-4" }), /* @__PURE__ */ h("span", { children: _.architecture })]
|
|
176
185
|
})]
|
|
177
186
|
}),
|
|
178
|
-
|
|
187
|
+
_.tunnelUrl && /* @__PURE__ */ g("div", {
|
|
179
188
|
className: "mt-3 flex items-center gap-2 text-sm",
|
|
180
189
|
children: [
|
|
181
|
-
/* @__PURE__ */
|
|
182
|
-
/* @__PURE__ */
|
|
190
|
+
/* @__PURE__ */ h(a, { className: "w-4 h-4 flex-shrink-0 text-text-secondary" }),
|
|
191
|
+
/* @__PURE__ */ h("span", {
|
|
183
192
|
className: "text-text-secondary flex-shrink-0",
|
|
184
193
|
children: "Tunnel:"
|
|
185
194
|
}),
|
|
186
|
-
/* @__PURE__ */
|
|
187
|
-
href:
|
|
195
|
+
/* @__PURE__ */ h("a", {
|
|
196
|
+
href: _.tunnelUrl,
|
|
188
197
|
target: "_blank",
|
|
189
198
|
rel: "noopener noreferrer",
|
|
190
199
|
onClick: (e) => e.stopPropagation(),
|
|
191
200
|
className: "text-action-primary-text hover:underline truncate",
|
|
192
|
-
children:
|
|
201
|
+
children: _.tunnelUrl
|
|
193
202
|
})
|
|
194
203
|
]
|
|
195
204
|
}),
|
|
196
|
-
|
|
205
|
+
_.capabilities && _.capabilities.length > 0 && /* @__PURE__ */ g("div", {
|
|
197
206
|
className: "mt-3 flex flex-wrap gap-1",
|
|
198
|
-
children: [
|
|
207
|
+
children: [_.capabilities.slice(0, 4).map((e) => /* @__PURE__ */ h("span", {
|
|
199
208
|
className: "px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded",
|
|
200
209
|
children: e
|
|
201
|
-
}, e)),
|
|
210
|
+
}, e)), _.capabilities.length > 4 && /* @__PURE__ */ g("span", {
|
|
202
211
|
className: "px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded",
|
|
203
|
-
children: ["+",
|
|
212
|
+
children: ["+", _.capabilities.length - 4]
|
|
204
213
|
})]
|
|
205
214
|
})
|
|
206
215
|
]
|
|
207
216
|
});
|
|
208
217
|
};
|
|
209
218
|
//#endregion
|
|
210
|
-
export {
|
|
219
|
+
export { _ as AgentCard };
|
|
211
220
|
|
|
212
221
|
//# sourceMappingURL=AgentCard.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentCard.js","names":[],"sources":["../../../src/components/agents/AgentCard.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport {\n Server,\n Clock,\n Cpu,\n MoreVertical,\n Play,\n Square,\n Settings,\n Globe,\n Link,\n} from 'lucide-react';\nimport { AgentStatusBadge } from './AgentStatusBadge';\nimport type { Agent, AgentStatus } from './types';\n\nexport interface AgentCardProps {\n agent: Agent;\n onSelect?: (agent: Agent) => void;\n onConfigure?: (agent: Agent) => void;\n onSettings?: (agent: Agent) => void;\n onStart?: (agent: Agent) => void;\n onStop?: (agent: Agent) => void;\n onDisconnect?: (agent: Agent) => void;\n onEditTunnelUrl?: (agent: Agent) => void;\n selected?: boolean;\n onToggleSelect?: () => void;\n className?: string;\n /**\n * Last error from /health/ready or the live probe — shown in the badge\n * tooltip when the agent is degraded so users know WHY without curling\n * the tunnel themselves.\n */\n healthErrorMessage?: string | null;\n}\n\n/**\n * AgentCard - Display agent information in a card format\n *\n * @example\n * ```tsx\n * <AgentCard\n * agent={myAgent}\n * onSelect={handleSelect}\n * onConfigure={handleConfigure}\n * />\n * ```\n */\nexport const AgentCard: FC<AgentCardProps> = ({\n agent,\n onSelect,\n onConfigure,\n onSettings,\n onStart,\n onStop,\n onDisconnect,\n onEditTunnelUrl,\n selected = false,\n onToggleSelect,\n className = '',\n healthErrorMessage,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n // Support both onConfigure and onSettings\n const handleConfigure = onConfigure || onSettings;\n const [showMenu, setShowMenu] = useState(false);\n\n const getStatus = (): AgentStatus => {\n if (agent.status) return agent.status;\n if (!agent.isActive) return 'OFFLINE';\n\n if (agent.lastHeartbeat) {\n const lastHeartbeatTime = new Date(agent.lastHeartbeat).getTime();\n const now = Date.now();\n const diff = now - lastHeartbeatTime;\n\n // If heartbeat is older than 5 minutes, consider it inactive\n if (diff > 5 * 60 * 1000) return 'DEGRADED';\n }\n\n return 'ACTIVE';\n };\n\n const status = getStatus();\n\n const formatLastHeartbeat = (timestamp: string | null | undefined) => {\n if (!timestamp) return tr('agentsPage.never', 'Never');\n const diff = Date.now() - new Date(timestamp).getTime();\n if (diff < 60000) return tr('agentsPage.justNow', '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\n const platformIcon =\n {\n darwin: '🍎',\n linux: '🐧',\n windows: '🪟',\n }[agent.platform || ''] || '💻';\n\n return (\n <motion.div\n whileHover={{ y: -2, transition: { duration: 0.15, ease: [0.4, 0, 0.2, 1] } }}\n whileTap={{ scale: 0.98 }}\n className={`bg-bg-surface border rounded-lg p-4 h-full hover:border-border-strong hover:shadow-md transition-all cursor-pointer ${\n selected\n ? 'border-action-primary-bg ring-1 ring-action-primary-bg/30'\n : 'border-border-default'\n } ${className}`}\n onClick={() => onSelect?.(agent)}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex items-start gap-2 sm:gap-3 min-w-0 flex-1\">\n {/* Selection Checkbox */}\n {onToggleSelect && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n onToggleSelect();\n }}\n className={`flex-shrink-0 w-5 h-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`}\n >\n {selected && (\n <svg\n className=\"w-3 h-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 <div className=\"flex-shrink-0 w-8 h-8 sm:w-10 sm:h-10 rounded-lg bg-bg-sunken flex items-center justify-center\">\n <Server className=\"w-4 h-4 sm:w-5 sm:h-5 text-text-secondary\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-1.5 sm:gap-2 min-w-0\">\n <h3 className=\"font-semibold text-text-primary truncate min-w-0 max-w-[120px] sm:max-w-none text-sm sm:text-base\">\n {agent.name}\n </h3>\n <span className=\"text-base sm:text-lg flex-shrink-0\">{platformIcon}</span>\n </div>\n <p className=\"text-xs sm:text-sm text-text-secondary truncate\">\n {agent.hostname || `ID: ${agent.id}`}\n </p>\n </div>\n </div>\n {/* Status and Actions - always visible */}\n <div className=\"flex items-center gap-1 sm:gap-2 flex-shrink-0\">\n <AgentStatusBadge status={status} reason={healthErrorMessage ?? null} />\n {(handleConfigure || onStart || onStop) && (\n <div className=\"relative\">\n <button\n onClick={(e) => {\n e.stopPropagation();\n setShowMenu(!showMenu);\n }}\n className=\"p-1 sm:p-1.5 rounded hover:bg-bg-sunken transition-colors flex items-center justify-center\"\n aria-label=\"Agent actions menu\"\n >\n <MoreVertical className=\"w-4 h-4 text-text-primary opacity-70 hover:opacity-100\" />\n </button>\n <AnimatePresence>\n {showMenu && (\n <motion.div\n initial={{ opacity: 0, scale: 0.95, y: -4 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, y: -4 }}\n transition={{ duration: 0.15 }}\n className=\"absolute right-0 mt-1 w-40 bg-bg-surface border border-border-default rounded-lg shadow-lg z-10\"\n >\n {status === 'ACTIVE' && onStop && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onStop(agent);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg\"\n >\n <Square className=\"w-4 h-4\" />\n {tr('agentsPage.stopAgent', 'Stop Agent')}\n </motion.button>\n )}\n {(status === 'STOPPED' || status === 'OFFLINE') && onStart && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onStart(agent);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg\"\n >\n <Play className=\"w-4 h-4\" />\n {tr('agentsPage.startAgent', 'Start Agent')}\n </motion.button>\n )}\n {(status === 'ACTIVE' || status === 'STOPPED' || status === 'OFFLINE') &&\n onDisconnect && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onDisconnect(agent);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-action-danger-text hover:bg-bg-sunken\"\n >\n <Square className=\"w-4 h-4\" />\n {status === 'OFFLINE' ? 'Clear Connection' : 'Disconnect'}\n </motion.button>\n )}\n {onEditTunnelUrl && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onEditTunnelUrl(agent);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Link className=\"w-4 h-4\" />\n Edit Tunnel\n </motion.button>\n )}\n {handleConfigure && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n handleConfigure(agent);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-b-lg\"\n >\n <Settings className=\"w-4 h-4\" />\n Settings\n </motion.button>\n )}\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )}\n </div>\n </div>\n\n <div className=\"mt-4 grid grid-cols-2 gap-3 text-sm\">\n <div className=\"flex items-center gap-2 text-text-secondary\">\n <Clock className=\"w-4 h-4\" />\n <span>{formatLastHeartbeat(agent.lastHeartbeat)}</span>\n </div>\n {agent.architecture && (\n <div className=\"flex items-center gap-2 text-text-secondary\">\n <Cpu className=\"w-4 h-4\" />\n <span>{agent.architecture}</span>\n </div>\n )}\n </div>\n\n {agent.tunnelUrl && (\n // Plain \"Tunnel: <link>\" row. The colored status chip that used\n // to sit here was a redundant second status surface — the\n // canonical state lives in the <AgentStatusBadge> top-right.\n // The Globe glyph stays as a neutral icon (no status color).\n <div className=\"mt-3 flex items-center gap-2 text-sm\">\n <Globe className=\"w-4 h-4 flex-shrink-0 text-text-secondary\" />\n <span className=\"text-text-secondary flex-shrink-0\">Tunnel:</span>\n <a\n href={agent.tunnelUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n onClick={(e) => e.stopPropagation()}\n className=\"text-action-primary-text hover:underline truncate\"\n >\n {agent.tunnelUrl}\n </a>\n </div>\n )}\n\n {agent.capabilities && agent.capabilities.length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-1\">\n {agent.capabilities.slice(0, 4).map((cap) => (\n <span\n key={cap}\n className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\"\n >\n {cap}\n </span>\n ))}\n {agent.capabilities.length > 4 && (\n <span className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n +{agent.capabilities.length - 4}\n </span>\n )}\n </div>\n )}\n </motion.div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AgentCard.js","names":[],"sources":["../../../src/components/agents/AgentCard.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport {\n Server,\n Clock,\n Cpu,\n MoreVertical,\n Play,\n Square,\n Settings,\n Globe,\n Link,\n RefreshCw,\n} from 'lucide-react';\nimport { AgentStatusBadge } from './AgentStatusBadge';\nimport type { Agent, AgentStatus } from './types';\n\nexport interface AgentCardProps {\n agent: Agent;\n onSelect?: (agent: Agent) => void;\n onConfigure?: (agent: Agent) => void;\n onSettings?: (agent: Agent) => void;\n onStart?: (agent: Agent) => void;\n onStop?: (agent: Agent) => void;\n onDisconnect?: (agent: Agent) => void;\n onEditTunnelUrl?: (agent: Agent) => void;\n selected?: boolean;\n onToggleSelect?: () => void;\n className?: string;\n /**\n * Last error from /health/ready or the live probe — shown in the badge\n * tooltip when the agent is degraded so users know WHY without curling\n * the tunnel themselves.\n */\n healthErrorMessage?: string | null;\n}\n\n/**\n * AgentCard - Display agent information in a card format\n *\n * @example\n * ```tsx\n * <AgentCard\n * agent={myAgent}\n * onSelect={handleSelect}\n * onConfigure={handleConfigure}\n * />\n * ```\n */\nexport const AgentCard: FC<AgentCardProps> = ({\n agent,\n onSelect,\n onConfigure,\n onSettings,\n onStart,\n onStop,\n onDisconnect,\n onEditTunnelUrl,\n selected = false,\n onToggleSelect,\n className = '',\n healthErrorMessage,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n // Support both onConfigure and onSettings\n const handleConfigure = onConfigure || onSettings;\n const [showMenu, setShowMenu] = useState(false);\n\n const getStatus = (): AgentStatus => {\n if (agent.status) return agent.status;\n if (!agent.isActive) return 'OFFLINE';\n\n if (agent.lastHeartbeat) {\n const lastHeartbeatTime = new Date(agent.lastHeartbeat).getTime();\n const now = Date.now();\n const diff = now - lastHeartbeatTime;\n\n // If heartbeat is older than 5 minutes, consider it inactive\n if (diff > 5 * 60 * 1000) return 'DEGRADED';\n }\n\n return 'ACTIVE';\n };\n\n const status = getStatus();\n\n const formatLastHeartbeat = (timestamp: string | null | undefined) => {\n if (!timestamp) return tr('agentsPage.never', 'Never');\n const diff = Date.now() - new Date(timestamp).getTime();\n if (diff < 60000) return tr('agentsPage.justNow', '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\n const platformIcon =\n {\n darwin: '🍎',\n linux: '🐧',\n windows: '🪟',\n }[agent.platform || ''] || '💻';\n\n return (\n <motion.div\n whileHover={{ y: -2, transition: { duration: 0.15, ease: [0.4, 0, 0.2, 1] } }}\n whileTap={{ scale: 0.98 }}\n className={`bg-bg-surface border rounded-lg p-4 h-full hover:border-border-strong hover:shadow-md transition-all cursor-pointer ${\n selected\n ? 'border-action-primary-bg ring-1 ring-action-primary-bg/30'\n : 'border-border-default'\n } ${className}`}\n onClick={() => onSelect?.(agent)}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex items-start gap-2 sm:gap-3 min-w-0 flex-1\">\n {/* Selection Checkbox */}\n {onToggleSelect && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n onToggleSelect();\n }}\n className={`flex-shrink-0 w-5 h-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`}\n >\n {selected && (\n <svg\n className=\"w-3 h-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 <div className=\"flex-shrink-0 w-8 h-8 sm:w-10 sm:h-10 rounded-lg bg-bg-sunken flex items-center justify-center\">\n <Server className=\"w-4 h-4 sm:w-5 sm:h-5 text-text-secondary\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-1.5 sm:gap-2 min-w-0\">\n <h3 className=\"font-semibold text-text-primary truncate min-w-0 max-w-[120px] sm:max-w-none text-sm sm:text-base\">\n {agent.name}\n </h3>\n <span className=\"text-base sm:text-lg flex-shrink-0\">{platformIcon}</span>\n </div>\n <p className=\"text-xs sm:text-sm text-text-secondary truncate\">\n {agent.hostname || `ID: ${agent.id}`}\n </p>\n </div>\n </div>\n {/* Status and Actions - always visible */}\n <div className=\"flex items-center gap-1 sm:gap-2 flex-shrink-0\">\n <AgentStatusBadge status={status} reason={healthErrorMessage ?? null} />\n {(handleConfigure || onStart || onStop) && (\n <div className=\"relative\">\n <button\n onClick={(e) => {\n e.stopPropagation();\n setShowMenu(!showMenu);\n }}\n className=\"p-1 sm:p-1.5 rounded hover:bg-bg-sunken transition-colors flex items-center justify-center\"\n aria-label=\"Agent actions menu\"\n >\n <MoreVertical className=\"w-4 h-4 text-text-primary opacity-70 hover:opacity-100\" />\n </button>\n <AnimatePresence>\n {showMenu && (\n <motion.div\n initial={{ opacity: 0, scale: 0.95, y: -4 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, y: -4 }}\n transition={{ duration: 0.15 }}\n className=\"absolute right-0 mt-1 w-40 bg-bg-surface border border-border-default rounded-lg shadow-lg z-10\"\n >\n {status === 'ACTIVE' && onStop && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onStop(agent);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg\"\n >\n <Square className=\"w-4 h-4\" />\n {tr('agentsPage.stopAgent', 'Stop Agent')}\n </motion.button>\n )}\n {(status === 'STOPPED' || status === 'OFFLINE') && onStart && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onStart(agent);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg\"\n >\n <Play className=\"w-4 h-4\" />\n {tr('agentsPage.startAgent', 'Start Agent')}\n </motion.button>\n )}\n {status === 'DEGRADED' && onStart && (\n // Reconnect on a degraded agent fires the same\n // startVibecontrolsAgent mutation as a fresh start —\n // svc-side that POSTs lifecycle/start to the agent's\n // tunnel, which usually clears transient failures\n // (tunnel hiccup, finalize retry). Labelled\n // \"Reconnect\" not \"Start\" so the user knows it's a\n // recovery action, not creating a new instance.\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onStart(agent);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg\"\n title={\n healthErrorMessage\n ? `Retry lifecycle/start — last error: ${healthErrorMessage}`\n : 'Retry lifecycle/start'\n }\n >\n <RefreshCw className=\"w-4 h-4\" />\n {tr('agentsPage.reconnectAgent', 'Reconnect')}\n </motion.button>\n )}\n {(status === 'ACTIVE' ||\n status === 'STOPPED' ||\n status === 'OFFLINE' ||\n status === 'DEGRADED') &&\n onDisconnect && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onDisconnect(agent);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-action-danger-text hover:bg-bg-sunken\"\n >\n <Square className=\"w-4 h-4\" />\n {status === 'OFFLINE' ? 'Clear Connection' : 'Disconnect'}\n </motion.button>\n )}\n {onEditTunnelUrl && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onEditTunnelUrl(agent);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Link className=\"w-4 h-4\" />\n Edit Tunnel\n </motion.button>\n )}\n {handleConfigure && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n handleConfigure(agent);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-b-lg\"\n >\n <Settings className=\"w-4 h-4\" />\n Settings\n </motion.button>\n )}\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )}\n </div>\n </div>\n\n <div className=\"mt-4 grid grid-cols-2 gap-3 text-sm\">\n <div className=\"flex items-center gap-2 text-text-secondary\">\n <Clock className=\"w-4 h-4\" />\n <span>{formatLastHeartbeat(agent.lastHeartbeat)}</span>\n </div>\n {agent.architecture && (\n <div className=\"flex items-center gap-2 text-text-secondary\">\n <Cpu className=\"w-4 h-4\" />\n <span>{agent.architecture}</span>\n </div>\n )}\n </div>\n\n {agent.tunnelUrl && (\n // Plain \"Tunnel: <link>\" row. The colored status chip that used\n // to sit here was a redundant second status surface — the\n // canonical state lives in the <AgentStatusBadge> top-right.\n // The Globe glyph stays as a neutral icon (no status color).\n <div className=\"mt-3 flex items-center gap-2 text-sm\">\n <Globe className=\"w-4 h-4 flex-shrink-0 text-text-secondary\" />\n <span className=\"text-text-secondary flex-shrink-0\">Tunnel:</span>\n <a\n href={agent.tunnelUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n onClick={(e) => e.stopPropagation()}\n className=\"text-action-primary-text hover:underline truncate\"\n >\n {agent.tunnelUrl}\n </a>\n </div>\n )}\n\n {agent.capabilities && agent.capabilities.length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-1\">\n {agent.capabilities.slice(0, 4).map((cap) => (\n <span\n key={cap}\n className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\"\n >\n {cap}\n </span>\n ))}\n {agent.capabilities.length > 4 && (\n <span className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n +{agent.capabilities.length - 4}\n </span>\n )}\n </div>\n )}\n </motion.div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -30,7 +30,7 @@ function E({ agent: e, agentApiKey: t, tunnelUrl: n }) {
|
|
|
30
30
|
let { t: u } = f(), m = s((e, t) => {
|
|
31
31
|
let n = u(e);
|
|
32
32
|
return n === e ? t : n;
|
|
33
|
-
}, [u]), [h, _] = d(null), [y, b] = d(null), [x, S] = d(!0), [T, E] = d([]), [A, j] = d(!1), [M, N] = d(null), [P, F] = d(null), [I, L] = d(null), R = s(async (t = !1) => {
|
|
33
|
+
}, [u]), [h, _] = d(null), [y, b] = d(null), [x, S] = d(!0), [T, E] = d([]), [A, j] = d(!1), [M, N] = d(null), [P, F] = d(null), [I, L] = d(null), [R, z] = d(!1), B = s(async (t = !1) => {
|
|
34
34
|
t || (S(!0), b(null));
|
|
35
35
|
try {
|
|
36
36
|
if (t) {
|
|
@@ -46,13 +46,13 @@ function E({ agent: e, agentApiKey: t, tunnelUrl: n }) {
|
|
|
46
46
|
}
|
|
47
47
|
}, [e]);
|
|
48
48
|
c(() => {
|
|
49
|
-
|
|
50
|
-
}, [
|
|
51
|
-
let
|
|
49
|
+
B();
|
|
50
|
+
}, [B]);
|
|
51
|
+
let V = s(async () => {
|
|
52
52
|
j(!0), L(null), F(null);
|
|
53
53
|
try {
|
|
54
54
|
let t = await a(e);
|
|
55
|
-
t.ok ? (F(m("plan.cliInstalled", "Plannotator CLI installed.")), await
|
|
55
|
+
t.ok ? (F(m("plan.cliInstalled", "Plannotator CLI installed.")), await B()) : L(t.error ?? "Install failed");
|
|
56
56
|
} catch (e) {
|
|
57
57
|
L(e instanceof Error ? e.message : "Install failed");
|
|
58
58
|
} finally {
|
|
@@ -60,13 +60,13 @@ function E({ agent: e, agentApiKey: t, tunnelUrl: n }) {
|
|
|
60
60
|
}
|
|
61
61
|
}, [
|
|
62
62
|
e,
|
|
63
|
-
|
|
63
|
+
B,
|
|
64
64
|
m
|
|
65
|
-
]),
|
|
65
|
+
]), H = s(async (t) => {
|
|
66
66
|
N(t), L(null), F(null);
|
|
67
67
|
try {
|
|
68
68
|
let n = await r(e, t);
|
|
69
|
-
n.ok ? (F(m("plan.hookConfigured", `Hook configured for ${t}.`)), await
|
|
69
|
+
n.ok ? (F(m("plan.hookConfigured", `Hook configured for ${t}.`)), await B()) : L(n.error ?? "Hook configure failed");
|
|
70
70
|
} catch (e) {
|
|
71
71
|
L(e instanceof Error ? e.message : "Hook configure failed");
|
|
72
72
|
} finally {
|
|
@@ -74,24 +74,25 @@ function E({ agent: e, agentApiKey: t, tunnelUrl: n }) {
|
|
|
74
74
|
}
|
|
75
75
|
}, [
|
|
76
76
|
e,
|
|
77
|
-
|
|
77
|
+
B,
|
|
78
78
|
m
|
|
79
|
-
]),
|
|
79
|
+
]), U = l(() => x ? "loading" : y ? "status-error" : h?.installed ? !T.some((e) => e.hookConfigured) || R ? "wizard" : "ready" : "install-cli", [
|
|
80
80
|
h,
|
|
81
81
|
y,
|
|
82
82
|
x,
|
|
83
|
-
T
|
|
83
|
+
T,
|
|
84
|
+
R
|
|
84
85
|
]);
|
|
85
86
|
return c(() => {
|
|
86
|
-
if (
|
|
87
|
+
if (U !== "ready") return;
|
|
87
88
|
let e = setInterval(() => {
|
|
88
|
-
document.hidden ||
|
|
89
|
+
document.hidden || B(!0);
|
|
89
90
|
}, 5e3);
|
|
90
91
|
return () => clearInterval(e);
|
|
91
|
-
}, [
|
|
92
|
+
}, [U, B]), U === "loading" ? /* @__PURE__ */ w("div", {
|
|
92
93
|
className: "flex items-center gap-2 p-6 text-text-secondary",
|
|
93
94
|
children: [/* @__PURE__ */ C(g, { className: "h-4 w-4 animate-spin" }), /* @__PURE__ */ C("span", { children: m("plan.loadingStatus", "Loading plan status…") })]
|
|
94
|
-
}) :
|
|
95
|
+
}) : U === "status-error" ? /* @__PURE__ */ C("div", {
|
|
95
96
|
className: "rounded-md border border-status-error-border bg-status-error-bg p-4 text-sm text-status-error-text",
|
|
96
97
|
children: /* @__PURE__ */ w("div", {
|
|
97
98
|
className: "flex items-start gap-2",
|
|
@@ -107,7 +108,7 @@ function E({ agent: e, agentApiKey: t, tunnelUrl: n }) {
|
|
|
107
108
|
children: y
|
|
108
109
|
}),
|
|
109
110
|
/* @__PURE__ */ w("button", {
|
|
110
|
-
onClick: () => void
|
|
111
|
+
onClick: () => void B(),
|
|
111
112
|
className: "mt-3 inline-flex items-center gap-1 text-xs font-medium hover:underline",
|
|
112
113
|
children: [
|
|
113
114
|
/* @__PURE__ */ C(v, { className: "h-3 w-3" }),
|
|
@@ -131,24 +132,28 @@ function E({ agent: e, agentApiKey: t, tunnelUrl: n }) {
|
|
|
131
132
|
className: "rounded-md border border-status-error-border bg-status-error-bg px-3 py-2 text-xs text-status-error-text",
|
|
132
133
|
children: I
|
|
133
134
|
}),
|
|
134
|
-
|
|
135
|
+
U === "install-cli" && /* @__PURE__ */ C(D, {
|
|
135
136
|
installing: A,
|
|
136
|
-
onInstall:
|
|
137
|
+
onInstall: V,
|
|
137
138
|
tr: m
|
|
138
139
|
}),
|
|
139
|
-
|
|
140
|
+
U === "wizard" && /* @__PURE__ */ C(O, {
|
|
140
141
|
agents: T,
|
|
141
142
|
configuring: M,
|
|
142
|
-
onConfigure:
|
|
143
|
+
onConfigure: H,
|
|
144
|
+
canExit: R && T.some((e) => e.hookConfigured),
|
|
145
|
+
onExit: () => z(!1),
|
|
143
146
|
tr: m
|
|
144
147
|
}),
|
|
145
|
-
|
|
148
|
+
U === "ready" && /* @__PURE__ */ C(k, {
|
|
146
149
|
agent: e,
|
|
147
150
|
agents: T,
|
|
148
151
|
agentApiKey: t,
|
|
149
152
|
tunnelUrl: n,
|
|
150
153
|
runningSessions: h?.runningSessions ?? [],
|
|
151
|
-
onReconfigure: () =>
|
|
154
|
+
onReconfigure: () => {
|
|
155
|
+
F(null), z(!0);
|
|
156
|
+
},
|
|
152
157
|
tr: m
|
|
153
158
|
})
|
|
154
159
|
]
|
|
@@ -181,8 +186,8 @@ function D({ installing: e, onInstall: t, tr: n }) {
|
|
|
181
186
|
})
|
|
182
187
|
});
|
|
183
188
|
}
|
|
184
|
-
function O({ agents: e, configuring: t, onConfigure: n,
|
|
185
|
-
let
|
|
189
|
+
function O({ agents: e, configuring: t, onConfigure: n, canExit: r, onExit: i, tr: a }) {
|
|
190
|
+
let o = e.filter((e) => e.cliInstalled);
|
|
186
191
|
return /* @__PURE__ */ C("div", {
|
|
187
192
|
className: "rounded-lg border border-border-default bg-bg-elevated p-6",
|
|
188
193
|
children: /* @__PURE__ */ w("div", {
|
|
@@ -190,17 +195,24 @@ function O({ agents: e, configuring: t, onConfigure: n, tr: r }) {
|
|
|
190
195
|
children: [/* @__PURE__ */ C(y, { className: "h-6 w-6 text-action-primary-bg shrink-0" }), /* @__PURE__ */ w("div", {
|
|
191
196
|
className: "flex-1",
|
|
192
197
|
children: [
|
|
193
|
-
/* @__PURE__ */
|
|
194
|
-
className: "
|
|
195
|
-
children:
|
|
198
|
+
/* @__PURE__ */ w("div", {
|
|
199
|
+
className: "flex items-center justify-between gap-3",
|
|
200
|
+
children: [/* @__PURE__ */ C("h3", {
|
|
201
|
+
className: "text-base font-semibold text-text-primary",
|
|
202
|
+
children: a("plan.wizardTitle", "Connect an AI coding agent")
|
|
203
|
+
}), r && i && /* @__PURE__ */ C("button", {
|
|
204
|
+
onClick: i,
|
|
205
|
+
className: "shrink-0 inline-flex items-center gap-1 text-xs text-text-secondary hover:text-text-primary",
|
|
206
|
+
children: a("plan.wizardDone", "Done")
|
|
207
|
+
})]
|
|
196
208
|
}),
|
|
197
209
|
/* @__PURE__ */ C("p", {
|
|
198
210
|
className: "text-sm text-text-secondary mt-1",
|
|
199
|
-
children:
|
|
211
|
+
children: a("plan.wizardDesc", "Configure a plannotator hook for at least one installed AI coding agent. The hook forwards plan content from the agent to plannotator on every plan-finalisation step.")
|
|
200
212
|
}),
|
|
201
|
-
|
|
213
|
+
o.length === 0 && /* @__PURE__ */ C("p", {
|
|
202
214
|
className: "mt-4 text-sm text-status-warning-text",
|
|
203
|
-
children:
|
|
215
|
+
children: a("plan.wizardNoAgents", "No AI coding agents detected in this sandbox. Install one (Claude Code, OpenCode, Codex, Pi, or Gemini) and refresh.")
|
|
204
216
|
}),
|
|
205
217
|
/* @__PURE__ */ C("ul", {
|
|
206
218
|
className: "mt-4 space-y-2",
|
|
@@ -221,15 +233,15 @@ function O({ agents: e, configuring: t, onConfigure: n, tr: r }) {
|
|
|
221
233
|
}),
|
|
222
234
|
!e.cliInstalled && /* @__PURE__ */ C("p", {
|
|
223
235
|
className: "text-xs text-status-warning-text mt-1",
|
|
224
|
-
children:
|
|
236
|
+
children: a("plan.agentCliMissing", "CLI not detected on this sandbox.")
|
|
225
237
|
})
|
|
226
238
|
]
|
|
227
239
|
}),
|
|
228
240
|
/* @__PURE__ */ C("button", {
|
|
229
|
-
disabled: !e.cliInstalled ||
|
|
241
|
+
disabled: !e.cliInstalled || t === e.agent,
|
|
230
242
|
onClick: () => n(e.agent),
|
|
231
243
|
className: "px-3 py-1.5 text-xs rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bgHover disabled:opacity-50",
|
|
232
|
-
children: e.
|
|
244
|
+
children: t === e.agent ? a("plan.configuring", "Configuring…") : e.hookConfigured ? a("plan.reconfigureHook", "Reconfigure") : a("plan.configureHook", "Configure hook")
|
|
233
245
|
})
|
|
234
246
|
]
|
|
235
247
|
}, e.agent))
|