@burdenoff/microfe-vibecontrols 2026.529.9 → 2026.529.10
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.
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
import { SearchableSelect as e } from "../shared/SearchableSelect.js";
|
|
2
2
|
import "../shared/index.js";
|
|
3
|
-
import { useCallback as t,
|
|
4
|
-
import { useI18n as
|
|
5
|
-
import { Check as
|
|
6
|
-
import { Fragment as
|
|
3
|
+
import { useCallback as t, useEffect as n, useState as r } from "react";
|
|
4
|
+
import { useI18n as i } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
5
|
+
import { Check as a, CheckCircle2 as o, ChevronDown as ee, ChevronLeft as te, ChevronRight as s, Copy as ne, Download as re, Eye as ie, EyeOff as ae, Loader2 as c, Plus as oe, RefreshCw as se, Settings2 as ce, Terminal as le, X as ue, XCircle as l } from "lucide-react";
|
|
6
|
+
import { Fragment as u, jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
7
7
|
//#region src/components/agents/AgentForm.tsx
|
|
8
|
-
function
|
|
9
|
-
let { t:
|
|
10
|
-
let n =
|
|
8
|
+
function p({ onSubmit: p, onCancel: m, isLoading: h, onNavigate: de, targets: g, targetsLoading: fe, onCreateTarget: _, extraSection: v }) {
|
|
9
|
+
let { t: pe } = i(), y = (e, t) => {
|
|
10
|
+
let n = pe(e);
|
|
11
11
|
return n === e ? t : n;
|
|
12
|
-
}, [
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
}, [b, x] = r("details"), [S, me] = r(!1), [C, he] = r(!1), [ge, w] = r(null), [T, E] = r(""), [D, _e] = r(!1), [O, k] = r(""), [A, j] = r(""), [M, N] = r("");
|
|
13
|
+
n(() => {
|
|
14
|
+
if (D || !M) return;
|
|
15
|
+
let e = (g ?? []).find((e) => e.id === M);
|
|
16
|
+
e?.name && E(e.name);
|
|
17
|
+
}, [
|
|
18
|
+
M,
|
|
19
|
+
g,
|
|
20
|
+
D
|
|
21
|
+
]);
|
|
22
|
+
let [P, F] = r(!1), [I, L] = r(""), [R, z] = r(!1), [B, V] = r(null), [H, U] = r(!1), [W, G] = r(null), [K, q] = r(null), [J, ve] = r(""), [ye, be] = r("linux"), [xe, Se] = r("x86_64"), [Ce, we] = r("1.0.0"), [Te, Y] = r(null), [X, Ee] = r(!1), [De, Oe] = r("default"), ke = t((e, t) => {
|
|
23
|
+
navigator.clipboard.writeText(e), w(t), setTimeout(() => w(null), 2e3);
|
|
24
|
+
}, []), Ae = (e) => {
|
|
15
25
|
let t = e.trim();
|
|
16
26
|
return t.endsWith("/") && (t = t.slice(0, -1)), t;
|
|
17
|
-
},
|
|
18
|
-
let e =
|
|
19
|
-
e && (
|
|
27
|
+
}, je = async () => {
|
|
28
|
+
let e = Ae(O);
|
|
29
|
+
e && (U(!0), G(null), q(null), await Promise.all([(async () => {
|
|
20
30
|
try {
|
|
21
31
|
let t = await fetch(`${e}/health`, { signal: AbortSignal.timeout(1e4) });
|
|
22
32
|
if (!t.ok) {
|
|
@@ -34,7 +44,7 @@ function h({ onSubmit: h, onCancel: g, isLoading: _, onNavigate: v, targets: y,
|
|
|
34
44
|
version: n.version || "unknown",
|
|
35
45
|
uptime: n.uptime || 0
|
|
36
46
|
}
|
|
37
|
-
}), n.version &&
|
|
47
|
+
}), n.version && we(n.version);
|
|
38
48
|
} catch (e) {
|
|
39
49
|
G({
|
|
40
50
|
ok: !1,
|
|
@@ -44,7 +54,7 @@ function h({ onSubmit: h, onCancel: g, isLoading: _, onNavigate: v, targets: y,
|
|
|
44
54
|
})(), (async () => {
|
|
45
55
|
try {
|
|
46
56
|
let t = {};
|
|
47
|
-
|
|
57
|
+
A && (t["x-agent-api-key"] = A);
|
|
48
58
|
let n = await fetch(`${e}/api/profiles/default/agent/identity`, {
|
|
49
59
|
headers: t,
|
|
50
60
|
signal: AbortSignal.timeout(1e4)
|
|
@@ -64,399 +74,399 @@ function h({ onSubmit: h, onCancel: g, isLoading: _, onNavigate: v, targets: y,
|
|
|
64
74
|
platform: a,
|
|
65
75
|
arch: o
|
|
66
76
|
}
|
|
67
|
-
}), i &&
|
|
77
|
+
}), i && ve(i), a && be(a), o && Se(o), !D && !M && i && E(i);
|
|
68
78
|
} catch (e) {
|
|
69
79
|
q({
|
|
70
80
|
ok: !1,
|
|
71
81
|
error: e instanceof DOMException && e.name === "AbortError" ? "Identity check timed out after 10s" : e instanceof Error ? e.message : "Identity check failed"
|
|
72
82
|
});
|
|
73
83
|
}
|
|
74
|
-
})()]),
|
|
75
|
-
},
|
|
84
|
+
})()]), U(!1));
|
|
85
|
+
}, Me = async () => {
|
|
76
86
|
Y(null);
|
|
77
|
-
let e =
|
|
78
|
-
name:
|
|
87
|
+
let e = De.trim() || "default", t = {
|
|
88
|
+
name: T || J || "Unnamed Agent",
|
|
79
89
|
hostname: J || "unknown",
|
|
80
|
-
platform:
|
|
81
|
-
architecture:
|
|
82
|
-
version:
|
|
83
|
-
tunnelUrl:
|
|
90
|
+
platform: ye,
|
|
91
|
+
architecture: xe,
|
|
92
|
+
version: Ce,
|
|
93
|
+
tunnelUrl: Ae(O),
|
|
84
94
|
tunnelStatus: "ACTIVE",
|
|
85
|
-
agentApiKey:
|
|
95
|
+
agentApiKey: A,
|
|
86
96
|
metadata: { profile: e },
|
|
87
|
-
...
|
|
97
|
+
...M ? { targetId: M } : {}
|
|
88
98
|
};
|
|
89
99
|
try {
|
|
90
|
-
let e = (await
|
|
100
|
+
let e = (await p(t))?.id || null;
|
|
91
101
|
if (!e) {
|
|
92
102
|
Y("Agent creation returned no ID. The agent may already exist.");
|
|
93
103
|
return;
|
|
94
104
|
}
|
|
95
|
-
|
|
105
|
+
de ? de(`/agents/${e}`) : m();
|
|
96
106
|
} catch (e) {
|
|
97
107
|
Y(e instanceof Error ? e.message : "Failed to create agent");
|
|
98
108
|
}
|
|
99
|
-
},
|
|
100
|
-
if (!(!
|
|
101
|
-
|
|
109
|
+
}, Ne = W?.ok && K?.ok, Z = g && g.length > 0, Pe = (_ || Z) && M.length === 0, Fe = O.trim().length > 0 && !Pe, Ie = t(async () => {
|
|
110
|
+
if (!(!_ || !I.trim())) {
|
|
111
|
+
z(!0), V(null);
|
|
102
112
|
try {
|
|
103
|
-
let e = await
|
|
104
|
-
e ? (
|
|
113
|
+
let e = await _(I.trim());
|
|
114
|
+
e ? (N(e.id), F(!1), L("")) : V(y("agentsPage.inlineTargetFailed", "Failed to create target. Please try again."));
|
|
105
115
|
} catch (e) {
|
|
106
|
-
|
|
116
|
+
V(e instanceof Error ? e.message : "Failed to create target");
|
|
107
117
|
} finally {
|
|
108
|
-
|
|
118
|
+
z(!1);
|
|
109
119
|
}
|
|
110
120
|
}
|
|
111
121
|
}, [
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
]),
|
|
122
|
+
_,
|
|
123
|
+
I,
|
|
124
|
+
y
|
|
125
|
+
]), Le = (e, t) => /* @__PURE__ */ d("button", {
|
|
116
126
|
type: "button",
|
|
117
|
-
onClick: () =>
|
|
127
|
+
onClick: () => ke(e, t),
|
|
118
128
|
className: "p-1 rounded hover:bg-bg-elevated transition-colors",
|
|
119
|
-
title:
|
|
120
|
-
children:
|
|
129
|
+
title: y("agentsPage.copy", "Copy"),
|
|
130
|
+
children: ge === t ? /* @__PURE__ */ d(a, { className: "size-3.5 text-status-success-text" }) : /* @__PURE__ */ d(ne, { className: "size-3.5 text-text-secondary" })
|
|
121
131
|
}), Q = [{
|
|
122
132
|
key: "details",
|
|
123
133
|
label: "Setup"
|
|
124
134
|
}, {
|
|
125
135
|
key: "validate",
|
|
126
136
|
label: "Validate"
|
|
127
|
-
}], $ = Q.findIndex((e) => e.key ===
|
|
128
|
-
return /* @__PURE__ */
|
|
137
|
+
}], $ = Q.findIndex((e) => e.key === b);
|
|
138
|
+
return /* @__PURE__ */ d("div", {
|
|
129
139
|
className: "fixed inset-0 z-50 flex items-center justify-center bg-bg-overlay",
|
|
130
|
-
children: /* @__PURE__ */
|
|
140
|
+
children: /* @__PURE__ */ f("div", {
|
|
131
141
|
className: "w-full max-w-xl bg-bg-surface rounded-lg shadow-xl max-h-[90vh] overflow-y-auto",
|
|
132
142
|
children: [
|
|
133
|
-
/* @__PURE__ */
|
|
143
|
+
/* @__PURE__ */ f("div", {
|
|
134
144
|
className: "flex items-center justify-between px-6 py-4 border-b border-border-default sticky top-0 bg-bg-surface z-10",
|
|
135
|
-
children: [/* @__PURE__ */
|
|
145
|
+
children: [/* @__PURE__ */ d("h2", {
|
|
136
146
|
className: "text-lg font-semibold text-text-primary",
|
|
137
|
-
children:
|
|
138
|
-
}), /* @__PURE__ */
|
|
147
|
+
children: y("agentsPage.addNewAgent", "Add New Agent")
|
|
148
|
+
}), /* @__PURE__ */ d("button", {
|
|
139
149
|
type: "button",
|
|
140
|
-
onClick:
|
|
150
|
+
onClick: m,
|
|
141
151
|
className: "p-1 rounded hover:bg-bg-sunken transition-colors",
|
|
142
|
-
children: /* @__PURE__ */
|
|
152
|
+
children: /* @__PURE__ */ d(ue, { className: "size-5 text-text-secondary" })
|
|
143
153
|
})]
|
|
144
154
|
}),
|
|
145
|
-
/* @__PURE__ */
|
|
155
|
+
/* @__PURE__ */ d("div", {
|
|
146
156
|
className: "flex items-center gap-2 px-6 pt-4 pb-2",
|
|
147
|
-
children: Q.map((e, t) => /* @__PURE__ */
|
|
157
|
+
children: Q.map((e, t) => /* @__PURE__ */ f("div", {
|
|
148
158
|
className: "flex items-center gap-2",
|
|
149
159
|
children: [
|
|
150
|
-
/* @__PURE__ */
|
|
160
|
+
/* @__PURE__ */ d("div", {
|
|
151
161
|
className: `flex items-center justify-center size-7 rounded-full text-xs font-semibold transition-colors ${t < $ ? "bg-status-success-bg text-status-success-text" : t === $ ? "bg-action-primary-bg text-action-primary-text" : "bg-bg-sunken text-text-secondary"}`,
|
|
152
|
-
children: t < $ ? /* @__PURE__ */
|
|
162
|
+
children: t < $ ? /* @__PURE__ */ d(o, { className: "size-4" }) : t + 1
|
|
153
163
|
}),
|
|
154
|
-
/* @__PURE__ */
|
|
164
|
+
/* @__PURE__ */ d("span", {
|
|
155
165
|
className: `text-sm ${t === $ ? "font-medium text-text-primary" : "text-text-secondary"}`,
|
|
156
166
|
children: e.label
|
|
157
167
|
}),
|
|
158
|
-
t < Q.length - 1 && /* @__PURE__ */
|
|
168
|
+
t < Q.length - 1 && /* @__PURE__ */ d(s, { className: "size-4 text-text-muted" })
|
|
159
169
|
]
|
|
160
170
|
}, e.key))
|
|
161
171
|
}),
|
|
162
|
-
/* @__PURE__ */
|
|
172
|
+
/* @__PURE__ */ f("div", {
|
|
163
173
|
className: "p-6",
|
|
164
|
-
children: [
|
|
174
|
+
children: [b === "details" && /* @__PURE__ */ f("div", {
|
|
165
175
|
className: "space-y-4",
|
|
166
176
|
children: [
|
|
167
|
-
/* @__PURE__ */
|
|
177
|
+
/* @__PURE__ */ f("div", {
|
|
168
178
|
className: "border border-border-default rounded-lg overflow-hidden",
|
|
169
|
-
children: [/* @__PURE__ */
|
|
179
|
+
children: [/* @__PURE__ */ f("button", {
|
|
170
180
|
type: "button",
|
|
171
|
-
onClick: () =>
|
|
181
|
+
onClick: () => he(!C),
|
|
172
182
|
className: "w-full flex items-center justify-between px-4 py-3 bg-bg-sunken hover:bg-bg-sunken/80 transition-colors",
|
|
173
|
-
children: [/* @__PURE__ */
|
|
183
|
+
children: [/* @__PURE__ */ f("div", {
|
|
174
184
|
className: "flex items-center gap-2",
|
|
175
|
-
children: [/* @__PURE__ */
|
|
185
|
+
children: [/* @__PURE__ */ d(re, { className: "size-4 text-action-primary-bg" }), /* @__PURE__ */ d("span", {
|
|
176
186
|
className: "text-sm font-medium text-text-primary",
|
|
177
|
-
children:
|
|
187
|
+
children: y("agentsPage.installInstructions", "Agent Install Instructions")
|
|
178
188
|
})]
|
|
179
|
-
}), /* @__PURE__ */
|
|
180
|
-
}),
|
|
189
|
+
}), /* @__PURE__ */ d(s, { className: `size-4 text-text-secondary transition-transform ${C ? "rotate-90" : ""}` })]
|
|
190
|
+
}), C && /* @__PURE__ */ d("div", {
|
|
181
191
|
className: "px-4 py-3 space-y-3 border-t border-border-default",
|
|
182
|
-
children: /* @__PURE__ */
|
|
192
|
+
children: /* @__PURE__ */ d("div", {
|
|
183
193
|
className: "space-y-2",
|
|
184
194
|
children: [
|
|
185
195
|
{
|
|
186
|
-
label:
|
|
196
|
+
label: y("agentsPage.installStep1", "1. Install the agent CLI"),
|
|
187
197
|
cmd: "npm install -g @vibecontrols/agent@latest"
|
|
188
198
|
},
|
|
189
199
|
{
|
|
190
|
-
label:
|
|
200
|
+
label: y("agentsPage.installStep2", "2. Start the agent"),
|
|
191
201
|
cmd: "vibe start"
|
|
192
202
|
},
|
|
193
203
|
{
|
|
194
|
-
label:
|
|
204
|
+
label: y("agentsPage.installStep3", "3. Get the API key"),
|
|
195
205
|
cmd: "vibe key"
|
|
196
206
|
},
|
|
197
207
|
{
|
|
198
|
-
label:
|
|
208
|
+
label: y("agentsPage.installStep4", "4. Start the tunnel"),
|
|
199
209
|
cmd: "vibe tunnel agent --start"
|
|
200
210
|
}
|
|
201
|
-
].map((e) => /* @__PURE__ */
|
|
211
|
+
].map((e) => /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("p", {
|
|
202
212
|
className: "text-xs text-text-secondary mb-1",
|
|
203
213
|
children: e.label
|
|
204
|
-
}), /* @__PURE__ */
|
|
214
|
+
}), /* @__PURE__ */ f("div", {
|
|
205
215
|
className: "flex items-center gap-2 bg-bg-sunken rounded-md px-3 py-1.5 font-mono text-xs text-text-primary",
|
|
206
216
|
children: [
|
|
207
|
-
/* @__PURE__ */
|
|
208
|
-
/* @__PURE__ */
|
|
217
|
+
/* @__PURE__ */ d(le, { className: "size-3 text-text-muted flex-shrink-0" }),
|
|
218
|
+
/* @__PURE__ */ d("code", {
|
|
209
219
|
className: "flex-1",
|
|
210
220
|
children: e.cmd
|
|
211
221
|
}),
|
|
212
|
-
|
|
222
|
+
Le(e.cmd, e.cmd)
|
|
213
223
|
]
|
|
214
224
|
})] }, e.cmd))
|
|
215
225
|
})
|
|
216
226
|
})]
|
|
217
227
|
}),
|
|
218
|
-
/* @__PURE__ */
|
|
219
|
-
/* @__PURE__ */
|
|
228
|
+
/* @__PURE__ */ f("div", { children: [
|
|
229
|
+
/* @__PURE__ */ f("label", {
|
|
220
230
|
className: "block text-sm font-medium text-text-primary mb-1",
|
|
221
231
|
children: [
|
|
222
|
-
|
|
232
|
+
y("agentsPage.target", "Target"),
|
|
223
233
|
" ",
|
|
224
|
-
/* @__PURE__ */
|
|
234
|
+
/* @__PURE__ */ d("span", {
|
|
225
235
|
className: "text-status-error-text",
|
|
226
236
|
children: "*"
|
|
227
237
|
})
|
|
228
238
|
]
|
|
229
239
|
}),
|
|
230
|
-
Z && !
|
|
231
|
-
placeholder:
|
|
232
|
-
searchPlaceholder:
|
|
233
|
-
options: (
|
|
240
|
+
Z && !P && /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ d(e, {
|
|
241
|
+
placeholder: y("agentsPage.selectTarget", "Select a target..."),
|
|
242
|
+
searchPlaceholder: y("agentsPage.searchTargets", "Search targets..."),
|
|
243
|
+
options: (g ?? []).map((e) => ({
|
|
234
244
|
value: e.id,
|
|
235
245
|
label: e.name,
|
|
236
246
|
description: `${e.type}${e.status ? ` · ${e.status}` : ""}`
|
|
237
247
|
})),
|
|
238
|
-
value:
|
|
239
|
-
onChange:
|
|
248
|
+
value: M,
|
|
249
|
+
onChange: N,
|
|
240
250
|
clearable: !0
|
|
241
|
-
}), /* @__PURE__ */
|
|
251
|
+
}), /* @__PURE__ */ f("p", {
|
|
242
252
|
className: "mt-1 text-xs text-text-muted",
|
|
243
253
|
children: [
|
|
244
|
-
|
|
254
|
+
y("agentsPage.targetDescription", "The target machine this agent will be installed on."),
|
|
245
255
|
" ",
|
|
246
|
-
|
|
256
|
+
_ && /* @__PURE__ */ f("button", {
|
|
247
257
|
type: "button",
|
|
248
258
|
onClick: () => {
|
|
249
|
-
|
|
259
|
+
F(!0), L(T.trim() || "");
|
|
250
260
|
},
|
|
251
261
|
className: "text-action-primary-bg hover:underline inline-flex items-center gap-1",
|
|
252
|
-
children: [/* @__PURE__ */
|
|
262
|
+
children: [/* @__PURE__ */ d(oe, { className: "size-3" }), y("agentsPage.createNewTarget", "Create a new target")]
|
|
253
263
|
})
|
|
254
264
|
]
|
|
255
265
|
})] }),
|
|
256
|
-
!Z && !
|
|
266
|
+
!Z && !fe && _ && !P && /* @__PURE__ */ f("div", {
|
|
257
267
|
className: "p-3 bg-bg-sunken border border-border-default rounded-lg",
|
|
258
|
-
children: [/* @__PURE__ */
|
|
268
|
+
children: [/* @__PURE__ */ d("p", {
|
|
259
269
|
className: "text-sm text-text-secondary mb-2",
|
|
260
|
-
children:
|
|
261
|
-
}), /* @__PURE__ */
|
|
270
|
+
children: y("agentsPage.noTargetsInlinePrompt", "You don't have any targets yet. Create one now to install this agent on it.")
|
|
271
|
+
}), /* @__PURE__ */ f("button", {
|
|
262
272
|
type: "button",
|
|
263
273
|
onClick: () => {
|
|
264
|
-
|
|
274
|
+
F(!0), L(T.trim() || "");
|
|
265
275
|
},
|
|
266
276
|
className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:opacity-90",
|
|
267
|
-
children: [/* @__PURE__ */
|
|
277
|
+
children: [/* @__PURE__ */ d(oe, { className: "size-4" }), y("agentsPage.createFirstTarget", "Create first target")]
|
|
268
278
|
})]
|
|
269
279
|
}),
|
|
270
|
-
|
|
280
|
+
P && _ && /* @__PURE__ */ f("div", {
|
|
271
281
|
className: "p-3 bg-bg-sunken border border-border-default rounded-lg space-y-2",
|
|
272
282
|
children: [
|
|
273
|
-
/* @__PURE__ */
|
|
283
|
+
/* @__PURE__ */ d("p", {
|
|
274
284
|
className: "text-xs font-medium text-text-secondary",
|
|
275
|
-
children:
|
|
285
|
+
children: y("agentsPage.newDirectTarget", "New target (Direct — you can change the type later)")
|
|
276
286
|
}),
|
|
277
|
-
/* @__PURE__ */
|
|
287
|
+
/* @__PURE__ */ d("input", {
|
|
278
288
|
"aria-label": "control",
|
|
279
289
|
type: "text",
|
|
280
|
-
value:
|
|
281
|
-
onChange: (e) =>
|
|
282
|
-
placeholder:
|
|
290
|
+
value: I,
|
|
291
|
+
onChange: (e) => L(e.target.value),
|
|
292
|
+
placeholder: y("agentsPage.targetName", "Target name"),
|
|
283
293
|
className: "w-full px-3 py-2 text-sm bg-bg-elevated border border-border-default rounded-md text-text-primary placeholder-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg",
|
|
284
294
|
onKeyDown: (e) => {
|
|
285
|
-
e.key === "Enter" && (e.preventDefault(),
|
|
295
|
+
e.key === "Enter" && (e.preventDefault(), Ie());
|
|
286
296
|
}
|
|
287
297
|
}),
|
|
288
|
-
|
|
298
|
+
B && /* @__PURE__ */ d("p", {
|
|
289
299
|
className: "text-xs text-status-error-text",
|
|
290
|
-
children:
|
|
300
|
+
children: B
|
|
291
301
|
}),
|
|
292
|
-
/* @__PURE__ */
|
|
302
|
+
/* @__PURE__ */ f("div", {
|
|
293
303
|
className: "flex items-center gap-2",
|
|
294
|
-
children: [/* @__PURE__ */
|
|
304
|
+
children: [/* @__PURE__ */ f("button", {
|
|
295
305
|
type: "button",
|
|
296
|
-
onClick: () => void
|
|
297
|
-
disabled: !
|
|
306
|
+
onClick: () => void Ie(),
|
|
307
|
+
disabled: !I.trim() || R,
|
|
298
308
|
className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:opacity-90 disabled:opacity-50",
|
|
299
|
-
children: [
|
|
300
|
-
}), /* @__PURE__ */
|
|
309
|
+
children: [R ? /* @__PURE__ */ d(c, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ d(a, { className: "size-3.5" }), y("agentsPage.create", "Create")]
|
|
310
|
+
}), /* @__PURE__ */ d("button", {
|
|
301
311
|
type: "button",
|
|
302
312
|
onClick: () => {
|
|
303
|
-
|
|
313
|
+
F(!1), L(""), V(null);
|
|
304
314
|
},
|
|
305
315
|
className: "px-3 py-1.5 text-sm text-text-secondary hover:text-text-primary",
|
|
306
|
-
children:
|
|
316
|
+
children: y("agentsPage.cancel", "Cancel")
|
|
307
317
|
})]
|
|
308
318
|
})
|
|
309
319
|
]
|
|
310
320
|
}),
|
|
311
|
-
|
|
321
|
+
fe && !Z && /* @__PURE__ */ f("div", {
|
|
312
322
|
className: "p-3 bg-bg-sunken border border-border-default rounded-lg flex items-center gap-2 text-sm text-text-secondary",
|
|
313
|
-
children: [/* @__PURE__ */
|
|
323
|
+
children: [/* @__PURE__ */ d(c, { className: "size-4 animate-spin" }), y("agentsPage.loadingTargets", "Loading targets...")]
|
|
314
324
|
})
|
|
315
325
|
] }),
|
|
316
|
-
/* @__PURE__ */
|
|
317
|
-
/* @__PURE__ */
|
|
326
|
+
/* @__PURE__ */ f("div", { children: [
|
|
327
|
+
/* @__PURE__ */ f("label", {
|
|
318
328
|
className: "block text-sm font-medium text-text-primary mb-1",
|
|
319
329
|
children: [
|
|
320
|
-
|
|
330
|
+
y("agentsPage.apiTunnelUrl", "API / Tunnel URL"),
|
|
321
331
|
" ",
|
|
322
|
-
/* @__PURE__ */
|
|
332
|
+
/* @__PURE__ */ d("span", {
|
|
323
333
|
className: "text-status-error-text",
|
|
324
334
|
children: "*"
|
|
325
335
|
})
|
|
326
336
|
]
|
|
327
337
|
}),
|
|
328
|
-
/* @__PURE__ */
|
|
338
|
+
/* @__PURE__ */ d("input", {
|
|
329
339
|
"aria-label": "https://your-tunnel.trycloudflare.com",
|
|
330
340
|
type: "url",
|
|
331
|
-
value:
|
|
332
|
-
onChange: (e) =>
|
|
341
|
+
value: O,
|
|
342
|
+
onChange: (e) => k(e.target.value),
|
|
333
343
|
placeholder: "https://your-tunnel.trycloudflare.com",
|
|
334
344
|
required: !0,
|
|
335
345
|
className: "w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg font-mono"
|
|
336
346
|
}),
|
|
337
|
-
/* @__PURE__ */
|
|
347
|
+
/* @__PURE__ */ d("p", {
|
|
338
348
|
className: "mt-1 text-xs text-text-muted",
|
|
339
|
-
children:
|
|
349
|
+
children: y("agentsPage.tunnelUrlHint", "The agent's tunnel URL or local API URL (e.g. http://localhost:3005)")
|
|
340
350
|
})
|
|
341
351
|
] }),
|
|
342
|
-
/* @__PURE__ */
|
|
343
|
-
/* @__PURE__ */
|
|
352
|
+
/* @__PURE__ */ f("div", { children: [
|
|
353
|
+
/* @__PURE__ */ f("label", {
|
|
344
354
|
className: "block text-sm font-medium text-text-primary mb-1",
|
|
345
355
|
children: [
|
|
346
|
-
|
|
356
|
+
y("agentsPage.agentApiKey", "Agent API Key"),
|
|
347
357
|
" ",
|
|
348
|
-
/* @__PURE__ */
|
|
358
|
+
/* @__PURE__ */ d("span", {
|
|
349
359
|
className: "text-status-error-text",
|
|
350
360
|
children: "*"
|
|
351
361
|
})
|
|
352
362
|
]
|
|
353
363
|
}),
|
|
354
|
-
/* @__PURE__ */
|
|
364
|
+
/* @__PURE__ */ f("div", {
|
|
355
365
|
className: "relative",
|
|
356
|
-
children: [/* @__PURE__ */
|
|
366
|
+
children: [/* @__PURE__ */ d("input", {
|
|
357
367
|
"aria-label": "vcak_...",
|
|
358
|
-
type:
|
|
359
|
-
value:
|
|
360
|
-
onChange: (e) =>
|
|
368
|
+
type: S ? "text" : "password",
|
|
369
|
+
value: A,
|
|
370
|
+
onChange: (e) => j(e.target.value),
|
|
361
371
|
placeholder: "vcak_...",
|
|
362
372
|
required: !0,
|
|
363
373
|
className: "w-full px-3 py-2 pr-10 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg font-mono"
|
|
364
|
-
}), /* @__PURE__ */
|
|
374
|
+
}), /* @__PURE__ */ d("button", {
|
|
365
375
|
type: "button",
|
|
366
|
-
onClick: () =>
|
|
376
|
+
onClick: () => me(!S),
|
|
367
377
|
className: "absolute right-2 top-1/2 -translate-y-1/2 p-1 rounded hover:bg-bg-elevated transition-colors",
|
|
368
|
-
children:
|
|
378
|
+
children: d(S ? ae : ie, { className: "size-4 text-text-secondary" })
|
|
369
379
|
})]
|
|
370
380
|
}),
|
|
371
|
-
/* @__PURE__ */
|
|
381
|
+
/* @__PURE__ */ f("p", {
|
|
372
382
|
className: "mt-1 text-xs text-text-muted",
|
|
373
383
|
children: [
|
|
374
|
-
|
|
384
|
+
y("agentsPage.apiKeyHint", "Run"),
|
|
375
385
|
" ",
|
|
376
|
-
/* @__PURE__ */
|
|
386
|
+
/* @__PURE__ */ d("code", {
|
|
377
387
|
className: "bg-bg-sunken px-1 rounded",
|
|
378
388
|
children: "vibe key"
|
|
379
389
|
}),
|
|
380
390
|
" ",
|
|
381
|
-
|
|
391
|
+
y("agentsPage.apiKeyHintSuffix", "on the agent to get this. Changes on restart.")
|
|
382
392
|
]
|
|
383
393
|
})
|
|
384
394
|
] }),
|
|
385
|
-
/* @__PURE__ */
|
|
395
|
+
/* @__PURE__ */ f("div", {
|
|
386
396
|
className: "border border-border-default rounded-lg overflow-hidden",
|
|
387
|
-
children: [/* @__PURE__ */
|
|
397
|
+
children: [/* @__PURE__ */ f("button", {
|
|
388
398
|
type: "button",
|
|
389
|
-
onClick: () =>
|
|
399
|
+
onClick: () => Ee((e) => !e),
|
|
390
400
|
className: "w-full flex items-center justify-between px-4 py-2.5 bg-bg-sunken hover:bg-bg-sunken/80 transition-colors",
|
|
391
401
|
"aria-expanded": X,
|
|
392
402
|
"data-testid": "agent-form-advanced-toggle",
|
|
393
|
-
children: [/* @__PURE__ */
|
|
403
|
+
children: [/* @__PURE__ */ f("div", {
|
|
394
404
|
className: "flex items-center gap-2",
|
|
395
|
-
children: [/* @__PURE__ */
|
|
405
|
+
children: [/* @__PURE__ */ d(ce, { className: "size-4 text-text-secondary" }), /* @__PURE__ */ d("span", {
|
|
396
406
|
className: "text-sm font-medium text-text-primary",
|
|
397
|
-
children:
|
|
407
|
+
children: y("agentsPage.advancedOptions", "Advanced options")
|
|
398
408
|
})]
|
|
399
|
-
}), /* @__PURE__ */
|
|
400
|
-
}), X && /* @__PURE__ */
|
|
409
|
+
}), /* @__PURE__ */ d(ee, { className: `size-4 text-text-secondary transition-transform ${X ? "rotate-180" : ""}` })]
|
|
410
|
+
}), X && /* @__PURE__ */ d("div", {
|
|
401
411
|
className: "px-4 py-3 space-y-3 border-t border-border-default",
|
|
402
|
-
children: /* @__PURE__ */
|
|
403
|
-
/* @__PURE__ */
|
|
412
|
+
children: /* @__PURE__ */ f("div", { children: [
|
|
413
|
+
/* @__PURE__ */ d("label", {
|
|
404
414
|
className: "block text-sm font-medium text-text-primary mb-1",
|
|
405
|
-
children:
|
|
415
|
+
children: y("agentsPage.profileName", "Profile name")
|
|
406
416
|
}),
|
|
407
|
-
/* @__PURE__ */
|
|
417
|
+
/* @__PURE__ */ d("input", {
|
|
408
418
|
"aria-label": "default",
|
|
409
419
|
type: "text",
|
|
410
|
-
value:
|
|
411
|
-
onChange: (e) =>
|
|
420
|
+
value: De,
|
|
421
|
+
onChange: (e) => Oe(e.target.value),
|
|
412
422
|
placeholder: "default",
|
|
413
423
|
className: "w-full px-3 py-2 text-sm bg-bg-surface border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg font-mono",
|
|
414
424
|
"data-testid": "agent-form-profile-input"
|
|
415
425
|
}),
|
|
416
|
-
/* @__PURE__ */
|
|
426
|
+
/* @__PURE__ */ d("p", {
|
|
417
427
|
className: "mt-1 text-xs text-text-muted",
|
|
418
|
-
children:
|
|
428
|
+
children: y("agentsPage.profileNameHint", "Each profile runs an isolated CLI on the daemon. Leave as \"default\" unless you want this agent to host multiple parallel profiles.")
|
|
419
429
|
})
|
|
420
430
|
] })
|
|
421
431
|
})]
|
|
422
432
|
}),
|
|
423
|
-
|
|
433
|
+
v && /* @__PURE__ */ d("div", {
|
|
424
434
|
className: "border-t border-border-default pt-4 mt-4",
|
|
425
|
-
children:
|
|
435
|
+
children: v
|
|
426
436
|
}),
|
|
427
|
-
/* @__PURE__ */
|
|
437
|
+
/* @__PURE__ */ f("div", {
|
|
428
438
|
className: "flex items-center justify-between pt-4",
|
|
429
|
-
children: [/* @__PURE__ */
|
|
439
|
+
children: [/* @__PURE__ */ d("button", {
|
|
430
440
|
type: "button",
|
|
431
|
-
onClick:
|
|
441
|
+
onClick: m,
|
|
432
442
|
className: "px-4 py-2 text-sm font-medium text-text-primary bg-bg-sunken border border-border-default rounded-md hover:bg-bg-elevated transition-colors",
|
|
433
|
-
children:
|
|
434
|
-
}), /* @__PURE__ */
|
|
443
|
+
children: y("agentsPage.cancel", "Cancel")
|
|
444
|
+
}), /* @__PURE__ */ f("button", {
|
|
435
445
|
type: "button",
|
|
436
|
-
disabled: !
|
|
446
|
+
disabled: !Fe,
|
|
437
447
|
onClick: () => {
|
|
438
|
-
|
|
448
|
+
x("validate"), je();
|
|
439
449
|
},
|
|
440
450
|
className: "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 disabled:opacity-50",
|
|
441
|
-
children: [
|
|
451
|
+
children: [y("agentsPage.nextValidate", "Next: Validate"), /* @__PURE__ */ d(s, { className: "size-4" })]
|
|
442
452
|
})]
|
|
443
453
|
})
|
|
444
454
|
]
|
|
445
|
-
}),
|
|
455
|
+
}), b === "validate" && /* @__PURE__ */ f("div", {
|
|
446
456
|
className: "space-y-4",
|
|
447
457
|
children: [
|
|
448
|
-
/* @__PURE__ */
|
|
458
|
+
/* @__PURE__ */ f("div", {
|
|
449
459
|
className: "space-y-3",
|
|
450
|
-
children: [/* @__PURE__ */
|
|
460
|
+
children: [/* @__PURE__ */ f("div", {
|
|
451
461
|
className: `flex items-center gap-3 p-3 rounded-lg border ${W === null ? "border-border-default bg-bg-sunken" : W.ok ? "border-status-success-text/20 bg-status-success-bg/30" : "border-status-error-text/20 bg-status-error-bg/30"}`,
|
|
452
|
-
children: [
|
|
462
|
+
children: [H && !W ? /* @__PURE__ */ d(c, { className: "size-5 text-text-secondary animate-spin" }) : W?.ok ? /* @__PURE__ */ d(o, { className: "size-5 text-status-success-text" }) : W ? /* @__PURE__ */ d(l, { className: "size-5 text-status-error-text" }) : /* @__PURE__ */ d("div", { className: "size-5 rounded-full border-2 border-border-default" }), /* @__PURE__ */ f("div", {
|
|
453
463
|
className: "flex-1",
|
|
454
464
|
children: [
|
|
455
|
-
/* @__PURE__ */
|
|
465
|
+
/* @__PURE__ */ d("p", {
|
|
456
466
|
className: "text-sm font-medium text-text-primary",
|
|
457
|
-
children:
|
|
467
|
+
children: y("agentsPage.healthCheck", "Health Check")
|
|
458
468
|
}),
|
|
459
|
-
W?.ok && W.data && /* @__PURE__ */
|
|
469
|
+
W?.ok && W.data && /* @__PURE__ */ f("p", {
|
|
460
470
|
className: "text-xs text-text-secondary",
|
|
461
471
|
children: [
|
|
462
472
|
"Status: ",
|
|
@@ -468,22 +478,22 @@ function h({ onSubmit: h, onCancel: g, isLoading: _, onNavigate: v, targets: y,
|
|
|
468
478
|
"s"
|
|
469
479
|
]
|
|
470
480
|
}),
|
|
471
|
-
W && !W.ok && /* @__PURE__ */
|
|
481
|
+
W && !W.ok && /* @__PURE__ */ d("p", {
|
|
472
482
|
className: "text-xs text-status-error-text",
|
|
473
483
|
children: W.error
|
|
474
484
|
})
|
|
475
485
|
]
|
|
476
486
|
})]
|
|
477
|
-
}), /* @__PURE__ */
|
|
487
|
+
}), /* @__PURE__ */ f("div", {
|
|
478
488
|
className: `flex items-center gap-3 p-3 rounded-lg border ${K === null ? "border-border-default bg-bg-sunken" : K.ok ? "border-status-success-text/20 bg-status-success-bg/30" : "border-status-error-text/20 bg-status-error-bg/30"}`,
|
|
479
|
-
children: [
|
|
489
|
+
children: [H && !K ? /* @__PURE__ */ d(c, { className: "size-5 text-text-secondary animate-spin" }) : K?.ok ? /* @__PURE__ */ d(o, { className: "size-5 text-status-success-text" }) : K ? /* @__PURE__ */ d(l, { className: "size-5 text-status-error-text" }) : /* @__PURE__ */ d("div", { className: "size-5 rounded-full border-2 border-border-default" }), /* @__PURE__ */ f("div", {
|
|
480
490
|
className: "flex-1",
|
|
481
491
|
children: [
|
|
482
|
-
/* @__PURE__ */
|
|
492
|
+
/* @__PURE__ */ d("p", {
|
|
483
493
|
className: "text-sm font-medium text-text-primary",
|
|
484
|
-
children:
|
|
494
|
+
children: y("agentsPage.apiAccess", "API Access")
|
|
485
495
|
}),
|
|
486
|
-
K?.ok && K.data && /* @__PURE__ */
|
|
496
|
+
K?.ok && K.data && /* @__PURE__ */ f("p", {
|
|
487
497
|
className: "text-xs text-text-secondary",
|
|
488
498
|
children: [
|
|
489
499
|
"Host: ",
|
|
@@ -495,7 +505,7 @@ function h({ onSubmit: h, onCancel: g, isLoading: _, onNavigate: v, targets: y,
|
|
|
495
505
|
K.data.arch
|
|
496
506
|
]
|
|
497
507
|
}),
|
|
498
|
-
K && !K.ok && /* @__PURE__ */
|
|
508
|
+
K && !K.ok && /* @__PURE__ */ d("p", {
|
|
499
509
|
className: "text-xs text-status-error-text",
|
|
500
510
|
children: K.error
|
|
501
511
|
})
|
|
@@ -503,70 +513,72 @@ function h({ onSubmit: h, onCancel: g, isLoading: _, onNavigate: v, targets: y,
|
|
|
503
513
|
})]
|
|
504
514
|
})]
|
|
505
515
|
}),
|
|
506
|
-
!
|
|
516
|
+
!H && (!W?.ok || !K?.ok) && /* @__PURE__ */ f("button", {
|
|
507
517
|
type: "button",
|
|
508
|
-
onClick:
|
|
518
|
+
onClick: je,
|
|
509
519
|
className: "flex items-center gap-2 px-3 py-1.5 text-sm text-text-secondary hover:text-text-primary hover:bg-bg-sunken rounded-md transition-colors",
|
|
510
|
-
children: [/* @__PURE__ */
|
|
520
|
+
children: [/* @__PURE__ */ d(se, { className: "size-3.5" }), y("agentsPage.retryValidation", "Retry validation")]
|
|
511
521
|
}),
|
|
512
|
-
|
|
522
|
+
Ne && /* @__PURE__ */ f("div", {
|
|
513
523
|
className: "space-y-3 pt-2",
|
|
514
524
|
children: [
|
|
515
|
-
/* @__PURE__ */
|
|
525
|
+
/* @__PURE__ */ d("p", {
|
|
516
526
|
className: "text-sm font-medium text-text-primary",
|
|
517
|
-
children:
|
|
527
|
+
children: y("agentsPage.detectedAgentInfo", "Detected Agent Info")
|
|
518
528
|
}),
|
|
519
|
-
/* @__PURE__ */
|
|
529
|
+
/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("label", {
|
|
520
530
|
className: "block text-xs font-medium text-text-secondary mb-1",
|
|
521
|
-
children:
|
|
522
|
-
}), /* @__PURE__ */
|
|
531
|
+
children: y("agentsPage.name", "Name")
|
|
532
|
+
}), /* @__PURE__ */ d("input", {
|
|
523
533
|
"aria-label": "control",
|
|
524
534
|
type: "text",
|
|
525
|
-
value:
|
|
526
|
-
onChange: (e) =>
|
|
535
|
+
value: T,
|
|
536
|
+
onChange: (e) => {
|
|
537
|
+
E(e.target.value), _e(!0);
|
|
538
|
+
},
|
|
527
539
|
placeholder: J,
|
|
528
540
|
className: "w-full px-3 py-1.5 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg"
|
|
529
541
|
})] }),
|
|
530
|
-
/* @__PURE__ */
|
|
542
|
+
/* @__PURE__ */ f("div", {
|
|
531
543
|
className: "grid grid-cols-2 gap-3",
|
|
532
|
-
children: [/* @__PURE__ */
|
|
544
|
+
children: [/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("label", {
|
|
533
545
|
className: "block text-xs font-medium text-text-secondary mb-1",
|
|
534
|
-
children:
|
|
535
|
-
}), /* @__PURE__ */
|
|
536
|
-
value:
|
|
537
|
-
onChange: (e) =>
|
|
546
|
+
children: y("agentsPage.platform", "Platform")
|
|
547
|
+
}), /* @__PURE__ */ f("select", {
|
|
548
|
+
value: ye,
|
|
549
|
+
onChange: (e) => be(e.target.value),
|
|
538
550
|
className: "w-full px-3 py-1.5 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg",
|
|
539
551
|
children: [
|
|
540
|
-
/* @__PURE__ */
|
|
552
|
+
/* @__PURE__ */ d("option", {
|
|
541
553
|
value: "linux",
|
|
542
554
|
children: "Linux"
|
|
543
555
|
}),
|
|
544
|
-
/* @__PURE__ */
|
|
556
|
+
/* @__PURE__ */ d("option", {
|
|
545
557
|
value: "darwin",
|
|
546
558
|
children: "macOS"
|
|
547
559
|
}),
|
|
548
|
-
/* @__PURE__ */
|
|
560
|
+
/* @__PURE__ */ d("option", {
|
|
549
561
|
value: "windows",
|
|
550
562
|
children: "Windows"
|
|
551
563
|
})
|
|
552
564
|
]
|
|
553
|
-
})] }), /* @__PURE__ */
|
|
565
|
+
})] }), /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("label", {
|
|
554
566
|
className: "block text-xs font-medium text-text-secondary mb-1",
|
|
555
|
-
children:
|
|
556
|
-
}), /* @__PURE__ */
|
|
557
|
-
value:
|
|
558
|
-
onChange: (e) =>
|
|
567
|
+
children: y("agentsPage.architecture", "Architecture")
|
|
568
|
+
}), /* @__PURE__ */ f("select", {
|
|
569
|
+
value: xe,
|
|
570
|
+
onChange: (e) => Se(e.target.value),
|
|
559
571
|
className: "w-full px-3 py-1.5 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg",
|
|
560
572
|
children: [
|
|
561
|
-
/* @__PURE__ */
|
|
573
|
+
/* @__PURE__ */ d("option", {
|
|
562
574
|
value: "x86_64",
|
|
563
575
|
children: "x86_64"
|
|
564
576
|
}),
|
|
565
|
-
/* @__PURE__ */
|
|
577
|
+
/* @__PURE__ */ d("option", {
|
|
566
578
|
value: "arm64",
|
|
567
579
|
children: "arm64"
|
|
568
580
|
}),
|
|
569
|
-
/* @__PURE__ */
|
|
581
|
+
/* @__PURE__ */ d("option", {
|
|
570
582
|
value: "aarch64",
|
|
571
583
|
children: "aarch64"
|
|
572
584
|
})
|
|
@@ -575,44 +587,44 @@ function h({ onSubmit: h, onCancel: g, isLoading: _, onNavigate: v, targets: y,
|
|
|
575
587
|
})
|
|
576
588
|
]
|
|
577
589
|
}),
|
|
578
|
-
|
|
590
|
+
Te && /* @__PURE__ */ f("div", {
|
|
579
591
|
className: "p-3 bg-status-error-bg/30 border border-status-error-text/20 rounded-lg",
|
|
580
|
-
children: [/* @__PURE__ */
|
|
592
|
+
children: [/* @__PURE__ */ f("div", {
|
|
581
593
|
className: "flex items-center gap-2",
|
|
582
|
-
children: [/* @__PURE__ */
|
|
594
|
+
children: [/* @__PURE__ */ d(l, { className: "size-5 text-status-error-text flex-shrink-0" }), /* @__PURE__ */ d("p", {
|
|
583
595
|
className: "text-sm text-status-error-text",
|
|
584
|
-
children:
|
|
596
|
+
children: Te
|
|
585
597
|
})]
|
|
586
|
-
}), /* @__PURE__ */
|
|
598
|
+
}), /* @__PURE__ */ f("div", {
|
|
587
599
|
className: "flex items-center gap-2 mt-3",
|
|
588
|
-
children: [/* @__PURE__ */
|
|
600
|
+
children: [/* @__PURE__ */ f("button", {
|
|
589
601
|
type: "button",
|
|
590
602
|
onClick: () => {
|
|
591
|
-
Y(null),
|
|
603
|
+
Y(null), x("details"), k(""), j(""), G(null), q(null);
|
|
592
604
|
},
|
|
593
605
|
className: "flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-text-primary bg-bg-sunken border border-border-default rounded-md hover:bg-bg-elevated transition-colors",
|
|
594
|
-
children: [/* @__PURE__ */
|
|
595
|
-
}), /* @__PURE__ */
|
|
606
|
+
children: [/* @__PURE__ */ d(se, { className: "size-3.5" }), y("agentsPage.tryDifferentAgent", "Try Different Agent")]
|
|
607
|
+
}), /* @__PURE__ */ d("button", {
|
|
596
608
|
type: "button",
|
|
597
|
-
onClick:
|
|
609
|
+
onClick: m,
|
|
598
610
|
className: "px-3 py-1.5 text-sm font-medium text-text-secondary hover:text-text-primary transition-colors",
|
|
599
|
-
children:
|
|
611
|
+
children: y("agentsPage.close", "Close")
|
|
600
612
|
})]
|
|
601
613
|
})]
|
|
602
614
|
}),
|
|
603
|
-
/* @__PURE__ */
|
|
615
|
+
/* @__PURE__ */ f("div", {
|
|
604
616
|
className: "flex items-center justify-between pt-4",
|
|
605
|
-
children: [/* @__PURE__ */
|
|
617
|
+
children: [/* @__PURE__ */ f("button", {
|
|
606
618
|
type: "button",
|
|
607
|
-
onClick: () =>
|
|
619
|
+
onClick: () => x("details"),
|
|
608
620
|
className: "flex items-center gap-1 px-4 py-2 text-sm font-medium text-text-primary bg-bg-sunken border border-border-default rounded-md hover:bg-bg-elevated transition-colors",
|
|
609
|
-
children: [/* @__PURE__ */
|
|
610
|
-
}), /* @__PURE__ */
|
|
621
|
+
children: [/* @__PURE__ */ d(te, { className: "size-4" }), y("agentsPage.back", "Back")]
|
|
622
|
+
}), /* @__PURE__ */ d("button", {
|
|
611
623
|
type: "button",
|
|
612
|
-
disabled: !
|
|
613
|
-
onClick:
|
|
624
|
+
disabled: !Ne || h || H,
|
|
625
|
+
onClick: Me,
|
|
614
626
|
className: "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 disabled:opacity-50",
|
|
615
|
-
children:
|
|
627
|
+
children: h ? /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ d(c, { className: "size-4 animate-spin" }), y("agentsPage.adding", "Adding...")] }) : /* @__PURE__ */ f(u, { children: [y("agentsPage.addAgent", "Add Agent"), /* @__PURE__ */ d(s, { className: "size-4" })] })
|
|
616
628
|
})]
|
|
617
629
|
})
|
|
618
630
|
]
|
|
@@ -623,6 +635,6 @@ function h({ onSubmit: h, onCancel: g, isLoading: _, onNavigate: v, targets: y,
|
|
|
623
635
|
});
|
|
624
636
|
}
|
|
625
637
|
//#endregion
|
|
626
|
-
export {
|
|
638
|
+
export { p as AgentForm };
|
|
627
639
|
|
|
628
640
|
//# sourceMappingURL=AgentForm.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentForm.js","names":[],"sources":["../../../src/components/agents/AgentForm.tsx"],"sourcesContent":["import { useState, useCallback } from 'react';\nimport type { ReactNode } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n X,\n Eye,\n EyeOff,\n CheckCircle2,\n XCircle,\n Loader2,\n ChevronRight,\n ChevronLeft,\n ChevronDown,\n Copy,\n Check,\n Terminal,\n Download,\n RefreshCw,\n Plus,\n Settings2,\n} from 'lucide-react';\n// useProbeAgentTunnelLazyQuery removed: validate now hits the agent's tunnel\n// URL directly from the browser (see validateAgent below).\nimport { SearchableSelect } from '@/components/shared';\nimport type { SearchableSelectOption } from '@/components/shared';\n\ntype WizardStage = 'details' | 'validate';\n\ninterface TargetOption {\n id: string;\n name: string;\n type: string;\n status: string;\n}\n\ninterface AgentFormProps {\n onSubmit: (data: Record<string, unknown>) => Promise<{ id: string } | void>;\n onCancel: () => void;\n isLoading?: boolean;\n /** Navigate to a path (used to go to agent details after creation) */\n onNavigate?: (path: string) => void;\n /** Available targets for the agent to be installed on */\n targets?: TargetOption[];\n /** Whether targets are still loading */\n targetsLoading?: boolean;\n /**\n * Create a new target inline. When provided, the form shows a\n * \"+ Create new target\" affordance that lets users create a DIRECT\n * target on the fly without leaving the dialog. Must return the new\n * target so the form can auto-select it.\n */\n onCreateTarget?: (name: string) => Promise<TargetOption | null>;\n /** Optional extra content (e.g. a draft EntityTagPicker) rendered above the configure-stage actions. */\n extraSection?: ReactNode;\n}\n\nexport function AgentForm({\n onSubmit,\n onCancel,\n isLoading,\n onNavigate,\n targets,\n targetsLoading,\n onCreateTarget,\n extraSection,\n}: AgentFormProps) {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n const [stage, setStage] = useState<WizardStage>('details');\n const [showApiKey, setShowApiKey] = useState(false);\n const [showInstallInstructions, setShowInstallInstructions] = useState(false);\n const [copiedCommand, setCopiedCommand] = useState<string | null>(null);\n\n // Stage 1: Basic details\n const [name, setName] = useState('');\n const [tunnelUrl, setTunnelUrl] = useState('');\n const [agentApiKey, setAgentApiKey] = useState('');\n const [selectedTargetId, setSelectedTargetId] = useState('');\n\n // Inline target creation state\n const [showInlineTarget, setShowInlineTarget] = useState(false);\n const [inlineTargetName, setInlineTargetName] = useState('');\n const [creatingTarget, setCreatingTarget] = useState(false);\n const [inlineTargetError, setInlineTargetError] = useState<string | null>(null);\n\n // Stage 2: Validation (via backend-proxied GraphQL query)\n const [validating, setValidating] = useState(false);\n const [healthResult, setHealthResult] = useState<{\n ok: boolean;\n data?: { status: string; version: string; uptime: number };\n error?: string;\n } | null>(null);\n const [identityResult, setIdentityResult] = useState<{\n ok: boolean;\n data?: { hostname: string; platform: string; arch: string };\n error?: string;\n } | null>(null);\n\n // Auto-detected fields (editable overrides)\n const [detectedHostname, setDetectedHostname] = useState('');\n const [detectedPlatform, setDetectedPlatform] = useState('linux');\n const [detectedArch, setDetectedArch] = useState('x86_64');\n const [detectedVersion, setDetectedVersion] = useState('1.0.0');\n\n // Error state for creation failures\n const [createError, setCreateError] = useState<string | null>(null);\n\n // Advanced options (Phase 5B: per-daemon profile name).\n // Defaults to \"default\" (autofilled but editable). Disclosure is closed\n // by default — users only need to touch this for multi-profile setups.\n const [showAdvanced, setShowAdvanced] = useState(false);\n const [profile, setProfile] = useState('default');\n\n const copyToClipboard = useCallback((text: string, id: string) => {\n navigator.clipboard.writeText(text);\n setCopiedCommand(id);\n setTimeout(() => setCopiedCommand(null), 2000);\n }, []);\n\n const normalizeUrl = (url: string) => {\n let u = url.trim();\n if (u.endsWith('/')) u = u.slice(0, -1);\n return u;\n };\n\n // Validate the agent by probing /health and /api/profiles/default/agent/identity\n // directly from the browser. The agent exposes CORS-allowed endpoints for\n // app.vibecontrols.com (see agent's createApp() — `@elysiajs/cors` is wired\n // for the host origin), so the browser can reach the tunnel URL without a\n // backend proxy. The earlier backend-proxy approach (probeAgentTunnel query)\n // is still wired into the schema, but in production we observed the proxy\n // hanging at the ACA egress layer regardless of timeout, so we bypass it for\n // the wizard validate step. Each fetch is bounded by a 10s AbortSignal so a\n // dead tunnel surfaces a real error instead of an infinite spinner.\n const validateAgent = async () => {\n const baseUrl = normalizeUrl(tunnelUrl);\n if (!baseUrl) return;\n\n setValidating(true);\n setHealthResult(null);\n setIdentityResult(null);\n\n const probeHealth = async (): Promise<void> => {\n try {\n const res = await fetch(`${baseUrl}/health`, {\n signal: AbortSignal.timeout(10_000),\n });\n if (!res.ok) {\n setHealthResult({ ok: false, error: `HTTP ${String(res.status)}: ${res.statusText}` });\n return;\n }\n const data = (await res.json()) as {\n status?: string;\n version?: string;\n uptime?: number;\n };\n setHealthResult({\n ok: true,\n data: {\n status: data.status || 'healthy',\n version: data.version || 'unknown',\n uptime: data.uptime || 0,\n },\n });\n if (data.version) setDetectedVersion(data.version);\n } catch (err) {\n const isTimeout = err instanceof DOMException && err.name === 'AbortError';\n setHealthResult({\n ok: false,\n error: isTimeout\n ? 'Health check timed out after 10s'\n : err instanceof Error\n ? err.message\n : 'Health check failed',\n });\n }\n };\n\n const probeIdentity = async (): Promise<void> => {\n try {\n const headers: Record<string, string> = {};\n if (agentApiKey) headers['x-agent-api-key'] = agentApiKey;\n const res = await fetch(`${baseUrl}/api/profiles/default/agent/identity`, {\n headers,\n signal: AbortSignal.timeout(10_000),\n });\n if (!res.ok) {\n setIdentityResult({\n ok: false,\n error:\n res.status === 401\n ? 'Invalid API key'\n : `HTTP ${String(res.status)}: ${res.statusText}`,\n });\n return;\n }\n const data = (await res.json()) as {\n hostname?: string;\n platform?: string;\n arch?: string;\n };\n const hostname = data.hostname || '';\n const platform = data.platform || 'linux';\n const arch = data.arch || 'x86_64';\n setIdentityResult({ ok: true, data: { hostname, platform, arch } });\n if (hostname) setDetectedHostname(hostname);\n if (platform) setDetectedPlatform(platform);\n if (arch) setDetectedArch(arch);\n if (!name && hostname) setName(hostname);\n } catch (err) {\n const isTimeout = err instanceof DOMException && err.name === 'AbortError';\n setIdentityResult({\n ok: false,\n error: isTimeout\n ? 'Identity check timed out after 10s'\n : err instanceof Error\n ? err.message\n : 'Identity check failed',\n });\n }\n };\n\n // Run both probes in parallel so the user sees results as fast as the\n // slower of the two — instead of stacking 20s in series on failure paths.\n await Promise.all([probeHealth(), probeIdentity()]);\n\n setValidating(false);\n };\n\n // Create the agent and close the modal\n const handleCreateAgent = async () => {\n setCreateError(null);\n\n // Pipe the chosen profile through `metadata.profile`. Phase 5A (svc) is\n // adding first-class AgentProfile rows; in the meantime the backend\n // also reads `metadata.profile` so the create path keeps working\n // regardless of which side of the cut-over the cluster is on.\n const trimmedProfile = profile.trim() || 'default';\n const data: Record<string, unknown> = {\n name: name || detectedHostname || 'Unnamed Agent',\n hostname: detectedHostname || 'unknown',\n platform: detectedPlatform,\n architecture: detectedArch,\n version: detectedVersion,\n tunnelUrl: normalizeUrl(tunnelUrl),\n tunnelStatus: 'ACTIVE',\n agentApiKey,\n metadata: { profile: trimmedProfile },\n ...(selectedTargetId ? { targetId: selectedTargetId } : {}),\n };\n\n try {\n const result = await onSubmit(data);\n const newAgentId = result?.id || null;\n\n if (!newAgentId) {\n // Mutation succeeded but returned no agent ID — treat as failure\n setCreateError('Agent creation returned no ID. The agent may already exist.');\n return;\n }\n\n // Close the modal — the agent list will refetch automatically\n if (onNavigate) {\n onNavigate(`/agents/${newAgentId}`);\n } else {\n onCancel();\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to create agent';\n setCreateError(message);\n }\n };\n\n const isValidationPassed = healthResult?.ok && identityResult?.ok;\n const hasTargets = targets && targets.length > 0;\n // The user must either pick or inline-create a target if inline creation\n // is enabled; otherwise we only require it when targets already exist.\n const targetRequiredButMissing = onCreateTarget\n ? selectedTargetId.length === 0\n : hasTargets && selectedTargetId.length === 0;\n const canProceedToValidate = tunnelUrl.trim().length > 0 && !targetRequiredButMissing;\n\n const handleInlineCreateTarget = useCallback(async () => {\n if (!onCreateTarget || !inlineTargetName.trim()) return;\n setCreatingTarget(true);\n setInlineTargetError(null);\n try {\n const created = await onCreateTarget(inlineTargetName.trim());\n if (created) {\n setSelectedTargetId(created.id);\n setShowInlineTarget(false);\n setInlineTargetName('');\n } else {\n setInlineTargetError(\n tr('agentsPage.inlineTargetFailed', 'Failed to create target. Please try again.')\n );\n }\n } catch (err) {\n setInlineTargetError(err instanceof Error ? err.message : 'Failed to create target');\n } finally {\n setCreatingTarget(false);\n }\n }, [onCreateTarget, inlineTargetName, tr]);\n\n const renderCopyButton = (text: string, id: string) => (\n <button\n type=\"button\"\n onClick={() => copyToClipboard(text, id)}\n className=\"p-1 rounded hover:bg-bg-elevated transition-colors\"\n title={tr('agentsPage.copy', 'Copy')}\n >\n {copiedCommand === id ? (\n <Check className=\"size-3.5 text-status-success-text\" />\n ) : (\n <Copy className=\"size-3.5 text-text-secondary\" />\n )}\n </button>\n );\n\n // Stepper\n const steps = [\n { key: 'details', label: 'Setup' },\n { key: 'validate', label: 'Validate' },\n ];\n\n const currentStepIndex = steps.findIndex((s) => s.key === stage);\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-overlay\">\n <div className=\"w-full max-w-xl bg-bg-surface rounded-lg shadow-xl max-h-[90vh] overflow-y-auto\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default sticky top-0 bg-bg-surface z-10\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('agentsPage.addNewAgent', 'Add New Agent')}\n </h2>\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n >\n <X className=\"size-5 text-text-secondary\" />\n </button>\n </div>\n\n {/* Stepper */}\n <div className=\"flex items-center gap-2 px-6 pt-4 pb-2\">\n {steps.map((step, i) => (\n <div key={step.key} className=\"flex items-center gap-2\">\n <div\n className={`flex items-center justify-center size-7 rounded-full text-xs font-semibold transition-colors ${\n i < currentStepIndex\n ? 'bg-status-success-bg text-status-success-text'\n : i === currentStepIndex\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-secondary'\n }`}\n >\n {i < currentStepIndex ? <CheckCircle2 className=\"size-4\" /> : i + 1}\n </div>\n <span\n className={`text-sm ${i === currentStepIndex ? 'font-medium text-text-primary' : 'text-text-secondary'}`}\n >\n {step.label}\n </span>\n {i < steps.length - 1 && <ChevronRight className=\"size-4 text-text-muted\" />}\n </div>\n ))}\n </div>\n\n <div className=\"p-6\">\n {/* Stage 1: Setup */}\n {stage === 'details' && (\n <div className=\"space-y-4\">\n {/* Install instructions (collapsible) */}\n <div className=\"border border-border-default rounded-lg overflow-hidden\">\n <button\n type=\"button\"\n onClick={() => setShowInstallInstructions(!showInstallInstructions)}\n className=\"w-full flex items-center justify-between px-4 py-3 bg-bg-sunken hover:bg-bg-sunken/80 transition-colors\"\n >\n <div className=\"flex items-center gap-2\">\n <Download className=\"size-4 text-action-primary-bg\" />\n <span className=\"text-sm font-medium text-text-primary\">\n {tr('agentsPage.installInstructions', 'Agent Install Instructions')}\n </span>\n </div>\n <ChevronRight\n className={`size-4 text-text-secondary transition-transform ${showInstallInstructions ? 'rotate-90' : ''}`}\n />\n </button>\n {showInstallInstructions && (\n <div className=\"px-4 py-3 space-y-3 border-t border-border-default\">\n <div className=\"space-y-2\">\n {[\n {\n label: tr('agentsPage.installStep1', '1. Install the agent CLI'),\n cmd: 'npm install -g @vibecontrols/agent@latest',\n },\n {\n label: tr('agentsPage.installStep2', '2. Start the agent'),\n cmd: 'vibe start',\n },\n {\n label: tr('agentsPage.installStep3', '3. Get the API key'),\n cmd: 'vibe key',\n },\n {\n label: tr('agentsPage.installStep4', '4. Start the tunnel'),\n cmd: 'vibe tunnel agent --start',\n },\n ].map((step) => (\n <div key={step.cmd}>\n <p className=\"text-xs text-text-secondary mb-1\">{step.label}</p>\n <div className=\"flex items-center gap-2 bg-bg-sunken rounded-md px-3 py-1.5 font-mono text-xs text-text-primary\">\n <Terminal className=\"size-3 text-text-muted flex-shrink-0\" />\n <code className=\"flex-1\">{step.cmd}</code>\n {renderCopyButton(step.cmd, step.cmd)}\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n\n {/* Target Selection — with inline creation */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('agentsPage.target', 'Target')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </label>\n\n {hasTargets && !showInlineTarget && (\n <>\n <SearchableSelect\n placeholder={tr('agentsPage.selectTarget', 'Select a target...')}\n searchPlaceholder={tr('agentsPage.searchTargets', 'Search targets...')}\n options={(targets ?? []).map(\n (tgt): SearchableSelectOption => ({\n value: tgt.id,\n label: tgt.name,\n description: `${tgt.type}${tgt.status ? ` · ${tgt.status}` : ''}`,\n })\n )}\n value={selectedTargetId}\n onChange={setSelectedTargetId}\n clearable\n />\n <p className=\"mt-1 text-xs text-text-muted\">\n {tr(\n 'agentsPage.targetDescription',\n 'The target machine this agent will be installed on.'\n )}{' '}\n {onCreateTarget && (\n <button\n type=\"button\"\n onClick={() => {\n setShowInlineTarget(true);\n setInlineTargetName(name.trim() || '');\n }}\n className=\"text-action-primary-bg hover:underline inline-flex items-center gap-1\"\n >\n <Plus className=\"size-3\" />\n {tr('agentsPage.createNewTarget', 'Create a new target')}\n </button>\n )}\n </p>\n </>\n )}\n\n {/* Empty state prompt (no targets exist yet) */}\n {!hasTargets && !targetsLoading && onCreateTarget && !showInlineTarget && (\n <div className=\"p-3 bg-bg-sunken border border-border-default rounded-lg\">\n <p className=\"text-sm text-text-secondary mb-2\">\n {tr(\n 'agentsPage.noTargetsInlinePrompt',\n \"You don't have any targets yet. Create one now to install this agent on it.\"\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => {\n setShowInlineTarget(true);\n setInlineTargetName(name.trim() || '');\n }}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:opacity-90\"\n >\n <Plus className=\"size-4\" />\n {tr('agentsPage.createFirstTarget', 'Create first target')}\n </button>\n </div>\n )}\n\n {/* Inline creation form */}\n {showInlineTarget && onCreateTarget && (\n <div className=\"p-3 bg-bg-sunken border border-border-default rounded-lg space-y-2\">\n <p className=\"text-xs font-medium text-text-secondary\">\n {tr(\n 'agentsPage.newDirectTarget',\n 'New target (Direct — you can change the type later)'\n )}\n </p>\n <input\n aria-label=\"control\"\n type=\"text\"\n value={inlineTargetName}\n onChange={(e) => setInlineTargetName(e.target.value)}\n placeholder={tr('agentsPage.targetName', 'Target name')}\n className=\"w-full px-3 py-2 text-sm bg-bg-elevated border border-border-default rounded-md text-text-primary placeholder-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n void handleInlineCreateTarget();\n }\n }}\n />\n {inlineTargetError && (\n <p className=\"text-xs text-status-error-text\">{inlineTargetError}</p>\n )}\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => void handleInlineCreateTarget()}\n disabled={!inlineTargetName.trim() || creatingTarget}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:opacity-90 disabled:opacity-50\"\n >\n {creatingTarget ? (\n <Loader2 className=\"size-3.5 animate-spin\" />\n ) : (\n <Check className=\"size-3.5\" />\n )}\n {tr('agentsPage.create', 'Create')}\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setShowInlineTarget(false);\n setInlineTargetName('');\n setInlineTargetError(null);\n }}\n className=\"px-3 py-1.5 text-sm text-text-secondary hover:text-text-primary\"\n >\n {tr('agentsPage.cancel', 'Cancel')}\n </button>\n </div>\n </div>\n )}\n\n {/* Loading placeholder */}\n {targetsLoading && !hasTargets && (\n <div className=\"p-3 bg-bg-sunken border border-border-default rounded-lg flex items-center gap-2 text-sm text-text-secondary\">\n <Loader2 className=\"size-4 animate-spin\" />\n {tr('agentsPage.loadingTargets', 'Loading targets...')}\n </div>\n )}\n </div>\n\n {/* API/Tunnel URL */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('agentsPage.apiTunnelUrl', 'API / Tunnel URL')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n aria-label=\"https://your-tunnel.trycloudflare.com\"\n type=\"url\"\n value={tunnelUrl}\n onChange={(e) => setTunnelUrl(e.target.value)}\n placeholder=\"https://your-tunnel.trycloudflare.com\"\n required\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg font-mono\"\n />\n <p className=\"mt-1 text-xs text-text-muted\">\n {tr(\n 'agentsPage.tunnelUrlHint',\n \"The agent's tunnel URL or local API URL (e.g. http://localhost:3005)\"\n )}\n </p>\n </div>\n\n {/* Agent API Key */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('agentsPage.agentApiKey', 'Agent API Key')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <div className=\"relative\">\n <input\n aria-label=\"vcak_...\"\n type={showApiKey ? 'text' : 'password'}\n value={agentApiKey}\n onChange={(e) => setAgentApiKey(e.target.value)}\n placeholder=\"vcak_...\"\n required\n className=\"w-full px-3 py-2 pr-10 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg font-mono\"\n />\n <button\n type=\"button\"\n onClick={() => setShowApiKey(!showApiKey)}\n className=\"absolute right-2 top-1/2 -translate-y-1/2 p-1 rounded hover:bg-bg-elevated transition-colors\"\n >\n {showApiKey ? (\n <EyeOff className=\"size-4 text-text-secondary\" />\n ) : (\n <Eye className=\"size-4 text-text-secondary\" />\n )}\n </button>\n </div>\n <p className=\"mt-1 text-xs text-text-muted\">\n {tr('agentsPage.apiKeyHint', 'Run')}{' '}\n <code className=\"bg-bg-sunken px-1 rounded\">vibe key</code>{' '}\n {tr(\n 'agentsPage.apiKeyHintSuffix',\n 'on the agent to get this. Changes on restart.'\n )}\n </p>\n </div>\n\n {/* Advanced options — per-daemon profile name (Phase 5B) */}\n <div className=\"border border-border-default rounded-lg overflow-hidden\">\n <button\n type=\"button\"\n onClick={() => setShowAdvanced((v) => !v)}\n className=\"w-full flex items-center justify-between px-4 py-2.5 bg-bg-sunken hover:bg-bg-sunken/80 transition-colors\"\n aria-expanded={showAdvanced}\n data-testid=\"agent-form-advanced-toggle\"\n >\n <div className=\"flex items-center gap-2\">\n <Settings2 className=\"size-4 text-text-secondary\" />\n <span className=\"text-sm font-medium text-text-primary\">\n {tr('agentsPage.advancedOptions', 'Advanced options')}\n </span>\n </div>\n <ChevronDown\n className={`size-4 text-text-secondary transition-transform ${showAdvanced ? 'rotate-180' : ''}`}\n />\n </button>\n {showAdvanced && (\n <div className=\"px-4 py-3 space-y-3 border-t border-border-default\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('agentsPage.profileName', 'Profile name')}\n </label>\n <input\n aria-label=\"default\"\n type=\"text\"\n value={profile}\n onChange={(e) => setProfile(e.target.value)}\n placeholder=\"default\"\n className=\"w-full px-3 py-2 text-sm bg-bg-surface border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg font-mono\"\n data-testid=\"agent-form-profile-input\"\n />\n <p className=\"mt-1 text-xs text-text-muted\">\n {tr(\n 'agentsPage.profileNameHint',\n 'Each profile runs an isolated CLI on the daemon. Leave as \"default\" unless you want this agent to host multiple parallel profiles.'\n )}\n </p>\n </div>\n </div>\n )}\n </div>\n\n {extraSection && (\n <div className=\"border-t border-border-default pt-4 mt-4\">{extraSection}</div>\n )}\n\n {/* Actions */}\n <div className=\"flex items-center justify-between pt-4\">\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"px-4 py-2 text-sm font-medium text-text-primary bg-bg-sunken border border-border-default rounded-md hover:bg-bg-elevated transition-colors\"\n >\n {tr('agentsPage.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n disabled={!canProceedToValidate}\n onClick={() => {\n setStage('validate');\n validateAgent();\n }}\n className=\"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 disabled:opacity-50\"\n >\n {tr('agentsPage.nextValidate', 'Next: Validate')}\n <ChevronRight className=\"size-4\" />\n </button>\n </div>\n </div>\n )}\n\n {/* Stage 2: Validate */}\n {stage === 'validate' && (\n <div className=\"space-y-4\">\n {/* Validation Results */}\n <div className=\"space-y-3\">\n {/* Health Check */}\n <div\n className={`flex items-center gap-3 p-3 rounded-lg border ${\n healthResult === null\n ? 'border-border-default bg-bg-sunken'\n : healthResult.ok\n ? 'border-status-success-text/20 bg-status-success-bg/30'\n : 'border-status-error-text/20 bg-status-error-bg/30'\n }`}\n >\n {validating && !healthResult ? (\n <Loader2 className=\"size-5 text-text-secondary animate-spin\" />\n ) : healthResult?.ok ? (\n <CheckCircle2 className=\"size-5 text-status-success-text\" />\n ) : healthResult ? (\n <XCircle className=\"size-5 text-status-error-text\" />\n ) : (\n <div className=\"size-5 rounded-full border-2 border-border-default\" />\n )}\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('agentsPage.healthCheck', 'Health Check')}\n </p>\n {healthResult?.ok && healthResult.data && (\n <p className=\"text-xs text-text-secondary\">\n Status: {healthResult.data.status} | Version: {healthResult.data.version} |\n Uptime: {Math.round(healthResult.data.uptime)}s\n </p>\n )}\n {healthResult && !healthResult.ok && (\n <p className=\"text-xs text-status-error-text\">{healthResult.error}</p>\n )}\n </div>\n </div>\n\n {/* API Access Check */}\n <div\n className={`flex items-center gap-3 p-3 rounded-lg border ${\n identityResult === null\n ? 'border-border-default bg-bg-sunken'\n : identityResult.ok\n ? 'border-status-success-text/20 bg-status-success-bg/30'\n : 'border-status-error-text/20 bg-status-error-bg/30'\n }`}\n >\n {validating && !identityResult ? (\n <Loader2 className=\"size-5 text-text-secondary animate-spin\" />\n ) : identityResult?.ok ? (\n <CheckCircle2 className=\"size-5 text-status-success-text\" />\n ) : identityResult ? (\n <XCircle className=\"size-5 text-status-error-text\" />\n ) : (\n <div className=\"size-5 rounded-full border-2 border-border-default\" />\n )}\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('agentsPage.apiAccess', 'API Access')}\n </p>\n {identityResult?.ok && identityResult.data && (\n <p className=\"text-xs text-text-secondary\">\n Host: {identityResult.data.hostname} | {identityResult.data.platform} /{' '}\n {identityResult.data.arch}\n </p>\n )}\n {identityResult && !identityResult.ok && (\n <p className=\"text-xs text-status-error-text\">{identityResult.error}</p>\n )}\n </div>\n </div>\n </div>\n\n {/* Retry button */}\n {!validating && (!healthResult?.ok || !identityResult?.ok) && (\n <button\n type=\"button\"\n onClick={validateAgent}\n className=\"flex items-center gap-2 px-3 py-1.5 text-sm text-text-secondary hover:text-text-primary hover:bg-bg-sunken rounded-md transition-colors\"\n >\n <RefreshCw className=\"size-3.5\" />\n {tr('agentsPage.retryValidation', 'Retry validation')}\n </button>\n )}\n\n {/* Detected info (editable overrides) */}\n {isValidationPassed && (\n <div className=\"space-y-3 pt-2\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('agentsPage.detectedAgentInfo', 'Detected Agent Info')}\n </p>\n\n <div>\n <label className=\"block text-xs font-medium text-text-secondary mb-1\">\n {tr('agentsPage.name', 'Name')}\n </label>\n <input\n aria-label=\"control\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder={detectedHostname}\n className=\"w-full px-3 py-1.5 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label className=\"block text-xs font-medium text-text-secondary mb-1\">\n {tr('agentsPage.platform', 'Platform')}\n </label>\n <select\n value={detectedPlatform}\n onChange={(e) => setDetectedPlatform(e.target.value)}\n className=\"w-full px-3 py-1.5 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n >\n <option value=\"linux\">Linux</option>\n <option value=\"darwin\">macOS</option>\n <option value=\"windows\">Windows</option>\n </select>\n </div>\n <div>\n <label className=\"block text-xs font-medium text-text-secondary mb-1\">\n {tr('agentsPage.architecture', 'Architecture')}\n </label>\n <select\n value={detectedArch}\n onChange={(e) => setDetectedArch(e.target.value)}\n className=\"w-full px-3 py-1.5 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n >\n <option value=\"x86_64\">x86_64</option>\n <option value=\"arm64\">arm64</option>\n <option value=\"aarch64\">aarch64</option>\n </select>\n </div>\n </div>\n </div>\n )}\n\n {/* Creation error */}\n {createError && (\n <div className=\"p-3 bg-status-error-bg/30 border border-status-error-text/20 rounded-lg\">\n <div className=\"flex items-center gap-2\">\n <XCircle className=\"size-5 text-status-error-text flex-shrink-0\" />\n <p className=\"text-sm text-status-error-text\">{createError}</p>\n </div>\n <div className=\"flex items-center gap-2 mt-3\">\n <button\n type=\"button\"\n onClick={() => {\n setCreateError(null);\n // Reset form to allow entering different credentials\n setStage('details');\n setTunnelUrl('');\n setAgentApiKey('');\n setHealthResult(null);\n setIdentityResult(null);\n }}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-text-primary bg-bg-sunken border border-border-default rounded-md hover:bg-bg-elevated transition-colors\"\n >\n <RefreshCw className=\"size-3.5\" />\n {tr('agentsPage.tryDifferentAgent', 'Try Different Agent')}\n </button>\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"px-3 py-1.5 text-sm font-medium text-text-secondary hover:text-text-primary transition-colors\"\n >\n {tr('agentsPage.close', 'Close')}\n </button>\n </div>\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex items-center justify-between pt-4\">\n <button\n type=\"button\"\n onClick={() => setStage('details')}\n className=\"flex items-center gap-1 px-4 py-2 text-sm font-medium text-text-primary bg-bg-sunken border border-border-default rounded-md hover:bg-bg-elevated transition-colors\"\n >\n <ChevronLeft className=\"size-4\" />\n {tr('agentsPage.back', 'Back')}\n </button>\n <button\n type=\"button\"\n disabled={!isValidationPassed || isLoading || validating}\n onClick={handleCreateAgent}\n className=\"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 disabled:opacity-50\"\n >\n {isLoading ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n {tr('agentsPage.adding', 'Adding...')}\n </>\n ) : (\n <>\n {tr('agentsPage.addAgent', 'Add Agent')}\n <ChevronRight className=\"size-4\" />\n </>\n )}\n </button>\n </div>\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AgentForm.js","names":[],"sources":["../../../src/components/agents/AgentForm.tsx"],"sourcesContent":["import { useEffect, useState, useCallback } from 'react';\nimport type { ReactNode } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n X,\n Eye,\n EyeOff,\n CheckCircle2,\n XCircle,\n Loader2,\n ChevronRight,\n ChevronLeft,\n ChevronDown,\n Copy,\n Check,\n Terminal,\n Download,\n RefreshCw,\n Plus,\n Settings2,\n} from 'lucide-react';\n// useProbeAgentTunnelLazyQuery removed: validate now hits the agent's tunnel\n// URL directly from the browser (see validateAgent below).\nimport { SearchableSelect } from '@/components/shared';\nimport type { SearchableSelectOption } from '@/components/shared';\n\ntype WizardStage = 'details' | 'validate';\n\ninterface TargetOption {\n id: string;\n name: string;\n type: string;\n status: string;\n}\n\ninterface AgentFormProps {\n onSubmit: (data: Record<string, unknown>) => Promise<{ id: string } | void>;\n onCancel: () => void;\n isLoading?: boolean;\n /** Navigate to a path (used to go to agent details after creation) */\n onNavigate?: (path: string) => void;\n /** Available targets for the agent to be installed on */\n targets?: TargetOption[];\n /** Whether targets are still loading */\n targetsLoading?: boolean;\n /**\n * Create a new target inline. When provided, the form shows a\n * \"+ Create new target\" affordance that lets users create a DIRECT\n * target on the fly without leaving the dialog. Must return the new\n * target so the form can auto-select it.\n */\n onCreateTarget?: (name: string) => Promise<TargetOption | null>;\n /** Optional extra content (e.g. a draft EntityTagPicker) rendered above the configure-stage actions. */\n extraSection?: ReactNode;\n}\n\nexport function AgentForm({\n onSubmit,\n onCancel,\n isLoading,\n onNavigate,\n targets,\n targetsLoading,\n onCreateTarget,\n extraSection,\n}: AgentFormProps) {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n const [stage, setStage] = useState<WizardStage>('details');\n const [showApiKey, setShowApiKey] = useState(false);\n const [showInstallInstructions, setShowInstallInstructions] = useState(false);\n const [copiedCommand, setCopiedCommand] = useState<string | null>(null);\n\n // Stage 1: Basic details\n const [name, setName] = useState('');\n // Tracks whether the operator has typed into the name field. Once true,\n // we stop overwriting the value from auto-fill sources (target select +\n // identity probe) so we never clobber their input.\n const [nameEdited, setNameEdited] = useState(false);\n const [tunnelUrl, setTunnelUrl] = useState('');\n const [agentApiKey, setAgentApiKey] = useState('');\n const [selectedTargetId, setSelectedTargetId] = useState('');\n\n // When the operator picks a target, autofill the agent name with the\n // target's display name. Skip if they've already typed something into\n // the name field. The svc append a `-2`/`-3` suffix on collision so\n // it's safe to suggest a duplicate; the actual stored name comes back\n // on the create response.\n useEffect(() => {\n if (nameEdited) return;\n if (!selectedTargetId) return;\n const picked = (targets ?? []).find((t) => t.id === selectedTargetId);\n if (picked?.name) setName(picked.name);\n }, [selectedTargetId, targets, nameEdited]);\n\n // Inline target creation state\n const [showInlineTarget, setShowInlineTarget] = useState(false);\n const [inlineTargetName, setInlineTargetName] = useState('');\n const [creatingTarget, setCreatingTarget] = useState(false);\n const [inlineTargetError, setInlineTargetError] = useState<string | null>(null);\n\n // Stage 2: Validation (via backend-proxied GraphQL query)\n const [validating, setValidating] = useState(false);\n const [healthResult, setHealthResult] = useState<{\n ok: boolean;\n data?: { status: string; version: string; uptime: number };\n error?: string;\n } | null>(null);\n const [identityResult, setIdentityResult] = useState<{\n ok: boolean;\n data?: { hostname: string; platform: string; arch: string };\n error?: string;\n } | null>(null);\n\n // Auto-detected fields (editable overrides)\n const [detectedHostname, setDetectedHostname] = useState('');\n const [detectedPlatform, setDetectedPlatform] = useState('linux');\n const [detectedArch, setDetectedArch] = useState('x86_64');\n const [detectedVersion, setDetectedVersion] = useState('1.0.0');\n\n // Error state for creation failures\n const [createError, setCreateError] = useState<string | null>(null);\n\n // Advanced options (Phase 5B: per-daemon profile name).\n // Defaults to \"default\" (autofilled but editable). Disclosure is closed\n // by default — users only need to touch this for multi-profile setups.\n const [showAdvanced, setShowAdvanced] = useState(false);\n const [profile, setProfile] = useState('default');\n\n const copyToClipboard = useCallback((text: string, id: string) => {\n navigator.clipboard.writeText(text);\n setCopiedCommand(id);\n setTimeout(() => setCopiedCommand(null), 2000);\n }, []);\n\n const normalizeUrl = (url: string) => {\n let u = url.trim();\n if (u.endsWith('/')) u = u.slice(0, -1);\n return u;\n };\n\n // Validate the agent by probing /health and /api/profiles/default/agent/identity\n // directly from the browser. The agent exposes CORS-allowed endpoints for\n // app.vibecontrols.com (see agent's createApp() — `@elysiajs/cors` is wired\n // for the host origin), so the browser can reach the tunnel URL without a\n // backend proxy. The earlier backend-proxy approach (probeAgentTunnel query)\n // is still wired into the schema, but in production we observed the proxy\n // hanging at the ACA egress layer regardless of timeout, so we bypass it for\n // the wizard validate step. Each fetch is bounded by a 10s AbortSignal so a\n // dead tunnel surfaces a real error instead of an infinite spinner.\n const validateAgent = async () => {\n const baseUrl = normalizeUrl(tunnelUrl);\n if (!baseUrl) return;\n\n setValidating(true);\n setHealthResult(null);\n setIdentityResult(null);\n\n const probeHealth = async (): Promise<void> => {\n try {\n const res = await fetch(`${baseUrl}/health`, {\n signal: AbortSignal.timeout(10_000),\n });\n if (!res.ok) {\n setHealthResult({ ok: false, error: `HTTP ${String(res.status)}: ${res.statusText}` });\n return;\n }\n const data = (await res.json()) as {\n status?: string;\n version?: string;\n uptime?: number;\n };\n setHealthResult({\n ok: true,\n data: {\n status: data.status || 'healthy',\n version: data.version || 'unknown',\n uptime: data.uptime || 0,\n },\n });\n if (data.version) setDetectedVersion(data.version);\n } catch (err) {\n const isTimeout = err instanceof DOMException && err.name === 'AbortError';\n setHealthResult({\n ok: false,\n error: isTimeout\n ? 'Health check timed out after 10s'\n : err instanceof Error\n ? err.message\n : 'Health check failed',\n });\n }\n };\n\n const probeIdentity = async (): Promise<void> => {\n try {\n const headers: Record<string, string> = {};\n if (agentApiKey) headers['x-agent-api-key'] = agentApiKey;\n const res = await fetch(`${baseUrl}/api/profiles/default/agent/identity`, {\n headers,\n signal: AbortSignal.timeout(10_000),\n });\n if (!res.ok) {\n setIdentityResult({\n ok: false,\n error:\n res.status === 401\n ? 'Invalid API key'\n : `HTTP ${String(res.status)}: ${res.statusText}`,\n });\n return;\n }\n const data = (await res.json()) as {\n hostname?: string;\n platform?: string;\n arch?: string;\n };\n const hostname = data.hostname || '';\n const platform = data.platform || 'linux';\n const arch = data.arch || 'x86_64';\n setIdentityResult({ ok: true, data: { hostname, platform, arch } });\n if (hostname) setDetectedHostname(hostname);\n if (platform) setDetectedPlatform(platform);\n if (arch) setDetectedArch(arch);\n // Auto-fill the name from the probe's hostname unless (a) the\n // operator has manually typed a name, or (b) a target is already\n // selected (target name wins as the canonical autogenerated\n // value — see the selectedTargetId effect below).\n if (!nameEdited && !selectedTargetId && hostname) setName(hostname);\n } catch (err) {\n const isTimeout = err instanceof DOMException && err.name === 'AbortError';\n setIdentityResult({\n ok: false,\n error: isTimeout\n ? 'Identity check timed out after 10s'\n : err instanceof Error\n ? err.message\n : 'Identity check failed',\n });\n }\n };\n\n // Run both probes in parallel so the user sees results as fast as the\n // slower of the two — instead of stacking 20s in series on failure paths.\n await Promise.all([probeHealth(), probeIdentity()]);\n\n setValidating(false);\n };\n\n // Create the agent and close the modal\n const handleCreateAgent = async () => {\n setCreateError(null);\n\n // Pipe the chosen profile through `metadata.profile`. Phase 5A (svc) is\n // adding first-class AgentProfile rows; in the meantime the backend\n // also reads `metadata.profile` so the create path keeps working\n // regardless of which side of the cut-over the cluster is on.\n const trimmedProfile = profile.trim() || 'default';\n const data: Record<string, unknown> = {\n name: name || detectedHostname || 'Unnamed Agent',\n hostname: detectedHostname || 'unknown',\n platform: detectedPlatform,\n architecture: detectedArch,\n version: detectedVersion,\n tunnelUrl: normalizeUrl(tunnelUrl),\n tunnelStatus: 'ACTIVE',\n agentApiKey,\n metadata: { profile: trimmedProfile },\n ...(selectedTargetId ? { targetId: selectedTargetId } : {}),\n };\n\n try {\n const result = await onSubmit(data);\n const newAgentId = result?.id || null;\n\n if (!newAgentId) {\n // Mutation succeeded but returned no agent ID — treat as failure\n setCreateError('Agent creation returned no ID. The agent may already exist.');\n return;\n }\n\n // Close the modal — the agent list will refetch automatically\n if (onNavigate) {\n onNavigate(`/agents/${newAgentId}`);\n } else {\n onCancel();\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to create agent';\n setCreateError(message);\n }\n };\n\n const isValidationPassed = healthResult?.ok && identityResult?.ok;\n const hasTargets = targets && targets.length > 0;\n // The user must either pick or inline-create a target if inline creation\n // is enabled; otherwise we only require it when targets already exist.\n const targetRequiredButMissing = onCreateTarget\n ? selectedTargetId.length === 0\n : hasTargets && selectedTargetId.length === 0;\n const canProceedToValidate = tunnelUrl.trim().length > 0 && !targetRequiredButMissing;\n\n const handleInlineCreateTarget = useCallback(async () => {\n if (!onCreateTarget || !inlineTargetName.trim()) return;\n setCreatingTarget(true);\n setInlineTargetError(null);\n try {\n const created = await onCreateTarget(inlineTargetName.trim());\n if (created) {\n setSelectedTargetId(created.id);\n setShowInlineTarget(false);\n setInlineTargetName('');\n } else {\n setInlineTargetError(\n tr('agentsPage.inlineTargetFailed', 'Failed to create target. Please try again.')\n );\n }\n } catch (err) {\n setInlineTargetError(err instanceof Error ? err.message : 'Failed to create target');\n } finally {\n setCreatingTarget(false);\n }\n }, [onCreateTarget, inlineTargetName, tr]);\n\n const renderCopyButton = (text: string, id: string) => (\n <button\n type=\"button\"\n onClick={() => copyToClipboard(text, id)}\n className=\"p-1 rounded hover:bg-bg-elevated transition-colors\"\n title={tr('agentsPage.copy', 'Copy')}\n >\n {copiedCommand === id ? (\n <Check className=\"size-3.5 text-status-success-text\" />\n ) : (\n <Copy className=\"size-3.5 text-text-secondary\" />\n )}\n </button>\n );\n\n // Stepper\n const steps = [\n { key: 'details', label: 'Setup' },\n { key: 'validate', label: 'Validate' },\n ];\n\n const currentStepIndex = steps.findIndex((s) => s.key === stage);\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-overlay\">\n <div className=\"w-full max-w-xl bg-bg-surface rounded-lg shadow-xl max-h-[90vh] overflow-y-auto\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default sticky top-0 bg-bg-surface z-10\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('agentsPage.addNewAgent', 'Add New Agent')}\n </h2>\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n >\n <X className=\"size-5 text-text-secondary\" />\n </button>\n </div>\n\n {/* Stepper */}\n <div className=\"flex items-center gap-2 px-6 pt-4 pb-2\">\n {steps.map((step, i) => (\n <div key={step.key} className=\"flex items-center gap-2\">\n <div\n className={`flex items-center justify-center size-7 rounded-full text-xs font-semibold transition-colors ${\n i < currentStepIndex\n ? 'bg-status-success-bg text-status-success-text'\n : i === currentStepIndex\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-secondary'\n }`}\n >\n {i < currentStepIndex ? <CheckCircle2 className=\"size-4\" /> : i + 1}\n </div>\n <span\n className={`text-sm ${i === currentStepIndex ? 'font-medium text-text-primary' : 'text-text-secondary'}`}\n >\n {step.label}\n </span>\n {i < steps.length - 1 && <ChevronRight className=\"size-4 text-text-muted\" />}\n </div>\n ))}\n </div>\n\n <div className=\"p-6\">\n {/* Stage 1: Setup */}\n {stage === 'details' && (\n <div className=\"space-y-4\">\n {/* Install instructions (collapsible) */}\n <div className=\"border border-border-default rounded-lg overflow-hidden\">\n <button\n type=\"button\"\n onClick={() => setShowInstallInstructions(!showInstallInstructions)}\n className=\"w-full flex items-center justify-between px-4 py-3 bg-bg-sunken hover:bg-bg-sunken/80 transition-colors\"\n >\n <div className=\"flex items-center gap-2\">\n <Download className=\"size-4 text-action-primary-bg\" />\n <span className=\"text-sm font-medium text-text-primary\">\n {tr('agentsPage.installInstructions', 'Agent Install Instructions')}\n </span>\n </div>\n <ChevronRight\n className={`size-4 text-text-secondary transition-transform ${showInstallInstructions ? 'rotate-90' : ''}`}\n />\n </button>\n {showInstallInstructions && (\n <div className=\"px-4 py-3 space-y-3 border-t border-border-default\">\n <div className=\"space-y-2\">\n {[\n {\n label: tr('agentsPage.installStep1', '1. Install the agent CLI'),\n cmd: 'npm install -g @vibecontrols/agent@latest',\n },\n {\n label: tr('agentsPage.installStep2', '2. Start the agent'),\n cmd: 'vibe start',\n },\n {\n label: tr('agentsPage.installStep3', '3. Get the API key'),\n cmd: 'vibe key',\n },\n {\n label: tr('agentsPage.installStep4', '4. Start the tunnel'),\n cmd: 'vibe tunnel agent --start',\n },\n ].map((step) => (\n <div key={step.cmd}>\n <p className=\"text-xs text-text-secondary mb-1\">{step.label}</p>\n <div className=\"flex items-center gap-2 bg-bg-sunken rounded-md px-3 py-1.5 font-mono text-xs text-text-primary\">\n <Terminal className=\"size-3 text-text-muted flex-shrink-0\" />\n <code className=\"flex-1\">{step.cmd}</code>\n {renderCopyButton(step.cmd, step.cmd)}\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n\n {/* Target Selection — with inline creation */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('agentsPage.target', 'Target')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </label>\n\n {hasTargets && !showInlineTarget && (\n <>\n <SearchableSelect\n placeholder={tr('agentsPage.selectTarget', 'Select a target...')}\n searchPlaceholder={tr('agentsPage.searchTargets', 'Search targets...')}\n options={(targets ?? []).map(\n (tgt): SearchableSelectOption => ({\n value: tgt.id,\n label: tgt.name,\n description: `${tgt.type}${tgt.status ? ` · ${tgt.status}` : ''}`,\n })\n )}\n value={selectedTargetId}\n onChange={setSelectedTargetId}\n clearable\n />\n <p className=\"mt-1 text-xs text-text-muted\">\n {tr(\n 'agentsPage.targetDescription',\n 'The target machine this agent will be installed on.'\n )}{' '}\n {onCreateTarget && (\n <button\n type=\"button\"\n onClick={() => {\n setShowInlineTarget(true);\n setInlineTargetName(name.trim() || '');\n }}\n className=\"text-action-primary-bg hover:underline inline-flex items-center gap-1\"\n >\n <Plus className=\"size-3\" />\n {tr('agentsPage.createNewTarget', 'Create a new target')}\n </button>\n )}\n </p>\n </>\n )}\n\n {/* Empty state prompt (no targets exist yet) */}\n {!hasTargets && !targetsLoading && onCreateTarget && !showInlineTarget && (\n <div className=\"p-3 bg-bg-sunken border border-border-default rounded-lg\">\n <p className=\"text-sm text-text-secondary mb-2\">\n {tr(\n 'agentsPage.noTargetsInlinePrompt',\n \"You don't have any targets yet. Create one now to install this agent on it.\"\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => {\n setShowInlineTarget(true);\n setInlineTargetName(name.trim() || '');\n }}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:opacity-90\"\n >\n <Plus className=\"size-4\" />\n {tr('agentsPage.createFirstTarget', 'Create first target')}\n </button>\n </div>\n )}\n\n {/* Inline creation form */}\n {showInlineTarget && onCreateTarget && (\n <div className=\"p-3 bg-bg-sunken border border-border-default rounded-lg space-y-2\">\n <p className=\"text-xs font-medium text-text-secondary\">\n {tr(\n 'agentsPage.newDirectTarget',\n 'New target (Direct — you can change the type later)'\n )}\n </p>\n <input\n aria-label=\"control\"\n type=\"text\"\n value={inlineTargetName}\n onChange={(e) => setInlineTargetName(e.target.value)}\n placeholder={tr('agentsPage.targetName', 'Target name')}\n className=\"w-full px-3 py-2 text-sm bg-bg-elevated border border-border-default rounded-md text-text-primary placeholder-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n void handleInlineCreateTarget();\n }\n }}\n />\n {inlineTargetError && (\n <p className=\"text-xs text-status-error-text\">{inlineTargetError}</p>\n )}\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => void handleInlineCreateTarget()}\n disabled={!inlineTargetName.trim() || creatingTarget}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:opacity-90 disabled:opacity-50\"\n >\n {creatingTarget ? (\n <Loader2 className=\"size-3.5 animate-spin\" />\n ) : (\n <Check className=\"size-3.5\" />\n )}\n {tr('agentsPage.create', 'Create')}\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setShowInlineTarget(false);\n setInlineTargetName('');\n setInlineTargetError(null);\n }}\n className=\"px-3 py-1.5 text-sm text-text-secondary hover:text-text-primary\"\n >\n {tr('agentsPage.cancel', 'Cancel')}\n </button>\n </div>\n </div>\n )}\n\n {/* Loading placeholder */}\n {targetsLoading && !hasTargets && (\n <div className=\"p-3 bg-bg-sunken border border-border-default rounded-lg flex items-center gap-2 text-sm text-text-secondary\">\n <Loader2 className=\"size-4 animate-spin\" />\n {tr('agentsPage.loadingTargets', 'Loading targets...')}\n </div>\n )}\n </div>\n\n {/* API/Tunnel URL */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('agentsPage.apiTunnelUrl', 'API / Tunnel URL')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n aria-label=\"https://your-tunnel.trycloudflare.com\"\n type=\"url\"\n value={tunnelUrl}\n onChange={(e) => setTunnelUrl(e.target.value)}\n placeholder=\"https://your-tunnel.trycloudflare.com\"\n required\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg font-mono\"\n />\n <p className=\"mt-1 text-xs text-text-muted\">\n {tr(\n 'agentsPage.tunnelUrlHint',\n \"The agent's tunnel URL or local API URL (e.g. http://localhost:3005)\"\n )}\n </p>\n </div>\n\n {/* Agent API Key */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('agentsPage.agentApiKey', 'Agent API Key')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <div className=\"relative\">\n <input\n aria-label=\"vcak_...\"\n type={showApiKey ? 'text' : 'password'}\n value={agentApiKey}\n onChange={(e) => setAgentApiKey(e.target.value)}\n placeholder=\"vcak_...\"\n required\n className=\"w-full px-3 py-2 pr-10 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg font-mono\"\n />\n <button\n type=\"button\"\n onClick={() => setShowApiKey(!showApiKey)}\n className=\"absolute right-2 top-1/2 -translate-y-1/2 p-1 rounded hover:bg-bg-elevated transition-colors\"\n >\n {showApiKey ? (\n <EyeOff className=\"size-4 text-text-secondary\" />\n ) : (\n <Eye className=\"size-4 text-text-secondary\" />\n )}\n </button>\n </div>\n <p className=\"mt-1 text-xs text-text-muted\">\n {tr('agentsPage.apiKeyHint', 'Run')}{' '}\n <code className=\"bg-bg-sunken px-1 rounded\">vibe key</code>{' '}\n {tr(\n 'agentsPage.apiKeyHintSuffix',\n 'on the agent to get this. Changes on restart.'\n )}\n </p>\n </div>\n\n {/* Advanced options — per-daemon profile name (Phase 5B) */}\n <div className=\"border border-border-default rounded-lg overflow-hidden\">\n <button\n type=\"button\"\n onClick={() => setShowAdvanced((v) => !v)}\n className=\"w-full flex items-center justify-between px-4 py-2.5 bg-bg-sunken hover:bg-bg-sunken/80 transition-colors\"\n aria-expanded={showAdvanced}\n data-testid=\"agent-form-advanced-toggle\"\n >\n <div className=\"flex items-center gap-2\">\n <Settings2 className=\"size-4 text-text-secondary\" />\n <span className=\"text-sm font-medium text-text-primary\">\n {tr('agentsPage.advancedOptions', 'Advanced options')}\n </span>\n </div>\n <ChevronDown\n className={`size-4 text-text-secondary transition-transform ${showAdvanced ? 'rotate-180' : ''}`}\n />\n </button>\n {showAdvanced && (\n <div className=\"px-4 py-3 space-y-3 border-t border-border-default\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('agentsPage.profileName', 'Profile name')}\n </label>\n <input\n aria-label=\"default\"\n type=\"text\"\n value={profile}\n onChange={(e) => setProfile(e.target.value)}\n placeholder=\"default\"\n className=\"w-full px-3 py-2 text-sm bg-bg-surface border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg font-mono\"\n data-testid=\"agent-form-profile-input\"\n />\n <p className=\"mt-1 text-xs text-text-muted\">\n {tr(\n 'agentsPage.profileNameHint',\n 'Each profile runs an isolated CLI on the daemon. Leave as \"default\" unless you want this agent to host multiple parallel profiles.'\n )}\n </p>\n </div>\n </div>\n )}\n </div>\n\n {extraSection && (\n <div className=\"border-t border-border-default pt-4 mt-4\">{extraSection}</div>\n )}\n\n {/* Actions */}\n <div className=\"flex items-center justify-between pt-4\">\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"px-4 py-2 text-sm font-medium text-text-primary bg-bg-sunken border border-border-default rounded-md hover:bg-bg-elevated transition-colors\"\n >\n {tr('agentsPage.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n disabled={!canProceedToValidate}\n onClick={() => {\n setStage('validate');\n validateAgent();\n }}\n className=\"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 disabled:opacity-50\"\n >\n {tr('agentsPage.nextValidate', 'Next: Validate')}\n <ChevronRight className=\"size-4\" />\n </button>\n </div>\n </div>\n )}\n\n {/* Stage 2: Validate */}\n {stage === 'validate' && (\n <div className=\"space-y-4\">\n {/* Validation Results */}\n <div className=\"space-y-3\">\n {/* Health Check */}\n <div\n className={`flex items-center gap-3 p-3 rounded-lg border ${\n healthResult === null\n ? 'border-border-default bg-bg-sunken'\n : healthResult.ok\n ? 'border-status-success-text/20 bg-status-success-bg/30'\n : 'border-status-error-text/20 bg-status-error-bg/30'\n }`}\n >\n {validating && !healthResult ? (\n <Loader2 className=\"size-5 text-text-secondary animate-spin\" />\n ) : healthResult?.ok ? (\n <CheckCircle2 className=\"size-5 text-status-success-text\" />\n ) : healthResult ? (\n <XCircle className=\"size-5 text-status-error-text\" />\n ) : (\n <div className=\"size-5 rounded-full border-2 border-border-default\" />\n )}\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('agentsPage.healthCheck', 'Health Check')}\n </p>\n {healthResult?.ok && healthResult.data && (\n <p className=\"text-xs text-text-secondary\">\n Status: {healthResult.data.status} | Version: {healthResult.data.version} |\n Uptime: {Math.round(healthResult.data.uptime)}s\n </p>\n )}\n {healthResult && !healthResult.ok && (\n <p className=\"text-xs text-status-error-text\">{healthResult.error}</p>\n )}\n </div>\n </div>\n\n {/* API Access Check */}\n <div\n className={`flex items-center gap-3 p-3 rounded-lg border ${\n identityResult === null\n ? 'border-border-default bg-bg-sunken'\n : identityResult.ok\n ? 'border-status-success-text/20 bg-status-success-bg/30'\n : 'border-status-error-text/20 bg-status-error-bg/30'\n }`}\n >\n {validating && !identityResult ? (\n <Loader2 className=\"size-5 text-text-secondary animate-spin\" />\n ) : identityResult?.ok ? (\n <CheckCircle2 className=\"size-5 text-status-success-text\" />\n ) : identityResult ? (\n <XCircle className=\"size-5 text-status-error-text\" />\n ) : (\n <div className=\"size-5 rounded-full border-2 border-border-default\" />\n )}\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('agentsPage.apiAccess', 'API Access')}\n </p>\n {identityResult?.ok && identityResult.data && (\n <p className=\"text-xs text-text-secondary\">\n Host: {identityResult.data.hostname} | {identityResult.data.platform} /{' '}\n {identityResult.data.arch}\n </p>\n )}\n {identityResult && !identityResult.ok && (\n <p className=\"text-xs text-status-error-text\">{identityResult.error}</p>\n )}\n </div>\n </div>\n </div>\n\n {/* Retry button */}\n {!validating && (!healthResult?.ok || !identityResult?.ok) && (\n <button\n type=\"button\"\n onClick={validateAgent}\n className=\"flex items-center gap-2 px-3 py-1.5 text-sm text-text-secondary hover:text-text-primary hover:bg-bg-sunken rounded-md transition-colors\"\n >\n <RefreshCw className=\"size-3.5\" />\n {tr('agentsPage.retryValidation', 'Retry validation')}\n </button>\n )}\n\n {/* Detected info (editable overrides) */}\n {isValidationPassed && (\n <div className=\"space-y-3 pt-2\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('agentsPage.detectedAgentInfo', 'Detected Agent Info')}\n </p>\n\n <div>\n <label className=\"block text-xs font-medium text-text-secondary mb-1\">\n {tr('agentsPage.name', 'Name')}\n </label>\n <input\n aria-label=\"control\"\n type=\"text\"\n value={name}\n onChange={(e) => {\n setName(e.target.value);\n setNameEdited(true);\n }}\n placeholder={detectedHostname}\n className=\"w-full px-3 py-1.5 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label className=\"block text-xs font-medium text-text-secondary mb-1\">\n {tr('agentsPage.platform', 'Platform')}\n </label>\n <select\n value={detectedPlatform}\n onChange={(e) => setDetectedPlatform(e.target.value)}\n className=\"w-full px-3 py-1.5 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n >\n <option value=\"linux\">Linux</option>\n <option value=\"darwin\">macOS</option>\n <option value=\"windows\">Windows</option>\n </select>\n </div>\n <div>\n <label className=\"block text-xs font-medium text-text-secondary mb-1\">\n {tr('agentsPage.architecture', 'Architecture')}\n </label>\n <select\n value={detectedArch}\n onChange={(e) => setDetectedArch(e.target.value)}\n className=\"w-full px-3 py-1.5 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n >\n <option value=\"x86_64\">x86_64</option>\n <option value=\"arm64\">arm64</option>\n <option value=\"aarch64\">aarch64</option>\n </select>\n </div>\n </div>\n </div>\n )}\n\n {/* Creation error */}\n {createError && (\n <div className=\"p-3 bg-status-error-bg/30 border border-status-error-text/20 rounded-lg\">\n <div className=\"flex items-center gap-2\">\n <XCircle className=\"size-5 text-status-error-text flex-shrink-0\" />\n <p className=\"text-sm text-status-error-text\">{createError}</p>\n </div>\n <div className=\"flex items-center gap-2 mt-3\">\n <button\n type=\"button\"\n onClick={() => {\n setCreateError(null);\n // Reset form to allow entering different credentials\n setStage('details');\n setTunnelUrl('');\n setAgentApiKey('');\n setHealthResult(null);\n setIdentityResult(null);\n }}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-text-primary bg-bg-sunken border border-border-default rounded-md hover:bg-bg-elevated transition-colors\"\n >\n <RefreshCw className=\"size-3.5\" />\n {tr('agentsPage.tryDifferentAgent', 'Try Different Agent')}\n </button>\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"px-3 py-1.5 text-sm font-medium text-text-secondary hover:text-text-primary transition-colors\"\n >\n {tr('agentsPage.close', 'Close')}\n </button>\n </div>\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex items-center justify-between pt-4\">\n <button\n type=\"button\"\n onClick={() => setStage('details')}\n className=\"flex items-center gap-1 px-4 py-2 text-sm font-medium text-text-primary bg-bg-sunken border border-border-default rounded-md hover:bg-bg-elevated transition-colors\"\n >\n <ChevronLeft className=\"size-4\" />\n {tr('agentsPage.back', 'Back')}\n </button>\n <button\n type=\"button\"\n disabled={!isValidationPassed || isLoading || validating}\n onClick={handleCreateAgent}\n className=\"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 disabled:opacity-50\"\n >\n {isLoading ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n {tr('agentsPage.adding', 'Adding...')}\n </>\n ) : (\n <>\n {tr('agentsPage.addAgent', 'Add Agent')}\n <ChevronRight className=\"size-4\" />\n </>\n )}\n </button>\n </div>\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|