@opengeni/react 0.12.0 → 0.15.0

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 (94) hide show
  1. package/README.md +20 -14
  2. package/dist/chunk-TOJR776I.js +2280 -0
  3. package/dist/chunk-TOJR776I.js.map +1 -0
  4. package/dist/index.d.ts +1040 -363
  5. package/dist/index.js +10938 -6281
  6. package/dist/index.js.map +1 -1
  7. package/dist/{machines-BeZ3bD3t.d.ts → machines-BpdwuQcD.d.ts} +134 -14
  8. package/dist/machines.d.ts +1 -1
  9. package/dist/machines.js +23 -1
  10. package/package.json +20 -12
  11. package/src/approvals.ts +16 -4
  12. package/src/client.ts +41 -5
  13. package/src/commands/index.ts +1 -7
  14. package/src/commands/registry.ts +46 -12
  15. package/src/components/chat-composer.tsx +417 -69
  16. package/src/components/code-editor.tsx +28 -36
  17. package/src/components/command-palette.tsx +26 -5
  18. package/src/components/desktop-viewer.tsx +29 -16
  19. package/src/components/diff-view.tsx +27 -189
  20. package/src/components/enrollment-consent.tsx +28 -10
  21. package/src/components/enrollment-device-flow.tsx +8 -5
  22. package/src/components/file-browser.tsx +190 -56
  23. package/src/components/fleet-tile.tsx +13 -4
  24. package/src/components/machine-card.tsx +93 -15
  25. package/src/components/machine-dock-bar.tsx +20 -8
  26. package/src/components/machine-health-pill.tsx +68 -0
  27. package/src/components/machine-metrics.tsx +28 -19
  28. package/src/components/machine-status-pill.tsx +26 -5
  29. package/src/components/machines/health.ts +146 -0
  30. package/src/components/machines/machine-detail.tsx +220 -0
  31. package/src/components/machines/metric-history-chart.tsx +298 -0
  32. package/src/components/machines/metric-sparkline.tsx +76 -0
  33. package/src/components/machines/series.ts +113 -0
  34. package/src/components/machines-dashboard.tsx +21 -3
  35. package/src/components/markdown.tsx +39 -9
  36. package/src/components/message-timeline.tsx +633 -109
  37. package/src/components/pierre-diff.tsx +83 -15
  38. package/src/components/pierre-file.tsx +10 -9
  39. package/src/components/queue-surface.tsx +578 -0
  40. package/src/components/sandbox-files.tsx +193 -225
  41. package/src/components/sandbox-terminal.tsx +256 -88
  42. package/src/components/sandbox-workspace.tsx +957 -0
  43. package/src/components/session-status.tsx +22 -2
  44. package/src/components/workbench-changes.tsx +585 -0
  45. package/src/components/workspace-dock.tsx +213 -59
  46. package/src/hooks/internal.ts +5 -2
  47. package/src/hooks/use-available-models.ts +7 -2
  48. package/src/hooks/use-billing-usage.ts +4 -1
  49. package/src/hooks/use-codex-accounts.ts +74 -44
  50. package/src/hooks/use-composer.ts +414 -63
  51. package/src/hooks/use-environments.ts +92 -47
  52. package/src/hooks/use-file-attachments.ts +101 -55
  53. package/src/hooks/use-goal.ts +52 -16
  54. package/src/hooks/use-machine-chip.ts +134 -0
  55. package/src/hooks/use-machines.ts +34 -16
  56. package/src/hooks/use-packs.ts +24 -19
  57. package/src/hooks/use-relay-frame-stream.ts +5 -2
  58. package/src/hooks/use-rigs.ts +335 -0
  59. package/src/hooks/use-sandbox-files.ts +213 -39
  60. package/src/hooks/use-sandbox-git.ts +188 -11
  61. package/src/hooks/use-sandbox-terminal.ts +18 -14
  62. package/src/hooks/use-scheduled-tasks.ts +10 -2
  63. package/src/hooks/use-session-capabilities.ts +77 -20
  64. package/src/hooks/use-session-control.ts +50 -23
  65. package/src/hooks/use-session-events.ts +50 -21
  66. package/src/hooks/use-session-lineage.ts +128 -0
  67. package/src/hooks/use-session.ts +53 -30
  68. package/src/hooks/use-slash-commands.ts +6 -4
  69. package/src/hooks/use-turn-queue.ts +202 -147
  70. package/src/hooks/use-windowed-sections.ts +204 -0
  71. package/src/hooks/use-workspace-capture.ts +233 -0
  72. package/src/hooks/use-workspace-edit.ts +231 -0
  73. package/src/hooks/use-workspace-sessions.ts +48 -5
  74. package/src/hooks/use-workspaces.ts +18 -15
  75. package/src/index.ts +133 -23
  76. package/src/lib/format.ts +3 -3
  77. package/src/lib/xterm-renderer.ts +65 -0
  78. package/src/lib/xterm-theme.ts +224 -18
  79. package/src/machines.ts +29 -3
  80. package/src/provider.tsx +195 -6
  81. package/src/timeline/activity-rail.tsx +213 -54
  82. package/src/timeline/disclosure-context.tsx +12 -2
  83. package/src/timeline/index.ts +21 -2
  84. package/src/timeline/parsers.ts +87 -16
  85. package/src/timeline/projection.ts +412 -87
  86. package/src/timeline/shared.tsx +111 -24
  87. package/src/timeline/tool-diff.tsx +24 -20
  88. package/src/timeline/tool-renderers.tsx +98 -21
  89. package/src/timeline/turn-summary.tsx +76 -23
  90. package/src/timeline/types.ts +96 -12
  91. package/styles/index.css +22 -0
  92. package/styles/tokens.css +2 -2
  93. package/dist/chunk-5TIXRCSI.js +0 -1291
  94. package/dist/chunk-5TIXRCSI.js.map +0 -1
@@ -0,0 +1,2280 @@
1
+ // src/types/machines.ts
2
+ function connectionStatusForState(state) {
3
+ switch (state) {
4
+ case "online":
5
+ case "consent_required":
6
+ case "display_unavailable":
7
+ return "online";
8
+ case "reconnecting":
9
+ case "enrolling":
10
+ return "reconnecting";
11
+ case "offline":
12
+ return "offline";
13
+ default: {
14
+ const _never = state;
15
+ return _never;
16
+ }
17
+ }
18
+ }
19
+
20
+ // src/lib/cn.ts
21
+ import { clsx } from "clsx";
22
+ import { extendTailwindMerge } from "tailwind-merge";
23
+ var twMerge = extendTailwindMerge({
24
+ extend: {
25
+ classGroups: {
26
+ "font-size": [{ text: ["og-xs", "og-sm", "og-base", "og-md"] }]
27
+ }
28
+ }
29
+ });
30
+ function cn(...inputs) {
31
+ return twMerge(clsx(inputs));
32
+ }
33
+
34
+ // src/components/machine-status-pill.tsx
35
+ import { jsx, jsxs } from "react/jsx-runtime";
36
+ var CONNECTION_STATUS_META = {
37
+ online: {
38
+ label: "Online",
39
+ dotClassName: "bg-og-status-running",
40
+ badgeClassName: "text-og-status-running border-og-status-running/30 bg-og-status-running/10",
41
+ pulse: false
42
+ },
43
+ reconnecting: {
44
+ label: "Reconnecting",
45
+ dotClassName: "bg-og-status-waiting",
46
+ badgeClassName: "text-og-status-waiting border-og-status-waiting/35 bg-og-status-waiting/10",
47
+ pulse: true
48
+ },
49
+ offline: {
50
+ label: "Offline",
51
+ dotClassName: "bg-og-status-failed",
52
+ badgeClassName: "text-og-fg-subtle border-og-border bg-og-status-failed/10",
53
+ pulse: false
54
+ }
55
+ };
56
+ var MACHINE_STATE_BADGE_META = {
57
+ consent_required: {
58
+ label: "Consent required",
59
+ badgeClassName: "text-og-status-waiting border-og-status-waiting/35 bg-og-status-waiting/10"
60
+ },
61
+ display_unavailable: {
62
+ label: "No display",
63
+ badgeClassName: "text-og-fg-muted border-og-border bg-og-surface-2"
64
+ },
65
+ enrolling: {
66
+ label: "Enrolling",
67
+ badgeClassName: "text-og-accent border-og-accent/30 bg-og-accent-soft"
68
+ }
69
+ };
70
+ function ConnectionStatusPill({
71
+ status,
72
+ label,
73
+ size = "md",
74
+ className
75
+ }) {
76
+ const meta = CONNECTION_STATUS_META[status];
77
+ return /* @__PURE__ */ jsxs(
78
+ "span",
79
+ {
80
+ "data-connection-status": status,
81
+ className: cn(
82
+ "og-root inline-flex shrink-0 items-center rounded-full border font-medium",
83
+ size === "sm" ? "gap-1 px-1.5 py-px text-og-xs" : "gap-1.5 px-2 py-0.5 text-og-sm",
84
+ meta.badgeClassName,
85
+ className
86
+ ),
87
+ children: [
88
+ /* @__PURE__ */ jsx(ConnectionDot, { status, className: size === "sm" ? "size-1" : "size-1.5" }),
89
+ label ?? meta.label
90
+ ]
91
+ }
92
+ );
93
+ }
94
+ function ConnectionDot({ status, className }) {
95
+ const meta = CONNECTION_STATUS_META[status];
96
+ return /* @__PURE__ */ jsx(
97
+ "span",
98
+ {
99
+ className: cn(
100
+ "relative inline-flex size-1.5 shrink-0 rounded-full",
101
+ meta.dotClassName,
102
+ className
103
+ ),
104
+ children: meta.pulse ? /* @__PURE__ */ jsx("span", { className: cn("absolute inset-0 animate-og-pulse rounded-full", meta.dotClassName) }) : null
105
+ }
106
+ );
107
+ }
108
+ function MachineStatusPill({
109
+ state,
110
+ sharedSessionCount,
111
+ size = "md",
112
+ className
113
+ }) {
114
+ const stateBadge = MACHINE_STATE_BADGE_META[state];
115
+ const shared = (sharedSessionCount ?? 0) > 1;
116
+ return /* @__PURE__ */ jsxs(
117
+ "span",
118
+ {
119
+ className: cn("og-root inline-flex flex-wrap items-center gap-1", className),
120
+ "data-machine-state": state,
121
+ children: [
122
+ /* @__PURE__ */ jsx(ConnectionStatusPill, { status: connectionStatusForState(state), size }),
123
+ stateBadge ? /* @__PURE__ */ jsx(
124
+ "span",
125
+ {
126
+ "data-state-badge": state,
127
+ className: cn(
128
+ "inline-flex shrink-0 items-center rounded-full border font-medium",
129
+ size === "sm" ? "px-1.5 py-px text-og-xs" : "px-2 py-0.5 text-og-sm",
130
+ stateBadge.badgeClassName
131
+ ),
132
+ children: stateBadge.label
133
+ }
134
+ ) : null,
135
+ shared ? /* @__PURE__ */ jsxs(
136
+ "span",
137
+ {
138
+ "data-shared-chip": true,
139
+ className: cn(
140
+ "inline-flex shrink-0 items-center gap-1 rounded-full border font-medium",
141
+ "text-og-accent border-og-accent/30 bg-og-accent-soft",
142
+ size === "sm" ? "px-1.5 py-px text-og-xs" : "px-2 py-0.5 text-og-sm"
143
+ ),
144
+ title: `${sharedSessionCount} sessions are on this machine`,
145
+ children: [
146
+ "Shared \xB7 ",
147
+ sharedSessionCount
148
+ ]
149
+ }
150
+ ) : null
151
+ ]
152
+ }
153
+ );
154
+ }
155
+
156
+ // src/lib/format.ts
157
+ function formatRelativeTime(iso, now = /* @__PURE__ */ new Date()) {
158
+ const then = new Date(iso).getTime();
159
+ if (Number.isNaN(then)) {
160
+ return "";
161
+ }
162
+ const seconds = Math.max(0, Math.floor((now.getTime() - then) / 1e3));
163
+ if (seconds < 10) {
164
+ return "now";
165
+ }
166
+ if (seconds < 60) {
167
+ return `${seconds}s`;
168
+ }
169
+ const minutes = Math.floor(seconds / 60);
170
+ if (minutes < 60) {
171
+ return `${minutes}m`;
172
+ }
173
+ const hours = Math.floor(minutes / 60);
174
+ if (hours < 24) {
175
+ return `${hours}h`;
176
+ }
177
+ const days = Math.floor(hours / 24);
178
+ if (days < 14) {
179
+ return `${days}d`;
180
+ }
181
+ return new Date(iso).toLocaleDateString();
182
+ }
183
+ function formatBytes(bytes) {
184
+ if (bytes < 1024) {
185
+ return `${bytes} B`;
186
+ }
187
+ const units = ["KB", "MB", "GB"];
188
+ let value = bytes / 1024;
189
+ for (const unit of units) {
190
+ if (value < 1024 || unit === "GB") {
191
+ return `${value.toFixed(value < 10 ? 1 : 0)} ${unit}`;
192
+ }
193
+ value /= 1024;
194
+ }
195
+ return `${bytes} B`;
196
+ }
197
+ function truncate(text, maxLength) {
198
+ const collapsed = text.replace(/\s+/g, " ").trim();
199
+ if (collapsed.length <= maxLength) {
200
+ return collapsed;
201
+ }
202
+ return `${collapsed.slice(0, Math.max(0, maxLength - 1)).trimEnd()}\u2026`;
203
+ }
204
+ function stringifyPayload(value) {
205
+ if (value === null || value === void 0) {
206
+ return "";
207
+ }
208
+ if (typeof value === "string") {
209
+ const parsed = tryParseJson(value);
210
+ if (parsed !== void 0 && typeof parsed === "object") {
211
+ return stringifyPayload(parsed);
212
+ }
213
+ return value;
214
+ }
215
+ try {
216
+ return JSON.stringify(value, null, 2) ?? String(value);
217
+ } catch {
218
+ return String(value);
219
+ }
220
+ }
221
+ function tryParseJson(text) {
222
+ const trimmed = text.trim();
223
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[") && !trimmed.startsWith('"')) {
224
+ return void 0;
225
+ }
226
+ try {
227
+ return JSON.parse(trimmed);
228
+ } catch {
229
+ return void 0;
230
+ }
231
+ }
232
+ var CREDIT_EXHAUSTION_MESSAGE = "Out of OpenGeni credits \u2014 this workspace's balance is empty. Add credits to continue; the conversation is preserved.";
233
+ function isCreditExhaustion(input) {
234
+ if (typeof input === "string") {
235
+ return input.toLowerCase().includes("insufficient opengeni credits");
236
+ }
237
+ if (input.segmentLimit === "budget_exhausted") {
238
+ return true;
239
+ }
240
+ for (const text of [input.error, input.detail]) {
241
+ if (typeof text === "string" && text.toLowerCase().includes("insufficient opengeni credits")) {
242
+ return true;
243
+ }
244
+ }
245
+ return false;
246
+ }
247
+ function humanizeFailureReason(reason) {
248
+ if (!reason) {
249
+ return reason;
250
+ }
251
+ if (isCreditExhaustion(reason)) {
252
+ return CREDIT_EXHAUSTION_MESSAGE;
253
+ }
254
+ const normalized = reason.toLowerCase();
255
+ const authFailure = normalized.includes("incorrect api key") || normalized.includes("invalid api key") || normalized.includes("invalid_api_key") || normalized.includes("platform.openai.com/account/api-keys") || normalized.includes("401") && (normalized.includes("api key") || normalized.includes("unauthorized"));
256
+ if (authFailure) {
257
+ return "The model provider rejected this deployment's engine credentials. Sending messages won't help until the deployment's engine configuration is fixed.";
258
+ }
259
+ const quotaFailure = normalized.includes("insufficient_quota") || normalized.includes("exceeded your current quota");
260
+ if (quotaFailure) {
261
+ return "The model provider refused the request: this deployment's provider quota is exhausted.";
262
+ }
263
+ return reason;
264
+ }
265
+
266
+ // src/components/machine-metrics.tsx
267
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
268
+ function pct(value) {
269
+ if (!Number.isFinite(value)) return 0;
270
+ return Math.max(0, Math.min(100, value));
271
+ }
272
+ function Meter({
273
+ label,
274
+ value,
275
+ fillPct,
276
+ tone
277
+ }) {
278
+ const fillClass = tone === "hot" ? "bg-og-status-failed" : tone === "warn" ? "bg-og-status-running" : "bg-og-status-idle";
279
+ return /* @__PURE__ */ jsxs2("div", { className: "flex min-w-0 flex-col gap-1", "data-metric": label.toLowerCase(), children: [
280
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-baseline justify-between gap-2", children: [
281
+ /* @__PURE__ */ jsx2("span", { className: "text-[10px] font-medium uppercase tracking-wide text-og-fg-subtle", children: label }),
282
+ /* @__PURE__ */ jsx2("span", { className: "font-og-mono text-[11px] tabular-nums text-og-fg-muted", children: value })
283
+ ] }),
284
+ /* @__PURE__ */ jsx2("div", { className: "h-1 w-full overflow-hidden rounded-full bg-og-surface-2", children: /* @__PURE__ */ jsx2(
285
+ "div",
286
+ {
287
+ className: cn("h-full rounded-full transition-[width] duration-500", fillClass),
288
+ style: { width: `${pct(fillPct)}%` }
289
+ }
290
+ ) })
291
+ ] });
292
+ }
293
+ function toneFor(p) {
294
+ if (p >= 90) return "hot";
295
+ if (p >= 70) return "warn";
296
+ return "ok";
297
+ }
298
+ function StatTriple({
299
+ load1,
300
+ load5,
301
+ load15,
302
+ runQueue
303
+ }) {
304
+ const stats = [
305
+ { label: "1m", value: load1 },
306
+ { label: "5m", value: load5 },
307
+ { label: "15m", value: load15 }
308
+ ];
309
+ return /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between gap-3", "data-metric": "load", children: [
310
+ /* @__PURE__ */ jsxs2("div", { className: "flex min-w-0 flex-col gap-1", children: [
311
+ /* @__PURE__ */ jsx2("span", { className: "text-[10px] font-medium uppercase tracking-wide text-og-fg-subtle", children: "Load" }),
312
+ /* @__PURE__ */ jsx2("div", { className: "flex items-baseline gap-3", children: stats.map((s) => /* @__PURE__ */ jsxs2("div", { className: "flex items-baseline gap-1", children: [
313
+ /* @__PURE__ */ jsx2("span", { className: "text-[10px] font-medium uppercase tracking-wide text-og-fg-subtle", children: s.label }),
314
+ /* @__PURE__ */ jsx2("span", { className: "font-og-mono text-[12px] tabular-nums text-og-fg", children: s.value.toFixed(2) })
315
+ ] }, s.label)) })
316
+ ] }),
317
+ runQueue > 0 ? /* @__PURE__ */ jsxs2(
318
+ "div",
319
+ {
320
+ className: "flex shrink-0 items-baseline gap-1.5 rounded-og-sm bg-og-surface-2 px-2 py-1 text-[11px] text-og-fg-subtle",
321
+ "data-metric": "runqueue",
322
+ children: [
323
+ /* @__PURE__ */ jsx2("span", { className: "font-medium uppercase tracking-wide", children: "Queue" }),
324
+ /* @__PURE__ */ jsx2("span", { className: "font-og-mono tabular-nums text-og-fg-muted", children: runQueue })
325
+ ]
326
+ }
327
+ ) : null
328
+ ] });
329
+ }
330
+ function MachineMetrics({ metrics, density = "compact", className }) {
331
+ if (!metrics) {
332
+ return /* @__PURE__ */ jsx2("div", { className: cn("text-[11px] text-og-fg-subtle", className), "data-metrics-empty": true, children: "No metrics yet" });
333
+ }
334
+ const memPct2 = metrics.memTotalBytes > 0 ? metrics.memUsedBytes / metrics.memTotalBytes * 100 : 0;
335
+ const diskPct2 = metrics.diskTotalBytes > 0 ? metrics.diskUsedBytes / metrics.diskTotalBytes * 100 : 0;
336
+ const hasGpu = metrics.gpuUtilPct !== null;
337
+ const gpuMemLabel = metrics.gpuMemBytes !== null ? formatBytes(metrics.gpuMemBytes) : null;
338
+ const ratioGridClass = density === "full" ? "grid-cols-1" : "grid-cols-1 sm:grid-cols-2";
339
+ return /* @__PURE__ */ jsxs2("div", { className: cn("flex flex-col gap-3", className), "data-machine-metrics": true, children: [
340
+ /* @__PURE__ */ jsxs2("div", { className: cn("grid gap-x-4 gap-y-2.5", ratioGridClass), children: [
341
+ /* @__PURE__ */ jsx2(
342
+ Meter,
343
+ {
344
+ label: "Memory",
345
+ value: `${formatBytes(metrics.memUsedBytes)} / ${formatBytes(metrics.memTotalBytes)}`,
346
+ fillPct: memPct2,
347
+ tone: toneFor(memPct2)
348
+ }
349
+ ),
350
+ /* @__PURE__ */ jsx2(
351
+ Meter,
352
+ {
353
+ label: "Disk",
354
+ value: `${formatBytes(metrics.diskUsedBytes)} / ${formatBytes(metrics.diskTotalBytes)}`,
355
+ fillPct: diskPct2,
356
+ tone: toneFor(diskPct2)
357
+ }
358
+ )
359
+ ] }),
360
+ /* @__PURE__ */ jsxs2("div", { className: "grid grid-cols-2 gap-x-4 gap-y-2.5", children: [
361
+ /* @__PURE__ */ jsx2(
362
+ Meter,
363
+ {
364
+ label: "CPU",
365
+ value: `${metrics.cpuPct.toFixed(0)}%`,
366
+ fillPct: metrics.cpuPct,
367
+ tone: toneFor(metrics.cpuPct)
368
+ }
369
+ ),
370
+ hasGpu ? /* @__PURE__ */ jsx2(
371
+ Meter,
372
+ {
373
+ label: "GPU",
374
+ value: gpuMemLabel ? `${metrics.gpuUtilPct.toFixed(0)}% \xB7 ${gpuMemLabel}` : `${metrics.gpuUtilPct.toFixed(0)}%`,
375
+ fillPct: metrics.gpuUtilPct,
376
+ tone: toneFor(metrics.gpuUtilPct)
377
+ }
378
+ ) : null
379
+ ] }),
380
+ /* @__PURE__ */ jsx2(
381
+ StatTriple,
382
+ {
383
+ load1: metrics.load1,
384
+ load5: metrics.load5,
385
+ load15: metrics.load15,
386
+ runQueue: metrics.runQueue
387
+ }
388
+ )
389
+ ] });
390
+ }
391
+
392
+ // src/components/machine-card.tsx
393
+ import {
394
+ ArrowRightIcon,
395
+ CpuIcon,
396
+ LaptopIcon,
397
+ MonitorIcon,
398
+ MonitorOffIcon,
399
+ ServerIcon,
400
+ UsersIcon
401
+ } from "lucide-react";
402
+
403
+ // src/components/machines/health.ts
404
+ var PRESSURE = {
405
+ cpu: { warn: 90, crit: 98 },
406
+ mem: { warn: 85, crit: 95 },
407
+ disk: { warn: 90, crit: 96 }
408
+ };
409
+ var STALE_AFTER_MS = 45e3;
410
+ function pct2(used, total) {
411
+ if (!Number.isFinite(used) || !Number.isFinite(total) || total <= 0) return 0;
412
+ return used / total * 100;
413
+ }
414
+ function deriveHealth(state, metrics, now = Date.now()) {
415
+ if (state === "offline") {
416
+ return { level: "offline", label: "Offline", reason: "Not reachable", stale: true };
417
+ }
418
+ if (state === "enrolling") {
419
+ return { level: "unknown", label: "Enrolling", reason: "Completing enrollment", stale: false };
420
+ }
421
+ const stale = metrics ? now - new Date(metrics.sampledAt).getTime() > STALE_AFTER_MS : true;
422
+ if (state === "reconnecting") {
423
+ return { level: "degraded", label: "Reconnecting", reason: "Re-establishing link", stale };
424
+ }
425
+ if (!metrics) {
426
+ return { level: "unknown", label: "Online", reason: "Awaiting first sample", stale };
427
+ }
428
+ if (stale) {
429
+ return { level: "degraded", label: "Telemetry stale", reason: "No recent sample", stale: true };
430
+ }
431
+ const memPct2 = pct2(metrics.memUsedBytes, metrics.memTotalBytes);
432
+ const diskPct2 = pct2(metrics.diskUsedBytes, metrics.diskTotalBytes);
433
+ const cpu = metrics.cpuPct;
434
+ const pressures = [
435
+ resolve("Disk", diskPct2, PRESSURE.disk),
436
+ resolve("Memory", memPct2, PRESSURE.mem),
437
+ resolve("CPU", cpu, PRESSURE.cpu)
438
+ ];
439
+ const worst = pressures.reduce((a, b) => b.sev > a.sev ? b : a);
440
+ if (worst.sev === 2) return { level: "critical", label: "Critical", reason: worst.reason, stale };
441
+ if (worst.sev === 1)
442
+ return { level: "degraded", label: "Under load", reason: worst.reason, stale };
443
+ return { level: "healthy", label: "Healthy", reason: "All systems nominal", stale };
444
+ }
445
+ function resolve(name, value, t) {
446
+ const rounded = Math.round(value);
447
+ if (value >= t.crit) return { level: "critical", reason: `${name} ${rounded}%`, sev: 2 };
448
+ if (value >= t.warn) return { level: "degraded", reason: `${name} ${rounded}%`, sev: 1 };
449
+ return { level: "healthy", reason: `${name} ${rounded}%`, sev: 0 };
450
+ }
451
+ var HEALTH_TOKEN = {
452
+ healthy: {
453
+ text: "text-og-status-idle",
454
+ dot: "bg-og-status-idle",
455
+ soft: "bg-og-status-idle/10 border-og-status-idle/25"
456
+ },
457
+ degraded: {
458
+ text: "text-og-status-running",
459
+ dot: "bg-og-status-running",
460
+ soft: "bg-og-status-running/10 border-og-status-running/25"
461
+ },
462
+ critical: {
463
+ text: "text-og-status-failed",
464
+ dot: "bg-og-status-failed",
465
+ soft: "bg-og-status-failed/10 border-og-status-failed/30"
466
+ },
467
+ offline: {
468
+ text: "text-og-fg-subtle",
469
+ dot: "bg-og-fg-subtle",
470
+ soft: "bg-og-surface-2 border-og-border"
471
+ },
472
+ unknown: {
473
+ text: "text-og-fg-muted",
474
+ dot: "bg-og-fg-muted",
475
+ soft: "bg-og-surface-2 border-og-border"
476
+ }
477
+ };
478
+ function healthPulses(level) {
479
+ return level === "degraded" || level === "critical";
480
+ }
481
+
482
+ // src/components/machine-health-pill.tsx
483
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
484
+ function MachineHealthPill({
485
+ state,
486
+ metrics,
487
+ now,
488
+ size = "sm",
489
+ className
490
+ }) {
491
+ const health = deriveHealth(state, metrics, now ?? Date.now());
492
+ const tone = HEALTH_TOKEN[health.level];
493
+ const badge = MACHINE_STATE_BADGE_META[state];
494
+ const pad = size === "md" ? "px-2.5 py-1 text-og-sm" : "px-2 py-0.5 text-og-xs";
495
+ return /* @__PURE__ */ jsxs3("div", { className: cn("flex shrink-0 items-center gap-1.5", className), children: [
496
+ badge ? /* @__PURE__ */ jsx3(
497
+ "span",
498
+ {
499
+ className: cn(
500
+ "rounded-full border px-2 py-0.5 text-og-xs font-medium",
501
+ badge.badgeClassName
502
+ ),
503
+ children: badge.label
504
+ }
505
+ ) : null,
506
+ /* @__PURE__ */ jsxs3(
507
+ "span",
508
+ {
509
+ className: cn(
510
+ "inline-flex items-center gap-1.5 rounded-full border font-medium",
511
+ pad,
512
+ tone.soft,
513
+ tone.text
514
+ ),
515
+ "data-health": health.level,
516
+ title: health.reason,
517
+ children: [
518
+ /* @__PURE__ */ jsxs3("span", { className: "relative flex size-2", children: [
519
+ healthPulses(health.level) && /* @__PURE__ */ jsx3(
520
+ "span",
521
+ {
522
+ className: cn(
523
+ "absolute inline-flex size-full animate-og-pulse rounded-full opacity-60",
524
+ tone.dot
525
+ )
526
+ }
527
+ ),
528
+ /* @__PURE__ */ jsx3("span", { className: cn("relative inline-flex size-2 rounded-full", tone.dot) })
529
+ ] }),
530
+ health.label
531
+ ]
532
+ }
533
+ )
534
+ ] });
535
+ }
536
+
537
+ // src/components/machines/metric-sparkline.tsx
538
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
539
+ var VW = 120;
540
+ function MetricSparkline({
541
+ points,
542
+ color = "var(--og-color-accent)",
543
+ yMax = "auto",
544
+ height = 28,
545
+ className
546
+ }) {
547
+ const valued = points.filter(
548
+ (p) => p.v != null && Number.isFinite(p.v)
549
+ );
550
+ if (valued.length < 2) {
551
+ return /* @__PURE__ */ jsx4("div", { className, style: { height }, "aria-hidden": true });
552
+ }
553
+ const vh = height;
554
+ const top = yMax === "auto" ? Math.max(1, ...valued.map((p) => p.v)) * 1.1 : yMax;
555
+ const tMin = valued[0].t;
556
+ const tSpan = Math.max(1, valued[valued.length - 1].t - tMin);
557
+ const xy = valued.map((p) => ({
558
+ x: (p.t - tMin) / tSpan * VW,
559
+ y: vh - Math.min(top, Math.max(0, p.v)) / Math.max(1e-6, top) * (vh - 2) - 1
560
+ }));
561
+ let d = `M${xy[0].x},${xy[0].y}`;
562
+ for (let i = 0; i < xy.length - 1; i++) {
563
+ const p0 = xy[i - 1] ?? xy[i];
564
+ const p1 = xy[i];
565
+ const p2 = xy[i + 1];
566
+ const p3 = xy[i + 2] ?? p2;
567
+ d += `C${p1.x + (p2.x - p0.x) / 6},${p1.y + (p2.y - p0.y) / 6} ${p2.x - (p3.x - p1.x) / 6},${p2.y - (p3.y - p1.y) / 6} ${p2.x},${p2.y}`;
568
+ }
569
+ const last = xy[xy.length - 1];
570
+ const gid = `spark-${Math.round(top)}-${color.length}`;
571
+ return /* @__PURE__ */ jsxs4(
572
+ "svg",
573
+ {
574
+ viewBox: `0 0 ${VW} ${vh}`,
575
+ preserveAspectRatio: "none",
576
+ className,
577
+ style: { height, width: "100%" },
578
+ "aria-hidden": true,
579
+ children: [
580
+ /* @__PURE__ */ jsx4("defs", { children: /* @__PURE__ */ jsxs4("linearGradient", { id: gid, x1: "0", y1: "0", x2: "0", y2: "1", children: [
581
+ /* @__PURE__ */ jsx4("stop", { offset: "0%", stopColor: color, stopOpacity: "0.22" }),
582
+ /* @__PURE__ */ jsx4("stop", { offset: "100%", stopColor: color, stopOpacity: "0" })
583
+ ] }) }),
584
+ /* @__PURE__ */ jsx4("path", { d: `${d} L${last.x},${vh} L${xy[0].x},${vh} Z`, fill: `url(#${gid})` }),
585
+ /* @__PURE__ */ jsx4(
586
+ "path",
587
+ {
588
+ d,
589
+ fill: "none",
590
+ stroke: color,
591
+ strokeWidth: 1.25,
592
+ strokeLinejoin: "round",
593
+ strokeLinecap: "round",
594
+ vectorEffect: "non-scaling-stroke"
595
+ }
596
+ ),
597
+ /* @__PURE__ */ jsx4("circle", { cx: last.x, cy: last.y, r: 1.6, fill: color, vectorEffect: "non-scaling-stroke" })
598
+ ]
599
+ }
600
+ );
601
+ }
602
+
603
+ // src/components/machine-card.tsx
604
+ import { Fragment, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
605
+ function KindIcon({ machine }) {
606
+ if (machine.isSessionGroup)
607
+ return /* @__PURE__ */ jsx5(ServerIcon, { className: "size-4 text-og-fg-subtle", "aria-hidden": true });
608
+ if (machine.kind === "modal") return /* @__PURE__ */ jsx5(CpuIcon, { className: "size-4 text-og-fg-subtle", "aria-hidden": true });
609
+ return /* @__PURE__ */ jsx5(LaptopIcon, { className: "size-4 text-og-fg-subtle", "aria-hidden": true });
610
+ }
611
+ function MachineCard({
612
+ machine,
613
+ onAttach,
614
+ attaching,
615
+ series,
616
+ onOpenDetail,
617
+ now,
618
+ className
619
+ }) {
620
+ const offline = machine.state === "offline";
621
+ const attachable = !machine.active && !offline && Boolean(onAttach);
622
+ const shared = machine.sharedSessionCount > 1;
623
+ const clockNow = now ?? Date.now();
624
+ const sampledAgo = machine.metrics ? formatRelativeTime(machine.metrics.sampledAt, new Date(clockNow)) : null;
625
+ const cpuPts = (series ?? []).map((s) => ({ t: new Date(s.sampledAt).getTime(), v: s.cpuPct }));
626
+ const openable = Boolean(onOpenDetail);
627
+ return /* @__PURE__ */ jsxs5(
628
+ "div",
629
+ {
630
+ "data-machine-card": machine.sandboxId,
631
+ "data-active": machine.active ? "true" : "false",
632
+ role: openable ? "button" : void 0,
633
+ tabIndex: openable ? 0 : void 0,
634
+ onClick: openable ? () => onOpenDetail?.(machine) : void 0,
635
+ onKeyDown: openable ? (e) => {
636
+ if (e.key === "Enter" || e.key === " ") {
637
+ e.preventDefault();
638
+ onOpenDetail?.(machine);
639
+ }
640
+ } : void 0,
641
+ className: cn(
642
+ "og-root group relative flex flex-col gap-3 overflow-hidden rounded-og-lg border border-og-border",
643
+ "bg-og-surface-1 p-4 shadow-og-sm transition-colors",
644
+ openable && "cursor-pointer hover:border-og-border-strong focus-visible:border-og-accent/60 focus-visible:outline-none",
645
+ machine.active && "border-og-accent/40",
646
+ className
647
+ ),
648
+ children: [
649
+ machine.active ? /* @__PURE__ */ jsx5("span", { "aria-hidden": true, className: "absolute inset-y-0 left-0 w-0.5 bg-og-accent" }) : null,
650
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-start justify-between gap-3", children: [
651
+ /* @__PURE__ */ jsxs5("div", { className: "flex min-w-0 items-start gap-2.5", children: [
652
+ /* @__PURE__ */ jsx5("span", { className: "mt-0.5 shrink-0", children: /* @__PURE__ */ jsx5(KindIcon, { machine }) }),
653
+ /* @__PURE__ */ jsxs5("div", { className: "min-w-0", children: [
654
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2", children: [
655
+ /* @__PURE__ */ jsx5("span", { className: "truncate text-og-base font-medium text-og-fg", children: machine.name }),
656
+ machine.active ? /* @__PURE__ */ jsx5(
657
+ "span",
658
+ {
659
+ "data-active-marker": true,
660
+ className: "shrink-0 rounded-full border border-og-accent/30 bg-og-accent-soft px-1.5 py-px text-og-xs font-medium text-og-accent",
661
+ children: "Active"
662
+ }
663
+ ) : null
664
+ ] }),
665
+ /* @__PURE__ */ jsxs5("div", { className: "mt-0.5 flex flex-wrap items-center gap-x-2 gap-y-0.5 text-og-xs text-og-fg-subtle", children: [
666
+ /* @__PURE__ */ jsx5("span", { className: "capitalize", children: machine.isSessionGroup ? "session sandbox" : machine.kind }),
667
+ /* @__PURE__ */ jsx5("span", { "aria-hidden": true, children: "\xB7" }),
668
+ /* @__PURE__ */ jsxs5("span", { className: "font-og-mono", children: [
669
+ machine.os,
670
+ "/",
671
+ machine.arch
672
+ ] }),
673
+ /* @__PURE__ */ jsx5("span", { "aria-hidden": true, children: "\xB7" }),
674
+ /* @__PURE__ */ jsxs5("span", { className: "inline-flex items-center gap-1", children: [
675
+ machine.hasDisplay ? /* @__PURE__ */ jsx5(MonitorIcon, { className: "size-3", "aria-hidden": true }) : /* @__PURE__ */ jsx5(MonitorOffIcon, { className: "size-3", "aria-hidden": true }),
676
+ machine.hasDisplay ? "display" : "headless"
677
+ ] })
678
+ ] })
679
+ ] })
680
+ ] }),
681
+ /* @__PURE__ */ jsx5(
682
+ MachineHealthPill,
683
+ {
684
+ state: machine.state,
685
+ metrics: machine.metrics,
686
+ now: clockNow,
687
+ size: "sm",
688
+ className: "shrink-0"
689
+ }
690
+ )
691
+ ] }),
692
+ shared ? /* @__PURE__ */ jsxs5(
693
+ "p",
694
+ {
695
+ "data-shared-disclosure": true,
696
+ className: "flex items-center gap-1.5 rounded-og-md border border-og-accent/25 bg-og-accent-soft px-2.5 py-1.5 text-og-xs text-og-fg-muted",
697
+ children: [
698
+ /* @__PURE__ */ jsx5(UsersIcon, { className: "size-3.5 shrink-0 text-og-accent", "aria-hidden": true }),
699
+ "Shared \u2014 ",
700
+ machine.sharedSessionCount,
701
+ " sessions are on this machine."
702
+ ]
703
+ }
704
+ ) : null,
705
+ /* @__PURE__ */ jsx5(MachineMetrics, { metrics: machine.metrics }),
706
+ cpuPts.length > 1 ? /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-1 rounded-og-md bg-og-surface-2/40 p-2.5", children: [
707
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between text-[10px] font-medium uppercase tracking-wide text-og-fg-subtle", children: [
708
+ /* @__PURE__ */ jsx5("span", { children: "CPU \xB7 last 15m" }),
709
+ openable ? /* @__PURE__ */ jsxs5("span", { className: "flex items-center gap-0.5 opacity-0 transition-opacity group-hover:opacity-100", children: [
710
+ "Telemetry ",
711
+ /* @__PURE__ */ jsx5(ArrowRightIcon, { className: "size-2.5", "aria-hidden": true })
712
+ ] }) : null
713
+ ] }),
714
+ /* @__PURE__ */ jsx5(MetricSparkline, { points: cpuPts, color: "var(--og-color-accent)", yMax: 100, height: 26 })
715
+ ] }) : null,
716
+ /* @__PURE__ */ jsxs5("div", { className: "mt-1 flex items-center justify-between gap-3 text-og-xs text-og-fg-subtle", children: [
717
+ /* @__PURE__ */ jsx5("span", { className: "inline-flex items-center gap-1.5", children: sampledAgo ? /* @__PURE__ */ jsxs5(Fragment, { children: [
718
+ /* @__PURE__ */ jsx5(
719
+ "span",
720
+ {
721
+ className: cn(
722
+ "size-1.5 rounded-full",
723
+ sampledAgo === "now" ? "bg-og-status-idle" : "bg-og-fg-subtle"
724
+ ),
725
+ "aria-hidden": true
726
+ }
727
+ ),
728
+ sampledAgo === "now" ? "Live" : `Updated ${sampledAgo} ago`
729
+ ] }) : machine.lastSeenAt ? /* @__PURE__ */ jsxs5(Fragment, { children: [
730
+ "Last seen ",
731
+ formatRelativeTime(machine.lastSeenAt, new Date(clockNow))
732
+ ] }) : "Never connected" }),
733
+ machine.active ? /* @__PURE__ */ jsx5("span", { className: "text-og-accent", children: "Routing here" }) : attachable ? /* @__PURE__ */ jsx5(
734
+ "button",
735
+ {
736
+ type: "button",
737
+ "data-attach": true,
738
+ disabled: attaching,
739
+ onClick: (e) => {
740
+ e.stopPropagation();
741
+ onAttach?.(machine);
742
+ },
743
+ className: cn(
744
+ "rounded-og-sm border border-og-border px-2.5 py-1 text-og-xs font-medium text-og-fg-muted transition-colors pointer-coarse:min-h-10",
745
+ "hover:border-og-border-strong hover:text-og-fg disabled:cursor-not-allowed disabled:opacity-50"
746
+ ),
747
+ children: attaching ? "Switching\u2026" : "Attach"
748
+ }
749
+ ) : /* @__PURE__ */ jsx5("span", { className: "text-og-fg-subtle/70", children: offline ? "Unavailable" : "\u2014" })
750
+ ] })
751
+ ]
752
+ }
753
+ );
754
+ }
755
+
756
+ // src/components/machines/metric-history-chart.tsx
757
+ import { useLayoutEffect, useRef, useState } from "react";
758
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
759
+ var PAD = { top: 10, right: 12, bottom: 18, left: 40 };
760
+ function niceCeil(v) {
761
+ if (v <= 0) return 1;
762
+ const mag = 10 ** Math.floor(Math.log10(v));
763
+ const n = v / mag;
764
+ const step = n <= 1 ? 1 : n <= 2 ? 2 : n <= 5 ? 5 : 10;
765
+ return step * mag;
766
+ }
767
+ function smoothPath(pts) {
768
+ if (pts.length === 0) return "";
769
+ if (pts.length === 1) return `M${pts[0].x},${pts[0].y}`;
770
+ let d = `M${pts[0].x},${pts[0].y}`;
771
+ for (let i = 0; i < pts.length - 1; i++) {
772
+ const p0 = pts[i - 1] ?? pts[i];
773
+ const p1 = pts[i];
774
+ const p2 = pts[i + 1];
775
+ const p3 = pts[i + 2] ?? p2;
776
+ const cp1x = p1.x + (p2.x - p0.x) / 6;
777
+ const cp1y = p1.y + (p2.y - p0.y) / 6;
778
+ const cp2x = p2.x - (p3.x - p1.x) / 6;
779
+ const cp2y = p2.y - (p3.y - p1.y) / 6;
780
+ d += `C${cp1x},${cp1y} ${cp2x},${cp2y} ${p2.x},${p2.y}`;
781
+ }
782
+ return d;
783
+ }
784
+ function fmtClock(t) {
785
+ const d = new Date(t);
786
+ return `${d.getHours().toString().padStart(2, "0")}:${d.getMinutes().toString().padStart(2, "0")}`;
787
+ }
788
+ function MetricHistoryChart({
789
+ points,
790
+ yMax = "auto",
791
+ yMin = 0,
792
+ format,
793
+ unit = "",
794
+ color = "var(--og-color-accent)",
795
+ thresholds,
796
+ height = 132,
797
+ rangeLabel = "in this range",
798
+ className
799
+ }) {
800
+ const ref = useRef(null);
801
+ const [w, setW] = useState(560);
802
+ const [hover, setHover] = useState(null);
803
+ useLayoutEffect(() => {
804
+ const el = ref.current;
805
+ if (!el) return;
806
+ const ro = new ResizeObserver((entries) => {
807
+ const cw = entries[0]?.contentRect.width;
808
+ if (cw && cw > 0) setW(cw);
809
+ });
810
+ ro.observe(el);
811
+ return () => ro.disconnect();
812
+ }, []);
813
+ const valued = points.filter(
814
+ (p) => p.v != null && Number.isFinite(p.v)
815
+ );
816
+ const fmt = format ?? ((v) => `${Math.round(v)}${unit}`);
817
+ const H = height;
818
+ const plotW = Math.max(1, w - PAD.left - PAD.right);
819
+ const plotH = Math.max(1, H - PAD.top - PAD.bottom);
820
+ const dataMax = valued.length ? Math.max(...valued.map((p) => p.v)) : 1;
821
+ const top = yMax === "auto" ? niceCeil(Math.max(dataMax * 1.15, thresholds?.warn ?? 0, 1)) : yMax;
822
+ const span = Math.max(1e-6, top - yMin);
823
+ const tMin = valued.length ? valued[0].t : 0;
824
+ const tMax = valued.length ? valued[valued.length - 1].t : 1;
825
+ const tSpan = Math.max(1, tMax - tMin);
826
+ const xOf = (t) => PAD.left + (t - tMin) / tSpan * plotW;
827
+ const yOf = (v) => PAD.top + (1 - (Math.min(top, Math.max(yMin, v)) - yMin) / span) * plotH;
828
+ const pxPts = valued.map((p) => ({ x: xOf(p.t), y: yOf(p.v), t: p.t, v: p.v }));
829
+ const sparse = pxPts.length > 0 && pxPts.length <= 4;
830
+ const line = smoothPath(pxPts);
831
+ const area = pxPts.length > 1 ? `${line} L${pxPts[pxPts.length - 1].x},${PAD.top + plotH} L${pxPts[0].x},${PAD.top + plotH} Z` : "";
832
+ const yTicks = [yMin, yMin + span / 2, top];
833
+ const gid = `og-mh-${Math.round(top)}-${Math.round(color.length)}`;
834
+ const hoverPt = hover != null ? pxPts[hover] : null;
835
+ return /* @__PURE__ */ jsxs6(
836
+ "div",
837
+ {
838
+ ref,
839
+ className: cn("relative w-full select-none", className),
840
+ style: { height: H },
841
+ "data-metric-chart": true,
842
+ children: [
843
+ valued.length === 0 ? /* @__PURE__ */ jsx6("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsxs6("span", { className: "text-og-xs text-og-fg-subtle", children: [
844
+ "No samples ",
845
+ rangeLabel
846
+ ] }) }) : /* @__PURE__ */ jsxs6(
847
+ "svg",
848
+ {
849
+ width: w,
850
+ height: H,
851
+ className: "overflow-visible",
852
+ role: "img",
853
+ onMouseLeave: () => setHover(null),
854
+ onMouseMove: (e) => {
855
+ const rect = e.currentTarget.getBoundingClientRect();
856
+ const mx = e.clientX - rect.left;
857
+ let best = 0;
858
+ let bestD = Infinity;
859
+ for (let i = 0; i < pxPts.length; i++) {
860
+ const d = Math.abs(pxPts[i].x - mx);
861
+ if (d < bestD) {
862
+ bestD = d;
863
+ best = i;
864
+ }
865
+ }
866
+ setHover(best);
867
+ },
868
+ children: [
869
+ /* @__PURE__ */ jsx6("defs", { children: /* @__PURE__ */ jsxs6("linearGradient", { id: gid, x1: "0", y1: "0", x2: "0", y2: "1", children: [
870
+ /* @__PURE__ */ jsx6("stop", { offset: "0%", stopColor: color, stopOpacity: "0.26" }),
871
+ /* @__PURE__ */ jsx6("stop", { offset: "100%", stopColor: color, stopOpacity: "0" })
872
+ ] }) }),
873
+ yTicks.map((v, i) => {
874
+ const y = yOf(v);
875
+ return /* @__PURE__ */ jsxs6("g", { children: [
876
+ /* @__PURE__ */ jsx6(
877
+ "line",
878
+ {
879
+ x1: PAD.left,
880
+ x2: w - PAD.right,
881
+ y1: y,
882
+ y2: y,
883
+ stroke: "var(--og-color-border)",
884
+ strokeOpacity: i === 0 ? 0.9 : 0.4,
885
+ strokeWidth: 1
886
+ }
887
+ ),
888
+ /* @__PURE__ */ jsx6(
889
+ "text",
890
+ {
891
+ x: PAD.left - 8,
892
+ y: y + 3,
893
+ textAnchor: "end",
894
+ className: "font-og-mono",
895
+ fontSize: 10,
896
+ fill: "var(--og-color-fg-subtle)",
897
+ children: fmt(v)
898
+ }
899
+ )
900
+ ] }, `yt-${v}`);
901
+ }),
902
+ ["warn", "crit"].map((k) => {
903
+ const tv = thresholds?.[k];
904
+ if (tv == null || tv > top) return null;
905
+ const y = yOf(tv);
906
+ const stroke = k === "crit" ? "var(--og-color-status-failed)" : "var(--og-color-status-waiting)";
907
+ return /* @__PURE__ */ jsx6(
908
+ "line",
909
+ {
910
+ x1: PAD.left,
911
+ x2: w - PAD.right,
912
+ y1: y,
913
+ y2: y,
914
+ stroke,
915
+ strokeOpacity: 0.5,
916
+ strokeWidth: 1,
917
+ strokeDasharray: "3 4"
918
+ },
919
+ k
920
+ );
921
+ }),
922
+ valued.length > 1 && [tMin, tMax].map((t, i) => /* @__PURE__ */ jsx6(
923
+ "text",
924
+ {
925
+ x: i === 0 ? PAD.left : w - PAD.right,
926
+ y: H - 4,
927
+ textAnchor: i === 0 ? "start" : "end",
928
+ className: "font-og-mono",
929
+ fontSize: 10,
930
+ fill: "var(--og-color-fg-subtle)",
931
+ children: fmtClock(t)
932
+ },
933
+ i === 0 ? "x-start" : "x-end"
934
+ )),
935
+ area && /* @__PURE__ */ jsx6("path", { d: area, fill: `url(#${gid})` }),
936
+ /* @__PURE__ */ jsx6(
937
+ "path",
938
+ {
939
+ d: line,
940
+ fill: "none",
941
+ stroke: color,
942
+ strokeWidth: 1.75,
943
+ strokeLinejoin: "round",
944
+ strokeLinecap: "round",
945
+ pathLength: 1,
946
+ className: "og-mh-draw"
947
+ }
948
+ ),
949
+ sparse && pxPts.map((p) => /* @__PURE__ */ jsx6("circle", { cx: p.x, cy: p.y, r: 2.5, fill: color }, `m-${p.t}`)),
950
+ hoverPt && /* @__PURE__ */ jsxs6("g", { pointerEvents: "none", children: [
951
+ /* @__PURE__ */ jsx6(
952
+ "line",
953
+ {
954
+ x1: hoverPt.x,
955
+ x2: hoverPt.x,
956
+ y1: PAD.top,
957
+ y2: PAD.top + plotH,
958
+ stroke: "var(--og-color-border-strong)",
959
+ strokeWidth: 1
960
+ }
961
+ ),
962
+ /* @__PURE__ */ jsx6(
963
+ "circle",
964
+ {
965
+ cx: hoverPt.x,
966
+ cy: hoverPt.y,
967
+ r: 3.5,
968
+ fill: color,
969
+ stroke: "var(--og-color-bg)",
970
+ strokeWidth: 1.5
971
+ }
972
+ )
973
+ ] })
974
+ ]
975
+ }
976
+ ),
977
+ hoverPt && /* @__PURE__ */ jsxs6(
978
+ "div",
979
+ {
980
+ className: "pointer-events-none absolute z-10 flex flex-col rounded-og-sm border border-og-border-strong bg-og-surface-3/95 px-2 py-1 shadow-og-md backdrop-blur-sm",
981
+ style: {
982
+ left: Math.min(Math.max(hoverPt.x - 30, 0), w - 78),
983
+ top: Math.max(hoverPt.y - 42, 0)
984
+ },
985
+ children: [
986
+ /* @__PURE__ */ jsx6("span", { className: "font-og-mono text-og-sm font-medium tabular-nums text-og-fg", children: fmt(hoverPt.v) }),
987
+ /* @__PURE__ */ jsx6("span", { className: "font-og-mono text-[10px] tabular-nums text-og-fg-subtle", children: fmtClock(hoverPt.t) })
988
+ ]
989
+ }
990
+ )
991
+ ]
992
+ }
993
+ );
994
+ }
995
+
996
+ // src/components/machines/machine-detail.tsx
997
+ import { ArrowLeftIcon, CpuIcon as CpuIcon2, LaptopIcon as LaptopIcon2, RadioIcon, ServerIcon as ServerIcon2 } from "lucide-react";
998
+
999
+ // src/components/machines/series.ts
1000
+ var METRIC_WINDOWS = ["15m", "1h", "6h", "24h"];
1001
+ var WINDOW_LABEL = {
1002
+ "15m": "in the last 15 min",
1003
+ "1h": "in the last hour",
1004
+ "6h": "in the last 6 hours",
1005
+ "24h": "in the last 24 hours"
1006
+ };
1007
+ function ptsFrom(samples, pick) {
1008
+ return samples.map((s) => ({ t: new Date(s.sampledAt).getTime(), v: pick(s) }));
1009
+ }
1010
+ var memPct = (s) => s.memTotalBytes > 0 ? s.memUsedBytes / s.memTotalBytes * 100 : null;
1011
+ var diskPct = (s) => s.diskTotalBytes > 0 ? s.diskUsedBytes / s.diskTotalBytes * 100 : null;
1012
+ var fmtBytes = (b) => {
1013
+ if (b < 1024) return `${b} B`;
1014
+ const u = ["KB", "MB", "GB", "TB"];
1015
+ let v = b / 1024;
1016
+ for (const unit of u) {
1017
+ if (v < 1024 || unit === "TB") return `${v.toFixed(v < 10 ? 1 : 0)} ${unit}`;
1018
+ v /= 1024;
1019
+ }
1020
+ return `${b} B`;
1021
+ };
1022
+ var METRICS = [
1023
+ {
1024
+ key: "cpu",
1025
+ title: "CPU",
1026
+ unit: "%",
1027
+ yMax: 100,
1028
+ color: "var(--og-color-accent)",
1029
+ thresholds: { warn: 90, crit: 98 },
1030
+ pick: (s) => s.cpuPct,
1031
+ current: (s) => `${Math.round(s.cpuPct)}%`,
1032
+ sub: (s) => `${s.runQueue} in run queue`
1033
+ },
1034
+ {
1035
+ key: "mem",
1036
+ title: "Memory",
1037
+ unit: "%",
1038
+ yMax: 100,
1039
+ color: "var(--og-color-status-idle)",
1040
+ thresholds: { warn: 85, crit: 95 },
1041
+ pick: memPct,
1042
+ current: (s) => `${Math.round(memPct(s) ?? 0)}%`,
1043
+ sub: (s) => `${fmtBytes(s.memUsedBytes)} / ${fmtBytes(s.memTotalBytes)}`
1044
+ },
1045
+ {
1046
+ key: "disk",
1047
+ title: "Disk",
1048
+ unit: "%",
1049
+ yMax: 100,
1050
+ color: "var(--og-color-status-waiting)",
1051
+ thresholds: { warn: 90, crit: 96 },
1052
+ pick: diskPct,
1053
+ current: (s) => `${Math.round(diskPct(s) ?? 0)}%`,
1054
+ sub: (s) => `${fmtBytes(s.diskUsedBytes)} / ${fmtBytes(s.diskTotalBytes)}`
1055
+ },
1056
+ {
1057
+ key: "load",
1058
+ title: "Load average",
1059
+ unit: "",
1060
+ yMax: "auto",
1061
+ color: "var(--og-color-accent-strong)",
1062
+ pick: (s) => s.load1,
1063
+ current: (s) => s.load1.toFixed(2),
1064
+ sub: (s) => `5m ${s.load5.toFixed(2)} \xB7 15m ${s.load15.toFixed(2)}`
1065
+ },
1066
+ {
1067
+ key: "gpu",
1068
+ title: "GPU",
1069
+ unit: "%",
1070
+ yMax: 100,
1071
+ color: "var(--og-color-status-running)",
1072
+ thresholds: { warn: 90, crit: 98 },
1073
+ pick: (s) => s.gpuUtilPct,
1074
+ current: (s) => s.gpuUtilPct == null ? "\u2014" : `${Math.round(s.gpuUtilPct)}%`,
1075
+ sub: (s) => s.gpuMemBytes == null ? null : `${fmtBytes(s.gpuMemBytes)} used`
1076
+ }
1077
+ ];
1078
+ function pointsFor(def, samples) {
1079
+ return ptsFrom(samples, def.pick);
1080
+ }
1081
+
1082
+ // src/components/machines/machine-detail.tsx
1083
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1084
+ function KindIcon2({ kind, session }) {
1085
+ if (session) return /* @__PURE__ */ jsx7(ServerIcon2, { className: "size-4 text-og-fg-subtle", "aria-hidden": true });
1086
+ if (kind === "modal") return /* @__PURE__ */ jsx7(CpuIcon2, { className: "size-4 text-og-fg-subtle", "aria-hidden": true });
1087
+ return /* @__PURE__ */ jsx7(LaptopIcon2, { className: "size-4 text-og-fg-subtle", "aria-hidden": true });
1088
+ }
1089
+ function MachineDetail({
1090
+ machine,
1091
+ series,
1092
+ window: window2,
1093
+ onWindowChange,
1094
+ loadingSeries,
1095
+ onBack,
1096
+ now = Date.now(),
1097
+ className
1098
+ }) {
1099
+ const health = deriveHealth(machine.state, machine.metrics, now);
1100
+ const tone = HEALTH_TOKEN[health.level];
1101
+ const latest = machine.metrics;
1102
+ const sampledAgo = latest ? formatRelativeTime(latest.sampledAt, new Date(now)) : null;
1103
+ const visible = METRICS.filter((m) => !(m.key === "gpu" && latest?.gpuUtilPct == null));
1104
+ const tileCols = visible.length >= 5 ? "grid-cols-2 sm:grid-cols-3 lg:grid-cols-5" : "grid-cols-2 sm:grid-cols-4";
1105
+ return /* @__PURE__ */ jsxs7("div", { className: cn("og-root mx-auto flex w-full max-w-5xl flex-col gap-5", className), children: [
1106
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between gap-3", children: [
1107
+ /* @__PURE__ */ jsxs7("div", { className: "flex min-w-0 items-center gap-2.5", children: [
1108
+ onBack && /* @__PURE__ */ jsx7(
1109
+ "button",
1110
+ {
1111
+ type: "button",
1112
+ onClick: onBack,
1113
+ className: "flex size-7 shrink-0 items-center justify-center rounded-og-sm border border-og-border bg-og-surface-1 text-og-fg-muted transition-colors hover:bg-og-surface-2 hover:text-og-fg",
1114
+ "aria-label": "Back to machines",
1115
+ children: /* @__PURE__ */ jsx7(ArrowLeftIcon, { className: "size-3.5", "aria-hidden": true })
1116
+ }
1117
+ ),
1118
+ /* @__PURE__ */ jsx7(KindIcon2, { kind: machine.kind, session: machine.isSessionGroup }),
1119
+ /* @__PURE__ */ jsx7("h1", { className: "truncate text-og-md font-semibold text-og-fg", children: machine.name }),
1120
+ /* @__PURE__ */ jsxs7("span", { className: "hidden shrink-0 rounded-og-sm border border-og-border bg-og-surface-1 px-1.5 py-0.5 font-og-mono text-og-xs text-og-fg-subtle sm:inline", children: [
1121
+ machine.os,
1122
+ " \xB7 ",
1123
+ machine.arch
1124
+ ] })
1125
+ ] }),
1126
+ /* @__PURE__ */ jsx7("div", { className: "flex shrink-0 items-center gap-0.5 rounded-og-md border border-og-border bg-og-surface-1 p-0.5", children: METRIC_WINDOWS.map((w) => /* @__PURE__ */ jsx7(
1127
+ "button",
1128
+ {
1129
+ type: "button",
1130
+ onClick: () => onWindowChange(w),
1131
+ "data-active": w === window2,
1132
+ className: cn(
1133
+ "rounded-og-sm px-2 py-1 font-og-mono text-og-xs transition-colors",
1134
+ w === window2 ? "bg-og-surface-3 text-og-fg shadow-og-sm" : "text-og-fg-subtle hover:text-og-fg-muted"
1135
+ ),
1136
+ children: w
1137
+ },
1138
+ w
1139
+ )) })
1140
+ ] }),
1141
+ /* @__PURE__ */ jsxs7(
1142
+ "div",
1143
+ {
1144
+ className: cn(
1145
+ "relative flex flex-col gap-4 overflow-hidden rounded-og-lg border p-5 shadow-og-sm",
1146
+ tone.soft
1147
+ ),
1148
+ children: [
1149
+ /* @__PURE__ */ jsx7("span", { "aria-hidden": true, className: cn("absolute inset-y-0 left-0 w-1", tone.dot) }),
1150
+ /* @__PURE__ */ jsxs7("div", { className: "flex flex-wrap items-center justify-between gap-3 pl-1", children: [
1151
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-3", children: [
1152
+ /* @__PURE__ */ jsxs7("span", { className: "relative flex size-3", children: [
1153
+ healthPulses(health.level) && /* @__PURE__ */ jsx7(
1154
+ "span",
1155
+ {
1156
+ className: cn(
1157
+ "absolute inline-flex size-full animate-og-pulse rounded-full opacity-60",
1158
+ tone.dot
1159
+ )
1160
+ }
1161
+ ),
1162
+ /* @__PURE__ */ jsx7("span", { className: cn("relative inline-flex size-3 rounded-full", tone.dot) })
1163
+ ] }),
1164
+ /* @__PURE__ */ jsxs7("div", { className: "flex flex-col", children: [
1165
+ /* @__PURE__ */ jsx7("span", { className: cn("text-og-md font-semibold", tone.text), children: health.label }),
1166
+ /* @__PURE__ */ jsx7("span", { className: "text-og-sm text-og-fg-muted", children: health.reason })
1167
+ ] })
1168
+ ] }),
1169
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-start gap-5 pr-1", children: [
1170
+ sampledAgo && /* @__PURE__ */ jsxs7("div", { className: "flex flex-col items-end gap-0.5", children: [
1171
+ /* @__PURE__ */ jsx7("span", { className: "text-[10px] font-medium uppercase tracking-wide text-og-fg-subtle", children: "Sampled" }),
1172
+ /* @__PURE__ */ jsxs7("span", { className: "flex items-center gap-1 font-og-mono text-og-sm tabular-nums text-og-fg", children: [
1173
+ /* @__PURE__ */ jsx7(
1174
+ RadioIcon,
1175
+ {
1176
+ className: cn("size-3", health.stale ? "text-og-fg-subtle" : tone.text),
1177
+ "aria-hidden": true
1178
+ }
1179
+ ),
1180
+ sampledAgo === "now" ? "live" : `${sampledAgo} ago`
1181
+ ] })
1182
+ ] }),
1183
+ machine.lastSeenAt && /* @__PURE__ */ jsxs7("div", { className: "hidden flex-col items-end gap-0.5 sm:flex", children: [
1184
+ /* @__PURE__ */ jsx7("span", { className: "text-[10px] font-medium uppercase tracking-wide text-og-fg-subtle", children: "Last seen" }),
1185
+ /* @__PURE__ */ jsx7("span", { className: "font-og-mono text-og-sm tabular-nums text-og-fg-muted", children: formatRelativeTime(machine.lastSeenAt, new Date(now)) })
1186
+ ] })
1187
+ ] })
1188
+ ] }),
1189
+ latest && /* @__PURE__ */ jsx7(
1190
+ "div",
1191
+ {
1192
+ className: cn(
1193
+ "grid gap-px overflow-hidden rounded-og-md border border-og-border bg-og-border",
1194
+ tileCols
1195
+ ),
1196
+ children: visible.map((m) => {
1197
+ const pts = pointsFor(m, series);
1198
+ return /* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-1.5 bg-og-surface-1 p-3", children: [
1199
+ /* @__PURE__ */ jsx7("span", { className: "text-[10px] font-medium uppercase tracking-wide text-og-fg-subtle", children: m.title }),
1200
+ /* @__PURE__ */ jsx7("span", { className: "font-og-mono text-og-md font-medium tabular-nums text-og-fg", children: m.current(latest) }),
1201
+ /* @__PURE__ */ jsx7(MetricSparkline, { points: pts, color: m.color, yMax: m.yMax, height: 22 }),
1202
+ m.sub && /* @__PURE__ */ jsx7("span", { className: "truncate font-og-mono text-[10px] text-og-fg-subtle", children: m.sub(latest) })
1203
+ ] }, m.key);
1204
+ })
1205
+ }
1206
+ )
1207
+ ]
1208
+ }
1209
+ ),
1210
+ /* @__PURE__ */ jsx7("div", { className: "grid grid-cols-1 gap-3 lg:grid-cols-2", children: visible.map((m) => {
1211
+ const pts = pointsFor(m, series);
1212
+ return /* @__PURE__ */ jsxs7(
1213
+ "div",
1214
+ {
1215
+ className: "flex flex-col gap-3 rounded-og-lg border border-og-border bg-og-surface-1 p-4 shadow-og-sm",
1216
+ children: [
1217
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-baseline justify-between", children: [
1218
+ /* @__PURE__ */ jsx7("span", { className: "text-og-sm font-medium text-og-fg", children: m.title }),
1219
+ latest && /* @__PURE__ */ jsx7("span", { className: "font-og-mono text-og-sm tabular-nums text-og-fg-muted", children: m.current(latest) })
1220
+ ] }),
1221
+ /* @__PURE__ */ jsx7(
1222
+ MetricHistoryChart,
1223
+ {
1224
+ points: pts,
1225
+ yMax: m.yMax,
1226
+ unit: m.unit,
1227
+ color: m.color,
1228
+ thresholds: m.thresholds,
1229
+ rangeLabel: WINDOW_LABEL[window2],
1230
+ height: 128,
1231
+ className: cn(loadingSeries && "opacity-50 transition-opacity")
1232
+ }
1233
+ )
1234
+ ]
1235
+ },
1236
+ m.key
1237
+ );
1238
+ }) })
1239
+ ] });
1240
+ }
1241
+
1242
+ // src/components/machines-dashboard.tsx
1243
+ import { LaptopIcon as LaptopIcon3, PlusIcon, RefreshCwIcon } from "lucide-react";
1244
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1245
+ function EmptyState({ onEnroll }) {
1246
+ return /* @__PURE__ */ jsxs8(
1247
+ "div",
1248
+ {
1249
+ "data-machines-empty": true,
1250
+ className: "flex flex-col items-center justify-center gap-3 rounded-og-lg border border-dashed border-og-border bg-og-surface-1 px-6 py-12 text-center",
1251
+ children: [
1252
+ /* @__PURE__ */ jsx8("span", { className: "flex size-10 items-center justify-center rounded-full bg-og-surface-2 text-og-fg-subtle", children: /* @__PURE__ */ jsx8(LaptopIcon3, { className: "size-5", "aria-hidden": true }) }),
1253
+ /* @__PURE__ */ jsxs8("div", { className: "space-y-1", children: [
1254
+ /* @__PURE__ */ jsx8("p", { className: "text-og-base font-medium text-og-fg", children: "No machines yet" }),
1255
+ /* @__PURE__ */ jsx8("p", { className: "max-w-xs text-og-sm text-og-fg-muted", children: "Enroll your own computer to run the agent on it \u2014 your files, your terminal, your desktop." })
1256
+ ] }),
1257
+ onEnroll ? /* @__PURE__ */ jsxs8(
1258
+ "button",
1259
+ {
1260
+ type: "button",
1261
+ "data-enroll-cta": true,
1262
+ onClick: onEnroll,
1263
+ className: "inline-flex items-center gap-1.5 rounded-og-sm bg-og-accent px-3 py-1.5 text-og-sm font-medium text-og-accent-fg transition-colors hover:bg-og-accent-strong pointer-coarse:min-h-10",
1264
+ children: [
1265
+ /* @__PURE__ */ jsx8(PlusIcon, { className: "size-3.5", "aria-hidden": true }),
1266
+ "Enroll a machine"
1267
+ ]
1268
+ }
1269
+ ) : null
1270
+ ]
1271
+ }
1272
+ );
1273
+ }
1274
+ function Header({
1275
+ count,
1276
+ onEnroll,
1277
+ onRefresh,
1278
+ loading
1279
+ }) {
1280
+ return /* @__PURE__ */ jsxs8("div", { className: "flex items-center justify-between gap-3", children: [
1281
+ /* @__PURE__ */ jsxs8("div", { className: "flex items-baseline gap-2", children: [
1282
+ /* @__PURE__ */ jsx8("h2", { className: "text-og-base font-semibold text-og-fg", children: "Machines" }),
1283
+ /* @__PURE__ */ jsx8("span", { className: "font-og-mono text-og-xs text-og-fg-subtle", children: count })
1284
+ ] }),
1285
+ /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-1.5", children: [
1286
+ onRefresh ? /* @__PURE__ */ jsx8(
1287
+ "button",
1288
+ {
1289
+ type: "button",
1290
+ "data-refresh": true,
1291
+ onClick: onRefresh,
1292
+ title: "Refresh",
1293
+ className: "rounded-og-sm p-1.5 text-og-fg-subtle transition-colors hover:bg-og-surface-2 hover:text-og-fg",
1294
+ children: /* @__PURE__ */ jsx8(RefreshCwIcon, { className: cn("size-3.5", loading && "animate-og-spin"), "aria-hidden": true })
1295
+ }
1296
+ ) : null,
1297
+ onEnroll ? /* @__PURE__ */ jsxs8(
1298
+ "button",
1299
+ {
1300
+ type: "button",
1301
+ "data-enroll-cta": true,
1302
+ onClick: onEnroll,
1303
+ className: "inline-flex items-center gap-1.5 rounded-og-sm border border-og-border px-2.5 py-1 text-og-sm font-medium text-og-fg-muted transition-colors hover:border-og-border-strong hover:text-og-fg pointer-coarse:min-h-10",
1304
+ children: [
1305
+ /* @__PURE__ */ jsx8(PlusIcon, { className: "size-3.5", "aria-hidden": true }),
1306
+ "Enroll machine"
1307
+ ]
1308
+ }
1309
+ ) : null
1310
+ ] })
1311
+ ] });
1312
+ }
1313
+ function MachinesDashboard({
1314
+ machines,
1315
+ activeSandboxId,
1316
+ loading,
1317
+ error,
1318
+ onAttach,
1319
+ attachingSandboxId,
1320
+ seriesByMachine,
1321
+ onOpenDetail,
1322
+ now,
1323
+ onEnroll,
1324
+ onRefresh,
1325
+ className
1326
+ }) {
1327
+ const isEmpty = !loading && !error && machines.length === 0;
1328
+ return /* @__PURE__ */ jsxs8("section", { "data-machines-dashboard": true, className: cn("og-root flex flex-col gap-3", className), children: [
1329
+ /* @__PURE__ */ jsx8(Header, { count: machines.length, onEnroll, onRefresh, loading }),
1330
+ error ? /* @__PURE__ */ jsxs8(
1331
+ "div",
1332
+ {
1333
+ "data-machines-error": true,
1334
+ className: "flex flex-wrap items-center justify-between gap-2 rounded-og-md border border-og-status-failed/30 bg-og-status-failed/10 px-3 py-2 text-og-sm text-og-status-failed",
1335
+ children: [
1336
+ /* @__PURE__ */ jsxs8("span", { children: [
1337
+ "Could not load machines: ",
1338
+ error.message
1339
+ ] }),
1340
+ onRefresh ? /* @__PURE__ */ jsxs8(
1341
+ "button",
1342
+ {
1343
+ type: "button",
1344
+ "data-machines-retry": true,
1345
+ onClick: onRefresh,
1346
+ disabled: loading,
1347
+ className: "inline-flex items-center gap-1.5 rounded-og-sm border border-og-status-failed/30 px-2 py-1 text-og-xs font-medium transition-colors hover:border-og-status-failed/50 disabled:cursor-not-allowed disabled:opacity-60 pointer-coarse:min-h-10",
1348
+ children: [
1349
+ /* @__PURE__ */ jsx8(RefreshCwIcon, { className: cn("size-3.5", loading && "animate-og-spin"), "aria-hidden": true }),
1350
+ loading ? "Retrying\u2026" : "Retry"
1351
+ ]
1352
+ }
1353
+ ) : null
1354
+ ]
1355
+ }
1356
+ ) : null,
1357
+ loading && machines.length === 0 ? /* @__PURE__ */ jsx8("div", { "data-machines-loading": true, className: "grid gap-3 sm:grid-cols-2", children: [0, 1].map((i) => /* @__PURE__ */ jsx8(
1358
+ "div",
1359
+ {
1360
+ className: "h-36 animate-og-pulse rounded-og-lg border border-og-border bg-og-surface-1"
1361
+ },
1362
+ i
1363
+ )) }) : isEmpty ? /* @__PURE__ */ jsx8(EmptyState, { onEnroll }) : /* @__PURE__ */ jsx8("div", { className: "grid gap-3 sm:grid-cols-2 xl:grid-cols-3", "data-machines-grid": true, children: machines.map((machine) => /* @__PURE__ */ jsx8(
1364
+ MachineCard,
1365
+ {
1366
+ machine: {
1367
+ ...machine,
1368
+ active: machine.active || machine.sandboxId === activeSandboxId
1369
+ },
1370
+ onAttach,
1371
+ attaching: attachingSandboxId === machine.sandboxId,
1372
+ series: seriesByMachine?.[machine.sandboxId],
1373
+ onOpenDetail,
1374
+ now
1375
+ },
1376
+ machine.sandboxId
1377
+ )) })
1378
+ ] });
1379
+ }
1380
+
1381
+ // src/components/machine-dock-bar.tsx
1382
+ import { LaptopIcon as LaptopIcon4, CpuIcon as CpuIcon3, UsersIcon as UsersIcon2 } from "lucide-react";
1383
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1384
+ function MachineDockBar({ name, kind, state, className }) {
1385
+ const Icon = kind === "selfhosted" ? LaptopIcon4 : CpuIcon3;
1386
+ const stateBadge = MACHINE_STATE_BADGE_META[state];
1387
+ return /* @__PURE__ */ jsxs9(
1388
+ "div",
1389
+ {
1390
+ "data-machine-dock-bar": true,
1391
+ className: cn(
1392
+ "flex shrink-0 items-center justify-between gap-2 border-b border-og-border bg-og-surface-1 px-2.5 py-1",
1393
+ className
1394
+ ),
1395
+ children: [
1396
+ /* @__PURE__ */ jsxs9("div", { className: "flex min-w-0 items-center gap-1.5", children: [
1397
+ /* @__PURE__ */ jsx9(Icon, { className: "size-3.5 shrink-0 text-og-fg-subtle", "aria-hidden": true }),
1398
+ /* @__PURE__ */ jsx9("span", { className: "truncate text-[11px] font-medium text-og-fg", children: name }),
1399
+ stateBadge ? /* @__PURE__ */ jsx9(
1400
+ "span",
1401
+ {
1402
+ "data-state-badge": state,
1403
+ className: cn(
1404
+ "shrink-0 rounded-full border px-1.5 py-px text-[10px] font-medium",
1405
+ stateBadge.badgeClassName
1406
+ ),
1407
+ children: stateBadge.label
1408
+ }
1409
+ ) : null
1410
+ ] }),
1411
+ /* @__PURE__ */ jsx9(
1412
+ ConnectionStatusPill,
1413
+ {
1414
+ status: connectionStatusForState(state),
1415
+ size: "sm",
1416
+ className: "shrink-0"
1417
+ }
1418
+ )
1419
+ ]
1420
+ }
1421
+ );
1422
+ }
1423
+ function SharedMachineDisclosure({
1424
+ sharedSessionCount,
1425
+ density = "compact",
1426
+ className
1427
+ }) {
1428
+ const others = Math.max(0, sharedSessionCount - 1);
1429
+ return /* @__PURE__ */ jsxs9(
1430
+ "div",
1431
+ {
1432
+ "data-shared-disclosure": true,
1433
+ className: cn(
1434
+ "flex items-center gap-1.5 border-og-accent/25 bg-og-accent-soft text-og-fg-muted",
1435
+ density === "full" ? "rounded-og-md border px-3 py-2 text-[12px]" : "border-b px-2.5 py-1 text-[11px]",
1436
+ className
1437
+ ),
1438
+ children: [
1439
+ /* @__PURE__ */ jsx9(UsersIcon2, { className: "size-3.5 shrink-0 text-og-accent", "aria-hidden": true }),
1440
+ /* @__PURE__ */ jsxs9("span", { children: [
1441
+ "Shared \u2014 ",
1442
+ others === 1 ? "another session is" : `${others} other sessions are`,
1443
+ " on this machine. They see the same terminal, files, and desktop."
1444
+ ] })
1445
+ ]
1446
+ }
1447
+ );
1448
+ }
1449
+
1450
+ // src/components/enrollment-device-flow.tsx
1451
+ import { CopyIcon, ExternalLinkIcon, LaptopIcon as LaptopIcon5, TerminalIcon } from "lucide-react";
1452
+ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1453
+ var PHASE_TO_STATE = {
1454
+ pending: "enrolling",
1455
+ authorized: "online",
1456
+ denied: "offline",
1457
+ expired: "offline",
1458
+ disabled: "offline"
1459
+ };
1460
+ function PhaseLabel({ phase }) {
1461
+ const text = {
1462
+ pending: "Waiting for approval",
1463
+ authorized: "Connected",
1464
+ denied: "Denied",
1465
+ expired: "Code expired",
1466
+ disabled: "Enrollment disabled"
1467
+ };
1468
+ return /* @__PURE__ */ jsx10("span", { children: text[phase] });
1469
+ }
1470
+ function EnrollmentDeviceFlow({
1471
+ userCode,
1472
+ verificationUri,
1473
+ verificationUriComplete,
1474
+ installCommand,
1475
+ phase = "pending",
1476
+ expiresInSeconds,
1477
+ onCopyCode,
1478
+ onCopyInstall,
1479
+ onOpenVerification,
1480
+ className
1481
+ }) {
1482
+ const href = verificationUriComplete ?? verificationUri;
1483
+ const codeIsPlaceholder = !/[A-Za-z0-9]/.test(userCode);
1484
+ const awaitingCode = phase === "pending" && codeIsPlaceholder;
1485
+ return /* @__PURE__ */ jsxs10(
1486
+ "div",
1487
+ {
1488
+ "data-enrollment-device-flow": true,
1489
+ className: cn(
1490
+ "og-root flex w-full max-w-md flex-col gap-4 rounded-og-lg border border-og-border bg-og-surface-1 p-5 shadow-og-sm",
1491
+ className
1492
+ ),
1493
+ children: [
1494
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-between gap-3", children: [
1495
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2", children: [
1496
+ /* @__PURE__ */ jsx10("span", { className: "flex size-7 items-center justify-center rounded-full bg-og-surface-2 text-og-fg-muted", children: /* @__PURE__ */ jsx10(LaptopIcon5, { className: "size-4", "aria-hidden": true }) }),
1497
+ /* @__PURE__ */ jsx10("h2", { className: "text-sm font-semibold text-og-fg", children: "Connect a machine" })
1498
+ ] }),
1499
+ /* @__PURE__ */ jsx10(
1500
+ ConnectionStatusPill,
1501
+ {
1502
+ status: PHASE_TO_STATE[phase] === "online" ? "online" : PHASE_TO_STATE[phase] === "offline" ? "offline" : "reconnecting",
1503
+ label: /* @__PURE__ */ jsx10(PhaseLabel, { phase }),
1504
+ size: "sm"
1505
+ }
1506
+ )
1507
+ ] }),
1508
+ installCommand ? /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-1.5", children: [
1509
+ /* @__PURE__ */ jsx10("span", { className: "text-[11px] font-medium uppercase tracking-wide text-og-fg-subtle", children: "1 \xB7 Run on the machine" }),
1510
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-between gap-2 overflow-hidden rounded-og-md border border-og-border bg-og-bg px-2.5 py-1.5", children: [
1511
+ /* @__PURE__ */ jsxs10("code", { className: "flex min-w-0 items-center gap-2 overflow-x-auto font-og-mono text-[12px] text-og-fg", children: [
1512
+ /* @__PURE__ */ jsx10(TerminalIcon, { className: "size-3.5 shrink-0 text-og-fg-subtle", "aria-hidden": true }),
1513
+ installCommand
1514
+ ] }),
1515
+ /* @__PURE__ */ jsx10(
1516
+ "button",
1517
+ {
1518
+ type: "button",
1519
+ "data-copy-install": true,
1520
+ onClick: onCopyInstall,
1521
+ title: "Copy command",
1522
+ className: "shrink-0 rounded-og-sm p-1.5 text-og-fg-subtle transition-colors hover:bg-og-surface-2 hover:text-og-fg",
1523
+ children: /* @__PURE__ */ jsx10(CopyIcon, { className: "size-4", "aria-hidden": true })
1524
+ }
1525
+ )
1526
+ ] })
1527
+ ] }) : null,
1528
+ /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-1.5", children: [
1529
+ /* @__PURE__ */ jsx10("span", { className: "text-[11px] font-medium uppercase tracking-wide text-og-fg-subtle", children: installCommand ? "2 \xB7 Approve the machine" : "Approve the machine" }),
1530
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-between gap-3 rounded-og-md border border-og-border bg-og-bg px-3 py-2.5", children: [
1531
+ awaitingCode ? /* @__PURE__ */ jsx10("span", { "data-user-code": true, className: "font-og-mono text-[13px] text-og-fg-subtle", children: "Run the command above \u2014 your code appears here" }) : /* @__PURE__ */ jsx10(
1532
+ "span",
1533
+ {
1534
+ "data-user-code": true,
1535
+ className: "select-all font-og-mono text-2xl font-semibold tracking-[0.25em] text-og-fg",
1536
+ children: userCode
1537
+ }
1538
+ ),
1539
+ /* @__PURE__ */ jsx10(
1540
+ "button",
1541
+ {
1542
+ type: "button",
1543
+ "data-copy-code": true,
1544
+ onClick: onCopyCode,
1545
+ disabled: awaitingCode,
1546
+ title: "Copy code",
1547
+ className: "shrink-0 rounded-og-sm p-1.5 text-og-fg-subtle transition-colors hover:bg-og-surface-2 hover:text-og-fg disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:bg-transparent disabled:hover:text-og-fg-subtle",
1548
+ children: /* @__PURE__ */ jsx10(CopyIcon, { className: "size-4", "aria-hidden": true })
1549
+ }
1550
+ )
1551
+ ] })
1552
+ ] }),
1553
+ /* @__PURE__ */ jsxs10(
1554
+ "a",
1555
+ {
1556
+ href,
1557
+ "data-open-verification": true,
1558
+ target: "_blank",
1559
+ rel: "noreferrer",
1560
+ onClick: onOpenVerification,
1561
+ className: "inline-flex items-center justify-center gap-1.5 rounded-og-sm bg-og-accent px-3 py-2 text-sm font-medium text-og-accent-fg transition-colors hover:bg-og-accent-strong",
1562
+ children: [
1563
+ "Open approval page",
1564
+ /* @__PURE__ */ jsx10(ExternalLinkIcon, { className: "size-3.5", "aria-hidden": true })
1565
+ ]
1566
+ }
1567
+ ),
1568
+ /* @__PURE__ */ jsx10("p", { className: "text-center text-[11px] text-og-fg-subtle", children: awaitingCode ? /* @__PURE__ */ jsxs10(Fragment2, { children: [
1569
+ "The one-liner prints a short code. Open",
1570
+ " ",
1571
+ /* @__PURE__ */ jsx10("span", { className: "font-og-mono text-og-fg-muted", children: verificationUri }),
1572
+ " and confirm the code shown there matches."
1573
+ ] }) : /* @__PURE__ */ jsxs10(Fragment2, { children: [
1574
+ "Confirm code ",
1575
+ /* @__PURE__ */ jsx10("span", { className: "font-og-mono text-og-fg-muted", children: userCode }),
1576
+ " \u2014 the one printed by the one-liner \u2014 at",
1577
+ " ",
1578
+ /* @__PURE__ */ jsx10("span", { className: "font-og-mono text-og-fg-muted", children: verificationUri }),
1579
+ ".",
1580
+ typeof expiresInSeconds === "number" ? /* @__PURE__ */ jsxs10(Fragment2, { children: [
1581
+ " Expires in ",
1582
+ Math.max(0, Math.round(expiresInSeconds / 60)),
1583
+ " min."
1584
+ ] }) : null
1585
+ ] }) })
1586
+ ]
1587
+ }
1588
+ );
1589
+ }
1590
+
1591
+ // src/components/enrollment-consent.tsx
1592
+ import { useState as useState2 } from "react";
1593
+ import {
1594
+ CircleAlertIcon,
1595
+ LaptopIcon as LaptopIcon6,
1596
+ MonitorIcon as MonitorIcon2,
1597
+ ScreenShareIcon,
1598
+ ShieldAlertIcon,
1599
+ TerminalIcon as TerminalIcon2,
1600
+ UsersIcon as UsersIcon3
1601
+ } from "lucide-react";
1602
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1603
+ function Capability({ icon, title, body }) {
1604
+ return /* @__PURE__ */ jsxs11("li", { className: "flex items-start gap-2.5", children: [
1605
+ /* @__PURE__ */ jsx11("span", { className: "mt-0.5 shrink-0 text-og-status-failed", children: icon }),
1606
+ /* @__PURE__ */ jsxs11("span", { className: "min-w-0", children: [
1607
+ /* @__PURE__ */ jsx11("span", { className: "block text-og-base font-medium text-og-fg", children: title }),
1608
+ /* @__PURE__ */ jsx11("span", { className: "block text-og-sm text-og-fg-muted", children: body })
1609
+ ] })
1610
+ ] });
1611
+ }
1612
+ function EnrollmentConsent({
1613
+ userCode,
1614
+ machine,
1615
+ phase = "review",
1616
+ onApprove,
1617
+ onDeny,
1618
+ errorMessage,
1619
+ className
1620
+ }) {
1621
+ const [allowScreenControl, setAllowScreenControl] = useState2(machine.requestsScreenControl);
1622
+ const busy = phase === "approving";
1623
+ if (phase === "approved") {
1624
+ return /* @__PURE__ */ jsx11(
1625
+ ConsentResult,
1626
+ {
1627
+ className,
1628
+ tone: "ok",
1629
+ title: "Machine connected",
1630
+ body: `${machine.machineName} is now enrolled in this workspace. You can close this page \u2014 it will appear in your Machines dashboard.`
1631
+ }
1632
+ );
1633
+ }
1634
+ if (phase === "denied") {
1635
+ return /* @__PURE__ */ jsx11(
1636
+ ConsentResult,
1637
+ {
1638
+ className,
1639
+ tone: "muted",
1640
+ title: "Enrollment denied",
1641
+ body: `You declined to connect ${machine.machineName}. The agent has no access to this machine. You can re-run the install one-liner to try again.`
1642
+ }
1643
+ );
1644
+ }
1645
+ if (phase === "error") {
1646
+ return /* @__PURE__ */ jsx11(
1647
+ ConsentResult,
1648
+ {
1649
+ className,
1650
+ tone: "danger",
1651
+ title: "Could not complete enrollment",
1652
+ body: errorMessage ?? "The code may have expired. Re-run the install one-liner on the machine for a fresh code."
1653
+ }
1654
+ );
1655
+ }
1656
+ return /* @__PURE__ */ jsxs11(
1657
+ "div",
1658
+ {
1659
+ "data-enrollment-consent": true,
1660
+ className: cn(
1661
+ "og-root mx-auto flex w-full max-w-md flex-col gap-5 rounded-og-lg border border-og-status-failed/30 bg-og-surface-1 p-6 shadow-og-md",
1662
+ className
1663
+ ),
1664
+ children: [
1665
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-start gap-3", children: [
1666
+ /* @__PURE__ */ jsx11("span", { className: "flex size-9 shrink-0 items-center justify-center rounded-full bg-og-status-failed/15 text-og-status-failed", children: /* @__PURE__ */ jsx11(ShieldAlertIcon, { className: "size-5", "aria-hidden": true }) }),
1667
+ /* @__PURE__ */ jsxs11("div", { className: "min-w-0", children: [
1668
+ /* @__PURE__ */ jsx11("h1", { className: "text-og-md font-semibold text-og-fg", children: "Give the agent your whole machine?" }),
1669
+ /* @__PURE__ */ jsxs11("p", { className: "mt-1 text-og-base text-og-fg-muted", children: [
1670
+ "Approving lets the OpenGeni agent run on",
1671
+ " ",
1672
+ /* @__PURE__ */ jsx11("span", { className: "font-medium text-og-fg", children: machine.machineName }),
1673
+ " with full access. This is your real computer \u2014 not a sandbox."
1674
+ ] })
1675
+ ] })
1676
+ ] }),
1677
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-wrap items-center gap-2 rounded-og-md border border-og-border bg-og-surface-2 px-3 py-2 text-og-sm", children: [
1678
+ /* @__PURE__ */ jsx11(LaptopIcon6, { className: "size-4 text-og-fg-subtle", "aria-hidden": true }),
1679
+ /* @__PURE__ */ jsx11("span", { className: "font-medium text-og-fg", children: machine.machineName }),
1680
+ /* @__PURE__ */ jsxs11("span", { className: "font-og-mono text-og-fg-subtle", children: [
1681
+ machine.os,
1682
+ "/",
1683
+ machine.arch
1684
+ ] }),
1685
+ /* @__PURE__ */ jsx11("span", { "aria-hidden": true, className: "text-og-fg-subtle", children: "\xB7" }),
1686
+ /* @__PURE__ */ jsxs11("span", { className: "text-og-fg-subtle", children: [
1687
+ "code ",
1688
+ /* @__PURE__ */ jsx11("span", { className: "font-og-mono font-medium text-og-fg", children: userCode })
1689
+ ] })
1690
+ ] }),
1691
+ /* @__PURE__ */ jsxs11("ul", { className: "flex flex-col gap-3", children: [
1692
+ /* @__PURE__ */ jsx11(
1693
+ Capability,
1694
+ {
1695
+ icon: /* @__PURE__ */ jsx11(TerminalIcon2, { className: "size-4", "aria-hidden": true }),
1696
+ title: "Read, write & run anything",
1697
+ body: "The agent can read and modify any file, run any command, and use your git credentials \u2014 as if it were you at the keyboard."
1698
+ }
1699
+ ),
1700
+ machine.canOfferDisplay ? /* @__PURE__ */ jsx11(
1701
+ Capability,
1702
+ {
1703
+ icon: /* @__PURE__ */ jsx11(MonitorIcon2, { className: "size-4", "aria-hidden": true }),
1704
+ title: "See your screen",
1705
+ body: "The agent can capture this machine's display to watch what it is doing."
1706
+ }
1707
+ ) : null,
1708
+ /* @__PURE__ */ jsx11(
1709
+ Capability,
1710
+ {
1711
+ icon: /* @__PURE__ */ jsx11(UsersIcon3, { className: "size-4", "aria-hidden": true }),
1712
+ title: "Shared while connected",
1713
+ body: "Any session in this workspace can use this machine while it is online. Disconnect the agent to revoke access instantly."
1714
+ }
1715
+ )
1716
+ ] }),
1717
+ machine.canOfferDisplay ? /* @__PURE__ */ jsxs11(
1718
+ "label",
1719
+ {
1720
+ "data-screen-control-toggle": true,
1721
+ className: "flex cursor-pointer items-start gap-3 rounded-og-md border border-og-border bg-og-bg px-3 py-2.5",
1722
+ children: [
1723
+ /* @__PURE__ */ jsx11(
1724
+ "input",
1725
+ {
1726
+ type: "checkbox",
1727
+ checked: allowScreenControl,
1728
+ disabled: busy,
1729
+ onChange: (e) => setAllowScreenControl(e.target.checked),
1730
+ className: "mt-0.5 size-4 accent-og-accent"
1731
+ }
1732
+ ),
1733
+ /* @__PURE__ */ jsxs11("span", { className: "min-w-0", children: [
1734
+ /* @__PURE__ */ jsxs11("span", { className: "flex items-center gap-1.5 text-og-base font-medium text-og-fg", children: [
1735
+ /* @__PURE__ */ jsx11(ScreenShareIcon, { className: "size-3.5 text-og-fg-muted", "aria-hidden": true }),
1736
+ "Also let the agent control my mouse & keyboard"
1737
+ ] }),
1738
+ /* @__PURE__ */ jsx11("span", { className: "mt-0.5 block text-og-sm text-og-fg-muted", children: "Optional. Enables computer-use \u2014 the agent can move the pointer, type, and click on this machine's desktop. Leave off to let it only watch." })
1739
+ ] })
1740
+ ]
1741
+ }
1742
+ ) : /* @__PURE__ */ jsxs11("p", { className: "flex items-start gap-2 rounded-og-md border border-og-border bg-og-bg px-3 py-2 text-og-sm text-og-fg-muted", children: [
1743
+ /* @__PURE__ */ jsx11(CircleAlertIcon, { className: "mt-px size-3.5 shrink-0 text-og-fg-subtle", "aria-hidden": true }),
1744
+ "This machine has no display, so screen control isn't available \u2014 files, terminal, and git only."
1745
+ ] }),
1746
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2", children: [
1747
+ /* @__PURE__ */ jsx11(
1748
+ "button",
1749
+ {
1750
+ type: "button",
1751
+ "data-deny": true,
1752
+ disabled: busy,
1753
+ onClick: () => onDeny?.(),
1754
+ className: "flex-1 rounded-og-sm border border-og-border px-3 py-2 text-og-sm font-medium text-og-fg-muted transition-colors hover:border-og-border-strong hover:text-og-fg disabled:opacity-50 pointer-coarse:min-h-10",
1755
+ children: "Cancel"
1756
+ }
1757
+ ),
1758
+ /* @__PURE__ */ jsx11(
1759
+ "button",
1760
+ {
1761
+ type: "button",
1762
+ "data-approve": true,
1763
+ disabled: busy,
1764
+ onClick: () => onApprove?.(allowScreenControl),
1765
+ className: "flex-1 rounded-og-sm bg-og-status-failed px-3 py-2 text-og-sm font-semibold text-og-accent-fg transition-colors hover:opacity-90 disabled:opacity-50 pointer-coarse:min-h-10",
1766
+ children: busy ? "Connecting\u2026" : "Grant full access"
1767
+ }
1768
+ )
1769
+ ] })
1770
+ ]
1771
+ }
1772
+ );
1773
+ }
1774
+ function ConsentResult({
1775
+ tone,
1776
+ title,
1777
+ body,
1778
+ className
1779
+ }) {
1780
+ const ring = tone === "ok" ? "border-og-status-running/30" : tone === "danger" ? "border-og-status-failed/30" : "border-og-border";
1781
+ const iconClass = tone === "ok" ? "text-og-status-running" : tone === "danger" ? "text-og-status-failed" : "text-og-fg-subtle";
1782
+ return /* @__PURE__ */ jsxs11(
1783
+ "div",
1784
+ {
1785
+ "data-enrollment-result": tone,
1786
+ className: cn(
1787
+ "og-root mx-auto flex w-full max-w-md flex-col items-center gap-3 rounded-og-lg border bg-og-surface-1 p-6 text-center shadow-og-md",
1788
+ ring,
1789
+ className
1790
+ ),
1791
+ children: [
1792
+ /* @__PURE__ */ jsx11(
1793
+ "span",
1794
+ {
1795
+ className: cn(
1796
+ "flex size-10 items-center justify-center rounded-full bg-og-surface-2",
1797
+ iconClass
1798
+ ),
1799
+ children: /* @__PURE__ */ jsx11(LaptopIcon6, { className: "size-5", "aria-hidden": true })
1800
+ }
1801
+ ),
1802
+ /* @__PURE__ */ jsx11("h1", { className: "text-og-md font-semibold text-og-fg", children: title }),
1803
+ /* @__PURE__ */ jsx11("p", { className: "max-w-sm text-og-base text-og-fg-muted", children: body })
1804
+ ]
1805
+ }
1806
+ );
1807
+ }
1808
+
1809
+ // src/hooks/use-machines.ts
1810
+ import { useCallback as useCallback3, useState as useState5 } from "react";
1811
+
1812
+ // src/provider.tsx
1813
+ import {
1814
+ OpenGeniApiContractMismatchError,
1815
+ OPENGENI_API_CONTRACT_REVISION
1816
+ } from "@opengeni/sdk";
1817
+ import {
1818
+ createContext,
1819
+ useCallback,
1820
+ useContext,
1821
+ useEffect,
1822
+ useMemo,
1823
+ useRef as useRef2,
1824
+ useState as useState3
1825
+ } from "react";
1826
+ import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
1827
+ var OpenGeniContext = createContext(null);
1828
+ var NOOP_REGISTER_RECONCILER = () => () => void 0;
1829
+ var NOOP_RECONCILE_SESSION = async () => void 0;
1830
+ var CONTRACT_RELOAD_STORAGE_PREFIX = "opengeni.reloadForApiContract:";
1831
+ function OpenGeniProvider({
1832
+ client,
1833
+ workspaceId,
1834
+ onWorkspaceControlEvent,
1835
+ children
1836
+ }) {
1837
+ const [workspaceControlEvent, setWorkspaceControlEvent] = useState3(
1838
+ null
1839
+ );
1840
+ const [workspaceControlConnectionState, setWorkspaceControlConnectionState] = useState3("idle");
1841
+ const [contractMismatch, setContractMismatch] = useState3(
1842
+ null
1843
+ );
1844
+ const callbackRef = useRef2(onWorkspaceControlEvent);
1845
+ const reconcilersRef = useRef2(/* @__PURE__ */ new Map());
1846
+ callbackRef.current = onWorkspaceControlEvent;
1847
+ const verifyApiContract = useCallback(async () => {
1848
+ try {
1849
+ const config = await client.getClientConfig();
1850
+ if (config.apiContractRevision !== OPENGENI_API_CONTRACT_REVISION) {
1851
+ throw new OpenGeniApiContractMismatchError(
1852
+ OPENGENI_API_CONTRACT_REVISION,
1853
+ String(config.apiContractRevision || "(missing)")
1854
+ );
1855
+ }
1856
+ } catch (error) {
1857
+ if (error instanceof OpenGeniApiContractMismatchError) {
1858
+ setContractMismatch(error);
1859
+ reloadForContractMismatchOnce(error);
1860
+ }
1861
+ throw error;
1862
+ }
1863
+ }, [client]);
1864
+ const registerSessionReconciler = useMemo(
1865
+ () => (sessionId, key, reconcile) => {
1866
+ const sessionReconcilers = reconcilersRef.current.get(sessionId) ?? /* @__PURE__ */ new Map();
1867
+ sessionReconcilers.set(key, reconcile);
1868
+ reconcilersRef.current.set(sessionId, sessionReconcilers);
1869
+ return () => {
1870
+ const current = reconcilersRef.current.get(sessionId);
1871
+ current?.delete(key);
1872
+ if (current?.size === 0) reconcilersRef.current.delete(sessionId);
1873
+ };
1874
+ },
1875
+ []
1876
+ );
1877
+ const reconcileSession = useMemo(
1878
+ () => async (sessionId) => {
1879
+ await verifyApiContract();
1880
+ const callbacks = [...reconcilersRef.current.get(sessionId)?.values() ?? []];
1881
+ await Promise.all(callbacks.map((reconcile) => reconcile()));
1882
+ },
1883
+ [verifyApiContract]
1884
+ );
1885
+ useEffect(() => {
1886
+ const controller = new AbortController();
1887
+ setWorkspaceControlEvent(null);
1888
+ setWorkspaceControlConnectionState("connecting");
1889
+ void (async () => {
1890
+ try {
1891
+ await verifyApiContract();
1892
+ const workspace = await client.getWorkspace(workspaceId);
1893
+ const stream = client.streamWorkspaceControlEvents(workspaceId, {
1894
+ after: workspace.inferenceControl.revision,
1895
+ signal: controller.signal,
1896
+ onStateChange: setWorkspaceControlConnectionState
1897
+ });
1898
+ for await (const event of stream) {
1899
+ if (controller.signal.aborted) return;
1900
+ setWorkspaceControlEvent(
1901
+ (current) => !current || event.sequence > current.sequence ? event : current
1902
+ );
1903
+ callbackRef.current?.(event);
1904
+ }
1905
+ } catch (error) {
1906
+ if (error instanceof OpenGeniApiContractMismatchError) {
1907
+ setContractMismatch(error);
1908
+ reloadForContractMismatchOnce(error);
1909
+ }
1910
+ if (!controller.signal.aborted) setWorkspaceControlConnectionState("error");
1911
+ }
1912
+ })();
1913
+ return () => controller.abort();
1914
+ }, [client, verifyApiContract, workspaceId]);
1915
+ const value = useMemo(
1916
+ () => ({
1917
+ client,
1918
+ workspaceId,
1919
+ workspaceControlEvent,
1920
+ workspaceControlConnectionState,
1921
+ registerSessionReconciler,
1922
+ reconcileSession
1923
+ }),
1924
+ [
1925
+ client,
1926
+ workspaceId,
1927
+ workspaceControlEvent,
1928
+ workspaceControlConnectionState,
1929
+ registerSessionReconciler,
1930
+ reconcileSession
1931
+ ]
1932
+ );
1933
+ return /* @__PURE__ */ jsxs12(OpenGeniContext.Provider, { value, children: [
1934
+ children,
1935
+ contractMismatch ? /* @__PURE__ */ jsx12(ApiContractMismatchScreen, { mismatch: contractMismatch }) : null
1936
+ ] });
1937
+ }
1938
+ function ApiContractMismatchScreen({ mismatch }) {
1939
+ return /* @__PURE__ */ jsx12(
1940
+ "div",
1941
+ {
1942
+ className: "og-root fixed inset-0 z-[2147483647] grid place-items-center bg-og-bg/95 p-6 backdrop-blur-sm",
1943
+ role: "alert",
1944
+ "aria-live": "assertive",
1945
+ "data-opengeni-api-contract-mismatch": true,
1946
+ children: /* @__PURE__ */ jsxs12("div", { className: "w-full max-w-md rounded-xl border border-og-border bg-og-surface p-6 shadow-2xl", children: [
1947
+ /* @__PURE__ */ jsx12("p", { className: "text-sm font-semibold text-og-fg", children: "OpenGeni updated" }),
1948
+ /* @__PURE__ */ jsx12("p", { className: "mt-2 text-sm leading-6 text-og-muted", children: "This tab cannot safely continue with the new server version. Reload it before sending or controlling work." }),
1949
+ /* @__PURE__ */ jsxs12("p", { className: "mt-3 font-mono text-xs text-og-subtle", children: [
1950
+ "Client ",
1951
+ mismatch.expected,
1952
+ " \xB7 API ",
1953
+ mismatch.actual
1954
+ ] }),
1955
+ /* @__PURE__ */ jsx12(
1956
+ "button",
1957
+ {
1958
+ type: "button",
1959
+ className: "mt-5 inline-flex h-9 items-center rounded-md bg-og-fg px-3 text-sm font-medium text-og-bg",
1960
+ onClick: () => window.location.reload(),
1961
+ children: "Reload now"
1962
+ }
1963
+ )
1964
+ ] })
1965
+ }
1966
+ );
1967
+ }
1968
+ function reloadForContractMismatchOnce(mismatch) {
1969
+ if (typeof window === "undefined" || typeof sessionStorage === "undefined") return;
1970
+ const key = `${CONTRACT_RELOAD_STORAGE_PREFIX}${mismatch.actual}`;
1971
+ if (sessionStorage.getItem(key) === OPENGENI_API_CONTRACT_REVISION) return;
1972
+ sessionStorage.setItem(key, OPENGENI_API_CONTRACT_REVISION);
1973
+ window.setTimeout(() => window.location.reload(), 150);
1974
+ }
1975
+ function useOpenGeni(override = {}) {
1976
+ const context = useContext(OpenGeniContext);
1977
+ const client = override.client ?? context?.client;
1978
+ const workspaceId = override.workspaceId ?? context?.workspaceId;
1979
+ if (!client || !workspaceId) {
1980
+ throw new Error(
1981
+ "@opengeni/react: no OpenGeni client/workspace available. Wrap the tree in <OpenGeniProvider> or pass { client, workspaceId } to the hook."
1982
+ );
1983
+ }
1984
+ return {
1985
+ client,
1986
+ workspaceId,
1987
+ workspaceControlEvent: context?.workspaceControlEvent ?? null,
1988
+ workspaceControlConnectionState: context?.workspaceControlConnectionState ?? "idle",
1989
+ registerSessionReconciler: context?.registerSessionReconciler ?? NOOP_REGISTER_RECONCILER,
1990
+ reconcileSession: context?.reconcileSession ?? NOOP_RECONCILE_SESSION
1991
+ };
1992
+ }
1993
+ function useOpenGeniClient(override = {}) {
1994
+ const context = useContext(OpenGeniContext);
1995
+ const client = override.client ?? context?.client;
1996
+ if (!client) {
1997
+ throw new Error(
1998
+ "@opengeni/react: no OpenGeni client available. Wrap the tree in <OpenGeniProvider> or pass { client } to the hook."
1999
+ );
2000
+ }
2001
+ return client;
2002
+ }
2003
+
2004
+ // src/hooks/internal.ts
2005
+ import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef3, useState as useState4 } from "react";
2006
+ function usePolledValue(load, options = {}) {
2007
+ const enabled = options.enabled ?? true;
2008
+ const pollIntervalMs = options.pollIntervalMs;
2009
+ const [data, setData] = useState4(null);
2010
+ const [loading, setLoading] = useState4(enabled);
2011
+ const [error, setError] = useState4(null);
2012
+ const generation = useRef3(0);
2013
+ const loadRef = useRef3(load);
2014
+ useEffect2(() => {
2015
+ if (loadRef.current !== load) {
2016
+ loadRef.current = load;
2017
+ setData(null);
2018
+ setError(null);
2019
+ }
2020
+ }, [load]);
2021
+ const run = useCallback2(async () => {
2022
+ const ticket = ++generation.current;
2023
+ try {
2024
+ const result = await load();
2025
+ if (ticket === generation.current) {
2026
+ setData(result);
2027
+ setError(null);
2028
+ setLoading(false);
2029
+ }
2030
+ } catch (cause) {
2031
+ if (ticket === generation.current) {
2032
+ setError(cause instanceof Error ? cause : new Error(String(cause)));
2033
+ setLoading(false);
2034
+ }
2035
+ }
2036
+ }, [load]);
2037
+ useEffect2(() => {
2038
+ if (!enabled) {
2039
+ setLoading(false);
2040
+ return;
2041
+ }
2042
+ setLoading(true);
2043
+ void run();
2044
+ if (pollIntervalMs === void 0 || pollIntervalMs <= 0) {
2045
+ return () => {
2046
+ generation.current += 1;
2047
+ };
2048
+ }
2049
+ const timer = setInterval(() => void run(), pollIntervalMs);
2050
+ return () => {
2051
+ clearInterval(timer);
2052
+ generation.current += 1;
2053
+ };
2054
+ }, [run, enabled, pollIntervalMs]);
2055
+ return { data, loading, error, refresh: run };
2056
+ }
2057
+ function useMutationRunner() {
2058
+ const [mutating, setMutating] = useState4(false);
2059
+ const [mutationError, setMutationError] = useState4(null);
2060
+ const inFlight = useRef3(0);
2061
+ const mounted = useRef3(true);
2062
+ useEffect2(() => {
2063
+ mounted.current = true;
2064
+ return () => {
2065
+ mounted.current = false;
2066
+ };
2067
+ }, []);
2068
+ const run = useCallback2(async (operation) => {
2069
+ inFlight.current += 1;
2070
+ if (mounted.current) {
2071
+ setMutating(true);
2072
+ setMutationError(null);
2073
+ }
2074
+ try {
2075
+ return await operation();
2076
+ } catch (cause) {
2077
+ if (mounted.current) {
2078
+ setMutationError(cause instanceof Error ? cause : new Error(String(cause)));
2079
+ }
2080
+ return null;
2081
+ } finally {
2082
+ inFlight.current -= 1;
2083
+ if (mounted.current && inFlight.current === 0) {
2084
+ setMutating(false);
2085
+ }
2086
+ }
2087
+ }, []);
2088
+ return {
2089
+ mutating,
2090
+ mutationError,
2091
+ clearMutationError: useCallback2(() => setMutationError(null), []),
2092
+ run
2093
+ };
2094
+ }
2095
+ function useSessionEventTrigger(client, workspaceId, sessionId, match, onEvent, options = {}) {
2096
+ const enabled = options.enabled ?? true;
2097
+ const events = options.events;
2098
+ const sharedFeed = events !== void 0;
2099
+ const matchRef = useRef3(match);
2100
+ matchRef.current = match;
2101
+ const onEventRef = useRef3(onEvent);
2102
+ onEventRef.current = onEvent;
2103
+ const consumedRef = useRef3(0);
2104
+ const feedKeyRef = useRef3(null);
2105
+ useEffect2(() => {
2106
+ if (!sharedFeed || !enabled || !sessionId) {
2107
+ return;
2108
+ }
2109
+ const feedKey = `${workspaceId}\0${sessionId}`;
2110
+ const firstSequence = events[0]?.sequence ?? 0;
2111
+ if (feedKeyRef.current !== feedKey || firstSequence > consumedRef.current + 1) {
2112
+ feedKeyRef.current = feedKey;
2113
+ consumedRef.current = 0;
2114
+ }
2115
+ for (const event of events) {
2116
+ if (event.sequence <= consumedRef.current) {
2117
+ continue;
2118
+ }
2119
+ consumedRef.current = event.sequence;
2120
+ if (matchRef.current(event)) {
2121
+ onEventRef.current(event);
2122
+ }
2123
+ }
2124
+ }, [sharedFeed, enabled, events, workspaceId, sessionId]);
2125
+ useEffect2(() => {
2126
+ if (sharedFeed || !enabled || !sessionId) {
2127
+ return;
2128
+ }
2129
+ const controller = new AbortController();
2130
+ void (async () => {
2131
+ try {
2132
+ const session = await client.getSession(workspaceId, sessionId);
2133
+ if (controller.signal.aborted) {
2134
+ return;
2135
+ }
2136
+ const stream = client.streamEvents(workspaceId, sessionId, {
2137
+ after: session.lastSequence,
2138
+ signal: controller.signal
2139
+ });
2140
+ for await (const event of stream) {
2141
+ if (matchRef.current(event)) {
2142
+ onEventRef.current(event);
2143
+ }
2144
+ }
2145
+ } catch {
2146
+ }
2147
+ })();
2148
+ return () => {
2149
+ controller.abort();
2150
+ };
2151
+ }, [sharedFeed, enabled, client, workspaceId, sessionId]);
2152
+ }
2153
+ function useDebouncedCallback(callback, delayMs = 150) {
2154
+ const callbackRef = useRef3(callback);
2155
+ callbackRef.current = callback;
2156
+ const timerRef = useRef3(null);
2157
+ useEffect2(() => {
2158
+ return () => {
2159
+ if (timerRef.current !== null) {
2160
+ clearTimeout(timerRef.current);
2161
+ }
2162
+ };
2163
+ }, []);
2164
+ return useCallback2(() => {
2165
+ if (timerRef.current !== null) {
2166
+ clearTimeout(timerRef.current);
2167
+ }
2168
+ timerRef.current = setTimeout(() => {
2169
+ timerRef.current = null;
2170
+ callbackRef.current();
2171
+ }, delayMs);
2172
+ }, [delayMs]);
2173
+ }
2174
+
2175
+ // src/hooks/use-machines.ts
2176
+ var EMPTY = { activeSandboxId: null, activeEpoch: 0, machines: [] };
2177
+ function useMachines(options = {}) {
2178
+ const { client, workspaceId } = useOpenGeni(options);
2179
+ const machinesClient = options.machinesClient ?? client;
2180
+ const sessionId = options.sessionId;
2181
+ const load = useCallback3(async () => {
2182
+ return await machinesClient.listMachines(workspaceId, sessionId ? { sessionId } : void 0);
2183
+ }, [machinesClient, workspaceId, sessionId]);
2184
+ const {
2185
+ data: loadedData,
2186
+ loading,
2187
+ error,
2188
+ refresh
2189
+ } = usePolledValue(load, {
2190
+ pollIntervalMs: options.pollIntervalMs,
2191
+ enabled: options.enabled
2192
+ });
2193
+ const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
2194
+ const [attachingSandboxId, setAttachingSandboxId] = useState5(null);
2195
+ const data = loadedData ?? EMPTY;
2196
+ const canAttach = sessionId !== void 0 && (typeof machinesClient.attachMachine === "function" || typeof machinesClient.swapActiveSandbox === "function");
2197
+ const attach = useCallback3(
2198
+ async (sandboxId) => {
2199
+ if (sessionId === void 0) return false;
2200
+ const runSwap = machinesClient.attachMachine ? () => machinesClient.attachMachine(workspaceId, sessionId, sandboxId) : machinesClient.swapActiveSandbox ? () => machinesClient.swapActiveSandbox(workspaceId, sessionId, { target: sandboxId }) : null;
2201
+ if (!runSwap) return false;
2202
+ setAttachingSandboxId(sandboxId);
2203
+ const result = await run(async () => {
2204
+ await runSwap();
2205
+ return true;
2206
+ });
2207
+ setAttachingSandboxId(null);
2208
+ if (result) await refresh();
2209
+ return result === true;
2210
+ },
2211
+ [machinesClient, workspaceId, sessionId, run, refresh]
2212
+ );
2213
+ const fetchSeries = useCallback3(
2214
+ async (enrollmentId, window2 = "1h") => {
2215
+ if (!machinesClient.machineMetricsSeries) return [];
2216
+ return await machinesClient.machineMetricsSeries(workspaceId, enrollmentId, { window: window2 });
2217
+ },
2218
+ [machinesClient, workspaceId]
2219
+ );
2220
+ return {
2221
+ machines: data.machines,
2222
+ activeSandboxId: data.activeSandboxId,
2223
+ activeEpoch: data.activeEpoch,
2224
+ loading,
2225
+ error,
2226
+ refresh,
2227
+ attach,
2228
+ canAttach,
2229
+ fetchSeries,
2230
+ attaching: mutating,
2231
+ attachingSandboxId,
2232
+ mutationError,
2233
+ clearMutationError
2234
+ };
2235
+ }
2236
+
2237
+ export {
2238
+ OpenGeniProvider,
2239
+ useOpenGeni,
2240
+ useOpenGeniClient,
2241
+ usePolledValue,
2242
+ useMutationRunner,
2243
+ useSessionEventTrigger,
2244
+ useDebouncedCallback,
2245
+ formatRelativeTime,
2246
+ formatBytes,
2247
+ truncate,
2248
+ stringifyPayload,
2249
+ tryParseJson,
2250
+ CREDIT_EXHAUSTION_MESSAGE,
2251
+ isCreditExhaustion,
2252
+ humanizeFailureReason,
2253
+ cn,
2254
+ useMachines,
2255
+ connectionStatusForState,
2256
+ CONNECTION_STATUS_META,
2257
+ MACHINE_STATE_BADGE_META,
2258
+ ConnectionStatusPill,
2259
+ ConnectionDot,
2260
+ MachineStatusPill,
2261
+ MachineDockBar,
2262
+ SharedMachineDisclosure,
2263
+ MachineMetrics,
2264
+ deriveHealth,
2265
+ HEALTH_TOKEN,
2266
+ healthPulses,
2267
+ MachineHealthPill,
2268
+ MetricSparkline,
2269
+ MachineCard,
2270
+ MetricHistoryChart,
2271
+ METRIC_WINDOWS,
2272
+ WINDOW_LABEL,
2273
+ METRICS,
2274
+ pointsFor,
2275
+ MachineDetail,
2276
+ MachinesDashboard,
2277
+ EnrollmentDeviceFlow,
2278
+ EnrollmentConsent
2279
+ };
2280
+ //# sourceMappingURL=chunk-TOJR776I.js.map