skydive-cli 0.5.0-beta.5 → 0.5.0-beta.52

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 (38) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +60 -12
  3. package/dist/js/api-TwLD7ibI.mjs +315 -0
  4. package/dist/js/{billing-blocked-Dgu5-oDy.mjs → billing-blocked-SE6tySLd.mjs} +1 -1
  5. package/dist/js/bin.mjs +658 -279
  6. package/dist/js/{boot-Brwh0it_.mjs → boot-D_VFIRJj.mjs} +3556 -995
  7. package/dist/js/chunk-BbwQpWto.mjs +33 -0
  8. package/dist/js/{client-Cn2af31H.mjs → client-Btq6bMzX.mjs} +108 -2
  9. package/dist/js/client-CkPQG8M1.mjs +5 -0
  10. package/dist/js/client-aFwHzCPG.mjs +963 -0
  11. package/dist/js/daemon-CORAITjj.mjs +7 -0
  12. package/dist/js/{daemon-client-DcuD4v12.mjs → daemon-client-Cfi66Xy9.mjs} +12 -8
  13. package/dist/js/daemon-client-DJPW9cRp.mjs +8 -0
  14. package/dist/js/{daemon-BhArnzeW.mjs → daemon-uzpOdRPL.mjs} +135 -10
  15. package/dist/js/dist-CRtjM7ba.mjs +1750 -0
  16. package/dist/js/forward-Ct8Vd2WW.mjs +208 -0
  17. package/dist/js/{profiler-LLaIFZgn.mjs → install-BBplm-Zp.mjs} +529 -215
  18. package/dist/js/launcher.mjs +49 -0
  19. package/dist/js/localhost-cert-Bn-UBUmj.mjs +67 -0
  20. package/dist/js/{print-ba_0hiV9.mjs → print-Cuao6LN6.mjs} +3 -3
  21. package/dist/js/{print-CbayCa87.mjs → print-DR6Gas-M.mjs} +291 -39
  22. package/dist/js/{print-share-uwUG16Ov.mjs → print-share-BypBUrNh.mjs} +9 -3
  23. package/dist/js/raw-pty-Ci2qFR9F.mjs +5 -0
  24. package/dist/js/{raw-pty-B6mAroiI.mjs → raw-pty-D5PhKZSl.mjs} +1 -1
  25. package/dist/js/rest-CJkBP2Jz.mjs +6 -0
  26. package/dist/js/{rest-BY2nADw5.mjs → rest-DkuT5_oX.mjs} +117 -28
  27. package/dist/js/tls-cert-CLgSQALB.mjs +4 -0
  28. package/dist/js/tls-cert-CV-pwxVN.mjs +67 -0
  29. package/package.json +11 -4
  30. package/dist/js/client-DZstLQ_1.mjs +0 -4
  31. package/dist/js/client-Dd5sMXPv.mjs +0 -620
  32. package/dist/js/daemon-CjzrUvXx.mjs +0 -5
  33. package/dist/js/daemon-client-D8bNXTxu.mjs +0 -6
  34. package/dist/js/raw-pty-3EkG-jjH.mjs +0 -5
  35. package/dist/js/rest-DADJh0bi.mjs +0 -6
  36. /package/dist/js/{billing-blocked-2wju4gC_.mjs → billing-blocked-D3l5kJlX.mjs} +0 -0
  37. /package/dist/js/{http-error-DzyrsLAZ.mjs → http-error-BF2NZZE3.mjs} +0 -0
  38. /package/dist/js/{output-DYzzdXYV.mjs → output-C9mb3sUB.mjs} +0 -0
@@ -1,620 +0,0 @@
1
- #!/usr/bin/env node
2
- import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
3
- import os from "node:os";
4
- import { z } from "zod";
5
- import { execFile, spawn } from "node:child_process";
6
- import { WebSocket } from "ws";
7
-
8
- //#region ../portal-daemon/src/machine.ts
9
- /**
10
- * Identity this machine registers under on the portal.
11
- *
12
- * One device row per (org, user, machine): the daemon owns the host's single
13
- * portal connection, and every surface (CLI, TUI, desktop app) shares that
14
- * row, so a grant applies to the machine no matter which surface the user
15
- * shared from. The name must therefore match what the desktop app registers —
16
- * on macOS that is `scutil --get LocalHostName` (the Rust client's own
17
- * derivation, stabler than the network-assigned hostname), with the plain
18
- * hostname as the fallback for other platforms or a failed read.
19
- *
20
- * Historically the CLI suffixed `-cli` to keep a device distinct from the
21
- * desktop's; `unifyLegacyCliGrants` migrates grants off those rows.
22
- */
23
- async function resolveMachineIdentity() {
24
- const fallback = fallbackHost();
25
- if (process.platform !== "darwin") return {
26
- machineName: fallback,
27
- friendlyName: fallback
28
- };
29
- const local = await scutilRead("LocalHostName") ?? fallback;
30
- return {
31
- machineName: local,
32
- friendlyName: await scutilRead("ComputerName") ?? local
33
- };
34
- }
35
- function fallbackHost() {
36
- return (os.hostname() || "machine").trim().replace(/\.local$/i, "") || "machine";
37
- }
38
- function scutilRead(key) {
39
- return new Promise((resolve) => {
40
- execFile("/usr/sbin/scutil", ["--get", key], { timeout: 2e3 }, (error, stdout) => {
41
- if (error) {
42
- resolve(null);
43
- return;
44
- }
45
- const value = stdout.trim();
46
- resolve(value.length > 0 ? value : null);
47
- });
48
- });
49
- }
50
- const INHERITED_ENV = [
51
- "HOME",
52
- "USER",
53
- "LOGNAME",
54
- "SHELL",
55
- "LANG",
56
- "LC_ALL",
57
- "TMPDIR",
58
- "TERM",
59
- "PATH"
60
- ];
61
- const DARWIN_PATH_PREPEND = ["/opt/homebrew/bin", "/usr/local/bin"];
62
- function buildEnv(extra) {
63
- const env = {};
64
- for (const key of INHERITED_ENV) {
65
- const value = process.env[key];
66
- if (value !== void 0) env[key] = value;
67
- }
68
- if (process.platform === "darwin") {
69
- const current = env.PATH ? env.PATH.split(":") : [];
70
- const missing = DARWIN_PATH_PREPEND.filter((dir) => !current.includes(dir));
71
- if (missing.length > 0) env.PATH = [...missing, ...current].join(":");
72
- }
73
- if (extra) for (const [key, value] of Object.entries(extra)) env[key] = value;
74
- return env;
75
- }
76
- /**
77
- * Build the desktop-portal WebSocket URL from the chat origin. Mirrors the Rust
78
- * desktop client: http→ws, https→wss, scheme-less defaults to wss, and the
79
- * machine/label ride as query pairs (percent-encoded by URL).
80
- */
81
- function portalWsUrl(appUrl, machine, label) {
82
- const base = appUrl.replace(/\/+$/, "");
83
- let wsBase;
84
- if (base.startsWith("https://")) wsBase = `wss://${base.slice(8)}`;
85
- else if (base.startsWith("http://")) wsBase = `ws://${base.slice(7)}`;
86
- else wsBase = `wss://${base}`;
87
- const url = new URL(`${wsBase}/api/v1/portal/desktop`);
88
- url.searchParams.set("machine", machine);
89
- url.searchParams.set("label", label);
90
- return url.toString();
91
- }
92
-
93
- //#endregion
94
- //#region ../portal-daemon/src/util.ts
95
- /** Narrowing helper for the unknown JSON payloads the daemon reads (persisted
96
- * state, wire frames). A type predicate (not an `as` cast), so call sites can
97
- * read properties without asserting. */
98
- function isRecord(value) {
99
- return typeof value === "object" && value !== null && !Array.isArray(value);
100
- }
101
- /** Best-effort message from an unknown thrown value. */
102
- function errorMessage(err) {
103
- return err instanceof Error ? err.message : String(err);
104
- }
105
-
106
- //#endregion
107
- //#region ../portal-protocol/src/index.ts
108
- const MAX_WS_FRAME_BYTES = 16 * 1024 * 1024;
109
- const T_DATA = 1;
110
- const T_CTRL = 2;
111
- const STREAM = {
112
- stdout: 0,
113
- stderr: 1,
114
- stdin: 2
115
- };
116
- const uuidToBytes = (id) => Buffer.from(id.replace(/-/g, ""), "hex");
117
- const bytesToUuid = (b) => {
118
- const h = b.toString("hex");
119
- return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20, 32)}`;
120
- };
121
- function encodeData(id, stream, seq, payload) {
122
- const head = Buffer.allocUnsafe(22);
123
- head[0] = T_DATA;
124
- uuidToBytes(id).copy(head, 1);
125
- head[17] = stream;
126
- head.writeUInt32BE(seq >>> 0, 18);
127
- return Buffer.concat([head, payload]);
128
- }
129
- const ctrlMessageSchema = z.discriminatedUnion("t", [
130
- z.object({
131
- t: z.literal("open"),
132
- argv: z.array(z.string()),
133
- env: z.record(z.string()).nullable(),
134
- conversationId: z.string().nullable().optional()
135
- }),
136
- z.object({ t: z.literal("stdin_eof") }),
137
- z.object({ t: z.literal("pause") }),
138
- z.object({ t: z.literal("resume") }),
139
- z.object({ t: z.literal("cancel") }),
140
- z.object({
141
- t: z.literal("close"),
142
- exitCode: z.number(),
143
- frames: z.number().int().nonnegative().optional()
144
- }),
145
- z.object({
146
- t: z.literal("error"),
147
- message: z.string()
148
- })
149
- ]);
150
- function encodeCtrl(id, obj) {
151
- const head = Buffer.allocUnsafe(17);
152
- head[0] = T_CTRL;
153
- uuidToBytes(id).copy(head, 1);
154
- return Buffer.concat([head, Buffer.from(JSON.stringify(obj), "utf8")]);
155
- }
156
- function decodeFrame(frame) {
157
- const id = bytesToUuid(frame.subarray(1, 17));
158
- if (frame[0] === T_DATA) return {
159
- kind: "data",
160
- id,
161
- stream: frame[17] ?? 0,
162
- seq: frame.readUInt32BE(18),
163
- payload: frame.subarray(22)
164
- };
165
- return {
166
- kind: "ctrl",
167
- id,
168
- obj: ctrlMessageSchema.parse(JSON.parse(frame.subarray(17).toString("utf8")))
169
- };
170
- }
171
-
172
- //#endregion
173
- //#region ../portal-daemon/src/exec.ts
174
- /**
175
- * Runs portal `exec` directives locally. Each `open` spawns a child process
176
- * whose stdout/stderr stream back as data frames and whose stdin is fed by
177
- * inbound data frames, with pause/resume backpressure and cancel/teardown that
178
- * kill the child. This is the TypeScript counterpart of the desktop's Rust
179
- * `portal/mod.rs` job machinery, minus the connection supervision (which lives
180
- * in the client).
181
- */
182
- var JobManager = class {
183
- jobs = /* @__PURE__ */ new Map();
184
- constructor(opts) {
185
- this.opts = opts;
186
- }
187
- handleFrame(raw) {
188
- let decoded;
189
- try {
190
- decoded = decodeFrame(raw);
191
- } catch (_error) {
192
- return;
193
- }
194
- if (decoded.kind === "ctrl") this.handleCtrl(decoded.id, decoded.obj);
195
- else if (decoded.stream === STREAM.stdin) this.jobs.get(decoded.id)?.child.stdin.write(decoded.payload);
196
- }
197
- killAll() {
198
- for (const job of this.jobs.values()) {
199
- job.settled = true;
200
- job.child.kill("SIGKILL");
201
- }
202
- this.jobs.clear();
203
- }
204
- handleCtrl(id, msg) {
205
- switch (msg.t) {
206
- case "open":
207
- this.startJob(id, msg.argv, msg.env, msg.conversationId ?? null);
208
- return;
209
- case "stdin_eof":
210
- this.jobs.get(id)?.child.stdin.end();
211
- return;
212
- case "pause":
213
- this.setPaused(id, true);
214
- return;
215
- case "resume":
216
- this.setPaused(id, false);
217
- return;
218
- case "cancel":
219
- this.jobs.get(id)?.child.kill("SIGKILL");
220
- return;
221
- case "close":
222
- case "error": return;
223
- default: return msg;
224
- }
225
- }
226
- setPaused(id, paused) {
227
- const job = this.jobs.get(id);
228
- if (!job) return;
229
- if (paused) {
230
- job.child.stdout.pause();
231
- job.child.stderr.pause();
232
- } else {
233
- job.child.stdout.resume();
234
- job.child.stderr.resume();
235
- }
236
- }
237
- startJob(id, argv, env, conversationId) {
238
- const [program, ...args] = argv;
239
- if (!program) {
240
- this.opts.send(encodeCtrl(id, {
241
- t: "error",
242
- message: "empty argv"
243
- }));
244
- return;
245
- }
246
- let child;
247
- try {
248
- child = spawn(program, args, {
249
- cwd: this.opts.resolveCwd(conversationId),
250
- env: buildEnv(env),
251
- stdio: [
252
- "pipe",
253
- "pipe",
254
- "pipe"
255
- ]
256
- });
257
- } catch (err) {
258
- this.opts.send(encodeCtrl(id, {
259
- t: "error",
260
- message: `spawn failed: ${errorMessage(err)}`
261
- }));
262
- return;
263
- }
264
- const job = {
265
- child,
266
- seq: 0,
267
- settled: false
268
- };
269
- this.jobs.set(id, job);
270
- child.on("error", (err) => {
271
- if (job.settled) return;
272
- job.settled = true;
273
- this.jobs.delete(id);
274
- this.opts.send(encodeCtrl(id, {
275
- t: "error",
276
- message: `spawn failed: ${errorMessage(err)}`
277
- }));
278
- });
279
- child.stdout.on("data", (chunk) => this.sendData(job, id, STREAM.stdout, chunk));
280
- child.stderr.on("data", (chunk) => this.sendData(job, id, STREAM.stderr, chunk));
281
- child.on("close", (code) => {
282
- if (job.settled) return;
283
- job.settled = true;
284
- this.jobs.delete(id);
285
- this.opts.send(encodeCtrl(id, {
286
- t: "close",
287
- exitCode: code ?? -1,
288
- frames: job.seq
289
- }));
290
- });
291
- }
292
- sendData(job, id, stream, chunk) {
293
- if (job.settled) return;
294
- this.opts.send(encodeData(id, stream, job.seq, chunk));
295
- job.seq = job.seq + 1 >>> 0;
296
- }
297
- };
298
-
299
- //#endregion
300
- //#region ../portal-daemon/src/api.ts
301
- /**
302
- * The portal's session-authed REST surface, shared by `PortalClient` (the
303
- * TUI/`portal open` connection) and the `skydive portal` management
304
- * commands, so the endpoint contracts and response schemas live in exactly
305
- * one place.
306
- */
307
- const deviceSchema = z.object({
308
- id: z.string(),
309
- machineName: z.string(),
310
- friendlyName: z.string(),
311
- connected: z.boolean(),
312
- lastSeen: z.string().nullable(),
313
- grantedAgentIds: z.array(z.string())
314
- });
315
- const devicesResponseSchema = z.object({
316
- devices: z.array(deviceSchema),
317
- agents: z.array(z.object({
318
- id: z.string(),
319
- name: z.string()
320
- }))
321
- });
322
- const deviceTokenSchema = z.object({ token: z.string().min(1) });
323
- async function portalFetch(auth, path, init) {
324
- const res = await fetch(`${auth.appUrl}${path}`, {
325
- method: init.method,
326
- headers: {
327
- authorization: `Bearer ${auth.sessionToken}`,
328
- accept: "application/json",
329
- ...init.body ? { "content-type": "application/json" } : {}
330
- },
331
- ...init.body ? { body: init.body } : {}
332
- });
333
- if (!res.ok) {
334
- const body = await res.text().catch(() => "");
335
- throw new HttpError(res.status, body);
336
- }
337
- return res.json();
338
- }
339
- async function fetchPortalDevices(auth) {
340
- const json = await portalFetch(auth, "/api/v1/portal/devices", { method: "GET" });
341
- return devicesResponseSchema.parse(json);
342
- }
343
- const registerResponseSchema = z.object({ device: z.object({ id: z.string() }) });
344
- /**
345
- * Register this machine's device row without connecting. Connecting registers
346
- * as a side effect; this covers granting an agent on a machine that has never
347
- * shared yet (the grant references the device row).
348
- */
349
- async function registerPortalDevice(auth, { machineName, friendlyName }) {
350
- const json = await portalFetch(auth, "/api/v1/portal/devices", {
351
- method: "POST",
352
- body: JSON.stringify({
353
- machineName,
354
- friendlyName
355
- })
356
- });
357
- return registerResponseSchema.parse(json).device;
358
- }
359
- /** Short-lived token the machine presents when dialing the portal WebSocket. */
360
- async function mintPortalDeviceToken(auth) {
361
- const json = await portalFetch(auth, "/api/v1/portal/device-token", { method: "POST" });
362
- return deviceTokenSchema.parse(json).token;
363
- }
364
- async function grantPortalAccess(auth, { deviceId, agentId }) {
365
- await portalFetch(auth, `/api/v1/portal/devices/${encodeURIComponent(deviceId)}/grants`, {
366
- method: "POST",
367
- body: JSON.stringify({ agentId })
368
- });
369
- }
370
- async function revokePortalAccess(auth, { deviceId, agentId }) {
371
- await portalFetch(auth, `/api/v1/portal/devices/${encodeURIComponent(deviceId)}/grants/${encodeURIComponent(agentId)}`, { method: "DELETE" });
372
- }
373
- /**
374
- * The device row for a given machine identity. Matching is by `machineName`
375
- * equality — the stable handle the machine registers under, not the display
376
- * label.
377
- */
378
- function findThisDevice(devices, machineName) {
379
- return devices.find((device) => device.machineName === machineName) ?? null;
380
- }
381
- /**
382
- * One-time grant migration onto the merged device. Earlier CLI builds
383
- * registered a separate `<machineName>-cli` device, so a user's existing
384
- * approvals hang off that row; the merged device would start with zero grants
385
- * and every already-authorized agent would ask again. Copy any grant the
386
- * merged device is missing (the grant endpoint upserts, so re-runs are
387
- * no-ops). The legacy row is left in place — an old CLI build may still
388
- * connect under it. Returns how many grants were copied.
389
- */
390
- async function unifyLegacyCliGrants(auth, machineName) {
391
- const { devices } = await fetchPortalDevices(auth);
392
- const merged = findThisDevice(devices, machineName);
393
- const legacy = findThisDevice(devices, `${machineName}-cli`);
394
- if (!merged || !legacy) return 0;
395
- const have = new Set(merged.grantedAgentIds);
396
- const missing = legacy.grantedAgentIds.filter((id) => !have.has(id));
397
- for (const agentId of missing) await grantPortalAccess(auth, {
398
- deviceId: merged.id,
399
- agentId
400
- });
401
- return missing.length;
402
- }
403
-
404
- //#endregion
405
- //#region ../portal-daemon/src/client.ts
406
- const INITIAL_BACKOFF_MS = 500;
407
- const MAX_BACKOFF_MS = 1e4;
408
- /**
409
- * Shares the local machine with agents over the portal: dials OUT to the api's
410
- * desktop-portal WebSocket (authenticating with a short-lived device token
411
- * minted from the CLI session), then runs inbound `exec` directives via a
412
- * `JobManager`. Reconnects with backoff while enabled; disabling drops presence
413
- * and kills any in-flight children. No inbound port is ever opened.
414
- *
415
- * Access stays default-deny: connecting only makes the machine reachable — an
416
- * agent can't run anything until the user grants it (`grantAgent`).
417
- */
418
- var PortalClient = class {
419
- enabled = false;
420
- disposed = false;
421
- ws = null;
422
- jobs = null;
423
- status = "off";
424
- error = null;
425
- deviceId = null;
426
- granted = /* @__PURE__ */ new Set();
427
- machineName = "";
428
- friendlyName = "";
429
- legacyGrantsChecked = false;
430
- constructor(opts) {
431
- this.opts = opts;
432
- }
433
- isEnabled() {
434
- return this.enabled;
435
- }
436
- isGranted(agentId) {
437
- return this.granted.has(agentId);
438
- }
439
- /** Agents currently authorized on this machine (last known server state). */
440
- grantedAgentIds() {
441
- return [...this.granted];
442
- }
443
- enable() {
444
- if (this.enabled || this.disposed) return;
445
- this.enabled = true;
446
- this.error = null;
447
- this.connectLoop();
448
- }
449
- disable() {
450
- if (!this.enabled) return;
451
- this.enabled = false;
452
- this.jobs?.killAll();
453
- this.ws?.close();
454
- this.ws = null;
455
- this.deviceId = null;
456
- this.granted = /* @__PURE__ */ new Set();
457
- this.setStatus("off");
458
- }
459
- /**
460
- * Tear down for good (app quit). Kills children synchronously and closes the
461
- * socket so it stops holding the event loop open — otherwise the process
462
- * would hang after the TUI is destroyed.
463
- */
464
- dispose() {
465
- this.disposed = true;
466
- this.enabled = false;
467
- this.jobs?.killAll();
468
- this.jobs = null;
469
- this.ws?.close();
470
- this.ws = null;
471
- }
472
- /** Grant one agent access to this machine (default-deny; user-initiated). */
473
- async grantAgent(agentId) {
474
- const auth = this.sessionAuth();
475
- if (!auth) throw new Error("granting needs a signed-in CLI session on this machine (the desktop connection alone cannot manage grants)");
476
- await grantPortalAccess(auth, {
477
- deviceId: await this.ensureDeviceId(),
478
- agentId
479
- });
480
- this.granted.add(agentId);
481
- this.emit();
482
- }
483
- /** Session-authed REST auth, or null when only a device token is held. */
484
- sessionAuth() {
485
- const { sessionToken } = this.opts.credentials();
486
- return sessionToken ? {
487
- appUrl: this.opts.appUrl,
488
- sessionToken
489
- } : null;
490
- }
491
- /**
492
- * The bearer token for the portal WebSocket dial: minted from the session
493
- * when one is held, otherwise the pre-minted device token as-is.
494
- */
495
- async wsToken() {
496
- const creds = this.opts.credentials();
497
- if (creds.sessionToken) return mintPortalDeviceToken({
498
- appUrl: this.opts.appUrl,
499
- sessionToken: creds.sessionToken
500
- });
501
- if (creds.deviceToken) return creds.deviceToken;
502
- throw new Error("no portal credentials (not signed in)");
503
- }
504
- setStatus(status, error = null) {
505
- this.status = status;
506
- this.error = error;
507
- this.emit();
508
- }
509
- emit() {
510
- this.opts.onState({
511
- status: this.status,
512
- machineName: this.machineName,
513
- friendlyName: this.friendlyName,
514
- error: this.error,
515
- grantedAgentIds: [...this.granted]
516
- });
517
- }
518
- async connectLoop() {
519
- if (!this.machineName) {
520
- const identity = await resolveMachineIdentity();
521
- this.machineName = identity.machineName;
522
- this.friendlyName = identity.friendlyName;
523
- }
524
- let backoff = INITIAL_BACKOFF_MS;
525
- while (this.enabled && !this.disposed) {
526
- this.setStatus("connecting");
527
- try {
528
- const token = await this.wsToken();
529
- await this.runConnection(token);
530
- backoff = INITIAL_BACKOFF_MS;
531
- } catch (err) {
532
- if (!this.enabled || this.disposed) break;
533
- this.setStatus("error", errorMessage(err));
534
- }
535
- if (!this.enabled || this.disposed) break;
536
- await sleep(backoff);
537
- backoff = Math.min(backoff * 2, MAX_BACKOFF_MS);
538
- }
539
- }
540
- runConnection(token) {
541
- return new Promise((resolve) => {
542
- const ws = new WebSocket(portalWsUrl(this.opts.appUrl, this.machineName, this.friendlyName), {
543
- headers: { authorization: `Bearer ${token}` },
544
- maxPayload: MAX_WS_FRAME_BYTES
545
- });
546
- this.ws = ws;
547
- const jobs = new JobManager({
548
- resolveCwd: this.opts.resolveCwd,
549
- send: (frame) => {
550
- if (ws.readyState === WebSocket.OPEN) ws.send(frame);
551
- }
552
- });
553
- this.jobs = jobs;
554
- ws.on("open", () => {
555
- this.setStatus("connected");
556
- this.syncDeviceState();
557
- });
558
- ws.on("message", (data, isBinary) => {
559
- if (isBinary) jobs.handleFrame(toBuffer(data));
560
- });
561
- ws.on("error", (err) => {
562
- this.error = errorMessage(err);
563
- });
564
- ws.on("close", () => {
565
- jobs.killAll();
566
- if (this.jobs === jobs) this.jobs = null;
567
- if (this.ws === ws) this.ws = null;
568
- resolve();
569
- });
570
- });
571
- }
572
- async ensureDeviceId() {
573
- if (this.deviceId) return this.deviceId;
574
- for (let attempt = 0; attempt < 10; attempt += 1) {
575
- await this.refreshDevice();
576
- if (this.deviceId) return this.deviceId;
577
- await sleep(300);
578
- }
579
- throw new Error("this machine is not connected yet");
580
- }
581
- async refreshDevice() {
582
- const auth = this.sessionAuth();
583
- if (!auth) return;
584
- try {
585
- const { devices } = await fetchPortalDevices(auth);
586
- const mine = findThisDevice(devices, this.machineName);
587
- if (!mine) return;
588
- this.deviceId = mine.id;
589
- this.granted = new Set(mine.grantedAgentIds);
590
- this.emit();
591
- } catch (_error) {}
592
- }
593
- /**
594
- * Post-connect device sync: read our row, then (once per process) copy any
595
- * grants still hanging off the legacy `<host>-cli` device onto this one so
596
- * agents the user already approved keep their access under the merged
597
- * identity.
598
- */
599
- async syncDeviceState() {
600
- await this.refreshDevice();
601
- if (this.legacyGrantsChecked) return;
602
- const auth = this.sessionAuth();
603
- if (!auth) return;
604
- this.legacyGrantsChecked = true;
605
- try {
606
- if (await unifyLegacyCliGrants(auth, this.machineName) > 0) await this.refreshDevice();
607
- } catch (_error) {}
608
- }
609
- };
610
- function toBuffer(data) {
611
- if (Buffer.isBuffer(data)) return data;
612
- if (Array.isArray(data)) return Buffer.concat(data);
613
- return Buffer.from(data);
614
- }
615
- function sleep(ms) {
616
- return new Promise((resolve) => setTimeout(resolve, ms));
617
- }
618
-
619
- //#endregion
620
- export { registerPortalDevice as a, resolveMachineIdentity as c, grantPortalAccess as i, fetchPortalDevices as n, revokePortalAccess as o, findThisDevice as r, isRecord as s, PortalClient as t };
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
- import "./client-Dd5sMXPv.mjs";
3
- import { a as runPortalDaemon, i as queryDaemonStatus, n as ensureDaemonRunning, o as startPortalDaemon, r as isDaemonListening, s as stopDaemon, t as PortalDaemon } from "./daemon-BhArnzeW.mjs";
4
-
5
- export { runPortalDaemon };
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env node
2
- import "./client-Dd5sMXPv.mjs";
3
- import "./daemon-BhArnzeW.mjs";
4
- import { t as PortalDaemonClient } from "./daemon-client-DcuD4v12.mjs";
5
-
6
- export { PortalDaemonClient };
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
- import "./client-Cn2af31H.mjs";
3
- import { t as runRawPtyPassthrough } from "./raw-pty-B6mAroiI.mjs";
4
-
5
- export { runRawPtyPassthrough };
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env node
2
- import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
3
- import { n as errorDetail, r as sendErrorMessage, t as createRestClient } from "./rest-BY2nADw5.mjs";
4
- import "./billing-blocked-2wju4gC_.mjs";
5
-
6
- export { createRestClient };