@burdenoff/microfe-vibecontrols 2026.526.2 → 2026.526.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,21 +5,26 @@ import { CheckCircle2 as a, Clock as o, Database as s, Loader2 as c, Play as l,
5
5
  import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
6
6
  //#region src/components/agents/backup/AgentBackupTab.tsx
7
7
  function b(e) {
8
- return e < 1024 ? `${e} B` : e < 1024 * 1024 ? `${(e / 1024).toFixed(1)} KB` : `${(e / (1024 * 1024)).toFixed(1)} MB`;
8
+ if (!e) return "Backup failed.";
9
+ let t = e.toLowerCase();
10
+ return t.includes("undefined is not an object") || t.includes("cannot read property") || t.includes("cannot read properties of") || /\btypeerror:/.test(t) || /\breferenceerror:/.test(t) ? e.includes("createResult.data.createFolder") ? "Backup failed: the agent could not reach the workspace files service (missing workspace context). Re-push credentials from the agent's Security tab and try again. See agent logs for the full error." : "Backup failed: the agent threw an internal error. See agent logs for the full error." : e.length > 200 ? e.slice(0, 200) + "…" : e;
9
11
  }
10
12
  function x(e) {
11
- return e < 1e3 ? `${e}ms` : `${(e / 1e3).toFixed(1)}s`;
13
+ return e < 1024 ? `${e} B` : e < 1024 * 1024 ? `${(e / 1024).toFixed(1)} KB` : `${(e / (1024 * 1024)).toFixed(1)} MB`;
12
14
  }
13
15
  function S(e) {
16
+ return e < 1e3 ? `${e}ms` : `${(e / 1e3).toFixed(1)}s`;
17
+ }
18
+ function C(e) {
14
19
  return new Date(e).toLocaleString();
15
20
  }
16
- function C({ agentTunnelUrl: C, agentApiKey: w, profile: T }) {
17
- let E = e(), [D, O] = i(null), [k, A] = i(null), [j, M] = i([]), [N, P] = i(!0), [F, I] = i(null), [L, R] = i(null), [z, B] = i(null), [V, H] = i(!1), U = C.replace(/\/+$/, ""), W = n(async (e, t) => {
18
- let n = await fetch(`${U}${e}`, {
21
+ function w({ agentTunnelUrl: w, agentApiKey: T, profile: E }) {
22
+ let D = e(), [O, k] = i(null), [A, j] = i(null), [M, N] = i([]), [P, F] = i(!0), [I, L] = i(null), [R, z] = i(null), [B, V] = i(null), [H, U] = i(!1), W = w.replace(/\/+$/, ""), G = n(async (e, t) => {
23
+ let n = await fetch(`${W}${e}`, {
19
24
  ...t,
20
25
  headers: {
21
26
  "Content-Type": "application/json",
22
- "x-agent-api-key": w,
27
+ "x-agent-api-key": T,
23
28
  ...t?.headers
24
29
  }
25
30
  });
@@ -28,109 +33,117 @@ function C({ agentTunnelUrl: C, agentApiKey: w, profile: T }) {
28
33
  throw Error(e || `HTTP ${n.status}`);
29
34
  }
30
35
  return n.json();
31
- }, [U, w]), G = n(async () => {
36
+ }, [W, T]), K = n(async () => {
32
37
  try {
33
- P(!0), R(null);
38
+ F(!0), z(null);
34
39
  let [e, n, r] = await Promise.all([
35
- W(t(T, "/backup/config")),
36
- W(t(T, "/backup/status")),
37
- W(`${t(T, "/backup/list")}?limit=20`)
40
+ G(t(E, "/backup/config")),
41
+ G(t(E, "/backup/status")),
42
+ G(`${t(E, "/backup/list")}?limit=20`)
38
43
  ]);
39
- O(e.config), A(n.status), M(r.backups || []);
44
+ k(e.config), j(n.status), N(r.backups || []);
40
45
  } catch (e) {
41
46
  let t = e instanceof Error ? e.message : String(e);
42
- R(t === "Failed to fetch" ? `Cannot reach agent REST API at ${U}. Ensure the agent is running and its tunnel URL is accessible.` : t);
47
+ z(t === "Failed to fetch" ? `Cannot reach agent REST API at ${W}. Ensure the agent is running and its tunnel URL is accessible.` : t);
43
48
  } finally {
44
- P(!1);
49
+ F(!1);
45
50
  }
46
- }, [W, U]);
51
+ }, [G, W]);
47
52
  r(() => {
48
- G();
49
- }, [G]), r(() => {
50
- if (z) {
51
- let e = setTimeout(() => B(null), 3e3);
53
+ K();
54
+ }, [K]), r(() => {
55
+ if (B) {
56
+ let e = setTimeout(() => V(null), 3e3);
52
57
  return () => clearTimeout(e);
53
58
  }
54
- }, [z]);
55
- let K = async (e) => {
59
+ }, [B]);
60
+ let q = async (e) => {
56
61
  try {
57
- I("backup"), R(null);
58
- let n = (await W(`${t(T, "/backup/run")}?force=${e}`, { method: "POST" })).backup;
59
- n.error === "Skipped: no changes" ? B("No changes detected — backup skipped") : B(`Backup completed: ${b(n.dbSizeBytes)} in ${x(n.durationMs)}`), await G();
62
+ L("backup"), z(null);
63
+ let n = await G(`${t(E, "/backup/run")}?force=${e}`, { method: "POST" }), r = n?.backup, i = typeof n?.error == "string" && n.error || (r && typeof r.error == "string" && r.error !== "Skipped: no changes" ? r.error : null);
64
+ if (i) {
65
+ z(b(i));
66
+ return;
67
+ }
68
+ if (!r) {
69
+ z(b(""));
70
+ return;
71
+ }
72
+ r.error === "Skipped: no changes" ? V("No changes detected — backup skipped") : V(`Backup completed: ${x(r.dbSizeBytes)} in ${S(r.durationMs)}`), await K();
60
73
  } catch (e) {
61
- R(e instanceof Error ? e.message : String(e));
74
+ z(b(e instanceof Error ? e.message : String(e)));
62
75
  } finally {
63
- I(null);
76
+ L(null);
64
77
  }
65
- }, q = async (e, n) => {
78
+ }, J = async (e, n) => {
66
79
  try {
67
- I(`restore-${e}`), R(null);
68
- let r = await W(t(T, "/backup/restore"), {
80
+ L(`restore-${e}`), z(null);
81
+ let r = await G(t(E, "/backup/restore"), {
69
82
  method: "POST",
70
83
  body: JSON.stringify({
71
84
  backupId: e,
72
85
  dryRun: n
73
86
  })
74
87
  });
75
- r.success ? B(r.message) : R(r.message);
88
+ r.success ? V(r.message) : z(r.message);
76
89
  } catch (e) {
77
- R(e instanceof Error ? e.message : String(e));
90
+ z(e instanceof Error ? e.message : String(e));
78
91
  } finally {
79
- I(null);
92
+ L(null);
80
93
  }
81
- }, J = async () => {
94
+ }, Y = async () => {
82
95
  try {
83
- I("toggle");
84
- let e = await W(t(T, "/backup/config"), {
96
+ L("toggle");
97
+ let e = await G(t(E, "/backup/config"), {
85
98
  method: "PUT",
86
- body: JSON.stringify({ enabled: !D?.enabled })
99
+ body: JSON.stringify({ enabled: !O?.enabled })
87
100
  });
88
- O(e.config), B(e.config.enabled ? "Backup enabled" : "Backup disabled"), await G();
101
+ k(e.config), V(e.config.enabled ? "Backup enabled" : "Backup disabled"), await K();
89
102
  } catch (e) {
90
- R(e instanceof Error ? e.message : String(e));
103
+ z(e instanceof Error ? e.message : String(e));
91
104
  } finally {
92
- I(null);
105
+ L(null);
93
106
  }
94
- }, Y = async () => {
107
+ }, X = async () => {
95
108
  try {
96
- I("test");
97
- let e = await W(t(T, "/backup/test-connection"), { method: "POST" });
98
- e.ok ? B(`Connection OK: ${e.message}`) : R(`Connection failed: ${e.message}`);
109
+ L("test");
110
+ let e = await G(t(E, "/backup/test-connection"), { method: "POST" });
111
+ e.ok ? V(`Connection OK: ${e.message}`) : z(`Connection failed: ${e.message}`);
99
112
  } catch (e) {
100
- R(e instanceof Error ? e.message : String(e));
113
+ z(e instanceof Error ? e.message : String(e));
101
114
  } finally {
102
- I(null);
115
+ L(null);
103
116
  }
104
- }, X = async (e) => {
117
+ }, Z = async (e) => {
105
118
  try {
106
- I(`delete-${e}`), await W(t(T, `/backup/backups/${e}`), { method: "DELETE" }), B("Backup deleted"), await G();
119
+ L(`delete-${e}`), await G(t(E, `/backup/backups/${e}`), { method: "DELETE" }), V("Backup deleted"), await K();
107
120
  } catch (e) {
108
- R(e instanceof Error ? e.message : String(e));
121
+ z(e instanceof Error ? e.message : String(e));
109
122
  } finally {
110
- I(null);
123
+ L(null);
111
124
  }
112
125
  };
113
- return N ? /* @__PURE__ */ y("div", {
126
+ return P ? /* @__PURE__ */ y("div", {
114
127
  className: "flex items-center justify-center py-12 text-text-secondary",
115
- children: [/* @__PURE__ */ v(c, { className: "w-5 h-5 animate-spin mr-2" }), E("vibecontrols.agents.backup.loading", "Loading backup settings...")]
128
+ children: [/* @__PURE__ */ v(c, { className: "w-5 h-5 animate-spin mr-2" }), D("vibecontrols.agents.backup.loading", "Loading backup settings...")]
116
129
  }) : /* @__PURE__ */ y("div", {
117
130
  className: "space-y-6",
118
131
  children: [
119
- L && /* @__PURE__ */ y("div", {
132
+ R && /* @__PURE__ */ y("div", {
120
133
  className: "flex items-center gap-2 rounded-lg border border-status-error-border bg-status-error-bg-subtle p-3 text-sm text-status-error-text",
121
134
  children: [
122
135
  /* @__PURE__ */ v(g, { className: "w-4 h-4 shrink-0" }),
123
- L,
136
+ R,
124
137
  /* @__PURE__ */ v("button", {
125
- onClick: () => R(null),
138
+ onClick: () => z(null),
126
139
  className: "ml-auto text-xs underline",
127
140
  children: "Dismiss"
128
141
  })
129
142
  ]
130
143
  }),
131
- z && /* @__PURE__ */ y("div", {
144
+ B && /* @__PURE__ */ y("div", {
132
145
  className: "flex items-center gap-2 rounded-lg border border-status-success-border bg-status-success-bg-subtle p-3 text-sm text-status-success-text",
133
- children: [/* @__PURE__ */ v(a, { className: "w-4 h-4 shrink-0" }), z]
146
+ children: [/* @__PURE__ */ v(a, { className: "w-4 h-4 shrink-0" }), B]
134
147
  }),
135
148
  /* @__PURE__ */ y("div", {
136
149
  className: "rounded-lg border border-border-default bg-bg-surface p-5",
@@ -141,17 +154,17 @@ function C({ agentTunnelUrl: C, agentApiKey: w, profile: T }) {
141
154
  className: "flex items-center gap-2",
142
155
  children: [/* @__PURE__ */ v(s, { className: "w-5 h-5 text-action-primary-bg" }), /* @__PURE__ */ v("h3", {
143
156
  className: "font-semibold text-text-primary",
144
- children: E("vibecontrols.agents.backup.title", "Database Backup")
157
+ children: D("vibecontrols.agents.backup.title", "Database Backup")
145
158
  })]
146
159
  }), /* @__PURE__ */ y("div", {
147
160
  className: "flex items-center gap-2",
148
161
  children: [/* @__PURE__ */ v("button", {
149
- onClick: G,
162
+ onClick: K,
150
163
  className: "p-2 rounded-lg hover:bg-bg-sunken transition-colors text-text-secondary",
151
164
  title: "Refresh",
152
165
  children: /* @__PURE__ */ v(u, { className: "w-4 h-4" })
153
166
  }), /* @__PURE__ */ v("button", {
154
- onClick: () => H(!V),
167
+ onClick: () => U(!H),
155
168
  className: "p-2 rounded-lg hover:bg-bg-sunken transition-colors text-text-secondary",
156
169
  title: "Settings",
157
170
  children: /* @__PURE__ */ v(f, { className: "w-4 h-4" })
@@ -163,84 +176,84 @@ function C({ agentTunnelUrl: C, agentApiKey: w, profile: T }) {
163
176
  children: [
164
177
  /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
165
178
  className: "text-xs text-text-muted mb-1",
166
- children: E("vibecontrols.agents.backup.status", "Status")
179
+ children: D("vibecontrols.agents.backup.status", "Status")
167
180
  }), /* @__PURE__ */ v("div", {
168
181
  className: "flex items-center gap-1.5",
169
- children: D?.enabled ? /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v("div", { className: "h-2 w-2 rounded-full bg-status-success-bg" }), /* @__PURE__ */ v("span", {
182
+ children: O?.enabled ? /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v("div", { className: "h-2 w-2 rounded-full bg-status-success-bg" }), /* @__PURE__ */ v("span", {
170
183
  className: "text-sm font-medium text-text-primary",
171
- children: E("vibecontrols.agents.backup.enabled", "Enabled")
184
+ children: D("vibecontrols.agents.backup.enabled", "Enabled")
172
185
  })] }) : /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v("div", { className: "h-2 w-2 rounded-full bg-text-tertiary" }), /* @__PURE__ */ v("span", {
173
186
  className: "text-sm font-medium text-text-secondary",
174
- children: E("vibecontrols.agents.backup.disabled", "Disabled")
187
+ children: D("vibecontrols.agents.backup.disabled", "Disabled")
175
188
  })] })
176
189
  })] }),
177
190
  /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
178
191
  className: "text-xs text-text-muted mb-1",
179
- children: E("vibecontrols.agents.backup.scheduler", "Scheduler")
192
+ children: D("vibecontrols.agents.backup.scheduler", "Scheduler")
180
193
  }), /* @__PURE__ */ v("span", {
181
194
  className: "text-sm font-medium text-text-primary",
182
- children: k?.schedulerRunning ? "Running" : "Stopped"
195
+ children: A?.schedulerRunning ? "Running" : "Stopped"
183
196
  })] }),
184
197
  /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
185
198
  className: "text-xs text-text-muted mb-1",
186
- children: E("vibecontrols.agents.backup.totalBackups", "Total Backups")
199
+ children: D("vibecontrols.agents.backup.totalBackups", "Total Backups")
187
200
  }), /* @__PURE__ */ v("span", {
188
201
  className: "text-sm font-medium text-text-primary",
189
- children: k?.totalBackups ?? 0
202
+ children: A?.totalBackups ?? 0
190
203
  })] }),
191
204
  /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
192
205
  className: "text-xs text-text-muted mb-1",
193
- children: E("vibecontrols.agents.backup.target", "Target")
206
+ children: D("vibecontrols.agents.backup.target", "Target")
194
207
  }), /* @__PURE__ */ v("span", {
195
208
  className: "text-sm font-medium text-text-primary",
196
- children: k?.storageTarget ?? "N/A"
209
+ children: A?.storageTarget ?? "N/A"
197
210
  })] })
198
211
  ]
199
212
  }),
200
- k?.lastBackupTime && /* @__PURE__ */ y("div", {
213
+ A?.lastBackupTime && /* @__PURE__ */ y("div", {
201
214
  className: "flex flex-wrap items-center gap-3 text-xs text-text-muted",
202
215
  children: [/* @__PURE__ */ y("span", { children: [
203
216
  /* @__PURE__ */ v(o, { className: "w-3 h-3 inline mr-1" }),
204
217
  "Last: ",
205
- S(k.lastBackupTime)
206
- ] }), k.nextScheduledBackup && /* @__PURE__ */ y("span", { children: ["Next: ", S(k.nextScheduledBackup)] })]
218
+ C(A.lastBackupTime)
219
+ ] }), A.nextScheduledBackup && /* @__PURE__ */ y("span", { children: ["Next: ", C(A.nextScheduledBackup)] })]
207
220
  }),
208
221
  /* @__PURE__ */ y("div", {
209
222
  className: "flex flex-wrap gap-2 mt-4 pt-4 border-t border-border-subtle",
210
223
  children: [
211
224
  /* @__PURE__ */ y("button", {
212
- onClick: () => K(!0),
213
- disabled: !!F,
225
+ onClick: () => q(!0),
226
+ disabled: !!I,
214
227
  className: "flex items-center gap-2 px-3 py-1.5 rounded-lg bg-action-primary-bg text-action-primary-text text-sm font-medium hover:bg-action-primary-bg-hover disabled:opacity-50 transition-colors",
215
- children: [F === "backup" ? /* @__PURE__ */ v(c, { className: "w-4 h-4 animate-spin" }) : /* @__PURE__ */ v(m, { className: "w-4 h-4" }), E("vibecontrols.agents.backup.backupNow", "Backup Now")]
228
+ children: [I === "backup" ? /* @__PURE__ */ v(c, { className: "w-4 h-4 animate-spin" }) : /* @__PURE__ */ v(m, { className: "w-4 h-4" }), D("vibecontrols.agents.backup.backupNow", "Backup Now")]
216
229
  }),
217
230
  /* @__PURE__ */ y("button", {
218
- onClick: () => K(!1),
219
- disabled: !!F,
231
+ onClick: () => q(!1),
232
+ disabled: !!I,
220
233
  className: "flex items-center gap-2 px-3 py-1.5 rounded-lg bg-bg-sunken text-text-primary text-sm font-medium hover:bg-bg-canvas border border-border-default disabled:opacity-50 transition-colors",
221
- children: [/* @__PURE__ */ v(l, { className: "w-4 h-4" }), E("vibecontrols.agents.backup.backupIfChanged", "Backup if Changed")]
234
+ children: [/* @__PURE__ */ v(l, { className: "w-4 h-4" }), D("vibecontrols.agents.backup.backupIfChanged", "Backup if Changed")]
222
235
  }),
223
236
  /* @__PURE__ */ y("button", {
224
- onClick: J,
225
- disabled: !!F,
237
+ onClick: Y,
238
+ disabled: !!I,
226
239
  className: "flex items-center gap-2 px-3 py-1.5 rounded-lg bg-bg-sunken text-text-primary text-sm font-medium hover:bg-bg-canvas border border-border-default disabled:opacity-50 transition-colors",
227
- children: [F === "toggle" ? /* @__PURE__ */ v(c, { className: "w-4 h-4 animate-spin" }) : D?.enabled ? /* @__PURE__ */ v(g, { className: "w-4 h-4" }) : /* @__PURE__ */ v(a, { className: "w-4 h-4" }), D?.enabled ? E("vibecontrols.agents.backup.disableSchedule", "Disable Schedule") : E("vibecontrols.agents.backup.enableSchedule", "Enable Schedule")]
240
+ children: [I === "toggle" ? /* @__PURE__ */ v(c, { className: "w-4 h-4 animate-spin" }) : O?.enabled ? /* @__PURE__ */ v(g, { className: "w-4 h-4" }) : /* @__PURE__ */ v(a, { className: "w-4 h-4" }), O?.enabled ? D("vibecontrols.agents.backup.disableSchedule", "Disable Schedule") : D("vibecontrols.agents.backup.enableSchedule", "Enable Schedule")]
228
241
  }),
229
242
  /* @__PURE__ */ y("button", {
230
- onClick: Y,
231
- disabled: !!F,
243
+ onClick: X,
244
+ disabled: !!I,
232
245
  className: "flex items-center gap-2 px-3 py-1.5 rounded-lg bg-bg-sunken text-text-primary text-sm font-medium hover:bg-bg-canvas border border-border-default disabled:opacity-50 transition-colors",
233
- children: [F === "test" ? /* @__PURE__ */ v(c, { className: "w-4 h-4 animate-spin" }) : /* @__PURE__ */ v(h, { className: "w-4 h-4" }), E("vibecontrols.agents.backup.testConnection", "Test Connection")]
246
+ children: [I === "test" ? /* @__PURE__ */ v(c, { className: "w-4 h-4 animate-spin" }) : /* @__PURE__ */ v(h, { className: "w-4 h-4" }), D("vibecontrols.agents.backup.testConnection", "Test Connection")]
234
247
  })
235
248
  ]
236
249
  })
237
250
  ]
238
251
  }),
239
- V && D && /* @__PURE__ */ y("div", {
252
+ H && O && /* @__PURE__ */ y("div", {
240
253
  className: "rounded-lg border border-border-default bg-bg-surface p-5",
241
254
  children: [/* @__PURE__ */ y("h4", {
242
255
  className: "font-semibold text-text-primary mb-3 flex items-center gap-2",
243
- children: [/* @__PURE__ */ v(f, { className: "w-4 h-4" }), E("vibecontrols.agents.backup.configuration", "Configuration")]
256
+ children: [/* @__PURE__ */ v(f, { className: "w-4 h-4" }), D("vibecontrols.agents.backup.configuration", "Configuration")]
244
257
  }), /* @__PURE__ */ y("div", {
245
258
  className: "grid grid-cols-1 md:grid-cols-2 gap-4 text-sm",
246
259
  children: [
@@ -252,7 +265,7 @@ function C({ agentTunnelUrl: C, agentApiKey: w, profile: T }) {
252
265
  " ",
253
266
  /* @__PURE__ */ v("span", {
254
267
  className: "text-text-primary font-medium",
255
- children: D.scheduleIntervalMs >= 36e5 ? `${D.scheduleIntervalMs / 36e5}h` : `${D.scheduleIntervalMs / 6e4}m`
268
+ children: O.scheduleIntervalMs >= 36e5 ? `${O.scheduleIntervalMs / 36e5}h` : `${O.scheduleIntervalMs / 6e4}m`
256
269
  })
257
270
  ] }),
258
271
  /* @__PURE__ */ y("div", { children: [
@@ -263,7 +276,7 @@ function C({ agentTunnelUrl: C, agentApiKey: w, profile: T }) {
263
276
  " ",
264
277
  /* @__PURE__ */ v("span", {
265
278
  className: "text-text-primary font-medium",
266
- children: D.changeOnlyBackup ? "On" : "Off"
279
+ children: O.changeOnlyBackup ? "On" : "Off"
267
280
  })
268
281
  ] }),
269
282
  /* @__PURE__ */ y("div", { children: [
@@ -274,10 +287,10 @@ function C({ agentTunnelUrl: C, agentApiKey: w, profile: T }) {
274
287
  " ",
275
288
  /* @__PURE__ */ v("span", {
276
289
  className: "text-text-primary font-medium",
277
- children: D.storageTarget
290
+ children: O.storageTarget
278
291
  })
279
292
  ] }),
280
- D.customS3 && /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ y("div", { children: [
293
+ O.customS3 && /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ y("div", { children: [
281
294
  /* @__PURE__ */ v("span", {
282
295
  className: "text-text-muted",
283
296
  children: "S3 Bucket:"
@@ -285,7 +298,7 @@ function C({ agentTunnelUrl: C, agentApiKey: w, profile: T }) {
285
298
  " ",
286
299
  /* @__PURE__ */ v("span", {
287
300
  className: "text-text-primary font-medium",
288
- children: D.customS3.bucket
301
+ children: O.customS3.bucket
289
302
  })
290
303
  ] }), /* @__PURE__ */ y("div", { children: [
291
304
  /* @__PURE__ */ v("span", {
@@ -295,10 +308,10 @@ function C({ agentTunnelUrl: C, agentApiKey: w, profile: T }) {
295
308
  " ",
296
309
  /* @__PURE__ */ v("span", {
297
310
  className: "text-text-primary font-medium",
298
- children: D.customS3.region
311
+ children: O.customS3.region
299
312
  })
300
313
  ] })] }),
301
- D.retentionDays && /* @__PURE__ */ y("div", { children: [
314
+ O.retentionDays && /* @__PURE__ */ y("div", { children: [
302
315
  /* @__PURE__ */ v("span", {
303
316
  className: "text-text-muted",
304
317
  children: "Retention:"
@@ -306,10 +319,10 @@ function C({ agentTunnelUrl: C, agentApiKey: w, profile: T }) {
306
319
  " ",
307
320
  /* @__PURE__ */ y("span", {
308
321
  className: "text-text-primary font-medium",
309
- children: [D.retentionDays, " days"]
322
+ children: [O.retentionDays, " days"]
310
323
  })
311
324
  ] }),
312
- D.maxBackups && /* @__PURE__ */ y("div", { children: [
325
+ O.maxBackups && /* @__PURE__ */ y("div", { children: [
313
326
  /* @__PURE__ */ v("span", {
314
327
  className: "text-text-muted",
315
328
  children: "Max Backups:"
@@ -317,7 +330,7 @@ function C({ agentTunnelUrl: C, agentApiKey: w, profile: T }) {
317
330
  " ",
318
331
  /* @__PURE__ */ v("span", {
319
332
  className: "text-text-primary font-medium",
320
- children: D.maxBackups
333
+ children: O.maxBackups
321
334
  })
322
335
  ] })
323
336
  ]
@@ -329,22 +342,22 @@ function C({ agentTunnelUrl: C, agentApiKey: w, profile: T }) {
329
342
  className: "font-semibold text-text-primary mb-3 flex items-center gap-2",
330
343
  children: [
331
344
  /* @__PURE__ */ v(o, { className: "w-4 h-4" }),
332
- E("vibecontrols.agents.backup.history", "Backup History"),
345
+ D("vibecontrols.agents.backup.history", "Backup History"),
333
346
  /* @__PURE__ */ y("span", {
334
347
  className: "text-xs text-text-muted font-normal",
335
348
  children: [
336
349
  "(",
337
- j.length,
350
+ M.length,
338
351
  " shown)"
339
352
  ]
340
353
  })
341
354
  ]
342
- }), j.length === 0 ? /* @__PURE__ */ v("p", {
355
+ }), M.length === 0 ? /* @__PURE__ */ v("p", {
343
356
  className: "text-sm text-text-muted py-4 text-center",
344
- children: E("vibecontrols.agents.backup.noBackups", "No backups yet. Click \"Backup Now\" to create one.")
357
+ children: D("vibecontrols.agents.backup.noBackups", "No backups yet. Click \"Backup Now\" to create one.")
345
358
  }) : /* @__PURE__ */ v("div", {
346
359
  className: "space-y-2",
347
- children: j.map((e) => /* @__PURE__ */ y("div", {
360
+ children: M.map((e) => /* @__PURE__ */ y("div", {
348
361
  className: "flex items-center justify-between p-3 rounded-lg border border-border-subtle hover:bg-bg-sunken transition-colors",
349
362
  children: [/* @__PURE__ */ y("div", {
350
363
  className: "flex items-center gap-3 min-w-0",
@@ -355,13 +368,13 @@ function C({ agentTunnelUrl: C, agentApiKey: w, profile: T }) {
355
368
  className: "min-w-0",
356
369
  children: [/* @__PURE__ */ v("p", {
357
370
  className: "text-sm font-medium text-text-primary truncate",
358
- children: S(e.timestamp)
371
+ children: C(e.timestamp)
359
372
  }), /* @__PURE__ */ y("p", {
360
373
  className: "text-xs text-text-muted truncate",
361
374
  children: [
362
- b(e.dbSizeBytes),
375
+ x(e.dbSizeBytes),
363
376
  " · ",
364
- x(e.durationMs),
377
+ S(e.durationMs),
365
378
  " ·",
366
379
  " ",
367
380
  e.storageTarget
@@ -372,27 +385,27 @@ function C({ agentTunnelUrl: C, agentApiKey: w, profile: T }) {
372
385
  className: "flex items-center gap-1 shrink-0",
373
386
  children: [
374
387
  /* @__PURE__ */ v("button", {
375
- onClick: () => q(e.id, !0),
376
- disabled: !!F,
388
+ onClick: () => J(e.id, !0),
389
+ disabled: !!I,
377
390
  className: "p-1.5 rounded hover:bg-bg-canvas text-text-secondary transition-colors",
378
391
  title: "Validate (dry run)",
379
- children: F === `restore-${e.id}` ? /* @__PURE__ */ v(c, { className: "w-4 h-4 animate-spin" }) : /* @__PURE__ */ v(a, { className: "w-4 h-4" })
392
+ children: I === `restore-${e.id}` ? /* @__PURE__ */ v(c, { className: "w-4 h-4 animate-spin" }) : /* @__PURE__ */ v(a, { className: "w-4 h-4" })
380
393
  }),
381
394
  /* @__PURE__ */ v("button", {
382
395
  onClick: () => {
383
- confirm("Restore this backup? The agent will need to restart.") && q(e.id, !1);
396
+ confirm("Restore this backup? The agent will need to restart.") && J(e.id, !1);
384
397
  },
385
- disabled: !!F,
398
+ disabled: !!I,
386
399
  className: "p-1.5 rounded hover:bg-bg-canvas text-text-secondary transition-colors",
387
400
  title: "Restore",
388
401
  children: /* @__PURE__ */ v(d, { className: "w-4 h-4" })
389
402
  }),
390
403
  /* @__PURE__ */ v("button", {
391
- onClick: () => X(e.id),
392
- disabled: !!F,
404
+ onClick: () => Z(e.id),
405
+ disabled: !!I,
393
406
  className: "p-1.5 rounded text-text-secondary transition-colors hover:bg-status-error-bg/80 hover:text-status-error-text",
394
407
  title: "Delete",
395
- children: F === `delete-${e.id}` ? /* @__PURE__ */ v(c, { className: "w-4 h-4 animate-spin" }) : /* @__PURE__ */ v(p, { className: "w-4 h-4" })
408
+ children: I === `delete-${e.id}` ? /* @__PURE__ */ v(c, { className: "w-4 h-4 animate-spin" }) : /* @__PURE__ */ v(p, { className: "w-4 h-4" })
396
409
  })
397
410
  ]
398
411
  })]
@@ -403,6 +416,6 @@ function C({ agentTunnelUrl: C, agentApiKey: w, profile: T }) {
403
416
  });
404
417
  }
405
418
  //#endregion
406
- export { C as AgentBackupTab };
419
+ export { w as AgentBackupTab };
407
420
 
408
421
  //# sourceMappingURL=AgentBackupTab.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AgentBackupTab.js","names":[],"sources":["../../../../src/components/agents/backup/AgentBackupTab.tsx"],"sourcesContent":["/**\n * AgentBackupTab — UI for managing SQLite database backups of a VibeControls agent.\n * Talks directly to the agent's REST API via its tunnel URL.\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport {\n Database,\n Upload,\n RefreshCw,\n Settings,\n Play,\n Clock,\n CheckCircle2,\n XCircle,\n Loader2,\n Trash2,\n RotateCcw,\n Wifi,\n} from 'lucide-react';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport { agentApiPath } from '@/utils/agentApiPath';\n\ninterface BackupConfig {\n enabled: boolean;\n scheduleIntervalMs: number;\n changeOnlyBackup: boolean;\n storageTarget: 'wspace' | 'custom-s3';\n customS3?: {\n endpoint: string;\n bucket: string;\n region: string;\n accessKeyId: string;\n secretAccessKey: string;\n pathPrefix?: string;\n };\n retentionDays?: number;\n maxBackups?: number;\n}\n\ninterface BackupRecord {\n id: string;\n timestamp: string;\n agentName: string;\n dbSizeBytes: number;\n sha256Hash: string;\n storageTarget: string;\n storagePath: string;\n durationMs: number;\n status: string;\n error?: string;\n}\n\ninterface BackupStatus {\n enabled: boolean;\n schedulerRunning: boolean;\n lastBackupTime: string | null;\n lastBackupHash: string | null;\n nextScheduledBackup: string | null;\n totalBackups: number;\n storageTarget: string;\n}\n\ninterface AgentBackupTabProps {\n agentTunnelUrl: string;\n agentApiKey: string;\n /** Canonical CLI profile name on the remote agent. */\n profile: string;\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n return `${(ms / 1000).toFixed(1)}s`;\n}\n\nfunction formatTime(iso: string): string {\n return new Date(iso).toLocaleString();\n}\n\nexport function AgentBackupTab({ agentTunnelUrl, agentApiKey, profile }: AgentBackupTabProps) {\n const tr = useTr();\n const [config, setConfig] = useState<BackupConfig | null>(null);\n const [status, setStatus] = useState<BackupStatus | null>(null);\n const [backups, setBackups] = useState<BackupRecord[]>([]);\n const [loading, setLoading] = useState(true);\n const [actionLoading, setActionLoading] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [successMsg, setSuccessMsg] = useState<string | null>(null);\n const [showConfig, setShowConfig] = useState(false);\n\n const apiUrl = agentTunnelUrl.replace(/\\/+$/, '');\n\n const apiFetch = useCallback(\n async (path: string, options?: RequestInit) => {\n const res = await fetch(`${apiUrl}${path}`, {\n ...options,\n headers: {\n 'Content-Type': 'application/json',\n 'x-agent-api-key': agentApiKey,\n ...options?.headers,\n },\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(text || `HTTP ${res.status}`);\n }\n return res.json();\n },\n [apiUrl, agentApiKey]\n );\n\n const refresh = useCallback(async () => {\n try {\n setLoading(true);\n setError(null);\n const [configRes, statusRes, listRes] = await Promise.all([\n apiFetch(agentApiPath(profile, '/backup/config')),\n apiFetch(agentApiPath(profile, '/backup/status')),\n apiFetch(`${agentApiPath(profile, '/backup/list')}?limit=20`),\n ]);\n setConfig(configRes.config);\n setStatus(statusRes.status);\n setBackups(listRes.backups || []);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n setError(\n msg === 'Failed to fetch'\n ? `Cannot reach agent REST API at ${apiUrl}. Ensure the agent is running and its tunnel URL is accessible.`\n : msg\n );\n } finally {\n setLoading(false);\n }\n }, [apiFetch, apiUrl]);\n\n useEffect(() => {\n refresh();\n }, [refresh]);\n\n // Clear success message after 3s\n useEffect(() => {\n if (successMsg) {\n const t = setTimeout(() => setSuccessMsg(null), 3000);\n return () => clearTimeout(t);\n }\n }, [successMsg]);\n\n const handleRunBackup = async (force: boolean) => {\n try {\n setActionLoading('backup');\n setError(null);\n const res = await apiFetch(`${agentApiPath(profile, '/backup/run')}?force=${force}`, {\n method: 'POST',\n });\n const b = res.backup;\n if (b.error === 'Skipped: no changes') {\n setSuccessMsg('No changes detected — backup skipped');\n } else {\n setSuccessMsg(\n `Backup completed: ${formatBytes(b.dbSizeBytes)} in ${formatDuration(b.durationMs)}`\n );\n }\n await refresh();\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleRestore = async (backupId: string, dryRun: boolean) => {\n try {\n setActionLoading(`restore-${backupId}`);\n setError(null);\n const res = await apiFetch(agentApiPath(profile, '/backup/restore'), {\n method: 'POST',\n body: JSON.stringify({ backupId, dryRun }),\n });\n if (res.success) {\n setSuccessMsg(res.message);\n } else {\n setError(res.message);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleToggleEnabled = async () => {\n try {\n setActionLoading('toggle');\n const res = await apiFetch(agentApiPath(profile, '/backup/config'), {\n method: 'PUT',\n body: JSON.stringify({ enabled: !config?.enabled }),\n });\n setConfig(res.config);\n setSuccessMsg(res.config.enabled ? 'Backup enabled' : 'Backup disabled');\n await refresh();\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleTestConnection = async () => {\n try {\n setActionLoading('test');\n const res = await apiFetch(agentApiPath(profile, '/backup/test-connection'), {\n method: 'POST',\n });\n if (res.ok) {\n setSuccessMsg(`Connection OK: ${res.message}`);\n } else {\n setError(`Connection failed: ${res.message}`);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleDeleteBackup = async (backupId: string) => {\n try {\n setActionLoading(`delete-${backupId}`);\n await apiFetch(agentApiPath(profile, `/backup/backups/${backupId}`), {\n method: 'DELETE',\n });\n setSuccessMsg('Backup deleted');\n await refresh();\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setActionLoading(null);\n }\n };\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center py-12 text-text-secondary\">\n <Loader2 className=\"w-5 h-5 animate-spin mr-2\" />\n {tr('vibecontrols.agents.backup.loading', 'Loading backup settings...')}\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6\">\n {/* Status Messages */}\n {error && (\n <div className=\"flex items-center gap-2 rounded-lg border border-status-error-border bg-status-error-bg-subtle p-3 text-sm text-status-error-text\">\n <XCircle className=\"w-4 h-4 shrink-0\" />\n {error}\n <button onClick={() => setError(null)} className=\"ml-auto text-xs underline\">\n Dismiss\n </button>\n </div>\n )}\n {successMsg && (\n <div className=\"flex items-center gap-2 rounded-lg border border-status-success-border bg-status-success-bg-subtle p-3 text-sm text-status-success-text\">\n <CheckCircle2 className=\"w-4 h-4 shrink-0\" />\n {successMsg}\n </div>\n )}\n\n {/* Status Card */}\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-5\">\n <div className=\"flex items-center justify-between mb-4\">\n <div className=\"flex items-center gap-2\">\n <Database className=\"w-5 h-5 text-action-primary-bg\" />\n <h3 className=\"font-semibold text-text-primary\">\n {tr('vibecontrols.agents.backup.title', 'Database Backup')}\n </h3>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n onClick={refresh}\n className=\"p-2 rounded-lg hover:bg-bg-sunken transition-colors text-text-secondary\"\n title=\"Refresh\"\n >\n <RefreshCw className=\"w-4 h-4\" />\n </button>\n <button\n onClick={() => setShowConfig(!showConfig)}\n className=\"p-2 rounded-lg hover:bg-bg-sunken transition-colors text-text-secondary\"\n title=\"Settings\"\n >\n <Settings className=\"w-4 h-4\" />\n </button>\n </div>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4 mb-4\">\n <div>\n <p className=\"text-xs text-text-muted mb-1\">\n {tr('vibecontrols.agents.backup.status', 'Status')}\n </p>\n <div className=\"flex items-center gap-1.5\">\n {config?.enabled ? (\n <>\n <div className=\"h-2 w-2 rounded-full bg-status-success-bg\" />\n <span className=\"text-sm font-medium text-text-primary\">\n {tr('vibecontrols.agents.backup.enabled', 'Enabled')}\n </span>\n </>\n ) : (\n <>\n <div className=\"h-2 w-2 rounded-full bg-text-tertiary\" />\n <span className=\"text-sm font-medium text-text-secondary\">\n {tr('vibecontrols.agents.backup.disabled', 'Disabled')}\n </span>\n </>\n )}\n </div>\n </div>\n <div>\n <p className=\"text-xs text-text-muted mb-1\">\n {tr('vibecontrols.agents.backup.scheduler', 'Scheduler')}\n </p>\n <span className=\"text-sm font-medium text-text-primary\">\n {status?.schedulerRunning ? 'Running' : 'Stopped'}\n </span>\n </div>\n <div>\n <p className=\"text-xs text-text-muted mb-1\">\n {tr('vibecontrols.agents.backup.totalBackups', 'Total Backups')}\n </p>\n <span className=\"text-sm font-medium text-text-primary\">\n {status?.totalBackups ?? 0}\n </span>\n </div>\n <div>\n <p className=\"text-xs text-text-muted mb-1\">\n {tr('vibecontrols.agents.backup.target', 'Target')}\n </p>\n <span className=\"text-sm font-medium text-text-primary\">\n {status?.storageTarget ?? 'N/A'}\n </span>\n </div>\n </div>\n\n {status?.lastBackupTime && (\n <div className=\"flex flex-wrap items-center gap-3 text-xs text-text-muted\">\n <span>\n <Clock className=\"w-3 h-3 inline mr-1\" />\n Last: {formatTime(status.lastBackupTime)}\n </span>\n {status.nextScheduledBackup && (\n <span>Next: {formatTime(status.nextScheduledBackup)}</span>\n )}\n </div>\n )}\n\n {/* Action Buttons */}\n <div className=\"flex flex-wrap gap-2 mt-4 pt-4 border-t border-border-subtle\">\n <button\n onClick={() => handleRunBackup(true)}\n disabled={!!actionLoading}\n className=\"flex items-center gap-2 px-3 py-1.5 rounded-lg bg-action-primary-bg text-action-primary-text text-sm font-medium hover:bg-action-primary-bg-hover disabled:opacity-50 transition-colors\"\n >\n {actionLoading === 'backup' ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Upload className=\"w-4 h-4\" />\n )}\n {tr('vibecontrols.agents.backup.backupNow', 'Backup Now')}\n </button>\n <button\n onClick={() => handleRunBackup(false)}\n disabled={!!actionLoading}\n className=\"flex items-center gap-2 px-3 py-1.5 rounded-lg bg-bg-sunken text-text-primary text-sm font-medium hover:bg-bg-canvas border border-border-default disabled:opacity-50 transition-colors\"\n >\n <Play className=\"w-4 h-4\" />\n {tr('vibecontrols.agents.backup.backupIfChanged', 'Backup if Changed')}\n </button>\n <button\n onClick={handleToggleEnabled}\n disabled={!!actionLoading}\n className=\"flex items-center gap-2 px-3 py-1.5 rounded-lg bg-bg-sunken text-text-primary text-sm font-medium hover:bg-bg-canvas border border-border-default disabled:opacity-50 transition-colors\"\n >\n {actionLoading === 'toggle' ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : config?.enabled ? (\n <XCircle className=\"w-4 h-4\" />\n ) : (\n <CheckCircle2 className=\"w-4 h-4\" />\n )}\n {config?.enabled\n ? tr('vibecontrols.agents.backup.disableSchedule', 'Disable Schedule')\n : tr('vibecontrols.agents.backup.enableSchedule', 'Enable Schedule')}\n </button>\n <button\n onClick={handleTestConnection}\n disabled={!!actionLoading}\n className=\"flex items-center gap-2 px-3 py-1.5 rounded-lg bg-bg-sunken text-text-primary text-sm font-medium hover:bg-bg-canvas border border-border-default disabled:opacity-50 transition-colors\"\n >\n {actionLoading === 'test' ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Wifi className=\"w-4 h-4\" />\n )}\n {tr('vibecontrols.agents.backup.testConnection', 'Test Connection')}\n </button>\n </div>\n </div>\n\n {/* Configuration Panel (collapsible) */}\n {showConfig && config && (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-5\">\n <h4 className=\"font-semibold text-text-primary mb-3 flex items-center gap-2\">\n <Settings className=\"w-4 h-4\" />\n {tr('vibecontrols.agents.backup.configuration', 'Configuration')}\n </h4>\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4 text-sm\">\n <div>\n <span className=\"text-text-muted\">Schedule Interval:</span>{' '}\n <span className=\"text-text-primary font-medium\">\n {config.scheduleIntervalMs >= 3600000\n ? `${config.scheduleIntervalMs / 3600000}h`\n : `${config.scheduleIntervalMs / 60000}m`}\n </span>\n </div>\n <div>\n <span className=\"text-text-muted\">Change Detection:</span>{' '}\n <span className=\"text-text-primary font-medium\">\n {config.changeOnlyBackup ? 'On' : 'Off'}\n </span>\n </div>\n <div>\n <span className=\"text-text-muted\">Storage Target:</span>{' '}\n <span className=\"text-text-primary font-medium\">{config.storageTarget}</span>\n </div>\n {config.customS3 && (\n <>\n <div>\n <span className=\"text-text-muted\">S3 Bucket:</span>{' '}\n <span className=\"text-text-primary font-medium\">{config.customS3.bucket}</span>\n </div>\n <div>\n <span className=\"text-text-muted\">S3 Region:</span>{' '}\n <span className=\"text-text-primary font-medium\">{config.customS3.region}</span>\n </div>\n </>\n )}\n {config.retentionDays && (\n <div>\n <span className=\"text-text-muted\">Retention:</span>{' '}\n <span className=\"text-text-primary font-medium\">{config.retentionDays} days</span>\n </div>\n )}\n {config.maxBackups && (\n <div>\n <span className=\"text-text-muted\">Max Backups:</span>{' '}\n <span className=\"text-text-primary font-medium\">{config.maxBackups}</span>\n </div>\n )}\n </div>\n </div>\n )}\n\n {/* Backup History */}\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-5\">\n <h4 className=\"font-semibold text-text-primary mb-3 flex items-center gap-2\">\n <Clock className=\"w-4 h-4\" />\n {tr('vibecontrols.agents.backup.history', 'Backup History')}\n <span className=\"text-xs text-text-muted font-normal\">({backups.length} shown)</span>\n </h4>\n\n {backups.length === 0 ? (\n <p className=\"text-sm text-text-muted py-4 text-center\">\n {tr(\n 'vibecontrols.agents.backup.noBackups',\n 'No backups yet. Click \"Backup Now\" to create one.'\n )}\n </p>\n ) : (\n <div className=\"space-y-2\">\n {backups.map((b) => (\n <div\n key={b.id}\n className=\"flex items-center justify-between p-3 rounded-lg border border-border-subtle hover:bg-bg-sunken transition-colors\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <div\n className={`w-8 h-8 rounded-lg flex items-center justify-center shrink-0 ${\n b.status === 'completed'\n ? 'border border-status-success-border bg-status-success-bg-subtle'\n : 'border border-status-error-border bg-status-error-bg-subtle'\n }`}\n >\n {b.status === 'completed' ? (\n <CheckCircle2 className=\"w-4 h-4 text-status-success-text\" />\n ) : (\n <XCircle className=\"w-4 h-4 text-status-error-text\" />\n )}\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">\n {formatTime(b.timestamp)}\n </p>\n <p className=\"text-xs text-text-muted truncate\">\n {formatBytes(b.dbSizeBytes)} &middot; {formatDuration(b.durationMs)} &middot;{' '}\n {b.storageTarget}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-1 shrink-0\">\n <button\n onClick={() => handleRestore(b.id, true)}\n disabled={!!actionLoading}\n className=\"p-1.5 rounded hover:bg-bg-canvas text-text-secondary transition-colors\"\n title=\"Validate (dry run)\"\n >\n {actionLoading === `restore-${b.id}` ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <CheckCircle2 className=\"w-4 h-4\" />\n )}\n </button>\n <button\n onClick={() => {\n if (confirm('Restore this backup? The agent will need to restart.')) {\n handleRestore(b.id, false);\n }\n }}\n disabled={!!actionLoading}\n className=\"p-1.5 rounded hover:bg-bg-canvas text-text-secondary transition-colors\"\n title=\"Restore\"\n >\n <RotateCcw className=\"w-4 h-4\" />\n </button>\n <button\n onClick={() => handleDeleteBackup(b.id)}\n disabled={!!actionLoading}\n className=\"p-1.5 rounded text-text-secondary transition-colors hover:bg-status-error-bg/80 hover:text-status-error-text\"\n title=\"Delete\"\n >\n {actionLoading === `delete-${b.id}` ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Trash2 className=\"w-4 h-4\" />\n )}\n </button>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AgentBackupTab.js","names":[],"sources":["../../../../src/components/agents/backup/AgentBackupTab.tsx"],"sourcesContent":["/**\n * AgentBackupTab — UI for managing SQLite database backups of a VibeControls agent.\n * Talks directly to the agent's REST API via its tunnel URL.\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport {\n Database,\n Upload,\n RefreshCw,\n Settings,\n Play,\n Clock,\n CheckCircle2,\n XCircle,\n Loader2,\n Trash2,\n RotateCcw,\n Wifi,\n} from 'lucide-react';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport { agentApiPath } from '@/utils/agentApiPath';\n\n/**\n * Detect error strings that look like a raw JavaScript runtime exception\n * the agent leaked through to the platform — e.g.\n * `undefined is not an object (evaluating 'createResult.data.createFolder')`.\n * The actual root cause lives in the agent's backup plugin (it doesn't\n * forward x-workspace-id when calling files-svc, so the GraphQL response\n * has no `data` and the plugin blindly dereferences it). Until the agent\n * fix lands we strip the JS-frame and show the user something they can act\n * on instead of pasting a stack-trace fragment into the UI. [BOFF-2613]\n */\nfunction sanitizeBackupError(raw: string): string {\n if (!raw) return 'Backup failed.';\n const m = raw.toLowerCase();\n // JS runtime exception leaked from the agent.\n if (\n m.includes('undefined is not an object') ||\n m.includes('cannot read property') ||\n m.includes('cannot read properties of') ||\n /\\btypeerror:/.test(m) ||\n /\\breferenceerror:/.test(m)\n ) {\n // Specific, actionable hint for the known x-workspace-id case.\n if (raw.includes('createResult.data.createFolder')) {\n return \"Backup failed: the agent could not reach the workspace files service (missing workspace context). Re-push credentials from the agent's Security tab and try again. See agent logs for the full error.\";\n }\n return 'Backup failed: the agent threw an internal error. See agent logs for the full error.';\n }\n if (raw.length > 200) return raw.slice(0, 200) + '…';\n return raw;\n}\n\ninterface BackupConfig {\n enabled: boolean;\n scheduleIntervalMs: number;\n changeOnlyBackup: boolean;\n storageTarget: 'wspace' | 'custom-s3';\n customS3?: {\n endpoint: string;\n bucket: string;\n region: string;\n accessKeyId: string;\n secretAccessKey: string;\n pathPrefix?: string;\n };\n retentionDays?: number;\n maxBackups?: number;\n}\n\ninterface BackupRecord {\n id: string;\n timestamp: string;\n agentName: string;\n dbSizeBytes: number;\n sha256Hash: string;\n storageTarget: string;\n storagePath: string;\n durationMs: number;\n status: string;\n error?: string;\n}\n\ninterface BackupStatus {\n enabled: boolean;\n schedulerRunning: boolean;\n lastBackupTime: string | null;\n lastBackupHash: string | null;\n nextScheduledBackup: string | null;\n totalBackups: number;\n storageTarget: string;\n}\n\ninterface AgentBackupTabProps {\n agentTunnelUrl: string;\n agentApiKey: string;\n /** Canonical CLI profile name on the remote agent. */\n profile: string;\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n return `${(ms / 1000).toFixed(1)}s`;\n}\n\nfunction formatTime(iso: string): string {\n return new Date(iso).toLocaleString();\n}\n\nexport function AgentBackupTab({ agentTunnelUrl, agentApiKey, profile }: AgentBackupTabProps) {\n const tr = useTr();\n const [config, setConfig] = useState<BackupConfig | null>(null);\n const [status, setStatus] = useState<BackupStatus | null>(null);\n const [backups, setBackups] = useState<BackupRecord[]>([]);\n const [loading, setLoading] = useState(true);\n const [actionLoading, setActionLoading] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [successMsg, setSuccessMsg] = useState<string | null>(null);\n const [showConfig, setShowConfig] = useState(false);\n\n const apiUrl = agentTunnelUrl.replace(/\\/+$/, '');\n\n const apiFetch = useCallback(\n async (path: string, options?: RequestInit) => {\n const res = await fetch(`${apiUrl}${path}`, {\n ...options,\n headers: {\n 'Content-Type': 'application/json',\n 'x-agent-api-key': agentApiKey,\n ...options?.headers,\n },\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(text || `HTTP ${res.status}`);\n }\n return res.json();\n },\n [apiUrl, agentApiKey]\n );\n\n const refresh = useCallback(async () => {\n try {\n setLoading(true);\n setError(null);\n const [configRes, statusRes, listRes] = await Promise.all([\n apiFetch(agentApiPath(profile, '/backup/config')),\n apiFetch(agentApiPath(profile, '/backup/status')),\n apiFetch(`${agentApiPath(profile, '/backup/list')}?limit=20`),\n ]);\n setConfig(configRes.config);\n setStatus(statusRes.status);\n setBackups(listRes.backups || []);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n setError(\n msg === 'Failed to fetch'\n ? `Cannot reach agent REST API at ${apiUrl}. Ensure the agent is running and its tunnel URL is accessible.`\n : msg\n );\n } finally {\n setLoading(false);\n }\n }, [apiFetch, apiUrl]);\n\n useEffect(() => {\n refresh();\n }, [refresh]);\n\n // Clear success message after 3s\n useEffect(() => {\n if (successMsg) {\n const t = setTimeout(() => setSuccessMsg(null), 3000);\n return () => clearTimeout(t);\n }\n }, [successMsg]);\n\n const handleRunBackup = async (force: boolean) => {\n try {\n setActionLoading('backup');\n setError(null);\n const res = await apiFetch(`${agentApiPath(profile, '/backup/run')}?force=${force}`, {\n method: 'POST',\n });\n // The agent's `/backup/run` returns HTTP 200 even when the backup\n // failed internally — the failure surfaces as `res.error` (top-level)\n // or as `res.backup === null`. Treat both as failures here and surface\n // a sanitized message. [BOFF-2613]\n const b = res?.backup;\n const failureMessage: string | null =\n (typeof res?.error === 'string' && res.error) ||\n (b && typeof b.error === 'string' && b.error !== 'Skipped: no changes' ? b.error : null);\n\n if (failureMessage) {\n setError(sanitizeBackupError(failureMessage));\n return;\n }\n if (!b) {\n // No backup payload AND no error string — defensive.\n setError(sanitizeBackupError(''));\n return;\n }\n if (b.error === 'Skipped: no changes') {\n setSuccessMsg('No changes detected — backup skipped');\n } else {\n setSuccessMsg(\n `Backup completed: ${formatBytes(b.dbSizeBytes)} in ${formatDuration(b.durationMs)}`\n );\n }\n await refresh();\n } catch (err) {\n setError(sanitizeBackupError(err instanceof Error ? err.message : String(err)));\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleRestore = async (backupId: string, dryRun: boolean) => {\n try {\n setActionLoading(`restore-${backupId}`);\n setError(null);\n const res = await apiFetch(agentApiPath(profile, '/backup/restore'), {\n method: 'POST',\n body: JSON.stringify({ backupId, dryRun }),\n });\n if (res.success) {\n setSuccessMsg(res.message);\n } else {\n setError(res.message);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleToggleEnabled = async () => {\n try {\n setActionLoading('toggle');\n const res = await apiFetch(agentApiPath(profile, '/backup/config'), {\n method: 'PUT',\n body: JSON.stringify({ enabled: !config?.enabled }),\n });\n setConfig(res.config);\n setSuccessMsg(res.config.enabled ? 'Backup enabled' : 'Backup disabled');\n await refresh();\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleTestConnection = async () => {\n try {\n setActionLoading('test');\n const res = await apiFetch(agentApiPath(profile, '/backup/test-connection'), {\n method: 'POST',\n });\n if (res.ok) {\n setSuccessMsg(`Connection OK: ${res.message}`);\n } else {\n setError(`Connection failed: ${res.message}`);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleDeleteBackup = async (backupId: string) => {\n try {\n setActionLoading(`delete-${backupId}`);\n await apiFetch(agentApiPath(profile, `/backup/backups/${backupId}`), {\n method: 'DELETE',\n });\n setSuccessMsg('Backup deleted');\n await refresh();\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setActionLoading(null);\n }\n };\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center py-12 text-text-secondary\">\n <Loader2 className=\"w-5 h-5 animate-spin mr-2\" />\n {tr('vibecontrols.agents.backup.loading', 'Loading backup settings...')}\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6\">\n {/* Status Messages */}\n {error && (\n <div className=\"flex items-center gap-2 rounded-lg border border-status-error-border bg-status-error-bg-subtle p-3 text-sm text-status-error-text\">\n <XCircle className=\"w-4 h-4 shrink-0\" />\n {error}\n <button onClick={() => setError(null)} className=\"ml-auto text-xs underline\">\n Dismiss\n </button>\n </div>\n )}\n {successMsg && (\n <div className=\"flex items-center gap-2 rounded-lg border border-status-success-border bg-status-success-bg-subtle p-3 text-sm text-status-success-text\">\n <CheckCircle2 className=\"w-4 h-4 shrink-0\" />\n {successMsg}\n </div>\n )}\n\n {/* Status Card */}\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-5\">\n <div className=\"flex items-center justify-between mb-4\">\n <div className=\"flex items-center gap-2\">\n <Database className=\"w-5 h-5 text-action-primary-bg\" />\n <h3 className=\"font-semibold text-text-primary\">\n {tr('vibecontrols.agents.backup.title', 'Database Backup')}\n </h3>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n onClick={refresh}\n className=\"p-2 rounded-lg hover:bg-bg-sunken transition-colors text-text-secondary\"\n title=\"Refresh\"\n >\n <RefreshCw className=\"w-4 h-4\" />\n </button>\n <button\n onClick={() => setShowConfig(!showConfig)}\n className=\"p-2 rounded-lg hover:bg-bg-sunken transition-colors text-text-secondary\"\n title=\"Settings\"\n >\n <Settings className=\"w-4 h-4\" />\n </button>\n </div>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4 mb-4\">\n <div>\n <p className=\"text-xs text-text-muted mb-1\">\n {tr('vibecontrols.agents.backup.status', 'Status')}\n </p>\n <div className=\"flex items-center gap-1.5\">\n {config?.enabled ? (\n <>\n <div className=\"h-2 w-2 rounded-full bg-status-success-bg\" />\n <span className=\"text-sm font-medium text-text-primary\">\n {tr('vibecontrols.agents.backup.enabled', 'Enabled')}\n </span>\n </>\n ) : (\n <>\n <div className=\"h-2 w-2 rounded-full bg-text-tertiary\" />\n <span className=\"text-sm font-medium text-text-secondary\">\n {tr('vibecontrols.agents.backup.disabled', 'Disabled')}\n </span>\n </>\n )}\n </div>\n </div>\n <div>\n <p className=\"text-xs text-text-muted mb-1\">\n {tr('vibecontrols.agents.backup.scheduler', 'Scheduler')}\n </p>\n <span className=\"text-sm font-medium text-text-primary\">\n {status?.schedulerRunning ? 'Running' : 'Stopped'}\n </span>\n </div>\n <div>\n <p className=\"text-xs text-text-muted mb-1\">\n {tr('vibecontrols.agents.backup.totalBackups', 'Total Backups')}\n </p>\n <span className=\"text-sm font-medium text-text-primary\">\n {status?.totalBackups ?? 0}\n </span>\n </div>\n <div>\n <p className=\"text-xs text-text-muted mb-1\">\n {tr('vibecontrols.agents.backup.target', 'Target')}\n </p>\n <span className=\"text-sm font-medium text-text-primary\">\n {status?.storageTarget ?? 'N/A'}\n </span>\n </div>\n </div>\n\n {status?.lastBackupTime && (\n <div className=\"flex flex-wrap items-center gap-3 text-xs text-text-muted\">\n <span>\n <Clock className=\"w-3 h-3 inline mr-1\" />\n Last: {formatTime(status.lastBackupTime)}\n </span>\n {status.nextScheduledBackup && (\n <span>Next: {formatTime(status.nextScheduledBackup)}</span>\n )}\n </div>\n )}\n\n {/* Action Buttons */}\n <div className=\"flex flex-wrap gap-2 mt-4 pt-4 border-t border-border-subtle\">\n <button\n onClick={() => handleRunBackup(true)}\n disabled={!!actionLoading}\n className=\"flex items-center gap-2 px-3 py-1.5 rounded-lg bg-action-primary-bg text-action-primary-text text-sm font-medium hover:bg-action-primary-bg-hover disabled:opacity-50 transition-colors\"\n >\n {actionLoading === 'backup' ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Upload className=\"w-4 h-4\" />\n )}\n {tr('vibecontrols.agents.backup.backupNow', 'Backup Now')}\n </button>\n <button\n onClick={() => handleRunBackup(false)}\n disabled={!!actionLoading}\n className=\"flex items-center gap-2 px-3 py-1.5 rounded-lg bg-bg-sunken text-text-primary text-sm font-medium hover:bg-bg-canvas border border-border-default disabled:opacity-50 transition-colors\"\n >\n <Play className=\"w-4 h-4\" />\n {tr('vibecontrols.agents.backup.backupIfChanged', 'Backup if Changed')}\n </button>\n <button\n onClick={handleToggleEnabled}\n disabled={!!actionLoading}\n className=\"flex items-center gap-2 px-3 py-1.5 rounded-lg bg-bg-sunken text-text-primary text-sm font-medium hover:bg-bg-canvas border border-border-default disabled:opacity-50 transition-colors\"\n >\n {actionLoading === 'toggle' ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : config?.enabled ? (\n <XCircle className=\"w-4 h-4\" />\n ) : (\n <CheckCircle2 className=\"w-4 h-4\" />\n )}\n {config?.enabled\n ? tr('vibecontrols.agents.backup.disableSchedule', 'Disable Schedule')\n : tr('vibecontrols.agents.backup.enableSchedule', 'Enable Schedule')}\n </button>\n <button\n onClick={handleTestConnection}\n disabled={!!actionLoading}\n className=\"flex items-center gap-2 px-3 py-1.5 rounded-lg bg-bg-sunken text-text-primary text-sm font-medium hover:bg-bg-canvas border border-border-default disabled:opacity-50 transition-colors\"\n >\n {actionLoading === 'test' ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Wifi className=\"w-4 h-4\" />\n )}\n {tr('vibecontrols.agents.backup.testConnection', 'Test Connection')}\n </button>\n </div>\n </div>\n\n {/* Configuration Panel (collapsible) */}\n {showConfig && config && (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-5\">\n <h4 className=\"font-semibold text-text-primary mb-3 flex items-center gap-2\">\n <Settings className=\"w-4 h-4\" />\n {tr('vibecontrols.agents.backup.configuration', 'Configuration')}\n </h4>\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4 text-sm\">\n <div>\n <span className=\"text-text-muted\">Schedule Interval:</span>{' '}\n <span className=\"text-text-primary font-medium\">\n {config.scheduleIntervalMs >= 3600000\n ? `${config.scheduleIntervalMs / 3600000}h`\n : `${config.scheduleIntervalMs / 60000}m`}\n </span>\n </div>\n <div>\n <span className=\"text-text-muted\">Change Detection:</span>{' '}\n <span className=\"text-text-primary font-medium\">\n {config.changeOnlyBackup ? 'On' : 'Off'}\n </span>\n </div>\n <div>\n <span className=\"text-text-muted\">Storage Target:</span>{' '}\n <span className=\"text-text-primary font-medium\">{config.storageTarget}</span>\n </div>\n {config.customS3 && (\n <>\n <div>\n <span className=\"text-text-muted\">S3 Bucket:</span>{' '}\n <span className=\"text-text-primary font-medium\">{config.customS3.bucket}</span>\n </div>\n <div>\n <span className=\"text-text-muted\">S3 Region:</span>{' '}\n <span className=\"text-text-primary font-medium\">{config.customS3.region}</span>\n </div>\n </>\n )}\n {config.retentionDays && (\n <div>\n <span className=\"text-text-muted\">Retention:</span>{' '}\n <span className=\"text-text-primary font-medium\">{config.retentionDays} days</span>\n </div>\n )}\n {config.maxBackups && (\n <div>\n <span className=\"text-text-muted\">Max Backups:</span>{' '}\n <span className=\"text-text-primary font-medium\">{config.maxBackups}</span>\n </div>\n )}\n </div>\n </div>\n )}\n\n {/* Backup History */}\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-5\">\n <h4 className=\"font-semibold text-text-primary mb-3 flex items-center gap-2\">\n <Clock className=\"w-4 h-4\" />\n {tr('vibecontrols.agents.backup.history', 'Backup History')}\n <span className=\"text-xs text-text-muted font-normal\">({backups.length} shown)</span>\n </h4>\n\n {backups.length === 0 ? (\n <p className=\"text-sm text-text-muted py-4 text-center\">\n {tr(\n 'vibecontrols.agents.backup.noBackups',\n 'No backups yet. Click \"Backup Now\" to create one.'\n )}\n </p>\n ) : (\n <div className=\"space-y-2\">\n {backups.map((b) => (\n <div\n key={b.id}\n className=\"flex items-center justify-between p-3 rounded-lg border border-border-subtle hover:bg-bg-sunken transition-colors\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <div\n className={`w-8 h-8 rounded-lg flex items-center justify-center shrink-0 ${\n b.status === 'completed'\n ? 'border border-status-success-border bg-status-success-bg-subtle'\n : 'border border-status-error-border bg-status-error-bg-subtle'\n }`}\n >\n {b.status === 'completed' ? (\n <CheckCircle2 className=\"w-4 h-4 text-status-success-text\" />\n ) : (\n <XCircle className=\"w-4 h-4 text-status-error-text\" />\n )}\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">\n {formatTime(b.timestamp)}\n </p>\n <p className=\"text-xs text-text-muted truncate\">\n {formatBytes(b.dbSizeBytes)} &middot; {formatDuration(b.durationMs)} &middot;{' '}\n {b.storageTarget}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-1 shrink-0\">\n <button\n onClick={() => handleRestore(b.id, true)}\n disabled={!!actionLoading}\n className=\"p-1.5 rounded hover:bg-bg-canvas text-text-secondary transition-colors\"\n title=\"Validate (dry run)\"\n >\n {actionLoading === `restore-${b.id}` ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <CheckCircle2 className=\"w-4 h-4\" />\n )}\n </button>\n <button\n onClick={() => {\n if (confirm('Restore this backup? The agent will need to restart.')) {\n handleRestore(b.id, false);\n }\n }}\n disabled={!!actionLoading}\n className=\"p-1.5 rounded hover:bg-bg-canvas text-text-secondary transition-colors\"\n title=\"Restore\"\n >\n <RotateCcw className=\"w-4 h-4\" />\n </button>\n <button\n onClick={() => handleDeleteBackup(b.id)}\n disabled={!!actionLoading}\n className=\"p-1.5 rounded text-text-secondary transition-colors hover:bg-status-error-bg/80 hover:text-status-error-text\"\n title=\"Delete\"\n >\n {actionLoading === `delete-${b.id}` ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Trash2 className=\"w-4 h-4\" />\n )}\n </button>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,57 +1,63 @@
1
1
  import { useTr as e } from "../../shared/hooks/useTr.js";
2
2
  import { agentAiGet as t, agentAiMutate as n } from "../../services/aiApi.js";
3
- import { validateAliasName as r } from "./aliasReservedNames.js";
4
- import { useCallback as ee, useEffect as te, useState as i } from "react";
5
- import { Check as ne, Loader2 as a, Pencil as re, Play as o, Plus as s, Terminal as ie, Trash2 as c, X as l } from "lucide-react";
3
+ import { validateAliasName as ee } from "./aliasReservedNames.js";
4
+ import { useCallback as r, useEffect as te, useState as i } from "react";
5
+ import { Check as ne, Loader2 as a, PackageOpen as re, Pencil as ie, Play as o, Plus as s, Terminal as ae, Trash2 as c, X as l } from "lucide-react";
6
6
  import { Fragment as u, jsx as d, jsxs as f } from "react/jsx-runtime";
7
7
  //#region src/components/aliases/AliasManager.tsx
8
+ function oe(e) {
9
+ if (!e) return !1;
10
+ let t = e.toLowerCase();
11
+ return t.includes("not installed on the agent") || t.includes("not_found") || t.includes(" 404");
12
+ }
8
13
  var p = 80;
9
- function ae(e, t = p) {
14
+ function se(e, t = p) {
10
15
  return e ? e.length > t ? `${e.slice(0, t)}…` : e : "";
11
16
  }
12
17
  function m({ agent: p }) {
13
- let m = e(), h = `/api/profiles/${encodeURIComponent(p.profile ?? "default")}/aliases`, [g, oe] = i([]), [se, _] = i(!0), [v, y] = i(null), [b, x] = i(""), [S, C] = i(""), [w, T] = i(""), [E, D] = i(!1), [O, k] = i(null), [A, j] = i(null), [M, N] = i(""), [P, F] = i(""), [I, L] = i(!1), [R, z] = i(null), [B, V] = i(""), [H, U] = i(null), [W, G] = i(!1), [K, q] = i(null), [J, Y] = i(null), [X, Z] = i(!1), Q = ee(async () => {
14
- _(!0), y(null);
18
+ let m = e(), h = `/api/profiles/${encodeURIComponent(p.profile ?? "default")}/aliases`, [g, ce] = i([]), [le, _] = i(!0), [v, y] = i(null), [ue, b] = i(!1), [x, S] = i(""), [C, w] = i(""), [T, E] = i(""), [D, O] = i(!1), [k, A] = i(null), [de, j] = i(null), [M, N] = i(""), [P, F] = i(""), [I, L] = i(!1), [R, z] = i(null), [B, V] = i(""), [H, U] = i(null), [W, G] = i(!1), [K, q] = i(null), [J, Y] = i(null), [X, Z] = i(!1), Q = r(async () => {
19
+ _(!0), y(null), b(!1);
15
20
  try {
16
21
  let e = await t(p.id, h);
17
- oe(Array.isArray(e) ? e : e.items ?? []);
22
+ ce(Array.isArray(e) ? e : e.items ?? []);
18
23
  } catch (e) {
19
- y(e instanceof Error ? e.message : String(e));
24
+ let t = e instanceof Error ? e.message : String(e);
25
+ oe(t) ? b(!0) : y(t);
20
26
  } finally {
21
27
  _(!1);
22
28
  }
23
- }, [p.id]);
29
+ }, [p.id, h]);
24
30
  te(() => {
25
31
  Q();
26
32
  }, [Q]);
27
- let ce = async () => {
28
- k(null);
29
- let e = r(b.trim());
33
+ let fe = async () => {
34
+ A(null);
35
+ let e = ee(x.trim());
30
36
  if (e) {
31
- k(e);
37
+ A(e);
32
38
  return;
33
39
  }
34
- if (!S.trim()) {
35
- k(m("aliases.commandRequired", "Command is required"));
40
+ if (!C.trim()) {
41
+ A(m("aliases.commandRequired", "Command is required"));
36
42
  return;
37
43
  }
38
- D(!0);
44
+ O(!0);
39
45
  try {
40
46
  await n(p.id, h, "POST", {
41
- name: b.trim(),
42
- command: S,
43
- description: w || void 0
44
- }), x(""), C(""), T(""), await Q();
47
+ name: x.trim(),
48
+ command: C,
49
+ description: T || void 0
50
+ }), S(""), w(""), E(""), await Q();
45
51
  } catch (e) {
46
- k(e instanceof Error ? e.message : String(e));
52
+ A(e instanceof Error ? e.message : String(e));
47
53
  } finally {
48
- D(!1);
54
+ O(!1);
49
55
  }
50
- }, le = (e) => {
56
+ }, pe = (e) => {
51
57
  j(e.name), N(e.command), F(e.description ?? "");
52
58
  }, $ = () => {
53
59
  j(null), N(""), F("");
54
- }, ue = async (e) => {
60
+ }, me = async (e) => {
55
61
  L(!0);
56
62
  try {
57
63
  await n(p.id, `${h}/${encodeURIComponent(e.name)}`, "PUT", {
@@ -63,7 +69,7 @@ function m({ agent: p }) {
63
69
  } finally {
64
70
  L(!1);
65
71
  }
66
- }, de = async () => {
72
+ }, he = async () => {
67
73
  if (J) {
68
74
  Z(!0);
69
75
  try {
@@ -74,7 +80,7 @@ function m({ agent: p }) {
74
80
  Z(!1);
75
81
  }
76
82
  }
77
- }, fe = async () => {
83
+ }, ge = async () => {
78
84
  if (R) {
79
85
  G(!0), q(null), U(null);
80
86
  try {
@@ -87,7 +93,26 @@ function m({ agent: p }) {
87
93
  }
88
94
  }
89
95
  };
90
- return /* @__PURE__ */ f("div", {
96
+ return ue ? /* @__PURE__ */ f("div", {
97
+ className: "rounded-lg border border-border-default bg-bg-surface p-8 text-center space-y-3",
98
+ children: [
99
+ /* @__PURE__ */ d(re, { className: "w-8 h-8 mx-auto text-text-muted" }),
100
+ /* @__PURE__ */ d("h3", {
101
+ className: "text-base font-medium text-text-primary",
102
+ children: m("aliases.pluginMissingTitle", "Aliases plugin not installed")
103
+ }),
104
+ /* @__PURE__ */ d("p", {
105
+ className: "text-sm text-text-secondary max-w-md mx-auto",
106
+ children: m("aliases.pluginMissingBody", "This agent doesn't have the aliases plugin installed yet. Install @vibecontrols/vibe-plugin-aliases from the Plugins tab to enable per-agent shell aliases.")
107
+ }),
108
+ /* @__PURE__ */ d("button", {
109
+ type: "button",
110
+ onClick: Q,
111
+ className: "inline-flex items-center gap-2 px-3 py-1.5 text-sm font-medium text-text-secondary border border-border-default rounded-md hover:bg-bg-sunken transition-colors",
112
+ children: m("aliases.retry", "Retry")
113
+ })
114
+ ]
115
+ }) : /* @__PURE__ */ f("div", {
91
116
  className: "space-y-4",
92
117
  children: [
93
118
  /* @__PURE__ */ f("div", {
@@ -101,43 +126,43 @@ function m({ agent: p }) {
101
126
  className: "grid grid-cols-1 sm:grid-cols-2 gap-3",
102
127
  children: [/* @__PURE__ */ d("input", {
103
128
  type: "text",
104
- value: b,
105
- onChange: (e) => x(e.target.value),
129
+ value: x,
130
+ onChange: (e) => S(e.target.value),
106
131
  placeholder: m("aliases.namePlaceholder", "Name (a-z, 0-9, _, -)"),
107
132
  className: "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"
108
133
  }), /* @__PURE__ */ d("input", {
109
134
  type: "text",
110
- value: w,
111
- onChange: (e) => T(e.target.value),
135
+ value: T,
136
+ onChange: (e) => E(e.target.value),
112
137
  placeholder: m("aliases.descriptionPlaceholder", "Description (optional)"),
113
138
  className: "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"
114
139
  })]
115
140
  }),
116
141
  /* @__PURE__ */ d("textarea", {
117
- value: S,
118
- onChange: (e) => C(e.target.value),
142
+ value: C,
143
+ onChange: (e) => w(e.target.value),
119
144
  placeholder: m("aliases.commandPlaceholder", "Command (e.g. git status)"),
120
145
  rows: 3,
121
146
  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"
122
147
  }),
123
- O && /* @__PURE__ */ d("div", {
148
+ k && /* @__PURE__ */ d("div", {
124
149
  className: "text-xs text-status-error-text",
125
- children: O
150
+ children: k
126
151
  }),
127
152
  /* @__PURE__ */ d("div", {
128
153
  className: "flex justify-end",
129
154
  children: /* @__PURE__ */ f("button", {
130
- onClick: ce,
131
- disabled: E || !b.trim() || !S.trim(),
155
+ onClick: fe,
156
+ disabled: D || !x.trim() || !C.trim(),
132
157
  className: "inline-flex items-center gap-2 px-3 py-1.5 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",
133
- children: [E ? /* @__PURE__ */ d(a, { className: "w-4 h-4 animate-spin" }) : /* @__PURE__ */ d(s, { className: "w-4 h-4" }), m("aliases.create", "Create")]
158
+ children: [D ? /* @__PURE__ */ d(a, { className: "w-4 h-4 animate-spin" }) : /* @__PURE__ */ d(s, { className: "w-4 h-4" }), m("aliases.create", "Create")]
134
159
  })
135
160
  })
136
161
  ]
137
162
  }),
138
163
  /* @__PURE__ */ d("div", {
139
164
  className: "rounded-lg border border-border-default overflow-hidden",
140
- children: se ? /* @__PURE__ */ d("div", {
165
+ children: le ? /* @__PURE__ */ d("div", {
141
166
  className: "p-8 text-center text-text-secondary",
142
167
  children: /* @__PURE__ */ d(a, { className: "w-5 h-5 animate-spin mx-auto" })
143
168
  }) : v ? /* @__PURE__ */ d("div", {
@@ -173,7 +198,7 @@ function m({ agent: p }) {
173
198
  })
174
199
  ] })
175
200
  }), /* @__PURE__ */ d("tbody", { children: g.map((e) => {
176
- let t = A === e.name;
201
+ let t = de === e.name;
177
202
  return /* @__PURE__ */ f("tr", {
178
203
  className: "border-t border-border-default",
179
204
  children: [
@@ -191,7 +216,7 @@ function m({ agent: p }) {
191
216
  }) : /* @__PURE__ */ d("code", {
192
217
  className: "text-xs text-text-primary",
193
218
  title: e.command,
194
- children: ae(e.command)
219
+ children: se(e.command)
195
220
  })
196
221
  }),
197
222
  /* @__PURE__ */ d("td", {
@@ -214,7 +239,7 @@ function m({ agent: p }) {
214
239
  children: /* @__PURE__ */ d("div", {
215
240
  className: "flex items-center justify-end gap-1",
216
241
  children: t ? /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ d("button", {
217
- onClick: () => void ue(e),
242
+ onClick: () => void me(e),
218
243
  disabled: I,
219
244
  className: "p-1.5 rounded hover:bg-bg-sunken text-status-success-text",
220
245
  title: m("aliases.save", "Save"),
@@ -234,10 +259,10 @@ function m({ agent: p }) {
234
259
  children: /* @__PURE__ */ d(o, { className: "w-4 h-4" })
235
260
  }),
236
261
  /* @__PURE__ */ d("button", {
237
- onClick: () => le(e),
262
+ onClick: () => pe(e),
238
263
  className: "p-1.5 rounded hover:bg-bg-sunken text-text-secondary",
239
264
  title: m("aliases.edit", "Edit"),
240
- children: /* @__PURE__ */ d(re, { className: "w-4 h-4" })
265
+ children: /* @__PURE__ */ d(ie, { className: "w-4 h-4" })
241
266
  }),
242
267
  /* @__PURE__ */ d("button", {
243
268
  onClick: () => Y(e),
@@ -263,7 +288,7 @@ function m({ agent: p }) {
263
288
  children: [/* @__PURE__ */ f("h2", {
264
289
  className: "text-lg font-semibold text-text-primary flex items-center gap-2",
265
290
  children: [
266
- /* @__PURE__ */ d(ie, { className: "w-5 h-5" }),
291
+ /* @__PURE__ */ d(ae, { className: "w-5 h-5" }),
267
292
  m("aliases.runTitle", "Run alias"),
268
293
  ": ",
269
294
  /* @__PURE__ */ d("code", { children: R.name })
@@ -332,7 +357,7 @@ function m({ agent: p }) {
332
357
  className: "px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated",
333
358
  children: m("common.close", "Close")
334
359
  }), /* @__PURE__ */ f("button", {
335
- onClick: () => void fe(),
360
+ onClick: () => void ge(),
336
361
  disabled: W,
337
362
  className: "inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover disabled:opacity-50",
338
363
  children: [W ? /* @__PURE__ */ d(a, { className: "w-4 h-4 animate-spin" }) : /* @__PURE__ */ d(o, { className: "w-4 h-4" }), m("aliases.run", "Run")]
@@ -368,7 +393,7 @@ function m({ agent: p }) {
368
393
  className: "px-3 py-1.5 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary hover:bg-bg-elevated",
369
394
  children: m("common.cancel", "Cancel")
370
395
  }), /* @__PURE__ */ f("button", {
371
- onClick: () => void de(),
396
+ onClick: () => void he(),
372
397
  disabled: X,
373
398
  className: "inline-flex items-center gap-2 px-3 py-1.5 text-sm font-medium text-status-error-text bg-status-error-bg rounded-md hover:opacity-80 disabled:opacity-50",
374
399
  children: [X ? /* @__PURE__ */ d(a, { className: "w-4 h-4 animate-spin" }) : /* @__PURE__ */ d(c, { className: "w-4 h-4" }), m("aliases.delete", "Delete")]
@@ -1 +1 @@
1
- {"version":3,"file":"AliasManager.js","names":[],"sources":["../../../src/components/aliases/AliasManager.tsx"],"sourcesContent":["/**\n * AliasManager — per-agent CRUDL for shell aliases exposed by\n * vibecontrols-agent under `/api/aliases/*`.\n *\n * Pairs with the agent's Phase A4 ship.\n *\n * Endpoints (proxied through wspace-vibecontrols-svc → agent):\n * GET /api/aliases → { items: Alias[] }\n * GET /api/aliases/:name → Alias\n * POST /api/aliases → Alias\n * PUT /api/aliases/:name → Alias\n * DELETE /api/aliases/:name\n * POST /api/aliases/:name/execute → { exit_code, stdout, stderr }\n */\n\nimport { useCallback, useEffect, useState } from 'react';\nimport { Plus, Trash2, Play, Pencil, Check, X, Loader2, Terminal } from 'lucide-react';\nimport { agentAiGet, agentAiMutate } from '@/services/aiApi';\nimport { useTr } from '@/shared/hooks/useTr';\nimport { validateAliasName } from './aliasReservedNames';\n\ninterface Alias {\n name: string;\n command: string;\n description?: string | null;\n updatedAt?: string | null;\n createdAt?: string | null;\n}\n\ninterface AliasListResponse {\n items?: Alias[];\n}\n\ninterface AliasExecuteResult {\n exit_code: number;\n stdout: string;\n stderr: string;\n}\n\ninterface AliasManagerProps {\n agent: { id: string; profile?: string };\n}\n\nconst TRUNCATE = 80;\n\nfunction truncate(str: string, n = TRUNCATE): string {\n if (!str) return '';\n return str.length > n ? `${str.slice(0, n)}…` : str;\n}\n\nexport function AliasManager({ agent }: AliasManagerProps) {\n const tr = useTr();\n // Canonical agent URL hierarchy: agent → profile → plugins/data.\n // The aliases plugin lives under `/api/profiles/<profile>/aliases/*`.\n // No client-side rewrites — every consumer constructs the canonical\n // path inline so the URL the agent sees is the URL the FE wrote.\n const profile = encodeURIComponent(agent.profile ?? 'default');\n const aliasesBase = `/api/profiles/${profile}/aliases`;\n const [aliases, setAliases] = useState<Alias[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n // Add-new form\n const [newName, setNewName] = useState('');\n const [newCommand, setNewCommand] = useState('');\n const [newDescription, setNewDescription] = useState('');\n const [creating, setCreating] = useState(false);\n const [createError, setCreateError] = useState<string | null>(null);\n\n // Edit row\n const [editingName, setEditingName] = useState<string | null>(null);\n const [editCommand, setEditCommand] = useState('');\n const [editDescription, setEditDescription] = useState('');\n const [savingEdit, setSavingEdit] = useState(false);\n\n // Run modal\n const [runTarget, setRunTarget] = useState<Alias | null>(null);\n const [runArgs, setRunArgs] = useState('');\n const [runResult, setRunResult] = useState<AliasExecuteResult | null>(null);\n const [runRunning, setRunRunning] = useState(false);\n const [runError, setRunError] = useState<string | null>(null);\n\n // Delete confirm\n const [deleteTarget, setDeleteTarget] = useState<Alias | null>(null);\n const [deleting, setDeleting] = useState(false);\n\n const refresh = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const data = await agentAiGet<AliasListResponse | Alias[]>(agent.id, aliasesBase);\n const items = Array.isArray(data) ? data : (data.items ?? []);\n setAliases(items);\n } catch (e) {\n setError(e instanceof Error ? e.message : String(e));\n } finally {\n setLoading(false);\n }\n }, [agent.id]);\n\n useEffect(() => {\n void refresh();\n }, [refresh]);\n\n // ── Create ────────────────────────────────────────────────────────\n const handleCreate = async () => {\n setCreateError(null);\n const nameErr = validateAliasName(newName.trim());\n if (nameErr) {\n setCreateError(nameErr);\n return;\n }\n if (!newCommand.trim()) {\n setCreateError(tr('aliases.commandRequired', 'Command is required'));\n return;\n }\n setCreating(true);\n try {\n await agentAiMutate(agent.id, aliasesBase, 'POST', {\n name: newName.trim(),\n command: newCommand,\n description: newDescription || undefined,\n });\n setNewName('');\n setNewCommand('');\n setNewDescription('');\n await refresh();\n } catch (e) {\n setCreateError(e instanceof Error ? e.message : String(e));\n } finally {\n setCreating(false);\n }\n };\n\n // ── Edit ──────────────────────────────────────────────────────────\n const startEdit = (alias: Alias) => {\n setEditingName(alias.name);\n setEditCommand(alias.command);\n setEditDescription(alias.description ?? '');\n };\n\n const cancelEdit = () => {\n setEditingName(null);\n setEditCommand('');\n setEditDescription('');\n };\n\n const saveEdit = async (alias: Alias) => {\n setSavingEdit(true);\n try {\n await agentAiMutate(agent.id, `${aliasesBase}/${encodeURIComponent(alias.name)}`, 'PUT', {\n command: editCommand,\n description: editDescription || undefined,\n });\n cancelEdit();\n await refresh();\n } catch (e) {\n setError(e instanceof Error ? e.message : String(e));\n } finally {\n setSavingEdit(false);\n }\n };\n\n // ── Delete ────────────────────────────────────────────────────────\n const confirmDelete = async () => {\n if (!deleteTarget) return;\n setDeleting(true);\n try {\n await agentAiMutate(\n agent.id,\n `${aliasesBase}/${encodeURIComponent(deleteTarget.name)}`,\n 'DELETE'\n );\n setDeleteTarget(null);\n await refresh();\n } catch (e) {\n setError(e instanceof Error ? e.message : String(e));\n } finally {\n setDeleting(false);\n }\n };\n\n // ── Run ───────────────────────────────────────────────────────────\n const runAlias = async () => {\n if (!runTarget) return;\n setRunRunning(true);\n setRunError(null);\n setRunResult(null);\n try {\n const args = runArgs.split(/\\s+/).filter(Boolean);\n const result = await agentAiMutate<AliasExecuteResult>(\n agent.id,\n `${aliasesBase}/${encodeURIComponent(runTarget.name)}/execute`,\n 'POST',\n { args }\n );\n setRunResult(result);\n } catch (e) {\n setRunError(e instanceof Error ? e.message : String(e));\n } finally {\n setRunRunning(false);\n }\n };\n\n return (\n <div className=\"space-y-4\">\n {/* Add new alias form */}\n <div className=\"rounded-lg border border-border-default bg-bg-sunken p-4 space-y-3\">\n <h3 className=\"text-sm font-medium text-text-primary flex items-center gap-2\">\n <Plus className=\"w-4 h-4\" />\n {tr('aliases.createTitle', 'New alias')}\n </h3>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n <input\n type=\"text\"\n value={newName}\n onChange={(e) => setNewName(e.target.value)}\n placeholder={tr('aliases.namePlaceholder', 'Name (a-z, 0-9, _, -)')}\n className=\"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 />\n <input\n type=\"text\"\n value={newDescription}\n onChange={(e) => setNewDescription(e.target.value)}\n placeholder={tr('aliases.descriptionPlaceholder', 'Description (optional)')}\n className=\"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\"\n />\n </div>\n <textarea\n value={newCommand}\n onChange={(e) => setNewCommand(e.target.value)}\n placeholder={tr('aliases.commandPlaceholder', 'Command (e.g. git status)')}\n rows={3}\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 />\n {createError && <div className=\"text-xs text-status-error-text\">{createError}</div>}\n <div className=\"flex justify-end\">\n <button\n onClick={handleCreate}\n disabled={creating || !newName.trim() || !newCommand.trim()}\n className=\"inline-flex items-center gap-2 px-3 py-1.5 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 {creating ? <Loader2 className=\"w-4 h-4 animate-spin\" /> : <Plus className=\"w-4 h-4\" />}\n {tr('aliases.create', 'Create')}\n </button>\n </div>\n </div>\n\n {/* List */}\n <div className=\"rounded-lg border border-border-default overflow-hidden\">\n {loading ? (\n <div className=\"p-8 text-center text-text-secondary\">\n <Loader2 className=\"w-5 h-5 animate-spin mx-auto\" />\n </div>\n ) : error ? (\n <div className=\"p-4 text-sm text-status-error-text\">{error}</div>\n ) : aliases.length === 0 ? (\n <div className=\"p-8 text-center text-sm text-text-secondary\">\n {tr('aliases.empty', 'No aliases yet. Create one above.')}\n </div>\n ) : (\n <table className=\"w-full text-sm\">\n <thead className=\"bg-bg-sunken text-text-secondary\">\n <tr>\n <th className=\"text-left px-3 py-2 font-medium\">{tr('aliases.colName', 'Name')}</th>\n <th className=\"text-left px-3 py-2 font-medium\">\n {tr('aliases.colCommand', 'Command')}\n </th>\n <th className=\"text-left px-3 py-2 font-medium\">\n {tr('aliases.colDescription', 'Description')}\n </th>\n <th className=\"text-left px-3 py-2 font-medium\">\n {tr('aliases.colUpdatedAt', 'Updated')}\n </th>\n <th className=\"text-right px-3 py-2 font-medium\">\n {tr('aliases.colActions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody>\n {aliases.map((alias) => {\n const isEditing = editingName === alias.name;\n return (\n <tr key={alias.name} className=\"border-t border-border-default\">\n <td className=\"px-3 py-2 font-mono text-text-primary align-top\">\n {alias.name}\n </td>\n <td className=\"px-3 py-2 align-top\">\n {isEditing ? (\n <textarea\n value={editCommand}\n onChange={(e) => setEditCommand(e.target.value)}\n rows={2}\n className=\"w-full px-2 py-1 text-xs bg-bg-surface border border-border-default rounded text-text-primary font-mono\"\n />\n ) : (\n <code className=\"text-xs text-text-primary\" title={alias.command}>\n {truncate(alias.command)}\n </code>\n )}\n </td>\n <td className=\"px-3 py-2 align-top\">\n {isEditing ? (\n <input\n value={editDescription}\n onChange={(e) => setEditDescription(e.target.value)}\n className=\"w-full px-2 py-1 text-xs bg-bg-surface border border-border-default rounded text-text-primary\"\n />\n ) : (\n <span className=\"text-xs text-text-secondary\">\n {alias.description ?? '—'}\n </span>\n )}\n </td>\n <td className=\"px-3 py-2 text-xs text-text-secondary align-top\">\n {alias.updatedAt ? new Date(alias.updatedAt).toLocaleString() : '—'}\n </td>\n <td className=\"px-3 py-2 align-top\">\n <div className=\"flex items-center justify-end gap-1\">\n {isEditing ? (\n <>\n <button\n onClick={() => void saveEdit(alias)}\n disabled={savingEdit}\n className=\"p-1.5 rounded hover:bg-bg-sunken text-status-success-text\"\n title={tr('aliases.save', 'Save')}\n >\n {savingEdit ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Check className=\"w-4 h-4\" />\n )}\n </button>\n <button\n onClick={cancelEdit}\n className=\"p-1.5 rounded hover:bg-bg-sunken text-text-secondary\"\n title={tr('common.cancel', 'Cancel')}\n >\n <X className=\"w-4 h-4\" />\n </button>\n </>\n ) : (\n <>\n <button\n onClick={() => {\n setRunTarget(alias);\n setRunArgs('');\n setRunResult(null);\n setRunError(null);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken text-action-primary-bg\"\n title={tr('aliases.run', 'Run')}\n >\n <Play className=\"w-4 h-4\" />\n </button>\n <button\n onClick={() => startEdit(alias)}\n className=\"p-1.5 rounded hover:bg-bg-sunken text-text-secondary\"\n title={tr('aliases.edit', 'Edit')}\n >\n <Pencil className=\"w-4 h-4\" />\n </button>\n <button\n onClick={() => setDeleteTarget(alias)}\n className=\"p-1.5 rounded hover:bg-bg-sunken text-status-error-text\"\n title={tr('aliases.delete', 'Delete')}\n >\n <Trash2 className=\"w-4 h-4\" />\n </button>\n </>\n )}\n </div>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n )}\n </div>\n\n {/* Run modal */}\n {runTarget && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-overlay\">\n <div className=\"w-full max-w-2xl bg-bg-surface rounded-lg shadow-xl max-h-[90vh] flex flex-col\">\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <h2 className=\"text-lg font-semibold text-text-primary flex items-center gap-2\">\n <Terminal className=\"w-5 h-5\" />\n {tr('aliases.runTitle', 'Run alias')}: <code>{runTarget.name}</code>\n </h2>\n <button onClick={() => setRunTarget(null)} className=\"p-1 rounded hover:bg-bg-sunken\">\n <X className=\"w-5 h-5 text-text-secondary\" />\n </button>\n </div>\n <div className=\"flex-1 overflow-y-auto p-6 space-y-3\">\n <div className=\"text-xs text-text-secondary font-mono break-all\">\n $ {runTarget.command}\n </div>\n <input\n type=\"text\"\n value={runArgs}\n onChange={(e) => setRunArgs(e.target.value)}\n placeholder={tr('aliases.argsPlaceholder', 'Args (space-separated, optional)')}\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary font-mono\"\n />\n {runError && <div className=\"text-sm text-status-error-text\">{runError}</div>}\n {runResult && (\n <div className=\"space-y-2\">\n <div className=\"text-xs text-text-secondary\">\n {tr('aliases.exitCode', 'Exit code')}:{' '}\n <span\n className={\n runResult.exit_code === 0\n ? 'text-status-success-text font-mono'\n : 'text-status-error-text font-mono'\n }\n >\n {runResult.exit_code}\n </span>\n </div>\n {runResult.stdout && (\n <div>\n <div className=\"text-xs text-text-secondary mb-1\">stdout</div>\n <pre className=\"text-xs bg-bg-sunken p-3 rounded font-mono whitespace-pre-wrap break-all max-h-64 overflow-y-auto text-text-primary\">\n {runResult.stdout}\n </pre>\n </div>\n )}\n {runResult.stderr && (\n <div>\n <div className=\"text-xs text-text-secondary mb-1\">stderr</div>\n <pre className=\"text-xs bg-bg-sunken p-3 rounded font-mono whitespace-pre-wrap break-all max-h-64 overflow-y-auto text-status-error-text\">\n {runResult.stderr}\n </pre>\n </div>\n )}\n </div>\n )}\n </div>\n <div className=\"flex items-center justify-end gap-3 px-6 py-4 border-t border-border-default bg-bg-sunken\">\n <button\n onClick={() => setRunTarget(null)}\n className=\"px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated\"\n >\n {tr('common.close', 'Close')}\n </button>\n <button\n onClick={() => void runAlias()}\n disabled={runRunning}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover disabled:opacity-50\"\n >\n {runRunning ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Play className=\"w-4 h-4\" />\n )}\n {tr('aliases.run', 'Run')}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Delete confirm */}\n {deleteTarget && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-overlay\">\n <div className=\"w-full max-w-md bg-bg-surface rounded-lg shadow-xl p-6 space-y-4\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('aliases.deleteTitle', 'Delete alias?')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr('aliases.deleteConfirm', 'This permanently removes the alias from this profile.')}{' '}\n <code className=\"font-mono text-text-primary\">{deleteTarget.name}</code>\n </p>\n <div className=\"flex justify-end gap-3\">\n <button\n onClick={() => setDeleteTarget(null)}\n className=\"px-3 py-1.5 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary hover:bg-bg-elevated\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n onClick={() => void confirmDelete()}\n disabled={deleting}\n className=\"inline-flex items-center gap-2 px-3 py-1.5 text-sm font-medium text-status-error-text bg-status-error-bg rounded-md hover:opacity-80 disabled:opacity-50\"\n >\n {deleting ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Trash2 className=\"w-4 h-4\" />\n )}\n {tr('aliases.delete', 'Delete')}\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AliasManager.js","names":[],"sources":["../../../src/components/aliases/AliasManager.tsx"],"sourcesContent":["/**\n * AliasManager — per-agent CRUDL for shell aliases exposed by\n * vibecontrols-agent under `/api/aliases/*`.\n *\n * Pairs with the agent's Phase A4 ship.\n *\n * Endpoints (proxied through wspace-vibecontrols-svc → agent):\n * GET /api/aliases → { items: Alias[] }\n * GET /api/aliases/:name → Alias\n * POST /api/aliases → Alias\n * PUT /api/aliases/:name → Alias\n * DELETE /api/aliases/:name\n * POST /api/aliases/:name/execute → { exit_code, stdout, stderr }\n */\n\nimport { useCallback, useEffect, useState } from 'react';\nimport { Plus, Trash2, Play, Pencil, Check, X, Loader2, Terminal, PackageOpen } from 'lucide-react';\nimport { agentAiGet, agentAiMutate } from '@/services/aiApi';\nimport { useTr } from '@/shared/hooks/useTr';\nimport { validateAliasName } from './aliasReservedNames';\n\n/**\n * Detect the signatures that mean \"the aliases plugin isn't installed on\n * this agent.\" The agent answers 404 with body `NOT_FOUND` when no plugin\n * is mounted at `/api/profiles/<profile>/aliases`; the svc proxy\n * sanitizes that to the \"feature is not installed\" string (see aiApi\n * `sanitizeError`). We also catch the underlying error text directly so\n * we don't depend on `sanitizeError` keeping that exact phrasing.\n *\n * Reviewer follow-up: deliberately NOT matching Apollo's default\n * `Unexpected error.` mask here. It would catch today's\n * `WorkspaceRequiredError`-from-`agentAiGet` symptom AND legitimate\n * future errors (auth failures, timeouts, etc.) once the plugin ships,\n * masking them as \"plugin not installed\" — a worse UX than the honest\n * raw error. The right cure for the workspace-context surface lives at\n * the svc resolver (translate to a typed GraphQL error code the FE can\n * pivot on) and the aiApi sanitizer; tracked in BOFF-2613.\n */\nfunction isAliasesPluginUnavailable(msg: string): boolean {\n if (!msg) return false;\n const m = msg.toLowerCase();\n return m.includes('not installed on the agent') || m.includes('not_found') || m.includes(' 404');\n}\n\ninterface Alias {\n name: string;\n command: string;\n description?: string | null;\n updatedAt?: string | null;\n createdAt?: string | null;\n}\n\ninterface AliasListResponse {\n items?: Alias[];\n}\n\ninterface AliasExecuteResult {\n exit_code: number;\n stdout: string;\n stderr: string;\n}\n\ninterface AliasManagerProps {\n agent: { id: string; profile?: string };\n}\n\nconst TRUNCATE = 80;\n\nfunction truncate(str: string, n = TRUNCATE): string {\n if (!str) return '';\n return str.length > n ? `${str.slice(0, n)}…` : str;\n}\n\nexport function AliasManager({ agent }: AliasManagerProps) {\n const tr = useTr();\n // Canonical agent URL hierarchy: agent → profile → plugins/data.\n // The aliases plugin lives under `/api/profiles/<profile>/aliases/*`.\n // No client-side rewrites — every consumer constructs the canonical\n // path inline so the URL the agent sees is the URL the FE wrote.\n const profile = encodeURIComponent(agent.profile ?? 'default');\n const aliasesBase = `/api/profiles/${profile}/aliases`;\n const [aliases, setAliases] = useState<Alias[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n // Distinguish \"the agent doesn't have this plugin installed\" from a generic\n // error so we can show a clean empty-state with an actionable hint instead\n // of a scary stack-trace fragment. [BOFF-2613]\n const [pluginUnavailable, setPluginUnavailable] = useState(false);\n\n // Add-new form\n const [newName, setNewName] = useState('');\n const [newCommand, setNewCommand] = useState('');\n const [newDescription, setNewDescription] = useState('');\n const [creating, setCreating] = useState(false);\n const [createError, setCreateError] = useState<string | null>(null);\n\n // Edit row\n const [editingName, setEditingName] = useState<string | null>(null);\n const [editCommand, setEditCommand] = useState('');\n const [editDescription, setEditDescription] = useState('');\n const [savingEdit, setSavingEdit] = useState(false);\n\n // Run modal\n const [runTarget, setRunTarget] = useState<Alias | null>(null);\n const [runArgs, setRunArgs] = useState('');\n const [runResult, setRunResult] = useState<AliasExecuteResult | null>(null);\n const [runRunning, setRunRunning] = useState(false);\n const [runError, setRunError] = useState<string | null>(null);\n\n // Delete confirm\n const [deleteTarget, setDeleteTarget] = useState<Alias | null>(null);\n const [deleting, setDeleting] = useState(false);\n\n const refresh = useCallback(async () => {\n setLoading(true);\n setError(null);\n setPluginUnavailable(false);\n try {\n const data = await agentAiGet<AliasListResponse | Alias[]>(agent.id, aliasesBase);\n const items = Array.isArray(data) ? data : (data.items ?? []);\n setAliases(items);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n if (isAliasesPluginUnavailable(msg)) {\n // Plugin not installed on this agent (or upstream context-required\n // error that we treat the same way for this tab) — show empty-state\n // instead of a scary error banner. [BOFF-2613]\n setPluginUnavailable(true);\n } else {\n setError(msg);\n }\n } finally {\n setLoading(false);\n }\n }, [agent.id, aliasesBase]);\n\n useEffect(() => {\n void refresh();\n }, [refresh]);\n\n // ── Create ────────────────────────────────────────────────────────\n const handleCreate = async () => {\n setCreateError(null);\n const nameErr = validateAliasName(newName.trim());\n if (nameErr) {\n setCreateError(nameErr);\n return;\n }\n if (!newCommand.trim()) {\n setCreateError(tr('aliases.commandRequired', 'Command is required'));\n return;\n }\n setCreating(true);\n try {\n await agentAiMutate(agent.id, aliasesBase, 'POST', {\n name: newName.trim(),\n command: newCommand,\n description: newDescription || undefined,\n });\n setNewName('');\n setNewCommand('');\n setNewDescription('');\n await refresh();\n } catch (e) {\n setCreateError(e instanceof Error ? e.message : String(e));\n } finally {\n setCreating(false);\n }\n };\n\n // ── Edit ──────────────────────────────────────────────────────────\n const startEdit = (alias: Alias) => {\n setEditingName(alias.name);\n setEditCommand(alias.command);\n setEditDescription(alias.description ?? '');\n };\n\n const cancelEdit = () => {\n setEditingName(null);\n setEditCommand('');\n setEditDescription('');\n };\n\n const saveEdit = async (alias: Alias) => {\n setSavingEdit(true);\n try {\n await agentAiMutate(agent.id, `${aliasesBase}/${encodeURIComponent(alias.name)}`, 'PUT', {\n command: editCommand,\n description: editDescription || undefined,\n });\n cancelEdit();\n await refresh();\n } catch (e) {\n setError(e instanceof Error ? e.message : String(e));\n } finally {\n setSavingEdit(false);\n }\n };\n\n // ── Delete ────────────────────────────────────────────────────────\n const confirmDelete = async () => {\n if (!deleteTarget) return;\n setDeleting(true);\n try {\n await agentAiMutate(\n agent.id,\n `${aliasesBase}/${encodeURIComponent(deleteTarget.name)}`,\n 'DELETE'\n );\n setDeleteTarget(null);\n await refresh();\n } catch (e) {\n setError(e instanceof Error ? e.message : String(e));\n } finally {\n setDeleting(false);\n }\n };\n\n // ── Run ───────────────────────────────────────────────────────────\n const runAlias = async () => {\n if (!runTarget) return;\n setRunRunning(true);\n setRunError(null);\n setRunResult(null);\n try {\n const args = runArgs.split(/\\s+/).filter(Boolean);\n const result = await agentAiMutate<AliasExecuteResult>(\n agent.id,\n `${aliasesBase}/${encodeURIComponent(runTarget.name)}/execute`,\n 'POST',\n { args }\n );\n setRunResult(result);\n } catch (e) {\n setRunError(e instanceof Error ? e.message : String(e));\n } finally {\n setRunRunning(false);\n }\n };\n\n if (pluginUnavailable) {\n // The aliases plugin isn't installed on this agent. Show a clean,\n // actionable empty-state instead of the form + the raw error string.\n // [BOFF-2613]\n return (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-8 text-center space-y-3\">\n <PackageOpen className=\"w-8 h-8 mx-auto text-text-muted\" />\n <h3 className=\"text-base font-medium text-text-primary\">\n {tr('aliases.pluginMissingTitle', 'Aliases plugin not installed')}\n </h3>\n <p className=\"text-sm text-text-secondary max-w-md mx-auto\">\n {tr(\n 'aliases.pluginMissingBody',\n \"This agent doesn't have the aliases plugin installed yet. Install @vibecontrols/vibe-plugin-aliases from the Plugins tab to enable per-agent shell aliases.\"\n )}\n </p>\n <button\n type=\"button\"\n onClick={refresh}\n className=\"inline-flex items-center gap-2 px-3 py-1.5 text-sm font-medium text-text-secondary border border-border-default rounded-md hover:bg-bg-sunken transition-colors\"\n >\n {tr('aliases.retry', 'Retry')}\n </button>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4\">\n {/* Add new alias form */}\n <div className=\"rounded-lg border border-border-default bg-bg-sunken p-4 space-y-3\">\n <h3 className=\"text-sm font-medium text-text-primary flex items-center gap-2\">\n <Plus className=\"w-4 h-4\" />\n {tr('aliases.createTitle', 'New alias')}\n </h3>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n <input\n type=\"text\"\n value={newName}\n onChange={(e) => setNewName(e.target.value)}\n placeholder={tr('aliases.namePlaceholder', 'Name (a-z, 0-9, _, -)')}\n className=\"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 />\n <input\n type=\"text\"\n value={newDescription}\n onChange={(e) => setNewDescription(e.target.value)}\n placeholder={tr('aliases.descriptionPlaceholder', 'Description (optional)')}\n className=\"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\"\n />\n </div>\n <textarea\n value={newCommand}\n onChange={(e) => setNewCommand(e.target.value)}\n placeholder={tr('aliases.commandPlaceholder', 'Command (e.g. git status)')}\n rows={3}\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 />\n {createError && <div className=\"text-xs text-status-error-text\">{createError}</div>}\n <div className=\"flex justify-end\">\n <button\n onClick={handleCreate}\n disabled={creating || !newName.trim() || !newCommand.trim()}\n className=\"inline-flex items-center gap-2 px-3 py-1.5 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 {creating ? <Loader2 className=\"w-4 h-4 animate-spin\" /> : <Plus className=\"w-4 h-4\" />}\n {tr('aliases.create', 'Create')}\n </button>\n </div>\n </div>\n\n {/* List */}\n <div className=\"rounded-lg border border-border-default overflow-hidden\">\n {loading ? (\n <div className=\"p-8 text-center text-text-secondary\">\n <Loader2 className=\"w-5 h-5 animate-spin mx-auto\" />\n </div>\n ) : error ? (\n <div className=\"p-4 text-sm text-status-error-text\">{error}</div>\n ) : aliases.length === 0 ? (\n <div className=\"p-8 text-center text-sm text-text-secondary\">\n {tr('aliases.empty', 'No aliases yet. Create one above.')}\n </div>\n ) : (\n <table className=\"w-full text-sm\">\n <thead className=\"bg-bg-sunken text-text-secondary\">\n <tr>\n <th className=\"text-left px-3 py-2 font-medium\">{tr('aliases.colName', 'Name')}</th>\n <th className=\"text-left px-3 py-2 font-medium\">\n {tr('aliases.colCommand', 'Command')}\n </th>\n <th className=\"text-left px-3 py-2 font-medium\">\n {tr('aliases.colDescription', 'Description')}\n </th>\n <th className=\"text-left px-3 py-2 font-medium\">\n {tr('aliases.colUpdatedAt', 'Updated')}\n </th>\n <th className=\"text-right px-3 py-2 font-medium\">\n {tr('aliases.colActions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody>\n {aliases.map((alias) => {\n const isEditing = editingName === alias.name;\n return (\n <tr key={alias.name} className=\"border-t border-border-default\">\n <td className=\"px-3 py-2 font-mono text-text-primary align-top\">\n {alias.name}\n </td>\n <td className=\"px-3 py-2 align-top\">\n {isEditing ? (\n <textarea\n value={editCommand}\n onChange={(e) => setEditCommand(e.target.value)}\n rows={2}\n className=\"w-full px-2 py-1 text-xs bg-bg-surface border border-border-default rounded text-text-primary font-mono\"\n />\n ) : (\n <code className=\"text-xs text-text-primary\" title={alias.command}>\n {truncate(alias.command)}\n </code>\n )}\n </td>\n <td className=\"px-3 py-2 align-top\">\n {isEditing ? (\n <input\n value={editDescription}\n onChange={(e) => setEditDescription(e.target.value)}\n className=\"w-full px-2 py-1 text-xs bg-bg-surface border border-border-default rounded text-text-primary\"\n />\n ) : (\n <span className=\"text-xs text-text-secondary\">\n {alias.description ?? '—'}\n </span>\n )}\n </td>\n <td className=\"px-3 py-2 text-xs text-text-secondary align-top\">\n {alias.updatedAt ? new Date(alias.updatedAt).toLocaleString() : '—'}\n </td>\n <td className=\"px-3 py-2 align-top\">\n <div className=\"flex items-center justify-end gap-1\">\n {isEditing ? (\n <>\n <button\n onClick={() => void saveEdit(alias)}\n disabled={savingEdit}\n className=\"p-1.5 rounded hover:bg-bg-sunken text-status-success-text\"\n title={tr('aliases.save', 'Save')}\n >\n {savingEdit ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Check className=\"w-4 h-4\" />\n )}\n </button>\n <button\n onClick={cancelEdit}\n className=\"p-1.5 rounded hover:bg-bg-sunken text-text-secondary\"\n title={tr('common.cancel', 'Cancel')}\n >\n <X className=\"w-4 h-4\" />\n </button>\n </>\n ) : (\n <>\n <button\n onClick={() => {\n setRunTarget(alias);\n setRunArgs('');\n setRunResult(null);\n setRunError(null);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken text-action-primary-bg\"\n title={tr('aliases.run', 'Run')}\n >\n <Play className=\"w-4 h-4\" />\n </button>\n <button\n onClick={() => startEdit(alias)}\n className=\"p-1.5 rounded hover:bg-bg-sunken text-text-secondary\"\n title={tr('aliases.edit', 'Edit')}\n >\n <Pencil className=\"w-4 h-4\" />\n </button>\n <button\n onClick={() => setDeleteTarget(alias)}\n className=\"p-1.5 rounded hover:bg-bg-sunken text-status-error-text\"\n title={tr('aliases.delete', 'Delete')}\n >\n <Trash2 className=\"w-4 h-4\" />\n </button>\n </>\n )}\n </div>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n )}\n </div>\n\n {/* Run modal */}\n {runTarget && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-overlay\">\n <div className=\"w-full max-w-2xl bg-bg-surface rounded-lg shadow-xl max-h-[90vh] flex flex-col\">\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <h2 className=\"text-lg font-semibold text-text-primary flex items-center gap-2\">\n <Terminal className=\"w-5 h-5\" />\n {tr('aliases.runTitle', 'Run alias')}: <code>{runTarget.name}</code>\n </h2>\n <button onClick={() => setRunTarget(null)} className=\"p-1 rounded hover:bg-bg-sunken\">\n <X className=\"w-5 h-5 text-text-secondary\" />\n </button>\n </div>\n <div className=\"flex-1 overflow-y-auto p-6 space-y-3\">\n <div className=\"text-xs text-text-secondary font-mono break-all\">\n $ {runTarget.command}\n </div>\n <input\n type=\"text\"\n value={runArgs}\n onChange={(e) => setRunArgs(e.target.value)}\n placeholder={tr('aliases.argsPlaceholder', 'Args (space-separated, optional)')}\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary font-mono\"\n />\n {runError && <div className=\"text-sm text-status-error-text\">{runError}</div>}\n {runResult && (\n <div className=\"space-y-2\">\n <div className=\"text-xs text-text-secondary\">\n {tr('aliases.exitCode', 'Exit code')}:{' '}\n <span\n className={\n runResult.exit_code === 0\n ? 'text-status-success-text font-mono'\n : 'text-status-error-text font-mono'\n }\n >\n {runResult.exit_code}\n </span>\n </div>\n {runResult.stdout && (\n <div>\n <div className=\"text-xs text-text-secondary mb-1\">stdout</div>\n <pre className=\"text-xs bg-bg-sunken p-3 rounded font-mono whitespace-pre-wrap break-all max-h-64 overflow-y-auto text-text-primary\">\n {runResult.stdout}\n </pre>\n </div>\n )}\n {runResult.stderr && (\n <div>\n <div className=\"text-xs text-text-secondary mb-1\">stderr</div>\n <pre className=\"text-xs bg-bg-sunken p-3 rounded font-mono whitespace-pre-wrap break-all max-h-64 overflow-y-auto text-status-error-text\">\n {runResult.stderr}\n </pre>\n </div>\n )}\n </div>\n )}\n </div>\n <div className=\"flex items-center justify-end gap-3 px-6 py-4 border-t border-border-default bg-bg-sunken\">\n <button\n onClick={() => setRunTarget(null)}\n className=\"px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated\"\n >\n {tr('common.close', 'Close')}\n </button>\n <button\n onClick={() => void runAlias()}\n disabled={runRunning}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover disabled:opacity-50\"\n >\n {runRunning ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Play className=\"w-4 h-4\" />\n )}\n {tr('aliases.run', 'Run')}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Delete confirm */}\n {deleteTarget && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-overlay\">\n <div className=\"w-full max-w-md bg-bg-surface rounded-lg shadow-xl p-6 space-y-4\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('aliases.deleteTitle', 'Delete alias?')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr('aliases.deleteConfirm', 'This permanently removes the alias from this profile.')}{' '}\n <code className=\"font-mono text-text-primary\">{deleteTarget.name}</code>\n </p>\n <div className=\"flex justify-end gap-3\">\n <button\n onClick={() => setDeleteTarget(null)}\n className=\"px-3 py-1.5 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary hover:bg-bg-elevated\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n onClick={() => void confirmDelete()}\n disabled={deleting}\n className=\"inline-flex items-center gap-2 px-3 py-1.5 text-sm font-medium text-status-error-text bg-status-error-bg rounded-md hover:opacity-80 disabled:opacity-50\"\n >\n {deleting ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Trash2 className=\"w-4 h-4\" />\n )}\n {tr('aliases.delete', 'Delete')}\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;AAsCA,SAAS,GAA2B,GAAsB;AACxD,KAAI,CAAC,EAAK,QAAO;CACjB,IAAM,IAAI,EAAI,aAAa;AAC3B,QAAO,EAAE,SAAS,6BAA6B,IAAI,EAAE,SAAS,YAAY,IAAI,EAAE,SAAS,OAAO;;AAyBlG,IAAM,IAAW;AAEjB,SAAS,GAAS,GAAa,IAAI,GAAkB;AAEnD,QADK,IACE,EAAI,SAAS,IAAI,GAAG,EAAI,MAAM,GAAG,EAAE,CAAC,KAAK,IAD/B;;AAInB,SAAgB,EAAa,EAAE,YAA4B;CACzD,IAAM,IAAK,GAAO,EAMZ,IAAc,iBADJ,mBAAmB,EAAM,WAAW,UAAU,CACjB,WACvC,CAAC,GAAS,MAAc,EAAkB,EAAE,CAAC,EAC7C,CAAC,IAAS,KAAc,EAAS,GAAK,EACtC,CAAC,GAAO,KAAY,EAAwB,KAAK,EAIjD,CAAC,IAAmB,KAAwB,EAAS,GAAM,EAG3D,CAAC,GAAS,KAAc,EAAS,GAAG,EACpC,CAAC,GAAY,KAAiB,EAAS,GAAG,EAC1C,CAAC,GAAgB,KAAqB,EAAS,GAAG,EAClD,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAG7D,CAAC,IAAa,KAAkB,EAAwB,KAAK,EAC7D,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAiB,KAAsB,EAAS,GAAG,EACpD,CAAC,GAAY,KAAiB,EAAS,GAAM,EAG7C,CAAC,GAAW,KAAgB,EAAuB,KAAK,EACxD,CAAC,GAAS,KAAc,EAAS,GAAG,EACpC,CAAC,GAAW,KAAgB,EAAoC,KAAK,EACrE,CAAC,GAAY,KAAiB,EAAS,GAAM,EAC7C,CAAC,GAAU,KAAe,EAAwB,KAAK,EAGvD,CAAC,GAAc,KAAmB,EAAuB,KAAK,EAC9D,CAAC,GAAU,KAAe,EAAS,GAAM,EAEzC,IAAU,EAAY,YAAY;AAGtC,EAFA,EAAW,GAAK,EAChB,EAAS,KAAK,EACd,EAAqB,GAAM;AAC3B,MAAI;GACF,IAAM,IAAO,MAAM,EAAwC,EAAM,IAAI,EAAY;AAEjF,MADc,MAAM,QAAQ,EAAK,GAAG,IAAQ,EAAK,SAAS,EAAE,CAC3C;WACV,GAAG;GACV,IAAM,IAAM,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE;AACtD,GAAI,GAA2B,EAAI,GAIjC,EAAqB,GAAK,GAE1B,EAAS,EAAI;YAEP;AACR,KAAW,GAAM;;IAElB,CAAC,EAAM,IAAI,EAAY,CAAC;AAE3B,UAAgB;AACT,KAAS;IACb,CAAC,EAAQ,CAAC;CAGb,IAAM,KAAe,YAAY;AAC/B,IAAe,KAAK;EACpB,IAAM,IAAU,GAAkB,EAAQ,MAAM,CAAC;AACjD,MAAI,GAAS;AACX,KAAe,EAAQ;AACvB;;AAEF,MAAI,CAAC,EAAW,MAAM,EAAE;AACtB,KAAe,EAAG,2BAA2B,sBAAsB,CAAC;AACpE;;AAEF,IAAY,GAAK;AACjB,MAAI;AASF,GARA,MAAM,EAAc,EAAM,IAAI,GAAa,QAAQ;IACjD,MAAM,EAAQ,MAAM;IACpB,SAAS;IACT,aAAa,KAAkB,KAAA;IAChC,CAAC,EACF,EAAW,GAAG,EACd,EAAc,GAAG,EACjB,EAAkB,GAAG,EACrB,MAAM,GAAS;WACR,GAAG;AACV,KAAe,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE,CAAC;YAClD;AACR,KAAY,GAAM;;IAKhB,MAAa,MAAiB;AAGlC,EAFA,EAAe,EAAM,KAAK,EAC1B,EAAe,EAAM,QAAQ,EAC7B,EAAmB,EAAM,eAAe,GAAG;IAGvC,UAAmB;AAGvB,EAFA,EAAe,KAAK,EACpB,EAAe,GAAG,EAClB,EAAmB,GAAG;IAGlB,KAAW,OAAO,MAAiB;AACvC,IAAc,GAAK;AACnB,MAAI;AAMF,GALA,MAAM,EAAc,EAAM,IAAI,GAAG,EAAY,GAAG,mBAAmB,EAAM,KAAK,IAAI,OAAO;IACvF,SAAS;IACT,aAAa,KAAmB,KAAA;IACjC,CAAC,EACF,GAAY,EACZ,MAAM,GAAS;WACR,GAAG;AACV,KAAS,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE,CAAC;YAC5C;AACR,KAAc,GAAM;;IAKlB,KAAgB,YAAY;AAC3B,SACL;KAAY,GAAK;AACjB,OAAI;AAOF,IANA,MAAM,EACJ,EAAM,IACN,GAAG,EAAY,GAAG,mBAAmB,EAAa,KAAK,IACvD,SACD,EACD,EAAgB,KAAK,EACrB,MAAM,GAAS;YACR,GAAG;AACV,MAAS,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE,CAAC;aAC5C;AACR,MAAY,GAAM;;;IAKhB,KAAW,YAAY;AACtB,SAGL;GAFA,EAAc,GAAK,EACnB,EAAY,KAAK,EACjB,EAAa,KAAK;AAClB,OAAI;IACF,IAAM,IAAO,EAAQ,MAAM,MAAM,CAAC,OAAO,QAAQ;AAOjD,MANe,MAAM,EACnB,EAAM,IACN,GAAG,EAAY,GAAG,mBAAmB,EAAU,KAAK,CAAC,WACrD,QACA,EAAE,SAAM,CACT,CACmB;YACb,GAAG;AACV,MAAY,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE,CAAC;aAC/C;AACR,MAAc,GAAM;;;;AA+BxB,QA3BI,KAKA,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,IAAD,EAAa,WAAU,mCAAoC,CAAA;GAC3D,kBAAC,MAAD;IAAI,WAAU;cACX,EAAG,8BAA8B,+BAA+B;IAC9D,CAAA;GACL,kBAAC,KAAD;IAAG,WAAU;cACV,EACC,6BACA,8JACD;IACC,CAAA;GACJ,kBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,WAAU;cAET,EAAG,iBAAiB,QAAQ;IACtB,CAAA;GACL;MAKR,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,MAAD;MAAI,WAAU;gBAAd,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAC3B,EAAG,uBAAuB,YAAY,CACpC;;KACL,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,SAAD;OACE,MAAK;OACL,OAAO;OACP,WAAW,MAAM,EAAW,EAAE,OAAO,MAAM;OAC3C,aAAa,EAAG,2BAA2B,wBAAwB;OACnE,WAAU;OACV,CAAA,EACF,kBAAC,SAAD;OACE,MAAK;OACL,OAAO;OACP,WAAW,MAAM,EAAkB,EAAE,OAAO,MAAM;OAClD,aAAa,EAAG,kCAAkC,yBAAyB;OAC3E,WAAU;OACV,CAAA,CACE;;KACN,kBAAC,YAAD;MACE,OAAO;MACP,WAAW,MAAM,EAAc,EAAE,OAAO,MAAM;MAC9C,aAAa,EAAG,8BAA8B,4BAA4B;MAC1E,MAAM;MACN,WAAU;MACV,CAAA;KACD,KAAe,kBAAC,OAAD;MAAK,WAAU;gBAAkC;MAAkB,CAAA;KACnF,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,UAAD;OACE,SAAS;OACT,UAAU,KAAY,CAAC,EAAQ,MAAM,IAAI,CAAC,EAAW,MAAM;OAC3D,WAAU;iBAHZ,CAKG,IAAW,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA,GAAG,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EACtF,EAAG,kBAAkB,SAAS,CACxB;;MACL,CAAA;KACF;;GAGN,kBAAC,OAAD;IAAK,WAAU;cACZ,KACC,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD,EAAS,WAAU,gCAAiC,CAAA;KAChD,CAAA,GACJ,IACF,kBAAC,OAAD;KAAK,WAAU;eAAsC;KAAY,CAAA,GAC/D,EAAQ,WAAW,IACrB,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAG,iBAAiB,oCAAoC;KACrD,CAAA,GAEN,kBAAC,SAAD;KAAO,WAAU;eAAjB,CACE,kBAAC,SAAD;MAAO,WAAU;gBACf,kBAAC,MAAD,EAAA,UAAA;OACE,kBAAC,MAAD;QAAI,WAAU;kBAAmC,EAAG,mBAAmB,OAAO;QAAM,CAAA;OACpF,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAG,sBAAsB,UAAU;QACjC,CAAA;OACL,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAG,0BAA0B,cAAc;QACzC,CAAA;OACL,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAG,wBAAwB,UAAU;QACnC,CAAA;OACL,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAG,sBAAsB,UAAU;QACjC,CAAA;OACF,EAAA,CAAA;MACC,CAAA,EACR,kBAAC,SAAD,EAAA,UACG,EAAQ,KAAK,MAAU;MACtB,IAAM,IAAY,OAAgB,EAAM;AACxC,aACE,kBAAC,MAAD;OAAqB,WAAU;iBAA/B;QACE,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAM;SACJ,CAAA;QACL,kBAAC,MAAD;SAAI,WAAU;mBACX,IACC,kBAAC,YAAD;UACE,OAAO;UACP,WAAW,MAAM,EAAe,EAAE,OAAO,MAAM;UAC/C,MAAM;UACN,WAAU;UACV,CAAA,GAEF,kBAAC,QAAD;UAAM,WAAU;UAA4B,OAAO,EAAM;oBACtD,GAAS,EAAM,QAAQ;UACnB,CAAA;SAEN,CAAA;QACL,kBAAC,MAAD;SAAI,WAAU;mBACX,IACC,kBAAC,SAAD;UACE,OAAO;UACP,WAAW,MAAM,EAAmB,EAAE,OAAO,MAAM;UACnD,WAAU;UACV,CAAA,GAEF,kBAAC,QAAD;UAAM,WAAU;oBACb,EAAM,eAAe;UACjB,CAAA;SAEN,CAAA;QACL,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAM,YAAY,IAAI,KAAK,EAAM,UAAU,CAAC,gBAAgB,GAAG;SAC7D,CAAA;QACL,kBAAC,MAAD;SAAI,WAAU;mBACZ,kBAAC,OAAD;UAAK,WAAU;oBACZ,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,UAAD;WACE,eAAe,KAAK,GAAS,EAAM;WACnC,UAAU;WACV,WAAU;WACV,OAAO,EAAG,gBAAgB,OAAO;qBAEhC,IACC,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA,GAE5C,kBAAC,IAAD,EAAO,WAAU,WAAY,CAAA;WAExB,CAAA,EACT,kBAAC,UAAD;WACE,SAAS;WACT,WAAU;WACV,OAAO,EAAG,iBAAiB,SAAS;qBAEpC,kBAAC,GAAD,EAAG,WAAU,WAAY,CAAA;WAClB,CAAA,CACR,EAAA,CAAA,GAEH,kBAAA,GAAA,EAAA,UAAA;WACE,kBAAC,UAAD;YACE,eAAe;AAIb,aAHA,EAAa,EAAM,EACnB,EAAW,GAAG,EACd,EAAa,KAAK,EAClB,EAAY,KAAK;;YAEnB,WAAU;YACV,OAAO,EAAG,eAAe,MAAM;sBAE/B,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;YACrB,CAAA;WACT,kBAAC,UAAD;YACE,eAAe,GAAU,EAAM;YAC/B,WAAU;YACV,OAAO,EAAG,gBAAgB,OAAO;sBAEjC,kBAAC,IAAD,EAAQ,WAAU,WAAY,CAAA;YACvB,CAAA;WACT,kBAAC,UAAD;YACE,eAAe,EAAgB,EAAM;YACrC,WAAU;YACV,OAAO,EAAG,kBAAkB,SAAS;sBAErC,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;YACvB,CAAA;WACR,EAAA,CAAA;UAED,CAAA;SACH,CAAA;QACF;SA1FI,EAAM,KA0FV;OAEP,EACI,CAAA,CACF;;IAEN,CAAA;GAGL,KACC,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,MAAD;QAAI,WAAU;kBAAd;SACE,kBAAC,IAAD,EAAU,WAAU,WAAY,CAAA;SAC/B,EAAG,oBAAoB,YAAY;SAAC;SAAE,kBAAC,QAAD,EAAA,UAAO,EAAU,MAAY,CAAA;SACjE;WACL,kBAAC,UAAD;QAAQ,eAAe,EAAa,KAAK;QAAE,WAAU;kBACnD,kBAAC,GAAD,EAAG,WAAU,+BAAgC,CAAA;QACtC,CAAA,CACL;;MACN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CAAiE,MAC5D,EAAU,QACT;;QACN,kBAAC,SAAD;SACE,MAAK;SACL,OAAO;SACP,WAAW,MAAM,EAAW,EAAE,OAAO,MAAM;SAC3C,aAAa,EAAG,2BAA2B,mCAAmC;SAC9E,WAAU;SACV,CAAA;QACD,KAAY,kBAAC,OAAD;SAAK,WAAU;mBAAkC;SAAe,CAAA;QAC5E,KACC,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,OAAD;WAAK,WAAU;qBAAf;YACG,EAAG,oBAAoB,YAAY;YAAC;YAAE;YACvC,kBAAC,QAAD;aACE,WACE,EAAU,cAAc,IACpB,uCACA;uBAGL,EAAU;aACN,CAAA;YACH;;UACL,EAAU,UACT,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;WAAK,WAAU;qBAAmC;WAAY,CAAA,EAC9D,kBAAC,OAAD;WAAK,WAAU;qBACZ,EAAU;WACP,CAAA,CACF,EAAA,CAAA;UAEP,EAAU,UACT,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;WAAK,WAAU;qBAAmC;WAAY,CAAA,EAC9D,kBAAC,OAAD;WAAK,WAAU;qBACZ,EAAU;WACP,CAAA,CACF,EAAA,CAAA;UAEJ;;QAEJ;;MACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,eAAe,EAAa,KAAK;QACjC,WAAU;kBAET,EAAG,gBAAgB,QAAQ;QACrB,CAAA,EACT,kBAAC,UAAD;QACE,eAAe,KAAK,IAAU;QAC9B,UAAU;QACV,WAAU;kBAHZ,CAKG,IACC,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA,GAE5C,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAE7B,EAAG,eAAe,MAAM,CAClB;UACL;;MACF;;IACF,CAAA;GAIP,KACC,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,uBAAuB,gBAAgB;OACxC,CAAA;MACL,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAG,yBAAyB,wDAAwD;QAAE;QACvF,kBAAC,QAAD;SAAM,WAAU;mBAA+B,EAAa;SAAY,CAAA;QACtE;;MACJ,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,eAAe,EAAgB,KAAK;QACpC,WAAU;kBAET,EAAG,iBAAiB,SAAS;QACvB,CAAA,EACT,kBAAC,UAAD;QACE,eAAe,KAAK,IAAe;QACnC,UAAU;QACV,WAAU;kBAHZ,CAKG,IACC,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA,GAE5C,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA,EAE/B,EAAG,kBAAkB,SAAS,CACxB;UACL;;MACF;;IACF,CAAA;GAEJ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-vibecontrols",
3
- "version": "2026.526.2",
3
+ "version": "2026.526.4",
4
4
  "description": "VibeControls microfrontend for Burdenoff products",
5
5
  "type": "module",
6
6
  "files": [