@opengeni/react 0.13.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 (41) hide show
  1. package/README.md +19 -13
  2. package/dist/chunk-TOJR776I.js +2280 -0
  3. package/dist/chunk-TOJR776I.js.map +1 -0
  4. package/dist/index.d.ts +314 -255
  5. package/dist/index.js +5862 -4404
  6. package/dist/index.js.map +1 -1
  7. package/dist/{machines-CnlMb7E-.d.ts → machines-BpdwuQcD.d.ts} +130 -10
  8. package/dist/machines.d.ts +1 -1
  9. package/dist/machines.js +23 -1
  10. package/package.json +5 -2
  11. package/src/client.ts +10 -1
  12. package/src/components/chat-composer.tsx +309 -57
  13. package/src/components/machine-card.tsx +81 -15
  14. package/src/components/machine-health-pill.tsx +68 -0
  15. package/src/components/machine-metrics.tsx +10 -24
  16. package/src/components/machines/health.ts +146 -0
  17. package/src/components/machines/machine-detail.tsx +220 -0
  18. package/src/components/machines/metric-history-chart.tsx +298 -0
  19. package/src/components/machines/metric-sparkline.tsx +76 -0
  20. package/src/components/machines/series.ts +113 -0
  21. package/src/components/machines-dashboard.tsx +13 -1
  22. package/src/components/queue-surface.tsx +578 -0
  23. package/src/components/sandbox-files.tsx +94 -9
  24. package/src/components/sandbox-workspace.tsx +186 -52
  25. package/src/components/session-status.tsx +0 -6
  26. package/src/components/workbench-changes.tsx +64 -20
  27. package/src/components/workspace-dock.tsx +146 -55
  28. package/src/hooks/use-composer.ts +369 -39
  29. package/src/hooks/use-session-control.ts +6 -7
  30. package/src/hooks/use-session-events.ts +3 -2
  31. package/src/hooks/use-session-lineage.ts +15 -6
  32. package/src/hooks/use-session.ts +10 -2
  33. package/src/hooks/use-turn-queue.ts +175 -47
  34. package/src/index.ts +13 -7
  35. package/src/machines.ts +16 -0
  36. package/src/provider.tsx +192 -5
  37. package/src/timeline/parsers.ts +43 -6
  38. package/src/timeline/projection.ts +24 -2
  39. package/styles/index.css +22 -0
  40. package/dist/chunk-NFYVQWIB.js +0 -1377
  41. package/dist/chunk-NFYVQWIB.js.map +0 -1
@@ -1,1377 +0,0 @@
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-waiting" : "bg-og-status-running";
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 toneTextClass(tone) {
299
- return tone === "hot" ? "text-og-status-failed" : tone === "warn" ? "text-og-status-waiting" : "text-og-status-running";
300
- }
301
- function StatTriple({
302
- load1,
303
- load5,
304
- load15,
305
- runQueue
306
- }) {
307
- const tone = toneFor(load1 * 100);
308
- const stats = [
309
- { label: "1m", value: load1 },
310
- { label: "5m", value: load5 },
311
- { label: "15m", value: load15 }
312
- ];
313
- return /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between gap-3", "data-metric": "load", children: [
314
- /* @__PURE__ */ jsxs2("div", { className: "flex min-w-0 flex-col gap-1", children: [
315
- /* @__PURE__ */ jsx2("span", { className: "text-[10px] font-medium uppercase tracking-wide text-og-fg-subtle", children: "Load" }),
316
- /* @__PURE__ */ jsx2("div", { className: "flex items-baseline gap-3", children: stats.map((s) => /* @__PURE__ */ jsxs2("div", { className: "flex items-baseline gap-1", children: [
317
- /* @__PURE__ */ jsx2(
318
- "span",
319
- {
320
- className: cn(
321
- "text-[10px] font-medium uppercase tracking-wide",
322
- toneTextClass(tone)
323
- ),
324
- children: s.label
325
- }
326
- ),
327
- /* @__PURE__ */ jsx2("span", { className: cn("font-og-mono text-[12px] tabular-nums", toneTextClass(tone)), children: s.value.toFixed(2) })
328
- ] }, s.label)) })
329
- ] }),
330
- runQueue > 0 ? /* @__PURE__ */ jsxs2(
331
- "div",
332
- {
333
- 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",
334
- "data-metric": "runqueue",
335
- children: [
336
- /* @__PURE__ */ jsx2("span", { className: "font-medium uppercase tracking-wide", children: "Queue" }),
337
- /* @__PURE__ */ jsx2("span", { className: "font-og-mono tabular-nums text-og-fg-muted", children: runQueue })
338
- ]
339
- }
340
- ) : null
341
- ] });
342
- }
343
- function MachineMetrics({ metrics, density = "compact", className }) {
344
- if (!metrics) {
345
- return /* @__PURE__ */ jsx2("div", { className: cn("text-[11px] text-og-fg-subtle", className), "data-metrics-empty": true, children: "No metrics yet" });
346
- }
347
- const memPct = metrics.memTotalBytes > 0 ? metrics.memUsedBytes / metrics.memTotalBytes * 100 : 0;
348
- const diskPct = metrics.diskTotalBytes > 0 ? metrics.diskUsedBytes / metrics.diskTotalBytes * 100 : 0;
349
- const hasGpu = metrics.gpuUtilPct !== null;
350
- const gpuMemLabel = metrics.gpuMemBytes !== null ? formatBytes(metrics.gpuMemBytes) : null;
351
- const ratioGridClass = density === "full" ? "grid-cols-1" : "grid-cols-1 sm:grid-cols-2";
352
- return /* @__PURE__ */ jsxs2("div", { className: cn("flex flex-col gap-3", className), "data-machine-metrics": true, children: [
353
- /* @__PURE__ */ jsxs2("div", { className: cn("grid gap-x-4 gap-y-2.5", ratioGridClass), children: [
354
- /* @__PURE__ */ jsx2(
355
- Meter,
356
- {
357
- label: "Memory",
358
- value: `${formatBytes(metrics.memUsedBytes)} / ${formatBytes(metrics.memTotalBytes)}`,
359
- fillPct: memPct,
360
- tone: toneFor(memPct)
361
- }
362
- ),
363
- /* @__PURE__ */ jsx2(
364
- Meter,
365
- {
366
- label: "Disk",
367
- value: `${formatBytes(metrics.diskUsedBytes)} / ${formatBytes(metrics.diskTotalBytes)}`,
368
- fillPct: diskPct,
369
- tone: toneFor(diskPct)
370
- }
371
- )
372
- ] }),
373
- /* @__PURE__ */ jsxs2("div", { className: "grid grid-cols-2 gap-x-4 gap-y-2.5", children: [
374
- /* @__PURE__ */ jsx2(
375
- Meter,
376
- {
377
- label: "CPU",
378
- value: `${metrics.cpuPct.toFixed(0)}%`,
379
- fillPct: metrics.cpuPct,
380
- tone: toneFor(metrics.cpuPct)
381
- }
382
- ),
383
- hasGpu ? /* @__PURE__ */ jsx2(
384
- Meter,
385
- {
386
- label: "GPU",
387
- value: gpuMemLabel ? `${metrics.gpuUtilPct.toFixed(0)}% \xB7 ${gpuMemLabel}` : `${metrics.gpuUtilPct.toFixed(0)}%`,
388
- fillPct: metrics.gpuUtilPct,
389
- tone: toneFor(metrics.gpuUtilPct)
390
- }
391
- ) : null
392
- ] }),
393
- /* @__PURE__ */ jsx2(
394
- StatTriple,
395
- {
396
- load1: metrics.load1,
397
- load5: metrics.load5,
398
- load15: metrics.load15,
399
- runQueue: metrics.runQueue
400
- }
401
- )
402
- ] });
403
- }
404
-
405
- // src/components/machine-card.tsx
406
- import {
407
- CpuIcon,
408
- LaptopIcon,
409
- MonitorIcon,
410
- MonitorOffIcon,
411
- ServerIcon,
412
- UsersIcon
413
- } from "lucide-react";
414
- import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
415
- function KindIcon({ machine }) {
416
- if (machine.isSessionGroup)
417
- return /* @__PURE__ */ jsx3(ServerIcon, { className: "size-4 text-og-fg-subtle", "aria-hidden": true });
418
- if (machine.kind === "modal") return /* @__PURE__ */ jsx3(CpuIcon, { className: "size-4 text-og-fg-subtle", "aria-hidden": true });
419
- return /* @__PURE__ */ jsx3(LaptopIcon, { className: "size-4 text-og-fg-subtle", "aria-hidden": true });
420
- }
421
- function MachineCard({ machine, onAttach, attaching, className }) {
422
- const offline = machine.state === "offline";
423
- const attachable = !machine.active && !offline && Boolean(onAttach);
424
- const shared = machine.sharedSessionCount > 1;
425
- return /* @__PURE__ */ jsxs3(
426
- "div",
427
- {
428
- "data-machine-card": machine.sandboxId,
429
- "data-active": machine.active ? "true" : "false",
430
- className: cn(
431
- "og-root relative flex flex-col gap-3 overflow-hidden rounded-og-lg border border-og-border",
432
- "bg-og-surface-1 p-4 shadow-og-sm",
433
- machine.active && "border-og-accent/40",
434
- className
435
- ),
436
- children: [
437
- machine.active ? /* @__PURE__ */ jsx3("span", { "aria-hidden": true, className: "absolute inset-y-0 left-0 w-0.5 bg-og-accent" }) : null,
438
- /* @__PURE__ */ jsxs3("div", { className: "flex items-start justify-between gap-3", children: [
439
- /* @__PURE__ */ jsxs3("div", { className: "flex min-w-0 items-start gap-2.5", children: [
440
- /* @__PURE__ */ jsx3("span", { className: "mt-0.5 shrink-0", children: /* @__PURE__ */ jsx3(KindIcon, { machine }) }),
441
- /* @__PURE__ */ jsxs3("div", { className: "min-w-0", children: [
442
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
443
- /* @__PURE__ */ jsx3("span", { className: "truncate text-og-base font-medium text-og-fg", children: machine.name }),
444
- machine.active ? /* @__PURE__ */ jsx3(
445
- "span",
446
- {
447
- "data-active-marker": true,
448
- 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",
449
- children: "Active"
450
- }
451
- ) : null
452
- ] }),
453
- /* @__PURE__ */ jsxs3("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: [
454
- /* @__PURE__ */ jsx3("span", { className: "capitalize", children: machine.isSessionGroup ? "session sandbox" : machine.kind }),
455
- /* @__PURE__ */ jsx3("span", { "aria-hidden": true, children: "\xB7" }),
456
- /* @__PURE__ */ jsxs3("span", { className: "font-og-mono", children: [
457
- machine.os,
458
- "/",
459
- machine.arch
460
- ] }),
461
- /* @__PURE__ */ jsx3("span", { "aria-hidden": true, children: "\xB7" }),
462
- /* @__PURE__ */ jsxs3("span", { className: "inline-flex items-center gap-1", children: [
463
- machine.hasDisplay ? /* @__PURE__ */ jsx3(MonitorIcon, { className: "size-3", "aria-hidden": true }) : /* @__PURE__ */ jsx3(MonitorOffIcon, { className: "size-3", "aria-hidden": true }),
464
- machine.hasDisplay ? "display" : "headless"
465
- ] })
466
- ] })
467
- ] })
468
- ] }),
469
- /* @__PURE__ */ jsx3(
470
- MachineStatusPill,
471
- {
472
- state: machine.state,
473
- sharedSessionCount: machine.sharedSessionCount,
474
- size: "sm",
475
- className: "shrink-0"
476
- }
477
- )
478
- ] }),
479
- shared ? /* @__PURE__ */ jsxs3(
480
- "p",
481
- {
482
- "data-shared-disclosure": true,
483
- 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",
484
- children: [
485
- /* @__PURE__ */ jsx3(UsersIcon, { className: "size-3.5 shrink-0 text-og-accent", "aria-hidden": true }),
486
- "Shared \u2014 ",
487
- machine.sharedSessionCount,
488
- " sessions are on this machine."
489
- ]
490
- }
491
- ) : null,
492
- /* @__PURE__ */ jsx3(MachineMetrics, { metrics: machine.metrics }),
493
- /* @__PURE__ */ jsxs3("div", { className: "mt-1 flex items-center justify-between gap-3 text-og-xs text-og-fg-subtle", children: [
494
- /* @__PURE__ */ jsx3("span", { children: machine.lastSeenAt ? /* @__PURE__ */ jsxs3(Fragment, { children: [
495
- "Last seen ",
496
- formatRelativeTime(machine.lastSeenAt)
497
- ] }) : "Never connected" }),
498
- machine.active ? /* @__PURE__ */ jsx3("span", { className: "text-og-accent", children: "Routing here" }) : attachable ? /* @__PURE__ */ jsx3(
499
- "button",
500
- {
501
- type: "button",
502
- "data-attach": true,
503
- disabled: attaching,
504
- onClick: () => onAttach?.(machine),
505
- className: cn(
506
- "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",
507
- "hover:border-og-border-strong hover:text-og-fg disabled:cursor-not-allowed disabled:opacity-50"
508
- ),
509
- children: attaching ? "Switching\u2026" : "Attach"
510
- }
511
- ) : /* @__PURE__ */ jsx3("span", { className: "text-og-fg-subtle/70", children: offline ? "Unavailable" : "\u2014" })
512
- ] })
513
- ]
514
- }
515
- );
516
- }
517
-
518
- // src/components/machines-dashboard.tsx
519
- import { LaptopIcon as LaptopIcon2, PlusIcon, RefreshCwIcon } from "lucide-react";
520
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
521
- function EmptyState({ onEnroll }) {
522
- return /* @__PURE__ */ jsxs4(
523
- "div",
524
- {
525
- "data-machines-empty": true,
526
- 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",
527
- children: [
528
- /* @__PURE__ */ jsx4("span", { className: "flex size-10 items-center justify-center rounded-full bg-og-surface-2 text-og-fg-subtle", children: /* @__PURE__ */ jsx4(LaptopIcon2, { className: "size-5", "aria-hidden": true }) }),
529
- /* @__PURE__ */ jsxs4("div", { className: "space-y-1", children: [
530
- /* @__PURE__ */ jsx4("p", { className: "text-og-base font-medium text-og-fg", children: "No machines yet" }),
531
- /* @__PURE__ */ jsx4("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." })
532
- ] }),
533
- onEnroll ? /* @__PURE__ */ jsxs4(
534
- "button",
535
- {
536
- type: "button",
537
- "data-enroll-cta": true,
538
- onClick: onEnroll,
539
- 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",
540
- children: [
541
- /* @__PURE__ */ jsx4(PlusIcon, { className: "size-3.5", "aria-hidden": true }),
542
- "Enroll a machine"
543
- ]
544
- }
545
- ) : null
546
- ]
547
- }
548
- );
549
- }
550
- function Header({
551
- count,
552
- onEnroll,
553
- onRefresh,
554
- loading
555
- }) {
556
- return /* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between gap-3", children: [
557
- /* @__PURE__ */ jsxs4("div", { className: "flex items-baseline gap-2", children: [
558
- /* @__PURE__ */ jsx4("h2", { className: "text-og-base font-semibold text-og-fg", children: "Machines" }),
559
- /* @__PURE__ */ jsx4("span", { className: "font-og-mono text-og-xs text-og-fg-subtle", children: count })
560
- ] }),
561
- /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-1.5", children: [
562
- onRefresh ? /* @__PURE__ */ jsx4(
563
- "button",
564
- {
565
- type: "button",
566
- "data-refresh": true,
567
- onClick: onRefresh,
568
- title: "Refresh",
569
- className: "rounded-og-sm p-1.5 text-og-fg-subtle transition-colors hover:bg-og-surface-2 hover:text-og-fg",
570
- children: /* @__PURE__ */ jsx4(RefreshCwIcon, { className: cn("size-3.5", loading && "animate-og-spin"), "aria-hidden": true })
571
- }
572
- ) : null,
573
- onEnroll ? /* @__PURE__ */ jsxs4(
574
- "button",
575
- {
576
- type: "button",
577
- "data-enroll-cta": true,
578
- onClick: onEnroll,
579
- 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",
580
- children: [
581
- /* @__PURE__ */ jsx4(PlusIcon, { className: "size-3.5", "aria-hidden": true }),
582
- "Enroll machine"
583
- ]
584
- }
585
- ) : null
586
- ] })
587
- ] });
588
- }
589
- function MachinesDashboard({
590
- machines,
591
- activeSandboxId,
592
- loading,
593
- error,
594
- onAttach,
595
- attachingSandboxId,
596
- onEnroll,
597
- onRefresh,
598
- className
599
- }) {
600
- const isEmpty = !loading && !error && machines.length === 0;
601
- return /* @__PURE__ */ jsxs4("section", { "data-machines-dashboard": true, className: cn("og-root flex flex-col gap-3", className), children: [
602
- /* @__PURE__ */ jsx4(Header, { count: machines.length, onEnroll, onRefresh, loading }),
603
- error ? /* @__PURE__ */ jsxs4(
604
- "div",
605
- {
606
- "data-machines-error": true,
607
- 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",
608
- children: [
609
- /* @__PURE__ */ jsxs4("span", { children: [
610
- "Could not load machines: ",
611
- error.message
612
- ] }),
613
- onRefresh ? /* @__PURE__ */ jsxs4(
614
- "button",
615
- {
616
- type: "button",
617
- "data-machines-retry": true,
618
- onClick: onRefresh,
619
- disabled: loading,
620
- 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",
621
- children: [
622
- /* @__PURE__ */ jsx4(RefreshCwIcon, { className: cn("size-3.5", loading && "animate-og-spin"), "aria-hidden": true }),
623
- loading ? "Retrying\u2026" : "Retry"
624
- ]
625
- }
626
- ) : null
627
- ]
628
- }
629
- ) : null,
630
- loading && machines.length === 0 ? /* @__PURE__ */ jsx4("div", { "data-machines-loading": true, className: "grid gap-3 sm:grid-cols-2", children: [0, 1].map((i) => /* @__PURE__ */ jsx4(
631
- "div",
632
- {
633
- className: "h-36 animate-og-pulse rounded-og-lg border border-og-border bg-og-surface-1"
634
- },
635
- i
636
- )) }) : isEmpty ? /* @__PURE__ */ jsx4(EmptyState, { onEnroll }) : /* @__PURE__ */ jsx4("div", { className: "grid gap-3 sm:grid-cols-2 xl:grid-cols-3", "data-machines-grid": true, children: machines.map((machine) => /* @__PURE__ */ jsx4(
637
- MachineCard,
638
- {
639
- machine: {
640
- ...machine,
641
- active: machine.active || machine.sandboxId === activeSandboxId
642
- },
643
- onAttach,
644
- attaching: attachingSandboxId === machine.sandboxId
645
- },
646
- machine.sandboxId
647
- )) })
648
- ] });
649
- }
650
-
651
- // src/components/machine-dock-bar.tsx
652
- import { LaptopIcon as LaptopIcon3, CpuIcon as CpuIcon2, UsersIcon as UsersIcon2 } from "lucide-react";
653
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
654
- function MachineDockBar({ name, kind, state, className }) {
655
- const Icon = kind === "selfhosted" ? LaptopIcon3 : CpuIcon2;
656
- const stateBadge = MACHINE_STATE_BADGE_META[state];
657
- return /* @__PURE__ */ jsxs5(
658
- "div",
659
- {
660
- "data-machine-dock-bar": true,
661
- className: cn(
662
- "flex shrink-0 items-center justify-between gap-2 border-b border-og-border bg-og-surface-1 px-2.5 py-1",
663
- className
664
- ),
665
- children: [
666
- /* @__PURE__ */ jsxs5("div", { className: "flex min-w-0 items-center gap-1.5", children: [
667
- /* @__PURE__ */ jsx5(Icon, { className: "size-3.5 shrink-0 text-og-fg-subtle", "aria-hidden": true }),
668
- /* @__PURE__ */ jsx5("span", { className: "truncate text-[11px] font-medium text-og-fg", children: name }),
669
- stateBadge ? /* @__PURE__ */ jsx5(
670
- "span",
671
- {
672
- "data-state-badge": state,
673
- className: cn(
674
- "shrink-0 rounded-full border px-1.5 py-px text-[10px] font-medium",
675
- stateBadge.badgeClassName
676
- ),
677
- children: stateBadge.label
678
- }
679
- ) : null
680
- ] }),
681
- /* @__PURE__ */ jsx5(
682
- ConnectionStatusPill,
683
- {
684
- status: connectionStatusForState(state),
685
- size: "sm",
686
- className: "shrink-0"
687
- }
688
- )
689
- ]
690
- }
691
- );
692
- }
693
- function SharedMachineDisclosure({
694
- sharedSessionCount,
695
- density = "compact",
696
- className
697
- }) {
698
- const others = Math.max(0, sharedSessionCount - 1);
699
- return /* @__PURE__ */ jsxs5(
700
- "div",
701
- {
702
- "data-shared-disclosure": true,
703
- className: cn(
704
- "flex items-center gap-1.5 border-og-accent/25 bg-og-accent-soft text-og-fg-muted",
705
- density === "full" ? "rounded-og-md border px-3 py-2 text-[12px]" : "border-b px-2.5 py-1 text-[11px]",
706
- className
707
- ),
708
- children: [
709
- /* @__PURE__ */ jsx5(UsersIcon2, { className: "size-3.5 shrink-0 text-og-accent", "aria-hidden": true }),
710
- /* @__PURE__ */ jsxs5("span", { children: [
711
- "Shared \u2014 ",
712
- others === 1 ? "another session is" : `${others} other sessions are`,
713
- " on this machine. They see the same terminal, files, and desktop."
714
- ] })
715
- ]
716
- }
717
- );
718
- }
719
-
720
- // src/components/enrollment-device-flow.tsx
721
- import { CopyIcon, ExternalLinkIcon, LaptopIcon as LaptopIcon4, TerminalIcon } from "lucide-react";
722
- import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
723
- var PHASE_TO_STATE = {
724
- pending: "enrolling",
725
- authorized: "online",
726
- denied: "offline",
727
- expired: "offline",
728
- disabled: "offline"
729
- };
730
- function PhaseLabel({ phase }) {
731
- const text = {
732
- pending: "Waiting for approval",
733
- authorized: "Connected",
734
- denied: "Denied",
735
- expired: "Code expired",
736
- disabled: "Enrollment disabled"
737
- };
738
- return /* @__PURE__ */ jsx6("span", { children: text[phase] });
739
- }
740
- function EnrollmentDeviceFlow({
741
- userCode,
742
- verificationUri,
743
- verificationUriComplete,
744
- installCommand,
745
- phase = "pending",
746
- expiresInSeconds,
747
- onCopyCode,
748
- onCopyInstall,
749
- onOpenVerification,
750
- className
751
- }) {
752
- const href = verificationUriComplete ?? verificationUri;
753
- const codeIsPlaceholder = !/[A-Za-z0-9]/.test(userCode);
754
- const awaitingCode = phase === "pending" && codeIsPlaceholder;
755
- return /* @__PURE__ */ jsxs6(
756
- "div",
757
- {
758
- "data-enrollment-device-flow": true,
759
- className: cn(
760
- "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",
761
- className
762
- ),
763
- children: [
764
- /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between gap-3", children: [
765
- /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
766
- /* @__PURE__ */ jsx6("span", { className: "flex size-7 items-center justify-center rounded-full bg-og-surface-2 text-og-fg-muted", children: /* @__PURE__ */ jsx6(LaptopIcon4, { className: "size-4", "aria-hidden": true }) }),
767
- /* @__PURE__ */ jsx6("h2", { className: "text-sm font-semibold text-og-fg", children: "Connect a machine" })
768
- ] }),
769
- /* @__PURE__ */ jsx6(
770
- ConnectionStatusPill,
771
- {
772
- status: PHASE_TO_STATE[phase] === "online" ? "online" : PHASE_TO_STATE[phase] === "offline" ? "offline" : "reconnecting",
773
- label: /* @__PURE__ */ jsx6(PhaseLabel, { phase }),
774
- size: "sm"
775
- }
776
- )
777
- ] }),
778
- installCommand ? /* @__PURE__ */ jsxs6("div", { className: "flex flex-col gap-1.5", children: [
779
- /* @__PURE__ */ jsx6("span", { className: "text-[11px] font-medium uppercase tracking-wide text-og-fg-subtle", children: "1 \xB7 Run on the machine" }),
780
- /* @__PURE__ */ jsxs6("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: [
781
- /* @__PURE__ */ jsxs6("code", { className: "flex min-w-0 items-center gap-2 overflow-x-auto font-og-mono text-[12px] text-og-fg", children: [
782
- /* @__PURE__ */ jsx6(TerminalIcon, { className: "size-3.5 shrink-0 text-og-fg-subtle", "aria-hidden": true }),
783
- installCommand
784
- ] }),
785
- /* @__PURE__ */ jsx6(
786
- "button",
787
- {
788
- type: "button",
789
- "data-copy-install": true,
790
- onClick: onCopyInstall,
791
- title: "Copy command",
792
- className: "shrink-0 rounded-og-sm p-1.5 text-og-fg-subtle transition-colors hover:bg-og-surface-2 hover:text-og-fg",
793
- children: /* @__PURE__ */ jsx6(CopyIcon, { className: "size-4", "aria-hidden": true })
794
- }
795
- )
796
- ] })
797
- ] }) : null,
798
- /* @__PURE__ */ jsxs6("div", { className: "flex flex-col gap-1.5", children: [
799
- /* @__PURE__ */ jsx6("span", { className: "text-[11px] font-medium uppercase tracking-wide text-og-fg-subtle", children: installCommand ? "2 \xB7 Approve the machine" : "Approve the machine" }),
800
- /* @__PURE__ */ jsxs6("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: [
801
- awaitingCode ? /* @__PURE__ */ jsx6("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__ */ jsx6(
802
- "span",
803
- {
804
- "data-user-code": true,
805
- className: "select-all font-og-mono text-2xl font-semibold tracking-[0.25em] text-og-fg",
806
- children: userCode
807
- }
808
- ),
809
- /* @__PURE__ */ jsx6(
810
- "button",
811
- {
812
- type: "button",
813
- "data-copy-code": true,
814
- onClick: onCopyCode,
815
- disabled: awaitingCode,
816
- title: "Copy code",
817
- 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",
818
- children: /* @__PURE__ */ jsx6(CopyIcon, { className: "size-4", "aria-hidden": true })
819
- }
820
- )
821
- ] })
822
- ] }),
823
- /* @__PURE__ */ jsxs6(
824
- "a",
825
- {
826
- href,
827
- "data-open-verification": true,
828
- target: "_blank",
829
- rel: "noreferrer",
830
- onClick: onOpenVerification,
831
- 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",
832
- children: [
833
- "Open approval page",
834
- /* @__PURE__ */ jsx6(ExternalLinkIcon, { className: "size-3.5", "aria-hidden": true })
835
- ]
836
- }
837
- ),
838
- /* @__PURE__ */ jsx6("p", { className: "text-center text-[11px] text-og-fg-subtle", children: awaitingCode ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
839
- "The one-liner prints a short code. Open",
840
- " ",
841
- /* @__PURE__ */ jsx6("span", { className: "font-og-mono text-og-fg-muted", children: verificationUri }),
842
- " and confirm the code shown there matches."
843
- ] }) : /* @__PURE__ */ jsxs6(Fragment2, { children: [
844
- "Confirm code ",
845
- /* @__PURE__ */ jsx6("span", { className: "font-og-mono text-og-fg-muted", children: userCode }),
846
- " \u2014 the one printed by the one-liner \u2014 at",
847
- " ",
848
- /* @__PURE__ */ jsx6("span", { className: "font-og-mono text-og-fg-muted", children: verificationUri }),
849
- ".",
850
- typeof expiresInSeconds === "number" ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
851
- " Expires in ",
852
- Math.max(0, Math.round(expiresInSeconds / 60)),
853
- " min."
854
- ] }) : null
855
- ] }) })
856
- ]
857
- }
858
- );
859
- }
860
-
861
- // src/components/enrollment-consent.tsx
862
- import { useState } from "react";
863
- import {
864
- CircleAlertIcon,
865
- LaptopIcon as LaptopIcon5,
866
- MonitorIcon as MonitorIcon2,
867
- ScreenShareIcon,
868
- ShieldAlertIcon,
869
- TerminalIcon as TerminalIcon2,
870
- UsersIcon as UsersIcon3
871
- } from "lucide-react";
872
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
873
- function Capability({ icon, title, body }) {
874
- return /* @__PURE__ */ jsxs7("li", { className: "flex items-start gap-2.5", children: [
875
- /* @__PURE__ */ jsx7("span", { className: "mt-0.5 shrink-0 text-og-status-failed", children: icon }),
876
- /* @__PURE__ */ jsxs7("span", { className: "min-w-0", children: [
877
- /* @__PURE__ */ jsx7("span", { className: "block text-og-base font-medium text-og-fg", children: title }),
878
- /* @__PURE__ */ jsx7("span", { className: "block text-og-sm text-og-fg-muted", children: body })
879
- ] })
880
- ] });
881
- }
882
- function EnrollmentConsent({
883
- userCode,
884
- machine,
885
- phase = "review",
886
- onApprove,
887
- onDeny,
888
- errorMessage,
889
- className
890
- }) {
891
- const [allowScreenControl, setAllowScreenControl] = useState(machine.requestsScreenControl);
892
- const busy = phase === "approving";
893
- if (phase === "approved") {
894
- return /* @__PURE__ */ jsx7(
895
- ConsentResult,
896
- {
897
- className,
898
- tone: "ok",
899
- title: "Machine connected",
900
- body: `${machine.machineName} is now enrolled in this workspace. You can close this page \u2014 it will appear in your Machines dashboard.`
901
- }
902
- );
903
- }
904
- if (phase === "denied") {
905
- return /* @__PURE__ */ jsx7(
906
- ConsentResult,
907
- {
908
- className,
909
- tone: "muted",
910
- title: "Enrollment denied",
911
- 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.`
912
- }
913
- );
914
- }
915
- if (phase === "error") {
916
- return /* @__PURE__ */ jsx7(
917
- ConsentResult,
918
- {
919
- className,
920
- tone: "danger",
921
- title: "Could not complete enrollment",
922
- body: errorMessage ?? "The code may have expired. Re-run the install one-liner on the machine for a fresh code."
923
- }
924
- );
925
- }
926
- return /* @__PURE__ */ jsxs7(
927
- "div",
928
- {
929
- "data-enrollment-consent": true,
930
- className: cn(
931
- "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",
932
- className
933
- ),
934
- children: [
935
- /* @__PURE__ */ jsxs7("div", { className: "flex items-start gap-3", children: [
936
- /* @__PURE__ */ jsx7("span", { className: "flex size-9 shrink-0 items-center justify-center rounded-full bg-og-status-failed/15 text-og-status-failed", children: /* @__PURE__ */ jsx7(ShieldAlertIcon, { className: "size-5", "aria-hidden": true }) }),
937
- /* @__PURE__ */ jsxs7("div", { className: "min-w-0", children: [
938
- /* @__PURE__ */ jsx7("h1", { className: "text-og-md font-semibold text-og-fg", children: "Give the agent your whole machine?" }),
939
- /* @__PURE__ */ jsxs7("p", { className: "mt-1 text-og-base text-og-fg-muted", children: [
940
- "Approving lets the OpenGeni agent run on",
941
- " ",
942
- /* @__PURE__ */ jsx7("span", { className: "font-medium text-og-fg", children: machine.machineName }),
943
- " with full access. This is your real computer \u2014 not a sandbox."
944
- ] })
945
- ] })
946
- ] }),
947
- /* @__PURE__ */ jsxs7("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: [
948
- /* @__PURE__ */ jsx7(LaptopIcon5, { className: "size-4 text-og-fg-subtle", "aria-hidden": true }),
949
- /* @__PURE__ */ jsx7("span", { className: "font-medium text-og-fg", children: machine.machineName }),
950
- /* @__PURE__ */ jsxs7("span", { className: "font-og-mono text-og-fg-subtle", children: [
951
- machine.os,
952
- "/",
953
- machine.arch
954
- ] }),
955
- /* @__PURE__ */ jsx7("span", { "aria-hidden": true, className: "text-og-fg-subtle", children: "\xB7" }),
956
- /* @__PURE__ */ jsxs7("span", { className: "text-og-fg-subtle", children: [
957
- "code ",
958
- /* @__PURE__ */ jsx7("span", { className: "font-og-mono font-medium text-og-fg", children: userCode })
959
- ] })
960
- ] }),
961
- /* @__PURE__ */ jsxs7("ul", { className: "flex flex-col gap-3", children: [
962
- /* @__PURE__ */ jsx7(
963
- Capability,
964
- {
965
- icon: /* @__PURE__ */ jsx7(TerminalIcon2, { className: "size-4", "aria-hidden": true }),
966
- title: "Read, write & run anything",
967
- 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."
968
- }
969
- ),
970
- machine.canOfferDisplay ? /* @__PURE__ */ jsx7(
971
- Capability,
972
- {
973
- icon: /* @__PURE__ */ jsx7(MonitorIcon2, { className: "size-4", "aria-hidden": true }),
974
- title: "See your screen",
975
- body: "The agent can capture this machine's display to watch what it is doing."
976
- }
977
- ) : null,
978
- /* @__PURE__ */ jsx7(
979
- Capability,
980
- {
981
- icon: /* @__PURE__ */ jsx7(UsersIcon3, { className: "size-4", "aria-hidden": true }),
982
- title: "Shared while connected",
983
- body: "Any session in this workspace can use this machine while it is online. Disconnect the agent to revoke access instantly."
984
- }
985
- )
986
- ] }),
987
- machine.canOfferDisplay ? /* @__PURE__ */ jsxs7(
988
- "label",
989
- {
990
- "data-screen-control-toggle": true,
991
- className: "flex cursor-pointer items-start gap-3 rounded-og-md border border-og-border bg-og-bg px-3 py-2.5",
992
- children: [
993
- /* @__PURE__ */ jsx7(
994
- "input",
995
- {
996
- type: "checkbox",
997
- checked: allowScreenControl,
998
- disabled: busy,
999
- onChange: (e) => setAllowScreenControl(e.target.checked),
1000
- className: "mt-0.5 size-4 accent-og-accent"
1001
- }
1002
- ),
1003
- /* @__PURE__ */ jsxs7("span", { className: "min-w-0", children: [
1004
- /* @__PURE__ */ jsxs7("span", { className: "flex items-center gap-1.5 text-og-base font-medium text-og-fg", children: [
1005
- /* @__PURE__ */ jsx7(ScreenShareIcon, { className: "size-3.5 text-og-fg-muted", "aria-hidden": true }),
1006
- "Also let the agent control my mouse & keyboard"
1007
- ] }),
1008
- /* @__PURE__ */ jsx7("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." })
1009
- ] })
1010
- ]
1011
- }
1012
- ) : /* @__PURE__ */ jsxs7("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: [
1013
- /* @__PURE__ */ jsx7(CircleAlertIcon, { className: "mt-px size-3.5 shrink-0 text-og-fg-subtle", "aria-hidden": true }),
1014
- "This machine has no display, so screen control isn't available \u2014 files, terminal, and git only."
1015
- ] }),
1016
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2", children: [
1017
- /* @__PURE__ */ jsx7(
1018
- "button",
1019
- {
1020
- type: "button",
1021
- "data-deny": true,
1022
- disabled: busy,
1023
- onClick: () => onDeny?.(),
1024
- 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",
1025
- children: "Cancel"
1026
- }
1027
- ),
1028
- /* @__PURE__ */ jsx7(
1029
- "button",
1030
- {
1031
- type: "button",
1032
- "data-approve": true,
1033
- disabled: busy,
1034
- onClick: () => onApprove?.(allowScreenControl),
1035
- 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",
1036
- children: busy ? "Connecting\u2026" : "Grant full access"
1037
- }
1038
- )
1039
- ] })
1040
- ]
1041
- }
1042
- );
1043
- }
1044
- function ConsentResult({
1045
- tone,
1046
- title,
1047
- body,
1048
- className
1049
- }) {
1050
- const ring = tone === "ok" ? "border-og-status-running/30" : tone === "danger" ? "border-og-status-failed/30" : "border-og-border";
1051
- const iconClass = tone === "ok" ? "text-og-status-running" : tone === "danger" ? "text-og-status-failed" : "text-og-fg-subtle";
1052
- return /* @__PURE__ */ jsxs7(
1053
- "div",
1054
- {
1055
- "data-enrollment-result": tone,
1056
- className: cn(
1057
- "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",
1058
- ring,
1059
- className
1060
- ),
1061
- children: [
1062
- /* @__PURE__ */ jsx7(
1063
- "span",
1064
- {
1065
- className: cn(
1066
- "flex size-10 items-center justify-center rounded-full bg-og-surface-2",
1067
- iconClass
1068
- ),
1069
- children: /* @__PURE__ */ jsx7(LaptopIcon5, { className: "size-5", "aria-hidden": true })
1070
- }
1071
- ),
1072
- /* @__PURE__ */ jsx7("h1", { className: "text-og-md font-semibold text-og-fg", children: title }),
1073
- /* @__PURE__ */ jsx7("p", { className: "max-w-sm text-og-base text-og-fg-muted", children: body })
1074
- ]
1075
- }
1076
- );
1077
- }
1078
-
1079
- // src/hooks/use-machines.ts
1080
- import { useCallback as useCallback2, useState as useState3 } from "react";
1081
-
1082
- // src/provider.tsx
1083
- import { createContext, useContext, useMemo } from "react";
1084
- import { jsx as jsx8 } from "react/jsx-runtime";
1085
- var OpenGeniContext = createContext(null);
1086
- function OpenGeniProvider({ client, workspaceId, children }) {
1087
- const value = useMemo(() => ({ client, workspaceId }), [client, workspaceId]);
1088
- return /* @__PURE__ */ jsx8(OpenGeniContext.Provider, { value, children });
1089
- }
1090
- function useOpenGeni(override = {}) {
1091
- const context = useContext(OpenGeniContext);
1092
- const client = override.client ?? context?.client;
1093
- const workspaceId = override.workspaceId ?? context?.workspaceId;
1094
- if (!client || !workspaceId) {
1095
- throw new Error(
1096
- "@opengeni/react: no OpenGeni client/workspace available. Wrap the tree in <OpenGeniProvider> or pass { client, workspaceId } to the hook."
1097
- );
1098
- }
1099
- return { client, workspaceId };
1100
- }
1101
- function useOpenGeniClient(override = {}) {
1102
- const context = useContext(OpenGeniContext);
1103
- const client = override.client ?? context?.client;
1104
- if (!client) {
1105
- throw new Error(
1106
- "@opengeni/react: no OpenGeni client available. Wrap the tree in <OpenGeniProvider> or pass { client } to the hook."
1107
- );
1108
- }
1109
- return client;
1110
- }
1111
-
1112
- // src/hooks/internal.ts
1113
- import { useCallback, useEffect, useRef, useState as useState2 } from "react";
1114
- function usePolledValue(load, options = {}) {
1115
- const enabled = options.enabled ?? true;
1116
- const pollIntervalMs = options.pollIntervalMs;
1117
- const [data, setData] = useState2(null);
1118
- const [loading, setLoading] = useState2(enabled);
1119
- const [error, setError] = useState2(null);
1120
- const generation = useRef(0);
1121
- const loadRef = useRef(load);
1122
- useEffect(() => {
1123
- if (loadRef.current !== load) {
1124
- loadRef.current = load;
1125
- setData(null);
1126
- setError(null);
1127
- }
1128
- }, [load]);
1129
- const run = useCallback(async () => {
1130
- const ticket = ++generation.current;
1131
- try {
1132
- const result = await load();
1133
- if (ticket === generation.current) {
1134
- setData(result);
1135
- setError(null);
1136
- setLoading(false);
1137
- }
1138
- } catch (cause) {
1139
- if (ticket === generation.current) {
1140
- setError(cause instanceof Error ? cause : new Error(String(cause)));
1141
- setLoading(false);
1142
- }
1143
- }
1144
- }, [load]);
1145
- useEffect(() => {
1146
- if (!enabled) {
1147
- setLoading(false);
1148
- return;
1149
- }
1150
- setLoading(true);
1151
- void run();
1152
- if (pollIntervalMs === void 0 || pollIntervalMs <= 0) {
1153
- return () => {
1154
- generation.current += 1;
1155
- };
1156
- }
1157
- const timer = setInterval(() => void run(), pollIntervalMs);
1158
- return () => {
1159
- clearInterval(timer);
1160
- generation.current += 1;
1161
- };
1162
- }, [run, enabled, pollIntervalMs]);
1163
- return { data, loading, error, refresh: run };
1164
- }
1165
- function useMutationRunner() {
1166
- const [mutating, setMutating] = useState2(false);
1167
- const [mutationError, setMutationError] = useState2(null);
1168
- const inFlight = useRef(0);
1169
- const mounted = useRef(true);
1170
- useEffect(() => {
1171
- mounted.current = true;
1172
- return () => {
1173
- mounted.current = false;
1174
- };
1175
- }, []);
1176
- const run = useCallback(async (operation) => {
1177
- inFlight.current += 1;
1178
- if (mounted.current) {
1179
- setMutating(true);
1180
- setMutationError(null);
1181
- }
1182
- try {
1183
- return await operation();
1184
- } catch (cause) {
1185
- if (mounted.current) {
1186
- setMutationError(cause instanceof Error ? cause : new Error(String(cause)));
1187
- }
1188
- return null;
1189
- } finally {
1190
- inFlight.current -= 1;
1191
- if (mounted.current && inFlight.current === 0) {
1192
- setMutating(false);
1193
- }
1194
- }
1195
- }, []);
1196
- return {
1197
- mutating,
1198
- mutationError,
1199
- clearMutationError: useCallback(() => setMutationError(null), []),
1200
- run
1201
- };
1202
- }
1203
- function useSessionEventTrigger(client, workspaceId, sessionId, match, onEvent, options = {}) {
1204
- const enabled = options.enabled ?? true;
1205
- const events = options.events;
1206
- const sharedFeed = events !== void 0;
1207
- const matchRef = useRef(match);
1208
- matchRef.current = match;
1209
- const onEventRef = useRef(onEvent);
1210
- onEventRef.current = onEvent;
1211
- const consumedRef = useRef(0);
1212
- const feedKeyRef = useRef(null);
1213
- useEffect(() => {
1214
- if (!sharedFeed || !enabled || !sessionId) {
1215
- return;
1216
- }
1217
- const feedKey = `${workspaceId}\0${sessionId}`;
1218
- const firstSequence = events[0]?.sequence ?? 0;
1219
- if (feedKeyRef.current !== feedKey || firstSequence > consumedRef.current + 1) {
1220
- feedKeyRef.current = feedKey;
1221
- consumedRef.current = 0;
1222
- }
1223
- for (const event of events) {
1224
- if (event.sequence <= consumedRef.current) {
1225
- continue;
1226
- }
1227
- consumedRef.current = event.sequence;
1228
- if (matchRef.current(event)) {
1229
- onEventRef.current(event);
1230
- }
1231
- }
1232
- }, [sharedFeed, enabled, events, workspaceId, sessionId]);
1233
- useEffect(() => {
1234
- if (sharedFeed || !enabled || !sessionId) {
1235
- return;
1236
- }
1237
- const controller = new AbortController();
1238
- void (async () => {
1239
- try {
1240
- const session = await client.getSession(workspaceId, sessionId);
1241
- if (controller.signal.aborted) {
1242
- return;
1243
- }
1244
- const stream = client.streamEvents(workspaceId, sessionId, {
1245
- after: session.lastSequence,
1246
- signal: controller.signal
1247
- });
1248
- for await (const event of stream) {
1249
- if (matchRef.current(event)) {
1250
- onEventRef.current(event);
1251
- }
1252
- }
1253
- } catch {
1254
- }
1255
- })();
1256
- return () => {
1257
- controller.abort();
1258
- };
1259
- }, [sharedFeed, enabled, client, workspaceId, sessionId]);
1260
- }
1261
- function useDebouncedCallback(callback, delayMs = 150) {
1262
- const callbackRef = useRef(callback);
1263
- callbackRef.current = callback;
1264
- const timerRef = useRef(null);
1265
- useEffect(() => {
1266
- return () => {
1267
- if (timerRef.current !== null) {
1268
- clearTimeout(timerRef.current);
1269
- }
1270
- };
1271
- }, []);
1272
- return useCallback(() => {
1273
- if (timerRef.current !== null) {
1274
- clearTimeout(timerRef.current);
1275
- }
1276
- timerRef.current = setTimeout(() => {
1277
- timerRef.current = null;
1278
- callbackRef.current();
1279
- }, delayMs);
1280
- }, [delayMs]);
1281
- }
1282
-
1283
- // src/hooks/use-machines.ts
1284
- var EMPTY = { activeSandboxId: null, activeEpoch: 0, machines: [] };
1285
- function useMachines(options = {}) {
1286
- const { client, workspaceId } = useOpenGeni(options);
1287
- const machinesClient = options.machinesClient ?? client;
1288
- const sessionId = options.sessionId;
1289
- const load = useCallback2(async () => {
1290
- return await machinesClient.listMachines(workspaceId, sessionId ? { sessionId } : void 0);
1291
- }, [machinesClient, workspaceId, sessionId]);
1292
- const {
1293
- data: loadedData,
1294
- loading,
1295
- error,
1296
- refresh
1297
- } = usePolledValue(load, {
1298
- pollIntervalMs: options.pollIntervalMs,
1299
- enabled: options.enabled
1300
- });
1301
- const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
1302
- const [attachingSandboxId, setAttachingSandboxId] = useState3(null);
1303
- const data = loadedData ?? EMPTY;
1304
- const canAttach = sessionId !== void 0 && (typeof machinesClient.attachMachine === "function" || typeof machinesClient.swapActiveSandbox === "function");
1305
- const attach = useCallback2(
1306
- async (sandboxId) => {
1307
- if (sessionId === void 0) return false;
1308
- const runSwap = machinesClient.attachMachine ? () => machinesClient.attachMachine(workspaceId, sessionId, sandboxId) : machinesClient.swapActiveSandbox ? () => machinesClient.swapActiveSandbox(workspaceId, sessionId, { target: sandboxId }) : null;
1309
- if (!runSwap) return false;
1310
- setAttachingSandboxId(sandboxId);
1311
- const result = await run(async () => {
1312
- await runSwap();
1313
- return true;
1314
- });
1315
- setAttachingSandboxId(null);
1316
- if (result) await refresh();
1317
- return result === true;
1318
- },
1319
- [machinesClient, workspaceId, sessionId, run, refresh]
1320
- );
1321
- const fetchSeries = useCallback2(
1322
- async (enrollmentId, window = "1h") => {
1323
- if (!machinesClient.machineMetricsSeries) return [];
1324
- return await machinesClient.machineMetricsSeries(workspaceId, enrollmentId, { window });
1325
- },
1326
- [machinesClient, workspaceId]
1327
- );
1328
- return {
1329
- machines: data.machines,
1330
- activeSandboxId: data.activeSandboxId,
1331
- activeEpoch: data.activeEpoch,
1332
- loading,
1333
- error,
1334
- refresh,
1335
- attach,
1336
- canAttach,
1337
- fetchSeries,
1338
- attaching: mutating,
1339
- attachingSandboxId,
1340
- mutationError,
1341
- clearMutationError
1342
- };
1343
- }
1344
-
1345
- export {
1346
- OpenGeniProvider,
1347
- useOpenGeni,
1348
- useOpenGeniClient,
1349
- usePolledValue,
1350
- useMutationRunner,
1351
- useSessionEventTrigger,
1352
- useDebouncedCallback,
1353
- formatRelativeTime,
1354
- formatBytes,
1355
- truncate,
1356
- stringifyPayload,
1357
- tryParseJson,
1358
- CREDIT_EXHAUSTION_MESSAGE,
1359
- isCreditExhaustion,
1360
- humanizeFailureReason,
1361
- cn,
1362
- useMachines,
1363
- connectionStatusForState,
1364
- CONNECTION_STATUS_META,
1365
- MACHINE_STATE_BADGE_META,
1366
- ConnectionStatusPill,
1367
- ConnectionDot,
1368
- MachineStatusPill,
1369
- MachineDockBar,
1370
- SharedMachineDisclosure,
1371
- MachineMetrics,
1372
- MachineCard,
1373
- MachinesDashboard,
1374
- EnrollmentDeviceFlow,
1375
- EnrollmentConsent
1376
- };
1377
- //# sourceMappingURL=chunk-NFYVQWIB.js.map