skydive-cli 0.5.0-beta.4 → 0.5.0-beta.44
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/CHANGELOG.md +10 -0
- package/README.md +35 -4
- package/dist/js/api-TwLD7ibI.mjs +315 -0
- package/dist/js/{billing-blocked-Dgu5-oDy.mjs → billing-blocked-SE6tySLd.mjs} +1 -1
- package/dist/js/bin.mjs +658 -279
- package/dist/js/{boot-B_93T51X.mjs → boot-ClKjP9Mn.mjs} +3263 -755
- package/dist/js/chunk-BbwQpWto.mjs +33 -0
- package/dist/js/{client-Dd5sMXPv.mjs → client-BnpgrtCC.mjs} +348 -141
- package/dist/js/{client-Cn2af31H.mjs → client-Btq6bMzX.mjs} +108 -2
- package/dist/js/client-Cga4PJUc.mjs +5 -0
- package/dist/js/daemon-client-D5YCk5Hc.mjs +8 -0
- package/dist/js/{daemon-client-DPjmvIDE.mjs → daemon-client-KKNss0NK.mjs} +12 -8
- package/dist/js/daemon-mLypjXdu.mjs +7 -0
- package/dist/js/{daemon-BYlIJN6x.mjs → daemon-w8vtZ81t.mjs} +43 -7
- package/dist/js/dist-CRtjM7ba.mjs +1750 -0
- package/dist/js/forward-Ct8Vd2WW.mjs +208 -0
- package/dist/js/{profiler-RmWjKIHD.mjs → install-DrtNldUN.mjs} +529 -215
- package/dist/js/launcher.mjs +49 -0
- package/dist/js/localhost-cert-Bn-UBUmj.mjs +67 -0
- package/dist/js/{print-ba_0hiV9.mjs → print-Dc-xRapT.mjs} +3 -3
- package/dist/js/{print-CbayCa87.mjs → print-Ds16-Q4O.mjs} +289 -37
- package/dist/js/{print-share-uwUG16Ov.mjs → print-share-Df6eAG12.mjs} +8 -2
- package/dist/js/raw-pty-Ci2qFR9F.mjs +5 -0
- package/dist/js/{raw-pty-B6mAroiI.mjs → raw-pty-D5PhKZSl.mjs} +1 -1
- package/dist/js/rest-Do3mPVpC.mjs +6 -0
- package/dist/js/{rest-BY2nADw5.mjs → rest-DpsJUYTL.mjs} +117 -28
- package/dist/js/tls-cert-CLgSQALB.mjs +4 -0
- package/dist/js/tls-cert-CV-pwxVN.mjs +67 -0
- package/package.json +11 -4
- package/dist/js/client-DZstLQ_1.mjs +0 -4
- package/dist/js/daemon-CBvFKDpy.mjs +0 -5
- package/dist/js/daemon-client-Z4Ce4zf-.mjs +0 -6
- package/dist/js/raw-pty-3EkG-jjH.mjs +0 -5
- package/dist/js/rest-DADJh0bi.mjs +0 -6
- /package/dist/js/{billing-blocked-2wju4gC_.mjs → billing-blocked-D3l5kJlX.mjs} +0 -0
- /package/dist/js/{http-error-DzyrsLAZ.mjs → http-error-BF2NZZE3.mjs} +0 -0
- /package/dist/js/{output-DYzzdXYV.mjs → output-C9mb3sUB.mjs} +0 -0
|
@@ -1,6 +1,108 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { WebSocket } from "ws";
|
|
3
3
|
|
|
4
|
+
//#region ../sandbox-stream-protocol/src/fs.ts
|
|
5
|
+
/** fs frame type bytes. Disjoint from FRAME.* in ./index.ts. */
|
|
6
|
+
const FS_FRAME = {
|
|
7
|
+
REQ: 32,
|
|
8
|
+
RES: 33
|
|
9
|
+
};
|
|
10
|
+
/** Filesystem operations the channel supports. One byte on the wire. */
|
|
11
|
+
const FS_OP = {
|
|
12
|
+
LIST: 1,
|
|
13
|
+
STAT: 2,
|
|
14
|
+
READ: 3,
|
|
15
|
+
WRITE: 4,
|
|
16
|
+
MKDIR: 5,
|
|
17
|
+
RENAME: 6,
|
|
18
|
+
REMOVE: 7,
|
|
19
|
+
EXISTS: 8
|
|
20
|
+
};
|
|
21
|
+
/** Reply status. OK carries a result; ERR carries a message in `json.message`. */
|
|
22
|
+
const FS_STATUS = {
|
|
23
|
+
OK: 0,
|
|
24
|
+
ERR: 1
|
|
25
|
+
};
|
|
26
|
+
const FS_MAX_BLOB_BYTES = 8 * 1024 * 1024;
|
|
27
|
+
const OP_TO_CODE = {
|
|
28
|
+
list: FS_OP.LIST,
|
|
29
|
+
stat: FS_OP.STAT,
|
|
30
|
+
read: FS_OP.READ,
|
|
31
|
+
write: FS_OP.WRITE,
|
|
32
|
+
mkdir: FS_OP.MKDIR,
|
|
33
|
+
rename: FS_OP.RENAME,
|
|
34
|
+
remove: FS_OP.REMOVE,
|
|
35
|
+
exists: FS_OP.EXISTS
|
|
36
|
+
};
|
|
37
|
+
const CODE_TO_OP = new Map(Object.entries(OP_TO_CODE).map(([op, code]) => [code, op]));
|
|
38
|
+
const textEncoder = new TextEncoder();
|
|
39
|
+
const textDecoder = new TextDecoder();
|
|
40
|
+
function frame(type, reqId, byte2, json, blob) {
|
|
41
|
+
const jsonBytes = textEncoder.encode(JSON.stringify(json ?? {}));
|
|
42
|
+
const out = new Uint8Array(10 + jsonBytes.length + blob.length);
|
|
43
|
+
const dv = new DataView(out.buffer);
|
|
44
|
+
out[0] = type;
|
|
45
|
+
dv.setUint32(1, reqId >>> 0);
|
|
46
|
+
out[5] = byte2;
|
|
47
|
+
dv.setUint32(6, jsonBytes.length);
|
|
48
|
+
out.set(jsonBytes, 10);
|
|
49
|
+
out.set(blob, 10 + jsonBytes.length);
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
const EMPTY = new Uint8Array(0);
|
|
53
|
+
/** client → server: encode an fs request. `blob` is the write payload, or null. */
|
|
54
|
+
function encodeFsRequest(reqId, req, blob) {
|
|
55
|
+
return frame(FS_FRAME.REQ, reqId, OP_TO_CODE[req.op], req, blob ?? EMPTY);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Decode a server fs reply frame. Returns null for a malformed frame so a peer
|
|
59
|
+
* on a newer protocol can't crash the client.
|
|
60
|
+
*/
|
|
61
|
+
function decodeFsResponse(frameBytes) {
|
|
62
|
+
const parsed = parseFrame(FS_FRAME.RES, frameBytes);
|
|
63
|
+
if (!parsed) return null;
|
|
64
|
+
if (parsed.byte2 === FS_STATUS.ERR) {
|
|
65
|
+
const message = typeof parsed.json.message === "string" ? parsed.json.message : "fs operation failed";
|
|
66
|
+
return {
|
|
67
|
+
reqId: parsed.reqId,
|
|
68
|
+
status: "error",
|
|
69
|
+
message
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
reqId: parsed.reqId,
|
|
74
|
+
status: "ok",
|
|
75
|
+
result: parsed.json,
|
|
76
|
+
blob: parsed.blob
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function parseFrame(expectedType, frameBytes) {
|
|
80
|
+
if (frameBytes.length < 10) return null;
|
|
81
|
+
if (frameBytes[0] !== expectedType) return null;
|
|
82
|
+
const dv = new DataView(frameBytes.buffer, frameBytes.byteOffset, frameBytes.byteLength);
|
|
83
|
+
const reqId = dv.getUint32(1);
|
|
84
|
+
const byte2 = frameBytes[5] ?? 0;
|
|
85
|
+
const jsonLen = dv.getUint32(6);
|
|
86
|
+
const jsonStart = 10;
|
|
87
|
+
const jsonEnd = jsonStart + jsonLen;
|
|
88
|
+
if (jsonEnd > frameBytes.length) return null;
|
|
89
|
+
let json;
|
|
90
|
+
try {
|
|
91
|
+
const parsed = jsonLen ? JSON.parse(textDecoder.decode(frameBytes.subarray(jsonStart, jsonEnd))) : {};
|
|
92
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
93
|
+
json = parsed;
|
|
94
|
+
} catch {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
reqId,
|
|
99
|
+
byte2,
|
|
100
|
+
json,
|
|
101
|
+
blob: frameBytes.subarray(jsonEnd)
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
//#endregion
|
|
4
106
|
//#region ../sandbox-stream-protocol/src/index.ts
|
|
5
107
|
const SANDBOX_STREAM_PATH = "/api/v1/sandbox/stream";
|
|
6
108
|
const FRAME = {
|
|
@@ -19,11 +121,15 @@ function streamSpecToQuery(spec) {
|
|
|
19
121
|
cols: String(spec.cols),
|
|
20
122
|
rows: String(spec.rows)
|
|
21
123
|
};
|
|
22
|
-
return {
|
|
124
|
+
if (spec.mode === "exec") return {
|
|
23
125
|
agentId: spec.agentId,
|
|
24
126
|
mode: "exec",
|
|
25
127
|
command: spec.command
|
|
26
128
|
};
|
|
129
|
+
return {
|
|
130
|
+
agentId: spec.agentId,
|
|
131
|
+
mode: "fs"
|
|
132
|
+
};
|
|
27
133
|
}
|
|
28
134
|
function withType(type, payload) {
|
|
29
135
|
const frame = new Uint8Array(1 + payload.length);
|
|
@@ -166,4 +272,4 @@ function toBuffer(data) {
|
|
|
166
272
|
}
|
|
167
273
|
|
|
168
274
|
//#endregion
|
|
169
|
-
export { SandboxStream as t };
|
|
275
|
+
export { encodeFsRequest as a, decodeFsResponse as i, SANDBOX_STREAM_PATH as n, streamSpecToQuery as r, SandboxStream as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { c as LOCAL_PROTOCOL_VERSION, d as encodeLine, f as makeLineParser, n as ensureDaemonRunning, p as parseDaemonMessage, u as daemonPaths } from "./daemon-
|
|
2
|
+
import { c as LOCAL_PROTOCOL_VERSION, d as encodeLine, f as makeLineParser, n as ensureDaemonRunning, p as parseDaemonMessage, u as daemonPaths } from "./daemon-w8vtZ81t.mjs";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import { connect } from "node:net";
|
|
5
5
|
|
|
@@ -12,7 +12,7 @@ var PortalDaemonClient = class {
|
|
|
12
12
|
lastBind = null;
|
|
13
13
|
fallbackCwd = null;
|
|
14
14
|
wantEnabled = false;
|
|
15
|
-
pendingGrants = /* @__PURE__ */ new
|
|
15
|
+
pendingGrants = /* @__PURE__ */ new Map();
|
|
16
16
|
pendingDeclines = /* @__PURE__ */ new Map();
|
|
17
17
|
grantedAgentIds = /* @__PURE__ */ new Set();
|
|
18
18
|
constructor(opts) {
|
|
@@ -50,9 +50,10 @@ var PortalDaemonClient = class {
|
|
|
50
50
|
conversationId: this.lastBind.conversationId,
|
|
51
51
|
cwd: this.lastBind.cwd
|
|
52
52
|
});
|
|
53
|
-
for (const agentId of this.pendingGrants) this.send({
|
|
53
|
+
for (const [agentId, conversationId] of this.pendingGrants) this.send({
|
|
54
54
|
t: "grant",
|
|
55
|
-
agentId
|
|
55
|
+
agentId,
|
|
56
|
+
conversationId
|
|
56
57
|
});
|
|
57
58
|
for (const [agentId, declined] of this.pendingDeclines) this.send({
|
|
58
59
|
t: "decline",
|
|
@@ -157,13 +158,16 @@ var PortalDaemonClient = class {
|
|
|
157
158
|
* Authorize one agent to run commands on this machine. Resolves once the
|
|
158
159
|
* request is sent to the daemon (the daemon performs the grant and pushes the
|
|
159
160
|
* updated state); kept async so it's a drop-in for the old in-process client's
|
|
160
|
-
* awaited `grantAgent`.
|
|
161
|
+
* awaited `grantAgent`. `conversationId` names the conversation whose run
|
|
162
|
+
* asked, so the grant can wake the waiting agent; null when the grant is not
|
|
163
|
+
* answering an in-chat request.
|
|
161
164
|
*/
|
|
162
|
-
grantAgent(agentId) {
|
|
163
|
-
this.pendingGrants.
|
|
165
|
+
grantAgent(agentId, conversationId) {
|
|
166
|
+
this.pendingGrants.set(agentId, conversationId);
|
|
164
167
|
this.send({
|
|
165
168
|
t: "grant",
|
|
166
|
-
agentId
|
|
169
|
+
agentId,
|
|
170
|
+
conversationId
|
|
167
171
|
});
|
|
168
172
|
return Promise.resolve();
|
|
169
173
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "./client-BnpgrtCC.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-w8vtZ81t.mjs";
|
|
4
|
+
import "./tls-cert-CV-pwxVN.mjs";
|
|
5
|
+
import "./api-TwLD7ibI.mjs";
|
|
6
|
+
|
|
7
|
+
export { runPortalDaemon };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { n as isRecord, t as PortalClient } from "./client-BnpgrtCC.mjs";
|
|
3
|
+
import { t as defaultTlsCertSource } from "./tls-cert-CV-pwxVN.mjs";
|
|
3
4
|
import os from "node:os";
|
|
4
5
|
import path from "node:path";
|
|
5
6
|
import { z } from "zod";
|
|
@@ -20,7 +21,7 @@ import { access, appendFile, mkdir, readFile, unlink, writeFile } from "node:fs/
|
|
|
20
21
|
* the commit time of the built tree is one monotonic clock they share.
|
|
21
22
|
*/
|
|
22
23
|
function portalDaemonBuild() {
|
|
23
|
-
return "
|
|
24
|
+
return "1786941153";
|
|
24
25
|
}
|
|
25
26
|
/**
|
|
26
27
|
* Whether a client carrying `mine` should replace a running daemon carrying
|
|
@@ -175,10 +176,18 @@ const clientCwdSchema = z.object({
|
|
|
175
176
|
t: z.literal("cwd"),
|
|
176
177
|
cwd: z.string().min(1)
|
|
177
178
|
});
|
|
178
|
-
/**
|
|
179
|
+
/**
|
|
180
|
+
* `grant` — authorize one agent to reach this machine (user-initiated).
|
|
181
|
+
* `conversationId` names the conversation whose run asked for access (the
|
|
182
|
+
* in-chat approval card); the grant carries it upstream so the server wakes
|
|
183
|
+
* the waiting agent. Defaults to null on parse so hellos from CLI builds that
|
|
184
|
+
* predate the field keep granting against a newer daemon — they just skip the
|
|
185
|
+
* wake, same as before the field existed.
|
|
186
|
+
*/
|
|
179
187
|
const clientGrantSchema = z.object({
|
|
180
188
|
t: z.literal("grant"),
|
|
181
|
-
agentId: z.string().min(1)
|
|
189
|
+
agentId: z.string().min(1),
|
|
190
|
+
conversationId: z.string().nullish().default(null)
|
|
182
191
|
});
|
|
183
192
|
/**
|
|
184
193
|
* `decline` — the user answered no to an agent's request for this machine
|
|
@@ -357,6 +366,7 @@ var PortalDaemon = class {
|
|
|
357
366
|
conns = /* @__PURE__ */ new Set();
|
|
358
367
|
cwds = /* @__PURE__ */ new Map();
|
|
359
368
|
declined = /* @__PURE__ */ new Set();
|
|
369
|
+
persistedMachineName = null;
|
|
360
370
|
fallbackCwd;
|
|
361
371
|
idleTimer = null;
|
|
362
372
|
sessionToken = null;
|
|
@@ -455,7 +465,7 @@ var PortalDaemon = class {
|
|
|
455
465
|
case "grant":
|
|
456
466
|
this.declined.delete(msg.agentId);
|
|
457
467
|
this.ensureClient();
|
|
458
|
-
this.client?.grantAgent(msg.agentId).catch((error) => {
|
|
468
|
+
this.client?.grantAgent(msg.agentId, msg.conversationId).catch((error) => {
|
|
459
469
|
this.logError("grantAgent failed", error);
|
|
460
470
|
});
|
|
461
471
|
return;
|
|
@@ -491,12 +501,29 @@ var PortalDaemon = class {
|
|
|
491
501
|
deviceToken: this.deviceToken
|
|
492
502
|
}),
|
|
493
503
|
resolveCwd: (conversationId) => this.resolveCwd(conversationId),
|
|
504
|
+
tlsCertSource: defaultTlsCertSource(process.env, (msg) => this.logInfo(msg)),
|
|
505
|
+
persistedMachineName: this.persistedMachineName,
|
|
506
|
+
onMachineName: (name, source) => this.onMachineName(name, source),
|
|
494
507
|
onState: (state) => {
|
|
495
508
|
this.lastState = state;
|
|
496
509
|
this.broadcastState();
|
|
497
510
|
}
|
|
498
511
|
});
|
|
499
512
|
}
|
|
513
|
+
/**
|
|
514
|
+
* The identity resolved on connect. Persist the name so a later scutil
|
|
515
|
+
* failure reuses it instead of adopting the volatile hostname, and log the
|
|
516
|
+
* source — a `hostname-fallback` after we'd previously registered under
|
|
517
|
+
* scutil is the tell that a flap just orphaned this machine's grants.
|
|
518
|
+
*/
|
|
519
|
+
onMachineName(name, source) {
|
|
520
|
+
if (source === "hostname-fallback" && this.persistedMachineName) this.logInfo(`portal identity WARNING: fell back to hostname "${name}" but had previously registered as "${this.persistedMachineName}" — grants may be orphaned`);
|
|
521
|
+
else this.logInfo(`portal identity: "${name}" (source=${source})`);
|
|
522
|
+
if (name && name !== this.persistedMachineName) {
|
|
523
|
+
this.persistedMachineName = name;
|
|
524
|
+
this.persistState();
|
|
525
|
+
}
|
|
526
|
+
}
|
|
500
527
|
/** The cwd an exec for `conversationId` runs in. */
|
|
501
528
|
resolveCwd(conversationId) {
|
|
502
529
|
if (conversationId) {
|
|
@@ -530,7 +557,14 @@ var PortalDaemon = class {
|
|
|
530
557
|
/** Append a line to the daemon log file (best-effort, for post-hoc debugging). */
|
|
531
558
|
logError(context, error) {
|
|
532
559
|
const message = error instanceof Error ? error.message : String(error);
|
|
533
|
-
|
|
560
|
+
this.logLine(`${context}: ${message}`);
|
|
561
|
+
}
|
|
562
|
+
/** Append an informational line to the daemon log file (best-effort). */
|
|
563
|
+
logInfo(message) {
|
|
564
|
+
this.logLine(message);
|
|
565
|
+
}
|
|
566
|
+
logLine(message) {
|
|
567
|
+
const line = `${(/* @__PURE__ */ new Date()).toISOString()} ${message}\n`;
|
|
534
568
|
appendFile(this.paths.logPath, line).catch((_error) => {});
|
|
535
569
|
}
|
|
536
570
|
/** Close the listener without exiting the process (tests own the process). */
|
|
@@ -590,6 +624,7 @@ var PortalDaemon = class {
|
|
|
590
624
|
if (Array.isArray(parsed.declined)) {
|
|
591
625
|
for (const id of parsed.declined) if (typeof id === "string") this.declined.add(id);
|
|
592
626
|
}
|
|
627
|
+
if (typeof parsed.machineName === "string" && parsed.machineName) this.persistedMachineName = parsed.machineName;
|
|
593
628
|
}
|
|
594
629
|
} catch (_error) {}
|
|
595
630
|
}
|
|
@@ -597,7 +632,8 @@ var PortalDaemon = class {
|
|
|
597
632
|
const state = {
|
|
598
633
|
version: LOCAL_PROTOCOL_VERSION,
|
|
599
634
|
cwds: Object.fromEntries(this.cwds),
|
|
600
|
-
declined: [...this.declined]
|
|
635
|
+
declined: [...this.declined],
|
|
636
|
+
machineName: this.persistedMachineName ?? void 0
|
|
601
637
|
};
|
|
602
638
|
writeFile(this.paths.statePath, JSON.stringify(state)).catch((error) => {
|
|
603
639
|
this.logError("persistState failed", error);
|