skydive-cli 0.1.0-beta.260 → 0.1.0-beta.276
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.
- package/README.md +43 -6
- package/dist/js/bin.mjs +336 -30
- package/dist/js/{boot-C5ztfr8K.mjs → boot-FPpnUcyM.mjs} +4 -445
- package/dist/js/client-_OL8-XGH.mjs +367 -0
- package/dist/js/rolldown-runtime-Cz4Tg37Z.mjs +19 -0
- package/package.json +1 -1
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { A as
|
|
2
|
+
import { A as themesForMode, C as monoTheme, D as themeMode, E as themeForMode, F as DEFAULT_APP_URL, I as getConfigPath, L as getSavedTheme, M as listWorkspaces, N as setActiveWorkspace, O as themeModeFromColorFgBg, P as DEFAULT_API_URL, R as resolveWebUrl, S as findTheme, T as theme, _ as isRecord, b as DEFAULT_THEME_ID, c as resolveAgent, d as parseExternalOauthConnectParams, f as parseOauthConnectParams, g as errorMessage, h as parseConnectCard, j as getActiveWorkspaceId, k as themeVersion, l as MASK_CHAR, m as resolveConnectUrl, p as reconcileMaskedInput, u as cardActionErrorMessage, v as HttpError, w as noColorRequested, x as applyTheme, y as createRestClient, z as saveTheme } from "./bin.mjs";
|
|
3
|
+
import { t as PortalClient } from "./client-_OL8-XGH.mjs";
|
|
3
4
|
import path, { basename, isAbsolute, join, win32 } from "node:path";
|
|
4
5
|
import { z } from "zod";
|
|
5
6
|
import open from "open";
|
|
6
7
|
import { execFile, spawn } from "node:child_process";
|
|
7
8
|
import { createHash } from "node:crypto";
|
|
8
9
|
import { constants } from "node:fs";
|
|
10
|
+
import * as os$1 from "node:os";
|
|
11
|
+
import { homedir, platform, release, tmpdir } from "node:os";
|
|
9
12
|
import { MarkdownRenderable, RenderableEvents, SyntaxStyle, createCliRenderer, decodePasteBytes, detectLinks } from "@opentui/core";
|
|
10
13
|
import { createRoot, extend, useKeyboard, usePaste, useRenderer, useTerminalDimensions } from "@opentui/react";
|
|
11
14
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
12
15
|
import { create } from "zustand";
|
|
13
16
|
import { WebSocket } from "ws";
|
|
14
|
-
import * as os$1 from "node:os";
|
|
15
|
-
import os, { homedir, platform, release, tmpdir } from "node:os";
|
|
16
17
|
import { createConnection } from "node:net";
|
|
17
18
|
import { access, appendFile, mkdir, mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
18
19
|
import { Fragment, jsx, jsxs } from "@opentui/react/jsx-runtime";
|
|
@@ -158,448 +159,6 @@ async function resolveInitialScreen({ rest, agentSelector }) {
|
|
|
158
159
|
};
|
|
159
160
|
}
|
|
160
161
|
|
|
161
|
-
//#endregion
|
|
162
|
-
//#region ../portal-protocol/src/index.ts
|
|
163
|
-
const MAX_WS_FRAME_BYTES = 16 * 1024 * 1024;
|
|
164
|
-
const T_DATA = 1;
|
|
165
|
-
const T_CTRL = 2;
|
|
166
|
-
const STREAM = {
|
|
167
|
-
stdout: 0,
|
|
168
|
-
stderr: 1,
|
|
169
|
-
stdin: 2
|
|
170
|
-
};
|
|
171
|
-
const uuidToBytes = (id) => Buffer.from(id.replace(/-/g, ""), "hex");
|
|
172
|
-
const bytesToUuid = (b) => {
|
|
173
|
-
const h = b.toString("hex");
|
|
174
|
-
return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20, 32)}`;
|
|
175
|
-
};
|
|
176
|
-
function encodeData(id, stream, seq, payload) {
|
|
177
|
-
const head = Buffer.allocUnsafe(22);
|
|
178
|
-
head[0] = T_DATA;
|
|
179
|
-
uuidToBytes(id).copy(head, 1);
|
|
180
|
-
head[17] = stream;
|
|
181
|
-
head.writeUInt32BE(seq >>> 0, 18);
|
|
182
|
-
return Buffer.concat([head, payload]);
|
|
183
|
-
}
|
|
184
|
-
const ctrlMessageSchema = z.discriminatedUnion("t", [
|
|
185
|
-
z.object({
|
|
186
|
-
t: z.literal("open"),
|
|
187
|
-
argv: z.array(z.string()),
|
|
188
|
-
env: z.record(z.string()).nullable()
|
|
189
|
-
}),
|
|
190
|
-
z.object({ t: z.literal("stdin_eof") }),
|
|
191
|
-
z.object({ t: z.literal("pause") }),
|
|
192
|
-
z.object({ t: z.literal("resume") }),
|
|
193
|
-
z.object({ t: z.literal("cancel") }),
|
|
194
|
-
z.object({
|
|
195
|
-
t: z.literal("close"),
|
|
196
|
-
exitCode: z.number()
|
|
197
|
-
}),
|
|
198
|
-
z.object({
|
|
199
|
-
t: z.literal("error"),
|
|
200
|
-
message: z.string()
|
|
201
|
-
})
|
|
202
|
-
]);
|
|
203
|
-
function encodeCtrl(id, obj) {
|
|
204
|
-
const head = Buffer.allocUnsafe(17);
|
|
205
|
-
head[0] = T_CTRL;
|
|
206
|
-
uuidToBytes(id).copy(head, 1);
|
|
207
|
-
return Buffer.concat([head, Buffer.from(JSON.stringify(obj), "utf8")]);
|
|
208
|
-
}
|
|
209
|
-
function decodeFrame(frame) {
|
|
210
|
-
const id = bytesToUuid(frame.subarray(1, 17));
|
|
211
|
-
if (frame[0] === T_DATA) return {
|
|
212
|
-
kind: "data",
|
|
213
|
-
id,
|
|
214
|
-
stream: frame[17] ?? 0,
|
|
215
|
-
seq: frame.readUInt32BE(18),
|
|
216
|
-
payload: frame.subarray(22)
|
|
217
|
-
};
|
|
218
|
-
return {
|
|
219
|
-
kind: "ctrl",
|
|
220
|
-
id,
|
|
221
|
-
obj: ctrlMessageSchema.parse(JSON.parse(frame.subarray(17).toString("utf8")))
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
//#endregion
|
|
226
|
-
//#region src/chat/portal/machine.ts
|
|
227
|
-
/**
|
|
228
|
-
* Identity this machine registers under when the CLI shares it via the portal.
|
|
229
|
-
*
|
|
230
|
-
* The `-cli` suffix / `(CLI)` label keep a CLI-shared machine a DISTINCT portal
|
|
231
|
-
* device from the same host's Skydive Desktop app. `portal_device` is unique on
|
|
232
|
-
* (org, user, machineName), and directives route to whichever socket holds the
|
|
233
|
-
* device — if the CLI and desktop registered the same name they'd share a
|
|
234
|
-
* device row and both execute every directive. Distinct names also make the
|
|
235
|
-
* grant UI unambiguous about which surface is being authorized.
|
|
236
|
-
*/
|
|
237
|
-
function machineIdentity() {
|
|
238
|
-
const host = (os.hostname() || "machine").trim().replace(/\.local$/i, "") || "machine";
|
|
239
|
-
return {
|
|
240
|
-
machineName: `${host}-cli`,
|
|
241
|
-
friendlyName: `${host} (CLI)`
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
const INHERITED_ENV = [
|
|
245
|
-
"HOME",
|
|
246
|
-
"USER",
|
|
247
|
-
"LOGNAME",
|
|
248
|
-
"SHELL",
|
|
249
|
-
"LANG",
|
|
250
|
-
"LC_ALL",
|
|
251
|
-
"TMPDIR",
|
|
252
|
-
"TERM",
|
|
253
|
-
"PATH"
|
|
254
|
-
];
|
|
255
|
-
function buildEnv(extra) {
|
|
256
|
-
const env = {};
|
|
257
|
-
for (const key of INHERITED_ENV) {
|
|
258
|
-
const value = process.env[key];
|
|
259
|
-
if (value !== void 0) env[key] = value;
|
|
260
|
-
}
|
|
261
|
-
if (extra) for (const [key, value] of Object.entries(extra)) env[key] = value;
|
|
262
|
-
return env;
|
|
263
|
-
}
|
|
264
|
-
/**
|
|
265
|
-
* Build the desktop-portal WebSocket URL from the chat origin. Mirrors the Rust
|
|
266
|
-
* desktop client: http→ws, https→wss, scheme-less defaults to wss, and the
|
|
267
|
-
* machine/label ride as query pairs (percent-encoded by URL).
|
|
268
|
-
*/
|
|
269
|
-
function portalWsUrl(appUrl, machine, label) {
|
|
270
|
-
const base = appUrl.replace(/\/+$/, "");
|
|
271
|
-
let wsBase;
|
|
272
|
-
if (base.startsWith("https://")) wsBase = `wss://${base.slice(8)}`;
|
|
273
|
-
else if (base.startsWith("http://")) wsBase = `ws://${base.slice(7)}`;
|
|
274
|
-
else wsBase = `wss://${base}`;
|
|
275
|
-
const url = new URL(`${wsBase}/api/v1/portal/desktop`);
|
|
276
|
-
url.searchParams.set("machine", machine);
|
|
277
|
-
url.searchParams.set("label", label);
|
|
278
|
-
return url.toString();
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
//#endregion
|
|
282
|
-
//#region src/chat/portal/exec.ts
|
|
283
|
-
/**
|
|
284
|
-
* Runs portal `exec` directives locally. Each `open` spawns a child process
|
|
285
|
-
* whose stdout/stderr stream back as data frames and whose stdin is fed by
|
|
286
|
-
* inbound data frames, with pause/resume backpressure and cancel/teardown that
|
|
287
|
-
* kill the child. This is the TypeScript counterpart of the desktop's Rust
|
|
288
|
-
* `portal/mod.rs` job machinery, minus the connection supervision (which lives
|
|
289
|
-
* in the client).
|
|
290
|
-
*/
|
|
291
|
-
var JobManager = class {
|
|
292
|
-
jobs = /* @__PURE__ */ new Map();
|
|
293
|
-
constructor(opts) {
|
|
294
|
-
this.opts = opts;
|
|
295
|
-
}
|
|
296
|
-
handleFrame(raw) {
|
|
297
|
-
let decoded;
|
|
298
|
-
try {
|
|
299
|
-
decoded = decodeFrame(raw);
|
|
300
|
-
} catch (_error) {
|
|
301
|
-
return;
|
|
302
|
-
}
|
|
303
|
-
if (decoded.kind === "ctrl") this.handleCtrl(decoded.id, decoded.obj);
|
|
304
|
-
else if (decoded.stream === STREAM.stdin) this.jobs.get(decoded.id)?.child.stdin.write(decoded.payload);
|
|
305
|
-
}
|
|
306
|
-
killAll() {
|
|
307
|
-
for (const job of this.jobs.values()) {
|
|
308
|
-
job.settled = true;
|
|
309
|
-
job.child.kill("SIGKILL");
|
|
310
|
-
}
|
|
311
|
-
this.jobs.clear();
|
|
312
|
-
}
|
|
313
|
-
handleCtrl(id, msg) {
|
|
314
|
-
switch (msg.t) {
|
|
315
|
-
case "open":
|
|
316
|
-
this.startJob(id, msg.argv, msg.env);
|
|
317
|
-
return;
|
|
318
|
-
case "stdin_eof":
|
|
319
|
-
this.jobs.get(id)?.child.stdin.end();
|
|
320
|
-
return;
|
|
321
|
-
case "pause":
|
|
322
|
-
this.setPaused(id, true);
|
|
323
|
-
return;
|
|
324
|
-
case "resume":
|
|
325
|
-
this.setPaused(id, false);
|
|
326
|
-
return;
|
|
327
|
-
case "cancel":
|
|
328
|
-
this.jobs.get(id)?.child.kill("SIGKILL");
|
|
329
|
-
return;
|
|
330
|
-
case "close":
|
|
331
|
-
case "error": return;
|
|
332
|
-
default: return msg;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
setPaused(id, paused) {
|
|
336
|
-
const job = this.jobs.get(id);
|
|
337
|
-
if (!job) return;
|
|
338
|
-
if (paused) {
|
|
339
|
-
job.child.stdout.pause();
|
|
340
|
-
job.child.stderr.pause();
|
|
341
|
-
} else {
|
|
342
|
-
job.child.stdout.resume();
|
|
343
|
-
job.child.stderr.resume();
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
startJob(id, argv, env) {
|
|
347
|
-
const [program, ...args] = argv;
|
|
348
|
-
if (!program) {
|
|
349
|
-
this.opts.send(encodeCtrl(id, {
|
|
350
|
-
t: "error",
|
|
351
|
-
message: "empty argv"
|
|
352
|
-
}));
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
let child;
|
|
356
|
-
try {
|
|
357
|
-
child = spawn(program, args, {
|
|
358
|
-
cwd: this.opts.cwd,
|
|
359
|
-
env: buildEnv(env),
|
|
360
|
-
stdio: [
|
|
361
|
-
"pipe",
|
|
362
|
-
"pipe",
|
|
363
|
-
"pipe"
|
|
364
|
-
]
|
|
365
|
-
});
|
|
366
|
-
} catch (err) {
|
|
367
|
-
this.opts.send(encodeCtrl(id, {
|
|
368
|
-
t: "error",
|
|
369
|
-
message: `spawn failed: ${errorMessage(err)}`
|
|
370
|
-
}));
|
|
371
|
-
return;
|
|
372
|
-
}
|
|
373
|
-
const job = {
|
|
374
|
-
child,
|
|
375
|
-
seq: 0,
|
|
376
|
-
settled: false
|
|
377
|
-
};
|
|
378
|
-
this.jobs.set(id, job);
|
|
379
|
-
child.on("error", (err) => {
|
|
380
|
-
if (job.settled) return;
|
|
381
|
-
job.settled = true;
|
|
382
|
-
this.jobs.delete(id);
|
|
383
|
-
this.opts.send(encodeCtrl(id, {
|
|
384
|
-
t: "error",
|
|
385
|
-
message: `spawn failed: ${errorMessage(err)}`
|
|
386
|
-
}));
|
|
387
|
-
});
|
|
388
|
-
child.stdout.on("data", (chunk) => this.sendData(job, id, STREAM.stdout, chunk));
|
|
389
|
-
child.stderr.on("data", (chunk) => this.sendData(job, id, STREAM.stderr, chunk));
|
|
390
|
-
child.on("close", (code) => {
|
|
391
|
-
if (job.settled) return;
|
|
392
|
-
job.settled = true;
|
|
393
|
-
this.jobs.delete(id);
|
|
394
|
-
this.opts.send(encodeCtrl(id, {
|
|
395
|
-
t: "close",
|
|
396
|
-
exitCode: code ?? -1
|
|
397
|
-
}));
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
sendData(job, id, stream, chunk) {
|
|
401
|
-
if (job.settled) return;
|
|
402
|
-
this.opts.send(encodeData(id, stream, job.seq, chunk));
|
|
403
|
-
job.seq = job.seq + 1 >>> 0;
|
|
404
|
-
}
|
|
405
|
-
};
|
|
406
|
-
|
|
407
|
-
//#endregion
|
|
408
|
-
//#region src/chat/portal/client.ts
|
|
409
|
-
const deviceTokenSchema = z.object({ token: z.string().min(1) });
|
|
410
|
-
const devicesSchema = z.object({ devices: z.array(z.object({
|
|
411
|
-
id: z.string(),
|
|
412
|
-
machineName: z.string(),
|
|
413
|
-
grantedAgentIds: z.array(z.string())
|
|
414
|
-
})) });
|
|
415
|
-
const INITIAL_BACKOFF_MS = 500;
|
|
416
|
-
const MAX_BACKOFF_MS = 1e4;
|
|
417
|
-
/**
|
|
418
|
-
* Shares the local machine with agents over the portal: dials OUT to the api's
|
|
419
|
-
* desktop-portal WebSocket (authenticating with a short-lived device token
|
|
420
|
-
* minted from the CLI session), then runs inbound `exec` directives via a
|
|
421
|
-
* `JobManager`. Reconnects with backoff while enabled; disabling drops presence
|
|
422
|
-
* and kills any in-flight children. No inbound port is ever opened.
|
|
423
|
-
*
|
|
424
|
-
* Access stays default-deny: connecting only makes the machine reachable — an
|
|
425
|
-
* agent can't run anything until the user grants it (`grantAgent`).
|
|
426
|
-
*/
|
|
427
|
-
var PortalClient = class {
|
|
428
|
-
enabled = false;
|
|
429
|
-
disposed = false;
|
|
430
|
-
ws = null;
|
|
431
|
-
jobs = null;
|
|
432
|
-
status = "off";
|
|
433
|
-
error = null;
|
|
434
|
-
deviceId = null;
|
|
435
|
-
granted = /* @__PURE__ */ new Set();
|
|
436
|
-
machineName;
|
|
437
|
-
friendlyName;
|
|
438
|
-
constructor(opts) {
|
|
439
|
-
this.opts = opts;
|
|
440
|
-
const identity = machineIdentity();
|
|
441
|
-
this.machineName = identity.machineName;
|
|
442
|
-
this.friendlyName = identity.friendlyName;
|
|
443
|
-
}
|
|
444
|
-
isEnabled() {
|
|
445
|
-
return this.enabled;
|
|
446
|
-
}
|
|
447
|
-
isGranted(agentId) {
|
|
448
|
-
return this.granted.has(agentId);
|
|
449
|
-
}
|
|
450
|
-
enable() {
|
|
451
|
-
if (this.enabled || this.disposed) return;
|
|
452
|
-
this.enabled = true;
|
|
453
|
-
this.error = null;
|
|
454
|
-
this.connectLoop();
|
|
455
|
-
}
|
|
456
|
-
disable() {
|
|
457
|
-
if (!this.enabled) return;
|
|
458
|
-
this.enabled = false;
|
|
459
|
-
this.jobs?.killAll();
|
|
460
|
-
this.ws?.close();
|
|
461
|
-
this.ws = null;
|
|
462
|
-
this.deviceId = null;
|
|
463
|
-
this.granted = /* @__PURE__ */ new Set();
|
|
464
|
-
this.setStatus("off");
|
|
465
|
-
}
|
|
466
|
-
/**
|
|
467
|
-
* Tear down for good (app quit). Kills children synchronously and closes the
|
|
468
|
-
* socket so it stops holding the event loop open — otherwise the process
|
|
469
|
-
* would hang after the TUI is destroyed.
|
|
470
|
-
*/
|
|
471
|
-
dispose() {
|
|
472
|
-
this.disposed = true;
|
|
473
|
-
this.enabled = false;
|
|
474
|
-
this.jobs?.killAll();
|
|
475
|
-
this.jobs = null;
|
|
476
|
-
this.ws?.close();
|
|
477
|
-
this.ws = null;
|
|
478
|
-
}
|
|
479
|
-
/** Grant one agent access to this machine (default-deny; user-initiated). */
|
|
480
|
-
async grantAgent(agentId) {
|
|
481
|
-
const deviceId = await this.ensureDeviceId();
|
|
482
|
-
const res = await this.fetchJson(`/api/v1/portal/devices/${encodeURIComponent(deviceId)}/grants`, {
|
|
483
|
-
method: "POST",
|
|
484
|
-
body: JSON.stringify({ agentId })
|
|
485
|
-
});
|
|
486
|
-
if (!res.ok) throw new Error(`grant failed (${res.status}): ${await shortBody(res)}`);
|
|
487
|
-
this.granted.add(agentId);
|
|
488
|
-
this.emit();
|
|
489
|
-
}
|
|
490
|
-
setStatus(status, error = null) {
|
|
491
|
-
this.status = status;
|
|
492
|
-
this.error = error;
|
|
493
|
-
this.emit();
|
|
494
|
-
}
|
|
495
|
-
emit() {
|
|
496
|
-
this.opts.onState({
|
|
497
|
-
status: this.status,
|
|
498
|
-
machineName: this.machineName,
|
|
499
|
-
friendlyName: this.friendlyName,
|
|
500
|
-
error: this.error,
|
|
501
|
-
grantedAgentIds: [...this.granted]
|
|
502
|
-
});
|
|
503
|
-
}
|
|
504
|
-
async connectLoop() {
|
|
505
|
-
let backoff = INITIAL_BACKOFF_MS;
|
|
506
|
-
while (this.enabled && !this.disposed) {
|
|
507
|
-
this.setStatus("connecting");
|
|
508
|
-
try {
|
|
509
|
-
const token = await this.mintDeviceToken();
|
|
510
|
-
await this.runConnection(token);
|
|
511
|
-
backoff = INITIAL_BACKOFF_MS;
|
|
512
|
-
} catch (err) {
|
|
513
|
-
if (!this.enabled || this.disposed) break;
|
|
514
|
-
this.setStatus("error", errorMessage(err));
|
|
515
|
-
}
|
|
516
|
-
if (!this.enabled || this.disposed) break;
|
|
517
|
-
await sleep(backoff);
|
|
518
|
-
backoff = Math.min(backoff * 2, MAX_BACKOFF_MS);
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
runConnection(token) {
|
|
522
|
-
return new Promise((resolve) => {
|
|
523
|
-
const ws = new WebSocket(portalWsUrl(this.opts.appUrl, this.machineName, this.friendlyName), {
|
|
524
|
-
headers: { authorization: `Bearer ${token}` },
|
|
525
|
-
maxPayload: MAX_WS_FRAME_BYTES
|
|
526
|
-
});
|
|
527
|
-
this.ws = ws;
|
|
528
|
-
const jobs = new JobManager({
|
|
529
|
-
cwd: this.opts.cwd,
|
|
530
|
-
send: (frame) => {
|
|
531
|
-
if (ws.readyState === WebSocket.OPEN) ws.send(frame);
|
|
532
|
-
}
|
|
533
|
-
});
|
|
534
|
-
this.jobs = jobs;
|
|
535
|
-
ws.on("open", () => {
|
|
536
|
-
this.setStatus("connected");
|
|
537
|
-
this.refreshDevice();
|
|
538
|
-
});
|
|
539
|
-
ws.on("message", (data, isBinary) => {
|
|
540
|
-
if (isBinary) jobs.handleFrame(toBuffer$1(data));
|
|
541
|
-
});
|
|
542
|
-
ws.on("error", (err) => {
|
|
543
|
-
this.error = errorMessage(err);
|
|
544
|
-
});
|
|
545
|
-
ws.on("close", () => {
|
|
546
|
-
jobs.killAll();
|
|
547
|
-
if (this.jobs === jobs) this.jobs = null;
|
|
548
|
-
if (this.ws === ws) this.ws = null;
|
|
549
|
-
resolve();
|
|
550
|
-
});
|
|
551
|
-
});
|
|
552
|
-
}
|
|
553
|
-
async mintDeviceToken() {
|
|
554
|
-
const res = await this.fetchJson("/api/v1/portal/device-token", { method: "POST" });
|
|
555
|
-
if (!res.ok) throw new Error(`device-token failed (${res.status}): ${await shortBody(res)}`);
|
|
556
|
-
return deviceTokenSchema.parse(await res.json()).token;
|
|
557
|
-
}
|
|
558
|
-
async ensureDeviceId() {
|
|
559
|
-
if (this.deviceId) return this.deviceId;
|
|
560
|
-
for (let attempt = 0; attempt < 10; attempt += 1) {
|
|
561
|
-
await this.refreshDevice();
|
|
562
|
-
if (this.deviceId) return this.deviceId;
|
|
563
|
-
await sleep(300);
|
|
564
|
-
}
|
|
565
|
-
throw new Error("this machine is not connected yet");
|
|
566
|
-
}
|
|
567
|
-
async refreshDevice() {
|
|
568
|
-
try {
|
|
569
|
-
const res = await this.fetchJson("/api/v1/portal/devices", { method: "GET" });
|
|
570
|
-
if (!res.ok) return;
|
|
571
|
-
const { devices } = devicesSchema.parse(await res.json());
|
|
572
|
-
const mine = devices.find((device) => device.machineName === this.machineName);
|
|
573
|
-
if (!mine) return;
|
|
574
|
-
this.deviceId = mine.id;
|
|
575
|
-
this.granted = new Set(mine.grantedAgentIds);
|
|
576
|
-
this.emit();
|
|
577
|
-
} catch (_error) {}
|
|
578
|
-
}
|
|
579
|
-
fetchJson(path, init) {
|
|
580
|
-
return fetch(`${this.opts.appUrl}${path}`, {
|
|
581
|
-
method: init.method,
|
|
582
|
-
headers: {
|
|
583
|
-
authorization: `Bearer ${this.opts.sessionToken}`,
|
|
584
|
-
accept: "application/json",
|
|
585
|
-
...init.body ? { "content-type": "application/json" } : {}
|
|
586
|
-
},
|
|
587
|
-
...init.body ? { body: init.body } : {}
|
|
588
|
-
});
|
|
589
|
-
}
|
|
590
|
-
};
|
|
591
|
-
function toBuffer$1(data) {
|
|
592
|
-
if (Buffer.isBuffer(data)) return data;
|
|
593
|
-
if (Array.isArray(data)) return Buffer.concat(data);
|
|
594
|
-
return Buffer.from(data);
|
|
595
|
-
}
|
|
596
|
-
function shortBody(res) {
|
|
597
|
-
return res.text().then((text) => text.slice(0, 120)).catch(() => "");
|
|
598
|
-
}
|
|
599
|
-
function sleep(ms) {
|
|
600
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
601
|
-
}
|
|
602
|
-
|
|
603
162
|
//#endregion
|
|
604
163
|
//#region src/chat/portal/use-portal.ts
|
|
605
164
|
/**
|