@tangle-network/sandbox-ui 0.14.0 → 0.15.1

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.
Files changed (104) hide show
  1. package/dist/auth.d.ts +1 -74
  2. package/dist/auth.js +1 -4
  3. package/dist/chat.d.ts +1 -136
  4. package/dist/chat.js +2 -15
  5. package/dist/chunk-2BUPSB7O.js +0 -0
  6. package/dist/chunk-3J6FG3FJ.js +18 -0
  7. package/dist/chunk-76IQLPW2.js +206 -0
  8. package/dist/chunk-7ZA5SEK3.js +239 -0
  9. package/dist/chunk-AHBZCBDO.js +2960 -0
  10. package/dist/chunk-AZ3AWMTM.js +8 -0
  11. package/dist/chunk-CMY7W45U.js +380 -0
  12. package/dist/{chunk-QMU2PWOU.js → chunk-DNZ4DTNA.js} +71 -17
  13. package/dist/chunk-EI44GEQ5.js +6 -0
  14. package/dist/{chunk-5OQ27N57.js → chunk-GPT7VKK6.js} +34 -38
  15. package/dist/chunk-JBGKGLD7.js +16 -0
  16. package/dist/chunk-NJNME4J4.js +14 -0
  17. package/dist/chunk-QPAJR74X.js +20 -0
  18. package/dist/chunk-TK46XFLM.js +28 -0
  19. package/dist/chunk-WID73FPH.js +89 -0
  20. package/dist/chunk-YVXK4XRO.js +30 -0
  21. package/dist/dashboard.d.ts +538 -4
  22. package/dist/dashboard.js +15 -886
  23. package/dist/editor.d.ts +1 -120
  24. package/dist/editor.js +1 -5
  25. package/dist/files.d.ts +1 -129
  26. package/dist/files.js +2 -7
  27. package/dist/globals.css +2 -1265
  28. package/dist/hooks.d.ts +114 -11
  29. package/dist/hooks.js +17 -88
  30. package/dist/index.d.ts +24 -99
  31. package/dist/index.js +247 -252
  32. package/dist/markdown.d.ts +1 -29
  33. package/dist/markdown.js +2 -2
  34. package/dist/openui.d.ts +8 -115
  35. package/dist/openui.js +1 -6
  36. package/dist/pages.d.ts +1 -2
  37. package/dist/pages.js +68 -66
  38. package/dist/primitives.d.ts +14 -49
  39. package/dist/primitives.js +69 -77
  40. package/dist/run.d.ts +1 -14
  41. package/dist/run.js +2 -22
  42. package/dist/sdk-hooks.d.ts +3 -283
  43. package/dist/sdk-hooks.js +10 -14
  44. package/dist/stores.d.ts +2 -14
  45. package/dist/stores.js +11 -39
  46. package/dist/styles.css +2 -1265
  47. package/dist/{usage-chart-CPTcNlGs.d.ts → template-card-UhV3pmRC.d.ts} +16 -1
  48. package/dist/types.d.ts +11 -8
  49. package/dist/types.js +1 -0
  50. package/dist/utils.d.ts +1 -44
  51. package/dist/utils.js +6 -12
  52. package/dist/workspace.d.ts +5 -10
  53. package/dist/workspace.js +3 -19
  54. package/package.json +19 -54
  55. package/dist/active-sessions-store-CeOmXgv5.d.ts +0 -85
  56. package/dist/artifact-pane-Bh45Ssco.d.ts +0 -24
  57. package/dist/branding-DCi5VEik.d.ts +0 -13
  58. package/dist/button-CMQuQEW_.d.ts +0 -17
  59. package/dist/chat-container-f4yEs6KN.d.ts +0 -106
  60. package/dist/chunk-34A66VBG.js +0 -214
  61. package/dist/chunk-34I7UFSX.js +0 -92
  62. package/dist/chunk-36QY2W5G.js +0 -802
  63. package/dist/chunk-4CLN43XT.js +0 -45
  64. package/dist/chunk-54SQQMMM.js +0 -156
  65. package/dist/chunk-66EZOYZR.js +0 -102
  66. package/dist/chunk-BX6AQMUS.js +0 -183
  67. package/dist/chunk-DI3NZ5ZX.js +0 -192
  68. package/dist/chunk-DPGIXDAI.js +0 -220
  69. package/dist/chunk-DXMIEK4K.js +0 -1426
  70. package/dist/chunk-GSZA3TSY.js +0 -79
  71. package/dist/chunk-HB5Y37YU.js +0 -54
  72. package/dist/chunk-LQNEZDRM.js +0 -109
  73. package/dist/chunk-MA7YKRUP.js +0 -131
  74. package/dist/chunk-MKTSMWVD.js +0 -109
  75. package/dist/chunk-MQXABZTB.js +0 -1348
  76. package/dist/chunk-MT5FJ3ZT.js +0 -186
  77. package/dist/chunk-NKUPJC34.js +0 -2070
  78. package/dist/chunk-OEX7NZE3.js +0 -321
  79. package/dist/chunk-OKLQVY3Y.js +0 -139
  80. package/dist/chunk-Q56BYXQF.js +0 -61
  81. package/dist/chunk-QD4QE5P5.js +0 -40
  82. package/dist/chunk-QDH5GEGY.js +0 -630
  83. package/dist/chunk-QID2OOMG.js +0 -133
  84. package/dist/chunk-RQHJBTEU.js +0 -10
  85. package/dist/chunk-T7HMZEVO.js +0 -216
  86. package/dist/chunk-U6QTHMY6.js +0 -1290
  87. package/dist/chunk-US6JKJKH.js +0 -124
  88. package/dist/chunk-VX3XOUEB.js +0 -63
  89. package/dist/chunk-XLG757B6.js +0 -933
  90. package/dist/chunk-ZMNSRDMH.js +0 -127
  91. package/dist/chunk-ZNCEM5CD.js +0 -316
  92. package/dist/document-editor-pane-A70-EhdQ.d.ts +0 -124
  93. package/dist/document-editor-pane-TLPVRBBU.js +0 -11
  94. package/dist/expanded-tool-detail-Dh99mcbY.d.ts +0 -63
  95. package/dist/file-tabs-BLfxfmAH.d.ts +0 -51
  96. package/dist/parts-CyGkM6Fp.d.ts +0 -50
  97. package/dist/run-CtFZ6s-D.d.ts +0 -41
  98. package/dist/sidebar-drop-zone-tDBsuOH5.d.ts +0 -301
  99. package/dist/sidecar-CFU2W9j1.d.ts +0 -8
  100. package/dist/template-card-BAtvcAkU.d.ts +0 -18
  101. package/dist/tool-call-feed-Bs3MyQMT.d.ts +0 -68
  102. package/dist/tool-display-Ct9nFAzJ.d.ts +0 -32
  103. package/dist/use-sandbox-metrics-DWc0k9Xm.d.ts +0 -153
  104. package/dist/variant-list-BrHYcBCk.d.ts +0 -540
package/dist/dashboard.js CHANGED
@@ -1,21 +1,23 @@
1
1
  import {
2
- InfoPanel,
3
- TemplateCard
4
- } from "./chunk-VX3XOUEB.js";
5
- import {
2
+ BackendConfig,
6
3
  BackendSelector,
7
4
  ClusterStatusBar,
8
5
  CreditBalance,
9
6
  DashboardLayout,
7
+ GitPanel,
10
8
  HARNESS_OPTIONS,
11
9
  HarnessPicker,
12
10
  InvoiceTable,
13
11
  ModelPicker,
12
+ NetworkConfig,
14
13
  NewSandboxCard,
15
14
  PlanCards,
15
+ PortsList,
16
+ ProcessList,
16
17
  ProfileAvatar,
17
18
  ProfileComparison,
18
19
  ProfileSelector,
20
+ PromoBanner,
19
21
  RailButton,
20
22
  RailModeButton,
21
23
  RailSeparator,
@@ -36,899 +38,26 @@ import {
36
38
  SidebarRailFooter,
37
39
  SidebarRailHeader,
38
40
  SidebarRailNav,
41
+ SnapshotList,
42
+ SystemLogsViewer,
43
+ UsageSummary,
39
44
  VariantList,
40
45
  canAdminSandbox,
41
46
  canonicalModelId,
42
47
  formatContext,
43
48
  formatPricing,
44
49
  useSidebar
45
- } from "./chunk-NKUPJC34.js";
50
+ } from "./chunk-AHBZCBDO.js";
46
51
  import {
47
52
  BillingDashboard,
53
+ InfoPanel,
48
54
  PricingPage,
55
+ TemplateCard,
49
56
  UsageChart,
50
57
  formatPrice
51
- } from "./chunk-QMU2PWOU.js";
52
- import {
53
- StatCard
54
- } from "./chunk-OKLQVY3Y.js";
55
- import "./chunk-34A66VBG.js";
56
- import {
57
- Skeleton
58
- } from "./chunk-66EZOYZR.js";
59
- import "./chunk-ZMNSRDMH.js";
60
- import "./chunk-MKTSMWVD.js";
61
- import {
62
- cn
63
- } from "./chunk-RQHJBTEU.js";
64
-
65
- // src/dashboard/system-logs.tsx
66
- import { Terminal } from "lucide-react";
67
- import { useEffect, useRef, useState } from "react";
68
- import { jsx, jsxs } from "react/jsx-runtime";
69
- function SystemLogsViewer({ apiUrl, token, className }) {
70
- const [logs, setLogs] = useState([]);
71
- const [error, setError] = useState(null);
72
- const [isFollowing, setIsFollowing] = useState(true);
73
- const scrollRef = useRef(null);
74
- useEffect(() => {
75
- let timeoutId;
76
- let backoff = 2e3;
77
- const controller = new AbortController();
78
- async function fetchLogs() {
79
- try {
80
- const res = await fetch(`${apiUrl}/debug/logs`, {
81
- headers: {
82
- Authorization: `Bearer ${token}`
83
- },
84
- signal: controller.signal
85
- });
86
- if (!res.ok) throw new Error(`HTTP Error: ${res.status}`);
87
- const data = await res.json();
88
- if (!controller.signal.aborted) {
89
- setLogs(data.logs || []);
90
- setError(null);
91
- backoff = 2e3;
92
- }
93
- } catch (err) {
94
- if (err.name === "AbortError") return;
95
- if (!controller.signal.aborted) {
96
- setError(err instanceof Error ? err.message : "Failed to fetch logs");
97
- backoff = Math.min(backoff * 2, 3e4);
98
- }
99
- }
100
- if (!controller.signal.aborted) {
101
- timeoutId = setTimeout(fetchLogs, backoff);
102
- }
103
- }
104
- fetchLogs();
105
- return () => {
106
- controller.abort();
107
- clearTimeout(timeoutId);
108
- };
109
- }, [apiUrl, token]);
110
- useEffect(() => {
111
- if (isFollowing && scrollRef.current) {
112
- scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
113
- }
114
- }, [logs, isFollowing]);
115
- const handleScroll = () => {
116
- if (!scrollRef.current) return;
117
- const { scrollTop, scrollHeight, clientHeight } = scrollRef.current;
118
- const isAtBottom = scrollHeight - scrollTop - clientHeight < 20;
119
- setIsFollowing((prev) => prev === isAtBottom ? prev : isAtBottom);
120
- };
121
- return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col h-full bg-background text-foreground font-mono text-sm leading-relaxed overflow-hidden rounded-lg border border-border", className), children: [
122
- /* @__PURE__ */ jsxs("div", { className: "flex-none flex items-center justify-between border-b border-border bg-muted/50 backdrop-blur-md px-4 py-2", children: [
123
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
124
- /* @__PURE__ */ jsx(Terminal, { className: "h-4 w-4 text-primary animate-pulse" }),
125
- /* @__PURE__ */ jsx("span", { className: "font-bold text-xs uppercase tracking-widest text-muted-foreground", children: "System Traces" })
126
- ] }),
127
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
128
- error && /* @__PURE__ */ jsxs("span", { className: "text-destructive text-xs flex items-center gap-1", children: [
129
- /* @__PURE__ */ jsx("span", { className: "w-2 h-2 rounded-full bg-destructive animate-ping" }),
130
- " Error fetching logs"
131
- ] }),
132
- /* @__PURE__ */ jsx(
133
- "button",
134
- {
135
- onClick: () => {
136
- setIsFollowing(!isFollowing);
137
- if (!isFollowing && scrollRef.current) {
138
- scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
139
- }
140
- },
141
- className: cn("px-3 py-1 text-[10px] font-bold uppercase rounded-md transition-colors", isFollowing ? "bg-primary/20 text-primary border border-primary/20" : "bg-muted text-muted-foreground border border-border hover:bg-accent hover:text-foreground"),
142
- children: isFollowing ? "Auto-Scroll ON" : "Auto-Scroll OFF"
143
- }
144
- )
145
- ] })
146
- ] }),
147
- /* @__PURE__ */ jsx(
148
- "div",
149
- {
150
- ref: scrollRef,
151
- onScroll: handleScroll,
152
- className: "flex-1 overflow-y-auto p-4 space-y-1",
153
- children: logs.length === 0 && !error ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center text-muted-foreground italic", children: "Waiting for orchestrator logs..." }) : logs.map((log, i) => /* @__PURE__ */ jsxs("div", { className: "break-words", children: [
154
- /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground mr-3 select-none", children: [
155
- "[",
156
- log.timestamp || i.toString().padStart(4, "0"),
157
- "]"
158
- ] }),
159
- /* @__PURE__ */ jsxs("span", { className: "text-primary/70 mr-2", children: [
160
- "[",
161
- log.level,
162
- "]"
163
- ] }),
164
- /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground mr-2", children: [
165
- "[",
166
- log.scope,
167
- "]"
168
- ] }),
169
- /* @__PURE__ */ jsx("span", { className: log.level.toUpperCase() === "ERROR" || log.message.toLowerCase().includes("failed") ? "text-destructive" : log.level.toUpperCase() === "WARN" ? "text-warning" : "text-foreground", children: log.message })
170
- ] }, `${log.timestamp}-${log.scope}-${i}`))
171
- }
172
- )
173
- ] });
174
- }
175
-
176
- // src/dashboard/usage-summary.tsx
177
- import { Clock, Layers, MessageSquare, DollarSign } from "lucide-react";
178
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
179
- function UsageSummary({ data, loading = false, className }) {
180
- if (loading || !data) {
181
- return /* @__PURE__ */ jsx2("div", { className: cn("grid grid-cols-2 gap-4 lg:grid-cols-4", className), children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsx2(Skeleton, { className: "h-28 rounded-xl" }, i)) });
182
- }
183
- return /* @__PURE__ */ jsxs2("div", { className: cn("grid grid-cols-2 gap-4 lg:grid-cols-4", className), children: [
184
- /* @__PURE__ */ jsx2(
185
- StatCard,
186
- {
187
- variant: "sandbox",
188
- title: "Compute Hours",
189
- value: data.computeHours.toFixed(1),
190
- subtitle: "This billing period",
191
- icon: /* @__PURE__ */ jsx2(Clock, { className: "h-5 w-5" })
192
- }
193
- ),
194
- /* @__PURE__ */ jsx2(
195
- StatCard,
196
- {
197
- variant: "sandbox",
198
- title: "Active Sessions",
199
- value: data.activeSessions,
200
- subtitle: "Currently running",
201
- icon: /* @__PURE__ */ jsx2(Layers, { className: "h-5 w-5" })
202
- }
203
- ),
204
- /* @__PURE__ */ jsx2(
205
- StatCard,
206
- {
207
- variant: "sandbox",
208
- title: "Messages Sent",
209
- value: data.messagesSent.toLocaleString(),
210
- subtitle: "Agent interactions",
211
- icon: /* @__PURE__ */ jsx2(MessageSquare, { className: "h-5 w-5" })
212
- }
213
- ),
214
- /* @__PURE__ */ jsx2(
215
- StatCard,
216
- {
217
- variant: "sandbox",
218
- title: "Estimated Cost",
219
- value: `$${data.estimatedCost.toFixed(2)}`,
220
- subtitle: "This billing period",
221
- icon: /* @__PURE__ */ jsx2(DollarSign, { className: "h-5 w-5" })
222
- }
223
- )
224
- ] });
225
- }
226
-
227
- // src/dashboard/git-panel.tsx
228
- import { GitBranch, GitCommit, FileEdit, FilePlus, File } from "lucide-react";
229
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
230
- function GitPanel({ status, log, loading = false, onRefresh, className }) {
231
- if (loading) {
232
- return /* @__PURE__ */ jsx3("div", { className: cn("rounded-lg border border-border bg-card p-5", className), children: /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
233
- /* @__PURE__ */ jsx3("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-primary border-t-transparent" }),
234
- "Loading git info..."
235
- ] }) });
236
- }
237
- if (!status) {
238
- return /* @__PURE__ */ jsx3("div", { className: cn("rounded-lg border border-border bg-card p-5", className), children: /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
239
- /* @__PURE__ */ jsx3(GitBranch, { className: "h-4 w-4" }),
240
- "No git repository detected"
241
- ] }) });
242
- }
243
- const changedCount = status.staged.length + status.modified.length + status.untracked.length;
244
- return /* @__PURE__ */ jsxs3("div", { className: cn("rounded-lg border border-border bg-card overflow-hidden", className), children: [
245
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between border-b border-border bg-muted/30 px-4 py-3", children: [
246
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
247
- /* @__PURE__ */ jsx3(GitBranch, { className: "h-4 w-4 text-primary" }),
248
- /* @__PURE__ */ jsx3("span", { className: "text-sm font-bold text-foreground", children: status.branch }),
249
- status.isDirty && /* @__PURE__ */ jsxs3("span", { className: "rounded-full bg-[var(--surface-warning-bg)] px-1.5 py-0.5 text-[10px] font-bold text-[var(--surface-warning-text)]", children: [
250
- changedCount,
251
- " change",
252
- changedCount !== 1 ? "s" : ""
253
- ] })
254
- ] }),
255
- onRefresh && /* @__PURE__ */ jsx3("button", { type: "button", onClick: onRefresh, className: "text-xs text-muted-foreground hover:text-foreground transition-colors", children: "Refresh" })
256
- ] }),
257
- changedCount > 0 && /* @__PURE__ */ jsx3("div", { className: "border-b border-border px-4 py-3", children: /* @__PURE__ */ jsxs3("div", { className: "space-y-1.5", children: [
258
- status.staged.map((f) => /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 text-xs", children: [
259
- /* @__PURE__ */ jsx3(FilePlus, { className: "h-3 w-3 text-[var(--surface-success-text)]" }),
260
- /* @__PURE__ */ jsx3("span", { className: "font-mono text-foreground truncate", children: f }),
261
- /* @__PURE__ */ jsx3("span", { className: "text-[var(--surface-success-text)] text-[10px] font-bold ml-auto", children: "STAGED" })
262
- ] }, `s-${f}`)),
263
- status.modified.map((f) => /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 text-xs", children: [
264
- /* @__PURE__ */ jsx3(FileEdit, { className: "h-3 w-3 text-[var(--surface-warning-text)]" }),
265
- /* @__PURE__ */ jsx3("span", { className: "font-mono text-foreground truncate", children: f })
266
- ] }, `m-${f}`)),
267
- status.untracked.map((f) => /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 text-xs", children: [
268
- /* @__PURE__ */ jsx3(File, { className: "h-3 w-3 text-muted-foreground" }),
269
- /* @__PURE__ */ jsx3("span", { className: "font-mono text-muted-foreground truncate", children: f })
270
- ] }, `u-${f}`))
271
- ] }) }),
272
- log.length > 0 && /* @__PURE__ */ jsxs3("div", { className: "px-4 py-3", children: [
273
- /* @__PURE__ */ jsx3("div", { className: "mb-2 text-[10px] font-bold uppercase tracking-widest text-muted-foreground", children: "Recent Commits" }),
274
- /* @__PURE__ */ jsx3("div", { className: "space-y-2", children: log.slice(0, 5).map((commit) => /* @__PURE__ */ jsxs3("div", { className: "flex items-start gap-2 text-xs", children: [
275
- /* @__PURE__ */ jsx3(GitCommit, { className: "h-3 w-3 text-muted-foreground mt-0.5 shrink-0" }),
276
- /* @__PURE__ */ jsxs3("div", { className: "min-w-0", children: [
277
- /* @__PURE__ */ jsx3("span", { className: "font-mono text-primary mr-1.5", children: commit.shortSha }),
278
- /* @__PURE__ */ jsx3("span", { className: "text-foreground", children: commit.message })
279
- ] })
280
- ] }, commit.shortSha)) })
281
- ] }),
282
- (status.ahead > 0 || status.behind > 0) && /* @__PURE__ */ jsxs3("div", { className: "border-t border-border px-4 py-2 flex items-center gap-3 text-xs text-muted-foreground", children: [
283
- status.ahead > 0 && /* @__PURE__ */ jsxs3("span", { children: [
284
- "\u2191 ",
285
- status.ahead,
286
- " ahead"
287
- ] }),
288
- status.behind > 0 && /* @__PURE__ */ jsxs3("span", { children: [
289
- "\u2193 ",
290
- status.behind,
291
- " behind"
292
- ] })
293
- ] })
294
- ] });
295
- }
296
-
297
- // src/dashboard/ports-list.tsx
298
- import * as React2 from "react";
299
- import { Copy, Check, Globe, Plus, Trash2 } from "lucide-react";
300
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
301
- function PortsList({ ports, onExposePort, onRemovePort, isExposing = false, className }) {
302
- const [newPort, setNewPort] = React2.useState("");
303
- const [copiedPort, setCopiedPort] = React2.useState(null);
304
- const copyTimerRef = React2.useRef(null);
305
- React2.useEffect(() => {
306
- return () => {
307
- if (copyTimerRef.current) clearTimeout(copyTimerRef.current);
308
- };
309
- }, []);
310
- const handleCopy = async (url, port) => {
311
- try {
312
- await navigator.clipboard.writeText(url);
313
- setCopiedPort(port);
314
- if (copyTimerRef.current) clearTimeout(copyTimerRef.current);
315
- copyTimerRef.current = setTimeout(() => setCopiedPort(null), 2e3);
316
- } catch (err) {
317
- console.warn("Clipboard write failed:", err);
318
- }
319
- };
320
- const handleExpose = () => {
321
- const port = parseInt(newPort, 10);
322
- if (port > 0 && port <= 65535) {
323
- onExposePort(port);
324
- setNewPort("");
325
- }
326
- };
327
- return /* @__PURE__ */ jsxs4("div", { className: cn("space-y-4", className), children: [
328
- ports.length > 0 ? /* @__PURE__ */ jsx4("div", { className: "rounded-lg border border-border overflow-hidden", children: /* @__PURE__ */ jsxs4("table", { className: "w-full text-sm", children: [
329
- /* @__PURE__ */ jsx4("thead", { className: "bg-muted/30 border-b border-border", children: /* @__PURE__ */ jsxs4("tr", { children: [
330
- /* @__PURE__ */ jsx4("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Port" }),
331
- /* @__PURE__ */ jsx4("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Public URL" }),
332
- /* @__PURE__ */ jsx4("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Status" }),
333
- /* @__PURE__ */ jsx4("th", { className: "px-4 py-2.5 text-right text-xs font-medium text-muted-foreground w-20" })
334
- ] }) }),
335
- /* @__PURE__ */ jsx4("tbody", { className: "divide-y divide-border", children: ports.map((p) => /* @__PURE__ */ jsxs4("tr", { children: [
336
- /* @__PURE__ */ jsx4("td", { className: "px-4 py-3 font-mono text-xs text-foreground", children: p.port }),
337
- /* @__PURE__ */ jsx4("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsxs4(
338
- "button",
339
- {
340
- type: "button",
341
- onClick: () => handleCopy(p.url, p.port),
342
- className: "flex items-center gap-2 font-mono text-xs text-primary hover:underline cursor-pointer group",
343
- children: [
344
- /* @__PURE__ */ jsx4("span", { className: "truncate max-w-[300px]", children: p.url }),
345
- copiedPort === p.port ? /* @__PURE__ */ jsx4(Check, { className: "h-3 w-3 text-[var(--surface-success-text)] shrink-0" }) : /* @__PURE__ */ jsx4(Copy, { className: "h-3 w-3 opacity-0 group-hover:opacity-100 transition-opacity shrink-0" })
346
- ]
347
- }
348
- ) }),
349
- /* @__PURE__ */ jsx4("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx4("span", { className: cn(
350
- "inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider",
351
- p.status === "active" ? "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]" : "bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)]"
352
- ), children: p.status }) }),
353
- /* @__PURE__ */ jsx4("td", { className: "px-4 py-3 text-right", children: onRemovePort && /* @__PURE__ */ jsx4(
354
- "button",
355
- {
356
- type: "button",
357
- onClick: () => onRemovePort(p.port),
358
- className: "p-1 text-muted-foreground hover:text-destructive transition-colors rounded",
359
- children: /* @__PURE__ */ jsx4(Trash2, { className: "h-3.5 w-3.5" })
360
- }
361
- ) })
362
- ] }, p.port)) })
363
- ] }) }) : /* @__PURE__ */ jsxs4("div", { className: "rounded-lg border border-border bg-muted/20 p-6 text-center", children: [
364
- /* @__PURE__ */ jsx4(Globe, { className: "mx-auto h-8 w-8 text-muted-foreground mb-2" }),
365
- /* @__PURE__ */ jsx4("p", { className: "text-sm text-muted-foreground", children: "No ports exposed yet" })
366
- ] }),
367
- /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-3", children: [
368
- /* @__PURE__ */ jsx4(
369
- "input",
370
- {
371
- type: "number",
372
- min: 1,
373
- max: 65535,
374
- placeholder: "Port (e.g. 3000)",
375
- value: newPort,
376
- onChange: (e) => setNewPort(e.target.value),
377
- onKeyDown: (e) => e.key === "Enter" && handleExpose(),
378
- className: "flex-1 rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
379
- }
380
- ),
381
- /* @__PURE__ */ jsxs4(
382
- "button",
383
- {
384
- type: "button",
385
- onClick: handleExpose,
386
- disabled: !newPort || isExposing,
387
- className: "inline-flex items-center gap-2 rounded-lg bg-primary/20 border border-primary/30 px-4 py-2 text-sm font-medium text-primary hover:bg-primary hover:text-primary-foreground transition-colors disabled:opacity-50",
388
- children: [
389
- /* @__PURE__ */ jsx4(Plus, { className: "h-4 w-4" }),
390
- "Expose"
391
- ]
392
- }
393
- )
394
- ] })
395
- ] });
396
- }
397
-
398
- // src/dashboard/process-list.tsx
399
- import * as React3 from "react";
400
- import { Activity, Plus as Plus2, Skull, Terminal as Terminal2 } from "lucide-react";
401
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
402
- function formatUptime(startedAt) {
403
- if (!startedAt) return "-";
404
- const ms = Date.now() - new Date(startedAt).getTime();
405
- if (Number.isNaN(ms) || ms < 0) return "-";
406
- if (ms < 6e4) return `${Math.floor(ms / 1e3)}s`;
407
- if (ms < 36e5) return `${Math.floor(ms / 6e4)}m`;
408
- return `${Math.floor(ms / 36e5)}h ${Math.floor(ms % 36e5 / 6e4)}m`;
409
- }
410
- function ProcessList({ processes, onSpawn, onKill, loading = false, className }) {
411
- const [newCommand, setNewCommand] = React3.useState("");
412
- const handleSpawn = () => {
413
- const cmd = newCommand.trim();
414
- if (cmd) {
415
- onSpawn(cmd);
416
- setNewCommand("");
417
- }
418
- };
419
- return /* @__PURE__ */ jsxs5("div", { className: cn("space-y-4", className), children: [
420
- loading ? /* @__PURE__ */ jsxs5("div", { className: "rounded-lg border border-border bg-muted/20 p-6 text-center", children: [
421
- /* @__PURE__ */ jsx5(Activity, { className: "mx-auto h-6 w-6 text-muted-foreground animate-spin mb-2" }),
422
- /* @__PURE__ */ jsx5("p", { className: "text-sm text-muted-foreground", children: "Loading processes..." })
423
- ] }) : processes.length > 0 ? /* @__PURE__ */ jsx5("div", { className: "rounded-lg border border-border overflow-hidden", children: /* @__PURE__ */ jsxs5("table", { className: "w-full text-sm", children: [
424
- /* @__PURE__ */ jsx5("thead", { className: "bg-muted/30 border-b border-border", children: /* @__PURE__ */ jsxs5("tr", { children: [
425
- /* @__PURE__ */ jsx5("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "PID" }),
426
- /* @__PURE__ */ jsx5("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Command" }),
427
- /* @__PURE__ */ jsx5("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Status" }),
428
- /* @__PURE__ */ jsx5("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Uptime" }),
429
- /* @__PURE__ */ jsx5("th", { className: "px-4 py-2.5 text-right text-xs font-medium text-muted-foreground w-20" })
430
- ] }) }),
431
- /* @__PURE__ */ jsx5("tbody", { className: "divide-y divide-border", children: processes.map((p) => /* @__PURE__ */ jsxs5("tr", { children: [
432
- /* @__PURE__ */ jsx5("td", { className: "px-4 py-3 font-mono text-xs text-foreground", children: p.pid }),
433
- /* @__PURE__ */ jsx5("td", { className: "px-4 py-3 font-mono text-xs text-foreground truncate max-w-[250px]", children: p.command }),
434
- /* @__PURE__ */ jsx5("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx5("span", { className: cn(
435
- "inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider",
436
- p.running ? "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]" : "bg-muted text-muted-foreground"
437
- ), children: p.running ? "running" : `exited (${p.exitCode ?? "?"})` }) }),
438
- /* @__PURE__ */ jsx5("td", { className: "px-4 py-3 font-mono text-xs text-muted-foreground", children: formatUptime(p.startedAt) }),
439
- /* @__PURE__ */ jsx5("td", { className: "px-4 py-3 text-right", children: p.running && /* @__PURE__ */ jsx5(
440
- "button",
441
- {
442
- type: "button",
443
- onClick: () => onKill(p.pid),
444
- className: "p-1 text-muted-foreground hover:text-destructive transition-colors rounded",
445
- title: "Kill process",
446
- children: /* @__PURE__ */ jsx5(Skull, { className: "h-3.5 w-3.5" })
447
- }
448
- ) })
449
- ] }, `${p.pid}-${p.startedAt ?? p.command}`)) })
450
- ] }) }) : /* @__PURE__ */ jsxs5("div", { className: "rounded-lg border border-border bg-muted/20 p-6 text-center", children: [
451
- /* @__PURE__ */ jsx5(Terminal2, { className: "mx-auto h-8 w-8 text-muted-foreground mb-2" }),
452
- /* @__PURE__ */ jsx5("p", { className: "text-sm text-muted-foreground", children: "No processes running" })
453
- ] }),
454
- /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-3", children: [
455
- /* @__PURE__ */ jsx5(
456
- "input",
457
- {
458
- type: "text",
459
- placeholder: "Command (e.g. node server.js)",
460
- value: newCommand,
461
- onChange: (e) => setNewCommand(e.target.value),
462
- onKeyDown: (e) => e.key === "Enter" && handleSpawn(),
463
- className: "flex-1 rounded-lg border border-border bg-background px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
464
- }
465
- ),
466
- /* @__PURE__ */ jsxs5(
467
- "button",
468
- {
469
- type: "button",
470
- onClick: handleSpawn,
471
- disabled: !newCommand.trim(),
472
- className: "inline-flex items-center gap-2 rounded-lg bg-primary/20 border border-primary/30 px-4 py-2 text-sm font-medium text-primary hover:bg-primary hover:text-primary-foreground transition-colors disabled:opacity-50",
473
- children: [
474
- /* @__PURE__ */ jsx5(Plus2, { className: "h-4 w-4" }),
475
- "Spawn"
476
- ]
477
- }
478
- )
479
- ] })
480
- ] });
481
- }
482
-
483
- // src/dashboard/network-config.tsx
484
- import * as React4 from "react";
485
- import { Network, Plus as Plus3, Trash2 as Trash22, ShieldAlert } from "lucide-react";
486
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
487
- function NetworkConfig({ config, onUpdate, loading = false, className }) {
488
- const [newCidr, setNewCidr] = React4.useState("");
489
- const isValidCidr = (value) => {
490
- const match = value.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\/(\d{1,2})$/);
491
- if (!match) return false;
492
- const octets = [match[1], match[2], match[3], match[4]].map(Number);
493
- const prefix = Number(match[5]);
494
- return octets.every((o) => o <= 255) && prefix <= 32;
495
- };
496
- const handleAddCidr = () => {
497
- const cidr = newCidr.trim();
498
- if (cidr && config && isValidCidr(cidr) && !config.allowList.includes(cidr)) {
499
- onUpdate({ allowList: [...config.allowList, cidr] });
500
- setNewCidr("");
501
- }
502
- };
503
- const handleRemoveCidr = (cidr) => {
504
- if (config) {
505
- onUpdate({ allowList: config.allowList.filter((c) => c !== cidr) });
506
- }
507
- };
508
- if (loading || !config) {
509
- return /* @__PURE__ */ jsxs6("div", { className: cn("rounded-lg border border-border bg-muted/20 p-6 text-center", className), children: [
510
- /* @__PURE__ */ jsx6(Network, { className: "mx-auto h-6 w-6 text-muted-foreground animate-pulse mb-2" }),
511
- /* @__PURE__ */ jsx6("p", { className: "text-sm text-muted-foreground", children: "Loading network configuration..." })
512
- ] });
513
- }
514
- return /* @__PURE__ */ jsxs6("div", { className: cn("space-y-4", className), children: [
515
- /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between rounded-lg border border-border bg-card px-4 py-3", children: [
516
- /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-3", children: [
517
- /* @__PURE__ */ jsx6(ShieldAlert, { className: "h-4 w-4 text-muted-foreground" }),
518
- /* @__PURE__ */ jsxs6("div", { children: [
519
- /* @__PURE__ */ jsx6("p", { className: "text-sm font-medium text-foreground", children: "Block Outbound Traffic" }),
520
- /* @__PURE__ */ jsx6("p", { className: "text-xs text-muted-foreground", children: "Prevent the sandbox from making external network requests" })
521
- ] })
522
- ] }),
523
- /* @__PURE__ */ jsx6(
524
- "button",
525
- {
526
- type: "button",
527
- role: "switch",
528
- "aria-checked": config.blockOutbound,
529
- "aria-label": "Block outbound traffic",
530
- onClick: () => onUpdate({ blockOutbound: !config.blockOutbound }),
531
- className: cn(
532
- "relative inline-flex h-6 w-11 items-center rounded-full transition-colors",
533
- config.blockOutbound ? "bg-destructive" : "bg-muted"
534
- ),
535
- children: /* @__PURE__ */ jsx6(
536
- "span",
537
- {
538
- className: cn(
539
- "inline-block h-4 w-4 rounded-full bg-white transition-transform shadow-sm",
540
- config.blockOutbound ? "translate-x-6" : "translate-x-1"
541
- )
542
- }
543
- )
544
- }
545
- )
546
- ] }),
547
- /* @__PURE__ */ jsxs6("div", { children: [
548
- /* @__PURE__ */ jsx6("h4", { className: "text-xs font-medium text-muted-foreground mb-2 uppercase tracking-wider", children: "Allowlist (CIDR)" }),
549
- config.allowList.length > 0 ? /* @__PURE__ */ jsx6("div", { className: "space-y-1.5 mb-3", children: config.allowList.map((cidr) => /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between rounded border border-border bg-muted/20 px-3 py-2", children: [
550
- /* @__PURE__ */ jsx6("span", { className: "font-mono text-xs text-foreground", children: cidr }),
551
- /* @__PURE__ */ jsx6(
552
- "button",
553
- {
554
- type: "button",
555
- onClick: () => handleRemoveCidr(cidr),
556
- className: "p-1 text-muted-foreground hover:text-destructive transition-colors rounded",
557
- children: /* @__PURE__ */ jsx6(Trash22, { className: "h-3 w-3" })
558
- }
559
- )
560
- ] }, cidr)) }) : /* @__PURE__ */ jsx6("p", { className: "text-xs text-muted-foreground mb-3", children: "No CIDR rules configured. All traffic is allowed." }),
561
- /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-3", children: [
562
- /* @__PURE__ */ jsx6(
563
- "input",
564
- {
565
- type: "text",
566
- placeholder: "CIDR (e.g. 10.0.0.0/8)",
567
- value: newCidr,
568
- onChange: (e) => setNewCidr(e.target.value),
569
- onKeyDown: (e) => e.key === "Enter" && handleAddCidr(),
570
- className: "flex-1 rounded-lg border border-border bg-background px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
571
- }
572
- ),
573
- /* @__PURE__ */ jsxs6(
574
- "button",
575
- {
576
- type: "button",
577
- onClick: handleAddCidr,
578
- disabled: !newCidr.trim(),
579
- className: "inline-flex items-center gap-2 rounded-lg bg-primary/20 border border-primary/30 px-4 py-2 text-sm font-medium text-primary hover:bg-primary hover:text-primary-foreground transition-colors disabled:opacity-50",
580
- children: [
581
- /* @__PURE__ */ jsx6(Plus3, { className: "h-4 w-4" }),
582
- "Add"
583
- ]
584
- }
585
- )
586
- ] })
587
- ] })
588
- ] });
589
- }
590
-
591
- // src/dashboard/backend-config.tsx
592
- import * as React5 from "react";
593
- import { Bot, Plus as Plus4, RefreshCw, Trash2 as Trash23, Server, Wrench } from "lucide-react";
594
- import { Fragment, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
595
- function BackendConfig({
596
- status,
597
- mcpServers,
598
- onAddMcp,
599
- onRemoveMcp,
600
- onRestart,
601
- loading = false,
602
- className
603
- }) {
604
- const [showAddMcp, setShowAddMcp] = React5.useState(false);
605
- const [mcpName, setMcpName] = React5.useState("");
606
- const [mcpCommand, setMcpCommand] = React5.useState("");
607
- const [mcpArgs, setMcpArgs] = React5.useState("");
608
- const handleAddMcp = () => {
609
- const name = mcpName.trim();
610
- const command = mcpCommand.trim();
611
- if (name && command) {
612
- onAddMcp({
613
- name,
614
- command,
615
- args: mcpArgs.trim() ? mcpArgs.trim().split(/\s+/) : void 0
616
- });
617
- setMcpName("");
618
- setMcpCommand("");
619
- setMcpArgs("");
620
- setShowAddMcp(false);
621
- }
622
- };
623
- if (loading || !status) {
624
- return /* @__PURE__ */ jsxs7("div", { className: cn("rounded-lg border border-border bg-muted/20 p-6 text-center", className), children: [
625
- /* @__PURE__ */ jsx7(Bot, { className: "mx-auto h-6 w-6 text-muted-foreground animate-pulse mb-2" }),
626
- /* @__PURE__ */ jsx7("p", { className: "text-sm text-muted-foreground", children: "Loading backend status..." })
627
- ] });
628
- }
629
- return /* @__PURE__ */ jsxs7("div", { className: cn("space-y-4", className), children: [
630
- /* @__PURE__ */ jsxs7("div", { className: "rounded-lg border border-border bg-card overflow-hidden", children: [
631
- /* @__PURE__ */ jsxs7("div", { className: "px-4 py-3 border-b border-border bg-muted/30 flex items-center justify-between", children: [
632
- /* @__PURE__ */ jsx7("h4", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider", children: "Agent Status" }),
633
- /* @__PURE__ */ jsxs7(
634
- "button",
635
- {
636
- type: "button",
637
- onClick: onRestart,
638
- className: "inline-flex items-center gap-1.5 rounded-md bg-muted px-2.5 py-1 text-xs font-medium text-foreground hover:bg-muted/80 transition-colors border border-border",
639
- children: [
640
- /* @__PURE__ */ jsx7(RefreshCw, { className: "h-3 w-3" }),
641
- "Restart"
642
- ]
643
- }
644
- )
645
- ] }),
646
- /* @__PURE__ */ jsx7("div", { className: "p-4", children: /* @__PURE__ */ jsxs7("dl", { className: "grid grid-cols-[100px_1fr] gap-y-3 text-sm", children: [
647
- /* @__PURE__ */ jsx7("dt", { className: "text-muted-foreground", children: "Status" }),
648
- /* @__PURE__ */ jsx7("dd", { children: /* @__PURE__ */ jsx7("span", { className: cn(
649
- "inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider",
650
- status.running ? "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]" : "bg-destructive/10 text-destructive"
651
- ), children: status.running ? "Running" : "Stopped" }) }),
652
- /* @__PURE__ */ jsx7("dt", { className: "text-muted-foreground", children: "Model" }),
653
- /* @__PURE__ */ jsx7("dd", { className: "font-mono text-xs", children: status.model ?? "Default" }),
654
- status.provider && /* @__PURE__ */ jsxs7(Fragment, { children: [
655
- /* @__PURE__ */ jsx7("dt", { className: "text-muted-foreground", children: "Provider" }),
656
- /* @__PURE__ */ jsx7("dd", { className: "font-mono text-xs", children: status.provider })
657
- ] })
658
- ] }) })
659
- ] }),
660
- /* @__PURE__ */ jsxs7("div", { className: "rounded-lg border border-border bg-card overflow-hidden", children: [
661
- /* @__PURE__ */ jsxs7("div", { className: "px-4 py-3 border-b border-border bg-muted/30 flex items-center justify-between", children: [
662
- /* @__PURE__ */ jsxs7("h4", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider flex items-center gap-2", children: [
663
- /* @__PURE__ */ jsx7(Wrench, { className: "h-3.5 w-3.5" }),
664
- "MCP Servers"
665
- ] }),
666
- /* @__PURE__ */ jsxs7(
667
- "button",
668
- {
669
- type: "button",
670
- onClick: () => setShowAddMcp(!showAddMcp),
671
- className: "inline-flex items-center gap-1.5 rounded-md bg-primary/20 border border-primary/30 px-2.5 py-1 text-xs font-medium text-primary hover:bg-primary hover:text-primary-foreground transition-colors",
672
- children: [
673
- /* @__PURE__ */ jsx7(Plus4, { className: "h-3 w-3" }),
674
- "Add"
675
- ]
676
- }
677
- )
678
- ] }),
679
- mcpServers.length > 0 ? /* @__PURE__ */ jsx7("div", { className: "divide-y divide-border", children: mcpServers.map((s) => /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between px-4 py-3", children: [
680
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-3 min-w-0", children: [
681
- /* @__PURE__ */ jsx7(Server, { className: "h-3.5 w-3.5 text-muted-foreground shrink-0" }),
682
- /* @__PURE__ */ jsxs7("div", { className: "min-w-0", children: [
683
- /* @__PURE__ */ jsx7("p", { className: "text-sm font-medium text-foreground truncate", children: s.name }),
684
- /* @__PURE__ */ jsxs7("p", { className: "text-xs font-mono text-muted-foreground truncate", children: [
685
- s.command,
686
- " ",
687
- s.args?.join(" ") ?? ""
688
- ] })
689
- ] })
690
- ] }),
691
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2 shrink-0", children: [
692
- s.status && /* @__PURE__ */ jsx7("span", { className: cn(
693
- "inline-flex items-center rounded-full px-1.5 py-0.5 text-[9px] font-bold uppercase",
694
- s.status === "running" ? "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]" : s.status === "error" ? "bg-destructive/10 text-destructive" : "bg-muted text-muted-foreground"
695
- ), children: s.status }),
696
- /* @__PURE__ */ jsx7(
697
- "button",
698
- {
699
- type: "button",
700
- onClick: () => onRemoveMcp(s.name),
701
- className: "p-1 text-muted-foreground hover:text-destructive transition-colors rounded",
702
- children: /* @__PURE__ */ jsx7(Trash23, { className: "h-3.5 w-3.5" })
703
- }
704
- )
705
- ] })
706
- ] }, s.name)) }) : /* @__PURE__ */ jsx7("div", { className: "p-4 text-center", children: /* @__PURE__ */ jsx7("p", { className: "text-xs text-muted-foreground", children: "No MCP servers configured" }) }),
707
- showAddMcp && /* @__PURE__ */ jsxs7("div", { className: "p-4 border-t border-border bg-muted/10 space-y-2", children: [
708
- /* @__PURE__ */ jsx7(
709
- "input",
710
- {
711
- type: "text",
712
- placeholder: "Server name",
713
- value: mcpName,
714
- onChange: (e) => setMcpName(e.target.value),
715
- className: "w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
716
- }
717
- ),
718
- /* @__PURE__ */ jsx7(
719
- "input",
720
- {
721
- type: "text",
722
- placeholder: "Command (e.g. npx @server/mcp)",
723
- value: mcpCommand,
724
- onChange: (e) => setMcpCommand(e.target.value),
725
- className: "w-full rounded-lg border border-border bg-background px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
726
- }
727
- ),
728
- /* @__PURE__ */ jsx7(
729
- "input",
730
- {
731
- type: "text",
732
- placeholder: "Arguments (space-separated, optional)",
733
- value: mcpArgs,
734
- onChange: (e) => setMcpArgs(e.target.value),
735
- className: "w-full rounded-lg border border-border bg-background px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
736
- }
737
- ),
738
- /* @__PURE__ */ jsxs7("div", { className: "flex justify-end gap-2 pt-1", children: [
739
- /* @__PURE__ */ jsx7(
740
- "button",
741
- {
742
- type: "button",
743
- onClick: () => setShowAddMcp(false),
744
- className: "rounded-md px-3 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors",
745
- children: "Cancel"
746
- }
747
- ),
748
- /* @__PURE__ */ jsx7(
749
- "button",
750
- {
751
- type: "button",
752
- onClick: handleAddMcp,
753
- disabled: !mcpName.trim() || !mcpCommand.trim(),
754
- className: "rounded-md bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 transition-colors disabled:opacity-50",
755
- children: "Add Server"
756
- }
757
- )
758
- ] })
759
- ] })
760
- ] })
761
- ] });
762
- }
763
-
764
- // src/dashboard/snapshot-list.tsx
765
- import * as React6 from "react";
766
- import { Camera, Clock as Clock2, HardDrive, Plus as Plus5, RotateCcw } from "lucide-react";
767
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
768
- function formatBytes(bytes) {
769
- if (bytes == null || bytes < 0) return "-";
770
- if (bytes === 0) return "0 B";
771
- const k = 1024;
772
- const sizes = ["B", "KB", "MB", "GB", "TB"];
773
- const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), sizes.length - 1);
774
- return `${Number.parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;
775
- }
776
- function formatDate(dateStr) {
777
- const d = new Date(dateStr);
778
- if (Number.isNaN(d.getTime())) return dateStr;
779
- return d.toLocaleDateString(void 0, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
780
- }
781
- function SnapshotList({ snapshots, onCreate, onRestore, onSaveAsTemplate, loading = false, className }) {
782
- const [showCreate, setShowCreate] = React6.useState(false);
783
- const [tags, setTags] = React6.useState("");
784
- const handleCreate = () => {
785
- const tagList = tags.trim() ? tags.trim().split(",").map((t) => t.trim()).filter(Boolean) : void 0;
786
- onCreate(tagList);
787
- setTags("");
788
- setShowCreate(false);
789
- };
790
- return /* @__PURE__ */ jsxs8("div", { className: cn("space-y-4", className), children: [
791
- /* @__PURE__ */ jsxs8("div", { className: "flex items-center justify-between", children: [
792
- /* @__PURE__ */ jsx8("h3", { className: "text-sm font-bold text-foreground", children: "Snapshots" }),
793
- /* @__PURE__ */ jsxs8(
794
- "button",
795
- {
796
- type: "button",
797
- onClick: () => setShowCreate(!showCreate),
798
- className: "inline-flex items-center gap-1.5 rounded-lg bg-primary/20 border border-primary/30 px-3 py-1.5 text-xs font-medium text-primary hover:bg-primary hover:text-primary-foreground transition-colors",
799
- children: [
800
- /* @__PURE__ */ jsx8(Plus5, { className: "h-3.5 w-3.5" }),
801
- "Create Snapshot"
802
- ]
803
- }
804
- )
805
- ] }),
806
- showCreate && /* @__PURE__ */ jsxs8("div", { className: "rounded-lg border border-primary/20 bg-primary/5 p-4 space-y-3", children: [
807
- /* @__PURE__ */ jsx8("p", { className: "text-sm text-foreground font-medium", children: "Create a new snapshot of the current sandbox state." }),
808
- /* @__PURE__ */ jsx8(
809
- "input",
810
- {
811
- type: "text",
812
- placeholder: "Tags (comma-separated, optional)",
813
- value: tags,
814
- onChange: (e) => setTags(e.target.value),
815
- onKeyDown: (e) => e.key === "Enter" && handleCreate(),
816
- className: "w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
817
- }
818
- ),
819
- /* @__PURE__ */ jsxs8("div", { className: "flex justify-end gap-2", children: [
820
- /* @__PURE__ */ jsx8(
821
- "button",
822
- {
823
- type: "button",
824
- onClick: () => setShowCreate(false),
825
- className: "rounded-md px-3 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors",
826
- children: "Cancel"
827
- }
828
- ),
829
- /* @__PURE__ */ jsxs8(
830
- "button",
831
- {
832
- type: "button",
833
- onClick: handleCreate,
834
- className: "rounded-md bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 transition-colors",
835
- children: [
836
- /* @__PURE__ */ jsx8(Camera, { className: "h-3 w-3 mr-1.5 inline" }),
837
- "Create"
838
- ]
839
- }
840
- )
841
- ] })
842
- ] }),
843
- loading ? /* @__PURE__ */ jsxs8("div", { className: "rounded-lg border border-border bg-muted/20 p-6 text-center", children: [
844
- /* @__PURE__ */ jsx8(Camera, { className: "mx-auto h-6 w-6 text-muted-foreground animate-pulse mb-2" }),
845
- /* @__PURE__ */ jsx8("p", { className: "text-sm text-muted-foreground", children: "Loading snapshots..." })
846
- ] }) : snapshots.length > 0 ? /* @__PURE__ */ jsx8("div", { className: "rounded-lg border border-border overflow-hidden", children: /* @__PURE__ */ jsxs8("table", { className: "w-full text-sm", children: [
847
- /* @__PURE__ */ jsx8("thead", { className: "bg-muted/30 border-b border-border", children: /* @__PURE__ */ jsxs8("tr", { children: [
848
- /* @__PURE__ */ jsx8("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "ID" }),
849
- /* @__PURE__ */ jsx8("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Created" }),
850
- /* @__PURE__ */ jsx8("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Size" }),
851
- /* @__PURE__ */ jsx8("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Tags" }),
852
- /* @__PURE__ */ jsx8("th", { className: "px-4 py-2.5 text-right text-xs font-medium text-muted-foreground w-24" })
853
- ] }) }),
854
- /* @__PURE__ */ jsx8("tbody", { className: "divide-y divide-border", children: snapshots.map((s) => /* @__PURE__ */ jsxs8("tr", { children: [
855
- /* @__PURE__ */ jsx8("td", { className: "px-4 py-3 font-mono text-xs text-foreground", children: s.id.slice(0, 12) }),
856
- /* @__PURE__ */ jsx8("td", { className: "px-4 py-3 text-xs text-muted-foreground", children: /* @__PURE__ */ jsxs8("span", { className: "inline-flex items-center gap-1.5", children: [
857
- /* @__PURE__ */ jsx8(Clock2, { className: "h-3 w-3" }),
858
- formatDate(s.createdAt)
859
- ] }) }),
860
- /* @__PURE__ */ jsx8("td", { className: "px-4 py-3 text-xs text-muted-foreground", children: /* @__PURE__ */ jsxs8("span", { className: "inline-flex items-center gap-1.5", children: [
861
- /* @__PURE__ */ jsx8(HardDrive, { className: "h-3 w-3" }),
862
- formatBytes(s.sizeBytes)
863
- ] }) }),
864
- /* @__PURE__ */ jsx8("td", { className: "px-4 py-3", children: s.tags?.length ? /* @__PURE__ */ jsx8("div", { className: "flex items-center gap-1 flex-wrap", children: s.tags.map((tag) => /* @__PURE__ */ jsx8("span", { className: "rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground border border-border", children: tag }, tag)) }) : /* @__PURE__ */ jsx8("span", { className: "text-xs text-muted-foreground", children: "-" }) }),
865
- /* @__PURE__ */ jsx8("td", { className: "px-4 py-3 text-right", children: /* @__PURE__ */ jsxs8("div", { className: "flex items-center justify-end gap-2", children: [
866
- /* @__PURE__ */ jsxs8(
867
- "button",
868
- {
869
- type: "button",
870
- onClick: () => onRestore(s.id),
871
- className: "inline-flex items-center gap-1.5 rounded-md bg-muted px-2.5 py-1 text-xs font-medium text-foreground hover:bg-muted/80 transition-colors border border-border",
872
- title: "Restore to new sandbox",
873
- children: [
874
- /* @__PURE__ */ jsx8(RotateCcw, { className: "h-3 w-3" }),
875
- "Restore"
876
- ]
877
- }
878
- ),
879
- onSaveAsTemplate && /* @__PURE__ */ jsx8(
880
- "button",
881
- {
882
- type: "button",
883
- onClick: () => onSaveAsTemplate(s.id),
884
- className: "inline-flex items-center gap-1.5 rounded-md bg-primary/10 px-2.5 py-1 text-xs font-medium text-primary hover:bg-primary/20 transition-colors border border-primary/20",
885
- title: "Save as reusable template",
886
- children: "Save as Template"
887
- }
888
- )
889
- ] }) })
890
- ] }, s.id)) })
891
- ] }) }) : /* @__PURE__ */ jsxs8("div", { className: "rounded-lg border border-border bg-muted/20 p-6 text-center", children: [
892
- /* @__PURE__ */ jsx8(Camera, { className: "mx-auto h-8 w-8 text-muted-foreground mb-2" }),
893
- /* @__PURE__ */ jsx8("p", { className: "text-sm text-muted-foreground", children: "No snapshots yet" }),
894
- /* @__PURE__ */ jsx8("p", { className: "text-xs text-muted-foreground mt-1", children: "Create a snapshot to save the current state of your sandbox." })
895
- ] })
896
- ] });
897
- }
898
-
899
- // src/dashboard/promo-banner.tsx
900
- import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
901
- function PromoBanner({
902
- title,
903
- description,
904
- buttonLabel,
905
- href,
906
- onClick,
907
- icon,
908
- disabled = false,
909
- className
910
- }) {
911
- const buttonClasses = cn(
912
- "mt-6 inline-flex items-center gap-2 rounded-md border border-white/20 bg-[var(--btn-primary-bg)] px-4 py-2 text-sm font-medium text-[var(--btn-primary-text)] transition-colors",
913
- disabled ? "opacity-50 cursor-not-allowed" : "hover:bg-[var(--btn-primary-hover)]"
914
- );
915
- const buttonContent = /* @__PURE__ */ jsxs9(Fragment2, { children: [
916
- buttonLabel,
917
- /* @__PURE__ */ jsxs9("svg", { "aria-hidden": "true", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "h-4 w-4", children: [
918
- /* @__PURE__ */ jsx9("path", { d: "M5 12h14" }),
919
- /* @__PURE__ */ jsx9("path", { d: "m12 5 7 7-7 7" })
920
- ] })
921
- ] });
922
- return /* @__PURE__ */ jsxs9("div", { className: cn("relative overflow-hidden rounded-xl bg-[var(--brand-strong)] p-8 md:flex md:items-center md:justify-between", className), children: [
923
- /* @__PURE__ */ jsxs9("div", { className: "relative z-10", children: [
924
- /* @__PURE__ */ jsx9("h3", { className: "text-xl font-bold text-[var(--brand-strong-text)]", children: title }),
925
- /* @__PURE__ */ jsx9("p", { className: "mt-2 max-w-md text-sm text-[var(--brand-strong-text-muted)]", children: description }),
926
- href && !disabled ? /* @__PURE__ */ jsx9("a", { href, target: "_blank", rel: "noopener noreferrer", onClick, className: buttonClasses, children: buttonContent }) : /* @__PURE__ */ jsx9("button", { type: "button", onClick, disabled, className: buttonClasses, children: buttonContent })
927
- ] }),
928
- icon && /* @__PURE__ */ jsx9("div", { className: "relative z-10 mt-6 flex items-center md:mt-0", children: /* @__PURE__ */ jsx9("div", { className: "flex h-16 w-16 items-center justify-center rounded-2xl border border-white/10 bg-white/10", children: icon }) }),
929
- /* @__PURE__ */ jsx9("div", { className: "pointer-events-none absolute inset-y-0 right-0 w-1/3 bg-gradient-to-l from-white/5 to-transparent" })
930
- ] });
931
- }
58
+ } from "./chunk-DNZ4DTNA.js";
59
+ import "./chunk-7ZA5SEK3.js";
60
+ import "./chunk-EI44GEQ5.js";
932
61
  export {
933
62
  BackendConfig,
934
63
  BackendSelector,