skydive-cli 0.4.0-beta.1332 → 0.4.0-beta.1340
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/dist/js/bin.mjs +13 -12
- package/dist/js/{boot-Cw_SOMZH.mjs → boot-Cz7I47B-.mjs} +5 -149
- package/dist/js/{daemon-DOWC0OOB.mjs → daemon-DaUaCMwC.mjs} +19 -1
- package/dist/js/daemon-client-CJxbCLOb.mjs +168 -0
- package/dist/js/daemon-client-DlblQIR9.mjs +6 -0
- package/dist/js/{daemon-wUaIOwEj.mjs → daemon-imLBqTGg.mjs} +1 -1
- package/dist/js/{profiler-Dh7VeDnQ.mjs → profiler-BoPzxsbP.mjs} +1 -1
- package/dist/js/raw-pty-3EkG-jjH.mjs +5 -0
- package/package.json +1 -1
- package/dist/js/raw-pty-skuWh-QO.mjs +0 -5
- /package/dist/js/{print-share-BBz_FpXL.mjs → print-share-CT4ltX7u.mjs} +0 -0
- /package/dist/js/{raw-pty-C7PxMDc1.mjs → raw-pty-B6mAroiI.mjs} +0 -0
package/dist/js/bin.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { C as ensureActiveOrganization, D as setActiveWorkspace, E as listWorkspaces, O as name, T as getSessionIdentity, a as installCrashHandler, k as version, t as maybeStartProfiling, u as brandHelpArt, w as getActiveWorkspaceId, x as themes } from "./profiler-
|
|
2
|
+
import { C as ensureActiveOrganization, D as setActiveWorkspace, E as listWorkspaces, O as name, T as getSessionIdentity, a as installCrashHandler, k as version, t as maybeStartProfiling, u as brandHelpArt, w as getActiveWorkspaceId, x as themes } from "./profiler-BoPzxsbP.mjs";
|
|
3
3
|
import { A as getStoredApiKeyWorkspaceName, C as getLastSeenVersion, F as resolveManagementAuth, I as resolveSession, M as resolveAppUrl, N as resolveChatAuth, O as getShareMachineDefault, P as resolveConfig, R as saveConfig, S as getConfigPath, T as getPromptHistoryPath, V as setLastSeenVersion, _ as API_KEY_PREFIX, b as PREFERENCES, g as API_KEY_FAMILY_PREFIX, h as API_KEYS_URL, i as resolveAgent, j as getUpdateCheckDisabled, k as getStoredApiKeyId, v as DEFAULT_API_URL, w as getPreference, x as deleteConfig, z as saveSession } from "./print-CbayCa87.mjs";
|
|
4
4
|
import { n as printError, r as printTable, t as output } from "./output-DYzzdXYV.mjs";
|
|
5
5
|
import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
|
|
6
6
|
import { t as createRestClient } from "./rest-BY2nADw5.mjs";
|
|
7
7
|
import "./billing-blocked-2wju4gC_.mjs";
|
|
8
8
|
import { a as registerPortalDevice, c as machineIdentity, i as grantPortalAccess, n as fetchPortalDevices, o as revokePortalAccess, r as findThisDevice } from "./client-Cf7cvftg.mjs";
|
|
9
|
-
import { c as PORTAL_DAEMON_FLAG, i as queryDaemonStatus, l as daemonPaths, n as ensureDaemonRunning, o as stopDaemon } from "./daemon-
|
|
9
|
+
import { c as PORTAL_DAEMON_FLAG, i as queryDaemonStatus, l as daemonPaths, n as ensureDaemonRunning, o as stopDaemon } from "./daemon-DaUaCMwC.mjs";
|
|
10
10
|
import { t as SandboxStream } from "./client-Cn2af31H.mjs";
|
|
11
11
|
import { hideBin } from "yargs/helpers";
|
|
12
12
|
import yargs from "yargs";
|
|
@@ -1515,7 +1515,7 @@ const importCommand = {
|
|
|
1515
1515
|
process.exit(1);
|
|
1516
1516
|
}
|
|
1517
1517
|
}
|
|
1518
|
-
const { runChat } = await import("./boot-
|
|
1518
|
+
const { runChat } = await import("./boot-Cz7I47B-.mjs");
|
|
1519
1519
|
await runChat({
|
|
1520
1520
|
appUrl,
|
|
1521
1521
|
sessionToken: session.value.sessionToken,
|
|
@@ -1542,7 +1542,7 @@ async function runImportPrintMode({ argv, appUrl }) {
|
|
|
1542
1542
|
printError(`${session.error.message} For non-interactive use, run \`skydive auth login --web\` first, or set SKYDIVE_SESSION_TOKEN.`);
|
|
1543
1543
|
process.exit(1);
|
|
1544
1544
|
}
|
|
1545
|
-
const { connectMachineShare } = await import("./print-share-
|
|
1545
|
+
const { connectMachineShare } = await import("./print-share-CT4ltX7u.mjs");
|
|
1546
1546
|
const machineShare = await connectMachineShare({
|
|
1547
1547
|
appUrl,
|
|
1548
1548
|
sessionToken: session.value.sessionToken,
|
|
@@ -1927,7 +1927,7 @@ const chatCommand = {
|
|
|
1927
1927
|
sessionToken: auth.value.token,
|
|
1928
1928
|
agentSelector: argv.agent ?? null
|
|
1929
1929
|
});
|
|
1930
|
-
const { runChat } = await import("./boot-
|
|
1930
|
+
const { runChat } = await import("./boot-Cz7I47B-.mjs");
|
|
1931
1931
|
await runChat({
|
|
1932
1932
|
appUrl: auth.value.appUrl,
|
|
1933
1933
|
sessionToken: auth.value.token,
|
|
@@ -2019,7 +2019,7 @@ async function runPrintMode({ argv, appUrl }) {
|
|
|
2019
2019
|
process.exit(1);
|
|
2020
2020
|
}
|
|
2021
2021
|
} else {
|
|
2022
|
-
const { connectMachineShare } = await import("./print-share-
|
|
2022
|
+
const { connectMachineShare } = await import("./print-share-CT4ltX7u.mjs");
|
|
2023
2023
|
machineShare = await connectMachineShare({
|
|
2024
2024
|
appUrl: auth.value.appUrl,
|
|
2025
2025
|
sessionToken: auth.value.token,
|
|
@@ -2286,7 +2286,7 @@ const switchCommand = {
|
|
|
2286
2286
|
printError("The workspace picker needs the Bun runtime and it could not be set up automatically. Pass a workspace slug instead, or install Bun and retry.");
|
|
2287
2287
|
process.exit(1);
|
|
2288
2288
|
}
|
|
2289
|
-
const { runWorkspacePicker } = await import("./boot-
|
|
2289
|
+
const { runWorkspacePicker } = await import("./boot-Cz7I47B-.mjs");
|
|
2290
2290
|
await runWorkspacePicker(session);
|
|
2291
2291
|
return;
|
|
2292
2292
|
}
|
|
@@ -2364,16 +2364,15 @@ const openCommand = {
|
|
|
2364
2364
|
const agent = argv.agent ? resolveAgent((await fetchPortalDevices(session)).agents, argv.agent) : null;
|
|
2365
2365
|
const cwd = argv.cwd ? path.resolve(argv.cwd) : process.cwd();
|
|
2366
2366
|
const { machineName } = machineIdentity();
|
|
2367
|
-
const {
|
|
2367
|
+
const { PortalDaemonClient } = await import("./daemon-client-DlblQIR9.mjs");
|
|
2368
2368
|
let lastLine = "";
|
|
2369
2369
|
let signalConnected;
|
|
2370
2370
|
const connected = new Promise((resolve) => {
|
|
2371
2371
|
signalConnected = resolve;
|
|
2372
2372
|
});
|
|
2373
|
-
const client = new
|
|
2373
|
+
const client = new PortalDaemonClient({
|
|
2374
2374
|
appUrl: session.appUrl,
|
|
2375
2375
|
sessionToken: session.sessionToken,
|
|
2376
|
-
resolveCwd: () => cwd,
|
|
2377
2376
|
onState: (state) => {
|
|
2378
2377
|
if (state.status === "connected") signalConnected();
|
|
2379
2378
|
const line = state.status === "error" ? `portal: connection error: ${state.error ?? "unknown"}; retrying` : `portal: ${state.status}`;
|
|
@@ -2389,6 +2388,8 @@ const openCommand = {
|
|
|
2389
2388
|
};
|
|
2390
2389
|
process.on("SIGINT", stop);
|
|
2391
2390
|
process.on("SIGTERM", stop);
|
|
2391
|
+
await client.start();
|
|
2392
|
+
client.setFallbackCwd(cwd);
|
|
2392
2393
|
client.enable();
|
|
2393
2394
|
await connected;
|
|
2394
2395
|
if (agent) {
|
|
@@ -2707,7 +2708,7 @@ async function runPty({ session, agentId, agentName }) {
|
|
|
2707
2708
|
return 1;
|
|
2708
2709
|
}
|
|
2709
2710
|
console.error(`Connecting to ${agentName}'s sandbox… (Ctrl-] detaches)`);
|
|
2710
|
-
const { runRawPtyPassthrough } = await import("./raw-pty-
|
|
2711
|
+
const { runRawPtyPassthrough } = await import("./raw-pty-3EkG-jjH.mjs");
|
|
2711
2712
|
const result = await runRawPtyPassthrough({
|
|
2712
2713
|
stdin: process.stdin,
|
|
2713
2714
|
stdout: process.stdout,
|
|
@@ -3897,7 +3898,7 @@ if (process.argv.includes(UPDATE_WORKER_FLAG)) {
|
|
|
3897
3898
|
process.exit(0);
|
|
3898
3899
|
}
|
|
3899
3900
|
if (process.argv.includes(PORTAL_DAEMON_FLAG)) {
|
|
3900
|
-
const { runPortalDaemon } = await import("./daemon-
|
|
3901
|
+
const { runPortalDaemon } = await import("./daemon-imLBqTGg.mjs");
|
|
3901
3902
|
runPortalDaemon(process.argv);
|
|
3902
3903
|
} else runCli();
|
|
3903
3904
|
function runCli() {
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { D as setActiveWorkspace, E as listWorkspaces, S as themesForMode, _ as themeForMode, a as installCrashHandler, b as themeVersion, c as MARK_CELLS, d as DEFAULT_THEME_ID, f as applyTheme, g as theme, h as noColorRequested, i as writeArtifact, l as WORDMARK, m as monoTheme, n as profilingEnabled, o as buildCrashReport, p as findTheme, r as record, s as writeCrashReport, v as themeMode, w as getActiveWorkspaceId, y as themeModeFromColorFgBg } from "./profiler-
|
|
2
|
+
import { D as setActiveWorkspace, E as listWorkspaces, S as themesForMode, _ as themeForMode, a as installCrashHandler, b as themeVersion, c as MARK_CELLS, d as DEFAULT_THEME_ID, f as applyTheme, g as theme, h as noColorRequested, i as writeArtifact, l as WORDMARK, m as monoTheme, n as profilingEnabled, o as buildCrashReport, p as findTheme, r as record, s as writeCrashReport, v as themeMode, w as getActiveWorkspaceId, y as themeModeFromColorFgBg } from "./profiler-BoPzxsbP.mjs";
|
|
3
3
|
import { B as saveTheme, D as getSavedTheme, E as getReviewStateDir, L as resolveWebUrl, S as getConfigPath, c as cardActionErrorMessage, d as reconcileMaskedInput, f as resolveConnectUrl, i as resolveAgent, l as parseExternalOauthConnectParams, m as specKeyFor, p as parseConnectCard, s as MASK_CHAR, u as parseOauthConnectParams, v as DEFAULT_API_URL, y as DEFAULT_APP_URL } from "./print-CbayCa87.mjs";
|
|
4
4
|
import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
|
|
5
5
|
import { a as isRecord, i as errorMessage, n as errorDetail, r as sendErrorMessage, t as createRestClient } from "./rest-BY2nADw5.mjs";
|
|
6
6
|
import { i as billingBlockedOutcomeFromSendResponse } from "./billing-blocked-2wju4gC_.mjs";
|
|
7
7
|
import { t as PortalClient } from "./client-Cf7cvftg.mjs";
|
|
8
|
-
import
|
|
8
|
+
import "./daemon-DaUaCMwC.mjs";
|
|
9
9
|
import { t as SandboxStream } from "./client-Cn2af31H.mjs";
|
|
10
|
-
import { t as
|
|
10
|
+
import { t as PortalDaemonClient } from "./daemon-client-CJxbCLOb.mjs";
|
|
11
|
+
import { t as runRawPtyPassthrough } from "./raw-pty-B6mAroiI.mjs";
|
|
11
12
|
import * as os$1 from "node:os";
|
|
12
13
|
import { homedir, platform, release, tmpdir } from "node:os";
|
|
13
14
|
import path, { basename, extname, isAbsolute, join, win32 } from "node:path";
|
|
@@ -15,7 +16,7 @@ import { z } from "zod";
|
|
|
15
16
|
import open from "open";
|
|
16
17
|
import { constants } from "node:fs";
|
|
17
18
|
import { execFile, spawn } from "node:child_process";
|
|
18
|
-
import { createHash
|
|
19
|
+
import { createHash } from "node:crypto";
|
|
19
20
|
import { connect, createConnection } from "node:net";
|
|
20
21
|
import { access, appendFile, mkdir, mkdtemp, readFile, readdir, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
21
22
|
import { MarkdownRenderable, RenderableEvents, StyledText, SyntaxStyle, addDefaultParsers, createCliRenderer, decodePasteBytes, detectLinks, fg, link } from "@opentui/core";
|
|
@@ -293,151 +294,6 @@ async function resolveInitialScreen({ rest, agentSelector, conversationId, newCo
|
|
|
293
294
|
};
|
|
294
295
|
}
|
|
295
296
|
|
|
296
|
-
//#endregion
|
|
297
|
-
//#region ../portal-daemon/src/daemon-client.ts
|
|
298
|
-
var PortalDaemonClient = class {
|
|
299
|
-
sessionId = randomUUID();
|
|
300
|
-
socket = null;
|
|
301
|
-
disposed = false;
|
|
302
|
-
reconnectTimer = null;
|
|
303
|
-
lastBind = null;
|
|
304
|
-
wantEnabled = false;
|
|
305
|
-
pendingGrants = /* @__PURE__ */ new Set();
|
|
306
|
-
grantedAgentIds = /* @__PURE__ */ new Set();
|
|
307
|
-
constructor(opts) {
|
|
308
|
-
this.opts = opts;
|
|
309
|
-
}
|
|
310
|
-
/** Spawn the daemon if needed and attach. Idempotent. */
|
|
311
|
-
async start() {
|
|
312
|
-
if (this.disposed) return;
|
|
313
|
-
await ensureDaemonRunning(this.opts.appUrl);
|
|
314
|
-
this.connect();
|
|
315
|
-
}
|
|
316
|
-
connect() {
|
|
317
|
-
if (this.disposed) return;
|
|
318
|
-
const { socketPath } = daemonPaths(this.opts.appUrl);
|
|
319
|
-
const socket = connect(socketPath);
|
|
320
|
-
socket.setEncoding("utf8");
|
|
321
|
-
this.socket = socket;
|
|
322
|
-
const parse = makeLineParser();
|
|
323
|
-
socket.on("connect", () => {
|
|
324
|
-
this.send({
|
|
325
|
-
t: "hello",
|
|
326
|
-
v: LOCAL_PROTOCOL_VERSION,
|
|
327
|
-
sessionId: this.sessionId,
|
|
328
|
-
token: this.opts.sessionToken
|
|
329
|
-
});
|
|
330
|
-
if (this.wantEnabled) this.send({ t: "enable" });
|
|
331
|
-
if (this.lastBind) this.send({
|
|
332
|
-
t: "bind",
|
|
333
|
-
sessionId: this.sessionId,
|
|
334
|
-
conversationId: this.lastBind.conversationId,
|
|
335
|
-
cwd: this.lastBind.cwd
|
|
336
|
-
});
|
|
337
|
-
for (const agentId of this.pendingGrants) this.send({
|
|
338
|
-
t: "grant",
|
|
339
|
-
agentId
|
|
340
|
-
});
|
|
341
|
-
});
|
|
342
|
-
socket.on("data", (chunk) => {
|
|
343
|
-
for (const line of parse(chunk)) {
|
|
344
|
-
const msg = parseDaemonMessage(line);
|
|
345
|
-
if (msg) this.onMessage(msg);
|
|
346
|
-
}
|
|
347
|
-
});
|
|
348
|
-
const onClose = () => {
|
|
349
|
-
if (this.socket === socket) this.socket = null;
|
|
350
|
-
this.scheduleReconnect();
|
|
351
|
-
};
|
|
352
|
-
socket.on("close", onClose);
|
|
353
|
-
socket.on("error", onClose);
|
|
354
|
-
}
|
|
355
|
-
scheduleReconnect() {
|
|
356
|
-
if (this.disposed || this.reconnectTimer) return;
|
|
357
|
-
this.reconnectTimer = setTimeout(() => {
|
|
358
|
-
this.reconnectTimer = null;
|
|
359
|
-
this.start();
|
|
360
|
-
}, 500);
|
|
361
|
-
this.reconnectTimer.unref();
|
|
362
|
-
}
|
|
363
|
-
onMessage(msg) {
|
|
364
|
-
if (msg.t === "state") {
|
|
365
|
-
this.grantedAgentIds = new Set(msg.grantedAgentIds);
|
|
366
|
-
this.opts.onState({
|
|
367
|
-
status: msg.status,
|
|
368
|
-
machineName: msg.machineName,
|
|
369
|
-
friendlyName: msg.friendlyName,
|
|
370
|
-
error: msg.error,
|
|
371
|
-
grantedAgentIds: msg.grantedAgentIds
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
/** Whether this agent is currently authorized on the machine (last daemon state). */
|
|
376
|
-
isGranted(agentId) {
|
|
377
|
-
return this.grantedAgentIds.has(agentId);
|
|
378
|
-
}
|
|
379
|
-
/** Whether the user has turned machine sharing on from this CLI. */
|
|
380
|
-
isEnabled() {
|
|
381
|
-
return this.wantEnabled;
|
|
382
|
-
}
|
|
383
|
-
send(msg) {
|
|
384
|
-
if (this.socket?.writable) this.socket.write(encodeLine(msg));
|
|
385
|
-
}
|
|
386
|
-
/** Turn machine sharing on (the daemon connects the portal). */
|
|
387
|
-
enable() {
|
|
388
|
-
this.wantEnabled = true;
|
|
389
|
-
this.send({ t: "enable" });
|
|
390
|
-
}
|
|
391
|
-
/** Turn machine sharing off. */
|
|
392
|
-
disable() {
|
|
393
|
-
this.wantEnabled = false;
|
|
394
|
-
this.send({ t: "disable" });
|
|
395
|
-
}
|
|
396
|
-
/** Register / update this conversation's working directory for exec routing. */
|
|
397
|
-
bind(conversationId, cwd) {
|
|
398
|
-
this.lastBind = {
|
|
399
|
-
conversationId,
|
|
400
|
-
cwd
|
|
401
|
-
};
|
|
402
|
-
this.send({
|
|
403
|
-
t: "bind",
|
|
404
|
-
sessionId: this.sessionId,
|
|
405
|
-
conversationId,
|
|
406
|
-
cwd
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
/**
|
|
410
|
-
* Authorize one agent to run commands on this machine. Resolves once the
|
|
411
|
-
* request is sent to the daemon (the daemon performs the grant and pushes the
|
|
412
|
-
* updated state); kept async so it's a drop-in for the old in-process client's
|
|
413
|
-
* awaited `grantAgent`.
|
|
414
|
-
*/
|
|
415
|
-
grantAgent(agentId) {
|
|
416
|
-
this.pendingGrants.add(agentId);
|
|
417
|
-
this.send({
|
|
418
|
-
t: "grant",
|
|
419
|
-
agentId
|
|
420
|
-
});
|
|
421
|
-
return Promise.resolve();
|
|
422
|
-
}
|
|
423
|
-
/** Detach this CLI. The daemon keeps running for other clients. */
|
|
424
|
-
dispose() {
|
|
425
|
-
this.disposed = true;
|
|
426
|
-
if (this.reconnectTimer) {
|
|
427
|
-
clearTimeout(this.reconnectTimer);
|
|
428
|
-
this.reconnectTimer = null;
|
|
429
|
-
}
|
|
430
|
-
this.send({
|
|
431
|
-
t: "bye",
|
|
432
|
-
sessionId: this.sessionId
|
|
433
|
-
});
|
|
434
|
-
try {
|
|
435
|
-
this.socket?.destroy();
|
|
436
|
-
} catch (_error) {}
|
|
437
|
-
this.socket = null;
|
|
438
|
-
}
|
|
439
|
-
};
|
|
440
|
-
|
|
441
297
|
//#endregion
|
|
442
298
|
//#region ../portal-daemon/src/create-portal-client.ts
|
|
443
299
|
/**
|
|
@@ -95,6 +95,15 @@ const clientBindSchema = z.object({
|
|
|
95
95
|
/** `enable`/`disable` — turn machine sharing on/off (user-initiated). */
|
|
96
96
|
const clientEnableSchema = z.object({ t: z.literal("enable") });
|
|
97
97
|
const clientDisableSchema = z.object({ t: z.literal("disable") });
|
|
98
|
+
/**
|
|
99
|
+
* `cwd` — set the directory an exec runs in when its conversation is unknown.
|
|
100
|
+
* `skydive portal open` has no conversation of its own: it shares the machine
|
|
101
|
+
* for one directory, so it sets the fallback directly instead of binding.
|
|
102
|
+
*/
|
|
103
|
+
const clientCwdSchema = z.object({
|
|
104
|
+
t: z.literal("cwd"),
|
|
105
|
+
cwd: z.string().min(1)
|
|
106
|
+
});
|
|
98
107
|
/** `grant` — authorize one agent to reach this machine (user-initiated). */
|
|
99
108
|
const clientGrantSchema = z.object({
|
|
100
109
|
t: z.literal("grant"),
|
|
@@ -122,6 +131,7 @@ const clientMessageSchema = z.discriminatedUnion("t", [
|
|
|
122
131
|
clientBindSchema,
|
|
123
132
|
clientEnableSchema,
|
|
124
133
|
clientDisableSchema,
|
|
134
|
+
clientCwdSchema,
|
|
125
135
|
clientGrantSchema,
|
|
126
136
|
clientByeSchema,
|
|
127
137
|
clientStatusSchema,
|
|
@@ -289,7 +299,8 @@ var PortalDaemon = class {
|
|
|
289
299
|
const conn = {
|
|
290
300
|
socket,
|
|
291
301
|
sessionId: null,
|
|
292
|
-
conversations: /* @__PURE__ */ new Set()
|
|
302
|
+
conversations: /* @__PURE__ */ new Set(),
|
|
303
|
+
wantsShare: false
|
|
293
304
|
};
|
|
294
305
|
this.conns.add(conn);
|
|
295
306
|
this.clearIdleTimer();
|
|
@@ -323,12 +334,17 @@ var PortalDaemon = class {
|
|
|
323
334
|
this.persistState();
|
|
324
335
|
return;
|
|
325
336
|
case "enable":
|
|
337
|
+
conn.wantsShare = true;
|
|
326
338
|
this.ensureClient();
|
|
327
339
|
this.client?.enable();
|
|
328
340
|
return;
|
|
329
341
|
case "disable":
|
|
342
|
+
for (const other of this.conns) other.wantsShare = false;
|
|
330
343
|
this.client?.disable();
|
|
331
344
|
return;
|
|
345
|
+
case "cwd":
|
|
346
|
+
this.fallbackCwd = msg.cwd;
|
|
347
|
+
return;
|
|
332
348
|
case "grant":
|
|
333
349
|
this.ensureClient();
|
|
334
350
|
this.client?.grantAgent(msg.agentId).catch((error) => {
|
|
@@ -353,6 +369,8 @@ var PortalDaemon = class {
|
|
|
353
369
|
try {
|
|
354
370
|
conn.socket.destroy();
|
|
355
371
|
} catch (_error) {}
|
|
372
|
+
conn.wantsShare = false;
|
|
373
|
+
if (![...this.conns].some((c) => c.wantsShare)) this.client?.disable();
|
|
356
374
|
if (this.conns.size === 0) this.armIdleTimer();
|
|
357
375
|
}
|
|
358
376
|
/** Create the single PortalClient the first time a client needs the portal. */
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { d as makeLineParser, f as parseDaemonMessage, l as daemonPaths, n as ensureDaemonRunning, s as LOCAL_PROTOCOL_VERSION, u as encodeLine } from "./daemon-DaUaCMwC.mjs";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { connect } from "node:net";
|
|
5
|
+
|
|
6
|
+
//#region ../portal-daemon/src/daemon-client.ts
|
|
7
|
+
var PortalDaemonClient = class {
|
|
8
|
+
sessionId = randomUUID();
|
|
9
|
+
socket = null;
|
|
10
|
+
disposed = false;
|
|
11
|
+
reconnectTimer = null;
|
|
12
|
+
lastBind = null;
|
|
13
|
+
fallbackCwd = null;
|
|
14
|
+
wantEnabled = false;
|
|
15
|
+
pendingGrants = /* @__PURE__ */ new Set();
|
|
16
|
+
grantedAgentIds = /* @__PURE__ */ new Set();
|
|
17
|
+
constructor(opts) {
|
|
18
|
+
this.opts = opts;
|
|
19
|
+
}
|
|
20
|
+
/** Spawn the daemon if needed and attach. Idempotent. */
|
|
21
|
+
async start() {
|
|
22
|
+
if (this.disposed) return;
|
|
23
|
+
await ensureDaemonRunning(this.opts.appUrl);
|
|
24
|
+
this.connect();
|
|
25
|
+
}
|
|
26
|
+
connect() {
|
|
27
|
+
if (this.disposed) return;
|
|
28
|
+
const { socketPath } = daemonPaths(this.opts.appUrl);
|
|
29
|
+
const socket = connect(socketPath);
|
|
30
|
+
socket.setEncoding("utf8");
|
|
31
|
+
this.socket = socket;
|
|
32
|
+
const parse = makeLineParser();
|
|
33
|
+
socket.on("connect", () => {
|
|
34
|
+
this.send({
|
|
35
|
+
t: "hello",
|
|
36
|
+
v: LOCAL_PROTOCOL_VERSION,
|
|
37
|
+
sessionId: this.sessionId,
|
|
38
|
+
token: this.opts.sessionToken
|
|
39
|
+
});
|
|
40
|
+
if (this.wantEnabled) this.send({ t: "enable" });
|
|
41
|
+
if (this.fallbackCwd) this.send({
|
|
42
|
+
t: "cwd",
|
|
43
|
+
cwd: this.fallbackCwd
|
|
44
|
+
});
|
|
45
|
+
if (this.lastBind) this.send({
|
|
46
|
+
t: "bind",
|
|
47
|
+
sessionId: this.sessionId,
|
|
48
|
+
conversationId: this.lastBind.conversationId,
|
|
49
|
+
cwd: this.lastBind.cwd
|
|
50
|
+
});
|
|
51
|
+
for (const agentId of this.pendingGrants) this.send({
|
|
52
|
+
t: "grant",
|
|
53
|
+
agentId
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
socket.on("data", (chunk) => {
|
|
57
|
+
for (const line of parse(chunk)) {
|
|
58
|
+
const msg = parseDaemonMessage(line);
|
|
59
|
+
if (msg) this.onMessage(msg);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
const onClose = () => {
|
|
63
|
+
if (this.socket === socket) this.socket = null;
|
|
64
|
+
this.scheduleReconnect();
|
|
65
|
+
};
|
|
66
|
+
socket.on("close", onClose);
|
|
67
|
+
socket.on("error", onClose);
|
|
68
|
+
}
|
|
69
|
+
scheduleReconnect() {
|
|
70
|
+
if (this.disposed || this.reconnectTimer) return;
|
|
71
|
+
this.reconnectTimer = setTimeout(() => {
|
|
72
|
+
this.reconnectTimer = null;
|
|
73
|
+
this.start();
|
|
74
|
+
}, 500);
|
|
75
|
+
this.reconnectTimer.unref();
|
|
76
|
+
}
|
|
77
|
+
onMessage(msg) {
|
|
78
|
+
if (msg.t === "state") {
|
|
79
|
+
this.grantedAgentIds = new Set(msg.grantedAgentIds);
|
|
80
|
+
this.opts.onState({
|
|
81
|
+
status: msg.status,
|
|
82
|
+
machineName: msg.machineName,
|
|
83
|
+
friendlyName: msg.friendlyName,
|
|
84
|
+
error: msg.error,
|
|
85
|
+
grantedAgentIds: msg.grantedAgentIds
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** Whether this agent is currently authorized on the machine (last daemon state). */
|
|
90
|
+
isGranted(agentId) {
|
|
91
|
+
return this.grantedAgentIds.has(agentId);
|
|
92
|
+
}
|
|
93
|
+
/** Whether the user has turned machine sharing on from this CLI. */
|
|
94
|
+
isEnabled() {
|
|
95
|
+
return this.wantEnabled;
|
|
96
|
+
}
|
|
97
|
+
send(msg) {
|
|
98
|
+
if (this.socket?.writable) this.socket.write(encodeLine(msg));
|
|
99
|
+
}
|
|
100
|
+
/** Turn machine sharing on (the daemon connects the portal). */
|
|
101
|
+
enable() {
|
|
102
|
+
this.wantEnabled = true;
|
|
103
|
+
this.send({ t: "enable" });
|
|
104
|
+
}
|
|
105
|
+
/** Turn machine sharing off. */
|
|
106
|
+
disable() {
|
|
107
|
+
this.wantEnabled = false;
|
|
108
|
+
this.send({ t: "disable" });
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Set the directory an exec runs in when the daemon can't place its
|
|
112
|
+
* conversation. `skydive portal open` shares one directory and has no
|
|
113
|
+
* conversation of its own, so this is how it routes.
|
|
114
|
+
*/
|
|
115
|
+
setFallbackCwd(cwd) {
|
|
116
|
+
this.fallbackCwd = cwd;
|
|
117
|
+
this.send({
|
|
118
|
+
t: "cwd",
|
|
119
|
+
cwd
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
/** Register / update this conversation's working directory for exec routing. */
|
|
123
|
+
bind(conversationId, cwd) {
|
|
124
|
+
this.lastBind = {
|
|
125
|
+
conversationId,
|
|
126
|
+
cwd
|
|
127
|
+
};
|
|
128
|
+
this.send({
|
|
129
|
+
t: "bind",
|
|
130
|
+
sessionId: this.sessionId,
|
|
131
|
+
conversationId,
|
|
132
|
+
cwd
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Authorize one agent to run commands on this machine. Resolves once the
|
|
137
|
+
* request is sent to the daemon (the daemon performs the grant and pushes the
|
|
138
|
+
* updated state); kept async so it's a drop-in for the old in-process client's
|
|
139
|
+
* awaited `grantAgent`.
|
|
140
|
+
*/
|
|
141
|
+
grantAgent(agentId) {
|
|
142
|
+
this.pendingGrants.add(agentId);
|
|
143
|
+
this.send({
|
|
144
|
+
t: "grant",
|
|
145
|
+
agentId
|
|
146
|
+
});
|
|
147
|
+
return Promise.resolve();
|
|
148
|
+
}
|
|
149
|
+
/** Detach this CLI. The daemon keeps running for other clients. */
|
|
150
|
+
dispose() {
|
|
151
|
+
this.disposed = true;
|
|
152
|
+
if (this.reconnectTimer) {
|
|
153
|
+
clearTimeout(this.reconnectTimer);
|
|
154
|
+
this.reconnectTimer = null;
|
|
155
|
+
}
|
|
156
|
+
this.send({
|
|
157
|
+
t: "bye",
|
|
158
|
+
sessionId: this.sessionId
|
|
159
|
+
});
|
|
160
|
+
try {
|
|
161
|
+
this.socket?.destroy();
|
|
162
|
+
} catch (_error) {}
|
|
163
|
+
this.socket = null;
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
//#endregion
|
|
168
|
+
export { PortalDaemonClient as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import "./client-Cf7cvftg.mjs";
|
|
3
|
-
import { a as runPortalDaemon, i as queryDaemonStatus, n as ensureDaemonRunning, o as stopDaemon, r as isDaemonListening, t as PortalDaemon } from "./daemon-
|
|
3
|
+
import { a as runPortalDaemon, i as queryDaemonStatus, n as ensureDaemonRunning, o as stopDaemon, r as isDaemonListening, t as PortalDaemon } from "./daemon-DaUaCMwC.mjs";
|
|
4
4
|
|
|
5
5
|
export { runPortalDaemon };
|
package/package.json
CHANGED
|
File without changes
|
|
File without changes
|