@ai-ecoverse/slicc-kernel 1.22.0 → 1.23.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.
- package/README.md +31 -3
- package/dist/driver-worker.js +87 -1
- package/dist/driver-worker.js.map +3 -3
- package/dist/index.js +126 -15
- package/dist/index.js.map +3 -3
- package/dist/kernel-worker.js +663 -62
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-driver-worker.js +87 -1
- package/dist/node-driver-worker.js.map +3 -3
- package/dist/node.js +1190 -511
- package/dist/node.js.map +4 -4
- package/package.json +1 -1
package/dist/kernel-worker.js
CHANGED
|
@@ -224,8 +224,51 @@ function pinScript(url) {
|
|
|
224
224
|
}
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
+
// src/cdp/port.ts
|
|
228
|
+
function portCdpHook(ask, gone) {
|
|
229
|
+
return (request2) => new Promise((resolve, reject) => {
|
|
230
|
+
const { port1, port2 } = new MessageChannel();
|
|
231
|
+
let closed = false;
|
|
232
|
+
const conn = {
|
|
233
|
+
send: (m) => {
|
|
234
|
+
if (!closed) port1.postMessage({ m });
|
|
235
|
+
},
|
|
236
|
+
onmessage: null,
|
|
237
|
+
onclose: null,
|
|
238
|
+
close: () => {
|
|
239
|
+
if (closed) return;
|
|
240
|
+
closed = true;
|
|
241
|
+
port1.postMessage({ close: true });
|
|
242
|
+
port1.close();
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
port1.addEventListener("message", ({ data }) => {
|
|
246
|
+
if ("m" in data) conn.onmessage?.(data.m);
|
|
247
|
+
else if ("open" in data) resolve(conn);
|
|
248
|
+
else if ("error" in data) {
|
|
249
|
+
closed = true;
|
|
250
|
+
port1.close();
|
|
251
|
+
reject(new Error(data.error));
|
|
252
|
+
} else if (!closed) {
|
|
253
|
+
closed = true;
|
|
254
|
+
port1.close();
|
|
255
|
+
conn.onclose?.(data.close ?? void 0);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
port1.start();
|
|
259
|
+
void gone?.then(() => {
|
|
260
|
+
if (closed) return;
|
|
261
|
+
closed = true;
|
|
262
|
+
port1.close();
|
|
263
|
+
reject(new Error("the CDP host is gone"));
|
|
264
|
+
conn.onclose?.("the CDP host is gone");
|
|
265
|
+
});
|
|
266
|
+
ask(request2, port2);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
227
270
|
// src/client/protocol.ts
|
|
228
|
-
var PROTOCOL = [1,
|
|
271
|
+
var PROTOCOL = [1, 5];
|
|
229
272
|
var FS_METHODS = [
|
|
230
273
|
"readFile",
|
|
231
274
|
"writeFile",
|
|
@@ -887,7 +930,12 @@ function keyOf(addr) {
|
|
|
887
930
|
var LoopbackNet = class {
|
|
888
931
|
bound = /* @__PURE__ */ new Map();
|
|
889
932
|
activators = /* @__PURE__ */ new Map();
|
|
933
|
+
internal = /* @__PURE__ */ new Set();
|
|
890
934
|
nextEphemeral = EPHEMERAL_FIRST;
|
|
935
|
+
kernelOnly(port, reserve = false) {
|
|
936
|
+
if (reserve) this.internal.add(port);
|
|
937
|
+
return this.internal.has(port);
|
|
938
|
+
}
|
|
891
939
|
activate(addr, start) {
|
|
892
940
|
this.activators.set(keyOf(addr), start);
|
|
893
941
|
}
|
|
@@ -948,6 +996,14 @@ var LOOPBACK_NAMES = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "::1", "
|
|
|
948
996
|
function dialSocket(net, port, host = "127.0.0.1") {
|
|
949
997
|
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new KernelError("EINVAL");
|
|
950
998
|
const target = LOOPBACK_NAMES.has(host.toLowerCase()) ? "127.0.0.1" : host;
|
|
999
|
+
if (net.kernelOnly(port)) {
|
|
1000
|
+
throw Object.assign(
|
|
1001
|
+
new Error(`ECONNREFUSED: port ${port} only takes connections from inside the kernel`),
|
|
1002
|
+
{
|
|
1003
|
+
code: "ECONNREFUSED"
|
|
1004
|
+
}
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
951
1007
|
return net.connect({ family: "inet", host: target, port });
|
|
952
1008
|
}
|
|
953
1009
|
var codeOf = (err) => {
|
|
@@ -1236,6 +1292,43 @@ function joinable(l, pgid, ours) {
|
|
|
1236
1292
|
if (sid === void 0) throw fsError("ESRCH", `no process group ${pgid}`);
|
|
1237
1293
|
if (!ours(sid)) throw fsError("EPERM", `process group ${pgid} is in another session`);
|
|
1238
1294
|
}
|
|
1295
|
+
function cdpRegistry(send, gone) {
|
|
1296
|
+
const registered = /* @__PURE__ */ new Map();
|
|
1297
|
+
const drop = (id) => {
|
|
1298
|
+
registered.get(id)?.();
|
|
1299
|
+
return registered.delete(id);
|
|
1300
|
+
};
|
|
1301
|
+
return {
|
|
1302
|
+
serve(l, req) {
|
|
1303
|
+
const hook = portCdpHook(
|
|
1304
|
+
(request2, cdp) => send({ cdpOpen: { ...request2, registration: req.id }, port: cdp }, [
|
|
1305
|
+
cdp
|
|
1306
|
+
]),
|
|
1307
|
+
gone
|
|
1308
|
+
);
|
|
1309
|
+
registered.set(req.id, l.cdp.register(hook, req.runtime));
|
|
1310
|
+
return req.id;
|
|
1311
|
+
},
|
|
1312
|
+
drop,
|
|
1313
|
+
close: () => {
|
|
1314
|
+
for (const id of [...registered.keys()]) drop(id);
|
|
1315
|
+
}
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1318
|
+
function watchOp(l, req, watches, send) {
|
|
1319
|
+
if (req.op === "unwatch") {
|
|
1320
|
+
watches.get(req.watch)?.();
|
|
1321
|
+
watches.delete(req.watch);
|
|
1322
|
+
return { result: true };
|
|
1323
|
+
}
|
|
1324
|
+
const unwatch = l.watchers.watch(
|
|
1325
|
+
req.paths,
|
|
1326
|
+
{ recursive: req.recursive },
|
|
1327
|
+
(change) => send({ watch: req.id, change })
|
|
1328
|
+
);
|
|
1329
|
+
watches.set(req.id, unwatch);
|
|
1330
|
+
return { result: req.id };
|
|
1331
|
+
}
|
|
1239
1332
|
function serveClient(port, host) {
|
|
1240
1333
|
let state = "new";
|
|
1241
1334
|
const terminals = /* @__PURE__ */ new Map();
|
|
@@ -1248,6 +1341,7 @@ function serveClient(port, host) {
|
|
|
1248
1341
|
if (state !== "closed") port.postMessage(message2, transfer);
|
|
1249
1342
|
};
|
|
1250
1343
|
const reply = (id, body, transfer) => send({ id, ...body }, transfer);
|
|
1344
|
+
const cdps = cdpRegistry(send, ended.promise);
|
|
1251
1345
|
function detach(kill = false, bye = "the slicc-kernel detached this client") {
|
|
1252
1346
|
if (state === "closed") return;
|
|
1253
1347
|
send({ bye });
|
|
@@ -1255,6 +1349,7 @@ function serveClient(port, host) {
|
|
|
1255
1349
|
net?.close();
|
|
1256
1350
|
for (const unwatch of watches.values()) unwatch();
|
|
1257
1351
|
watches.clear();
|
|
1352
|
+
cdps.close();
|
|
1258
1353
|
for (const session of terminals.values()) session.close();
|
|
1259
1354
|
terminals.clear();
|
|
1260
1355
|
if (kill) killGroups(host, [...groups]);
|
|
@@ -1349,19 +1444,9 @@ function serveClient(port, host) {
|
|
|
1349
1444
|
throw new Error(`unknown file system call ${req.method}`);
|
|
1350
1445
|
}
|
|
1351
1446
|
return fsCall(l.fs, req.method, req.args);
|
|
1352
|
-
case "watch":
|
|
1353
|
-
const unwatch = l.watchers.watch(
|
|
1354
|
-
req.paths,
|
|
1355
|
-
{ recursive: req.recursive },
|
|
1356
|
-
(change) => send({ watch: req.id, change })
|
|
1357
|
-
);
|
|
1358
|
-
watches.set(req.id, unwatch);
|
|
1359
|
-
return { result: req.id };
|
|
1360
|
-
}
|
|
1447
|
+
case "watch":
|
|
1361
1448
|
case "unwatch":
|
|
1362
|
-
|
|
1363
|
-
watches.delete(req.watch);
|
|
1364
|
-
return { result: true };
|
|
1449
|
+
return watchOp(l, req, watches, send);
|
|
1365
1450
|
case "mount":
|
|
1366
1451
|
return { result: await l.mount(req.spec) };
|
|
1367
1452
|
case "umount":
|
|
@@ -1371,6 +1456,10 @@ function serveClient(port, host) {
|
|
|
1371
1456
|
return { result: l.mounts.list() };
|
|
1372
1457
|
case "dial":
|
|
1373
1458
|
return dialFor(l, req, watches);
|
|
1459
|
+
case "serve-cdp":
|
|
1460
|
+
return { result: cdps.serve(l, req) };
|
|
1461
|
+
case "unserve-cdp":
|
|
1462
|
+
return { result: cdps.drop(req.registration) };
|
|
1374
1463
|
case "detach":
|
|
1375
1464
|
return { result: true };
|
|
1376
1465
|
default:
|
|
@@ -1934,6 +2023,8 @@ var REASON = {
|
|
|
1934
2023
|
200: "OK",
|
|
1935
2024
|
400: "Bad Request",
|
|
1936
2025
|
403: "Forbidden",
|
|
2026
|
+
404: "Not Found",
|
|
2027
|
+
405: "Method Not Allowed",
|
|
1937
2028
|
408: "Request Timeout",
|
|
1938
2029
|
411: "Length Required",
|
|
1939
2030
|
413: "Content Too Large",
|
|
@@ -1943,6 +2034,7 @@ var REASON = {
|
|
|
1943
2034
|
501: "Not Implemented",
|
|
1944
2035
|
502: "Bad Gateway",
|
|
1945
2036
|
503: "Service Unavailable",
|
|
2037
|
+
504: "Gateway Timeout",
|
|
1946
2038
|
505: "HTTP Version Not Supported"
|
|
1947
2039
|
};
|
|
1948
2040
|
var Incoming = class {
|
|
@@ -3388,6 +3480,465 @@ var RemoteTransport = class {
|
|
|
3388
3480
|
}
|
|
3389
3481
|
};
|
|
3390
3482
|
|
|
3483
|
+
// src/cdp/types.ts
|
|
3484
|
+
var CDP_PORT = 9222;
|
|
3485
|
+
var NO_CDP_HOST = "slicc-kernel: no CDP host is attached (createKernel({ cdp }) or client.serveCdp)";
|
|
3486
|
+
|
|
3487
|
+
// src/cdp/websocket.ts
|
|
3488
|
+
var GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
3489
|
+
var MAX_MESSAGE = 256 * 1024 * 1024;
|
|
3490
|
+
var CLOSE = {
|
|
3491
|
+
normal: 1e3,
|
|
3492
|
+
protocol: 1002,
|
|
3493
|
+
unsupported: 1003,
|
|
3494
|
+
invalid: 1007,
|
|
3495
|
+
tooBig: 1009,
|
|
3496
|
+
error: 1011
|
|
3497
|
+
};
|
|
3498
|
+
var OP_TEXT = 1;
|
|
3499
|
+
var OP = { continuation: 0, text: OP_TEXT, binary: 2, close: 8, ping: 9, pong: 10 };
|
|
3500
|
+
var WsError = class extends Error {
|
|
3501
|
+
code;
|
|
3502
|
+
constructor(code, message2) {
|
|
3503
|
+
super(message2);
|
|
3504
|
+
this.code = code;
|
|
3505
|
+
}
|
|
3506
|
+
};
|
|
3507
|
+
function field(headers, name2) {
|
|
3508
|
+
return headers.find(([n]) => n.toLowerCase() === name2)?.[1];
|
|
3509
|
+
}
|
|
3510
|
+
async function acceptKey(headers) {
|
|
3511
|
+
const tokens = (name2) => (field(headers, name2) ?? "").split(",").map((t) => t.trim().toLowerCase());
|
|
3512
|
+
if (!tokens("upgrade").includes("websocket") || !tokens("connection").includes("upgrade")) {
|
|
3513
|
+
throw new HttpError(400, "expected a WebSocket upgrade");
|
|
3514
|
+
}
|
|
3515
|
+
if (field(headers, "sec-websocket-version") !== "13") {
|
|
3516
|
+
throw new HttpError(400, "Sec-WebSocket-Version must be 13");
|
|
3517
|
+
}
|
|
3518
|
+
const key = field(headers, "sec-websocket-key") ?? "";
|
|
3519
|
+
if (!/^[A-Za-z0-9+/]{22}==$/.test(key)) throw new HttpError(400, "bad Sec-WebSocket-Key");
|
|
3520
|
+
const digest = await crypto.subtle.digest("SHA-1", new TextEncoder().encode(key + GUID));
|
|
3521
|
+
return btoa(String.fromCharCode(...new Uint8Array(digest)));
|
|
3522
|
+
}
|
|
3523
|
+
function isUpgrade(headers) {
|
|
3524
|
+
return (field(headers, "upgrade") ?? "").toLowerCase().includes("websocket");
|
|
3525
|
+
}
|
|
3526
|
+
function frame(opcode, payload) {
|
|
3527
|
+
const n = payload.length;
|
|
3528
|
+
const size = n < 126 ? 2 : n < 65536 ? 4 : 10;
|
|
3529
|
+
const out = new Uint8Array(size + n);
|
|
3530
|
+
const view = new DataView(out.buffer);
|
|
3531
|
+
out[0] = 128 | opcode;
|
|
3532
|
+
if (n < 126) out[1] = n;
|
|
3533
|
+
else if (n < 65536) {
|
|
3534
|
+
out[1] = 126;
|
|
3535
|
+
view.setUint16(2, n);
|
|
3536
|
+
} else {
|
|
3537
|
+
out[1] = 127;
|
|
3538
|
+
view.setBigUint64(2, BigInt(n));
|
|
3539
|
+
}
|
|
3540
|
+
out.set(payload, size);
|
|
3541
|
+
return out;
|
|
3542
|
+
}
|
|
3543
|
+
function closeFrame(code, reason = "") {
|
|
3544
|
+
let bytes = new TextEncoder().encode(reason);
|
|
3545
|
+
if (bytes.length > 123) bytes = bytes.subarray(0, 123);
|
|
3546
|
+
const payload = new Uint8Array(2 + bytes.length);
|
|
3547
|
+
new DataView(payload.buffer).setUint16(0, code);
|
|
3548
|
+
payload.set(bytes, 2);
|
|
3549
|
+
return frame(OP.close, payload);
|
|
3550
|
+
}
|
|
3551
|
+
function pongFrame(payload) {
|
|
3552
|
+
return frame(OP.pong, payload);
|
|
3553
|
+
}
|
|
3554
|
+
async function fill(incoming, out, signal) {
|
|
3555
|
+
for (let at = 0; at < out.length; ) {
|
|
3556
|
+
const piece = await incoming.some(out.length - at, signal);
|
|
3557
|
+
if (piece.length === 0) throw new WsError(CLOSE.protocol, "the connection closed mid-frame");
|
|
3558
|
+
out.set(piece, at);
|
|
3559
|
+
at += piece.length;
|
|
3560
|
+
}
|
|
3561
|
+
}
|
|
3562
|
+
var fatal = new TextDecoder("utf-8", { fatal: true });
|
|
3563
|
+
function decode(bytes) {
|
|
3564
|
+
try {
|
|
3565
|
+
return fatal.decode(bytes);
|
|
3566
|
+
} catch {
|
|
3567
|
+
throw new WsError(CLOSE.invalid, "a text message is not UTF-8");
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
var WsReader = class {
|
|
3571
|
+
incoming;
|
|
3572
|
+
max;
|
|
3573
|
+
parts = [];
|
|
3574
|
+
size = 0;
|
|
3575
|
+
continuing = false;
|
|
3576
|
+
constructor(incoming, max = MAX_MESSAGE) {
|
|
3577
|
+
this.incoming = incoming;
|
|
3578
|
+
this.max = max;
|
|
3579
|
+
}
|
|
3580
|
+
async next(signal) {
|
|
3581
|
+
for (; ; ) {
|
|
3582
|
+
const first = await this.incoming.some(1, signal);
|
|
3583
|
+
if (first.length === 0) return null;
|
|
3584
|
+
const head = new Uint8Array(2);
|
|
3585
|
+
head[0] = first[0];
|
|
3586
|
+
await fill(this.incoming, head.subarray(1), signal);
|
|
3587
|
+
const fin = (head[0] & 128) !== 0;
|
|
3588
|
+
const opcode = head[0] & 15;
|
|
3589
|
+
if ((head[0] & 112) !== 0) throw new WsError(CLOSE.protocol, "reserved bits are set");
|
|
3590
|
+
if ((head[1] & 128) === 0) throw new WsError(CLOSE.protocol, "client frames must be masked");
|
|
3591
|
+
const length2 = await this.length(head[1] & 127, signal);
|
|
3592
|
+
const control = opcode >= 8;
|
|
3593
|
+
if (control && (length2 > 125 || !fin)) {
|
|
3594
|
+
throw new WsError(CLOSE.protocol, "a control frame is too long or fragmented");
|
|
3595
|
+
}
|
|
3596
|
+
if (!control && this.size + length2 > this.max) {
|
|
3597
|
+
throw new WsError(CLOSE.tooBig, `a message is over ${this.max} bytes`);
|
|
3598
|
+
}
|
|
3599
|
+
const mask = new Uint8Array(4);
|
|
3600
|
+
await fill(this.incoming, mask, signal);
|
|
3601
|
+
const payload = new Uint8Array(length2);
|
|
3602
|
+
await fill(this.incoming, payload, signal);
|
|
3603
|
+
for (let i = 0; i < length2; i++) payload[i] ^= mask[i & 3];
|
|
3604
|
+
const event = this.take(opcode, fin, payload);
|
|
3605
|
+
if (event) return event;
|
|
3606
|
+
}
|
|
3607
|
+
}
|
|
3608
|
+
async length(short, signal) {
|
|
3609
|
+
if (short < 126) return short;
|
|
3610
|
+
const ext = new Uint8Array(short === 126 ? 2 : 8);
|
|
3611
|
+
await fill(this.incoming, ext, signal);
|
|
3612
|
+
const view = new DataView(ext.buffer);
|
|
3613
|
+
if (short === 126) return view.getUint16(0);
|
|
3614
|
+
const long = view.getBigUint64(0);
|
|
3615
|
+
return long > BigInt(this.max) ? this.max + 1 : Number(long);
|
|
3616
|
+
}
|
|
3617
|
+
take(opcode, fin, payload) {
|
|
3618
|
+
switch (opcode) {
|
|
3619
|
+
case OP.ping:
|
|
3620
|
+
return { kind: "ping", payload };
|
|
3621
|
+
case OP.pong:
|
|
3622
|
+
return void 0;
|
|
3623
|
+
case OP.close: {
|
|
3624
|
+
if (payload.length === 1) throw new WsError(CLOSE.protocol, "a close frame is malformed");
|
|
3625
|
+
const code = payload.length >= 2 ? new DataView(payload.buffer).getUint16(0) : 1005;
|
|
3626
|
+
return { kind: "close", code, reason: decode(payload.subarray(2)) };
|
|
3627
|
+
}
|
|
3628
|
+
case OP.text:
|
|
3629
|
+
case OP.continuation: {
|
|
3630
|
+
if (opcode === OP.continuation !== this.continuing) {
|
|
3631
|
+
throw new WsError(CLOSE.protocol, "unexpected continuation frame");
|
|
3632
|
+
}
|
|
3633
|
+
this.parts.push(payload);
|
|
3634
|
+
this.size += payload.length;
|
|
3635
|
+
this.continuing = !fin;
|
|
3636
|
+
if (!fin) return void 0;
|
|
3637
|
+
const whole = this.parts.length === 1 ? payload : concat(this.parts, this.size);
|
|
3638
|
+
this.parts = [];
|
|
3639
|
+
this.size = 0;
|
|
3640
|
+
return { kind: "text", text: decode(whole) };
|
|
3641
|
+
}
|
|
3642
|
+
case OP.binary:
|
|
3643
|
+
throw new WsError(CLOSE.unsupported, "CDP messages are text");
|
|
3644
|
+
default:
|
|
3645
|
+
throw new WsError(CLOSE.protocol, `unknown opcode ${opcode}`);
|
|
3646
|
+
}
|
|
3647
|
+
}
|
|
3648
|
+
};
|
|
3649
|
+
function switchingHead(accept2) {
|
|
3650
|
+
return latin1Bytes(
|
|
3651
|
+
`HTTP/1.1 101 Switching Protocols\r
|
|
3652
|
+
Upgrade: websocket\r
|
|
3653
|
+
Connection: Upgrade\r
|
|
3654
|
+
Sec-WebSocket-Accept: ${accept2}\r
|
|
3655
|
+
\r
|
|
3656
|
+
`
|
|
3657
|
+
);
|
|
3658
|
+
}
|
|
3659
|
+
|
|
3660
|
+
// src/cdp/facade.ts
|
|
3661
|
+
var MAX_HEAD = 64 * 1024;
|
|
3662
|
+
var LIST_TIMEOUT = 1e4;
|
|
3663
|
+
function respond(status, body, type = "text/plain; charset=utf-8") {
|
|
3664
|
+
const bytes = new TextEncoder().encode(body);
|
|
3665
|
+
const head = latin1Bytes(
|
|
3666
|
+
`HTTP/1.1 ${status} ${REASON[status]}\r
|
|
3667
|
+
Content-Type: ${type}\r
|
|
3668
|
+
Content-Length: ${bytes.length}\r
|
|
3669
|
+
Connection: close\r
|
|
3670
|
+
\r
|
|
3671
|
+
`
|
|
3672
|
+
);
|
|
3673
|
+
const out = new Uint8Array(head.length + bytes.length);
|
|
3674
|
+
out.set(head);
|
|
3675
|
+
out.set(bytes, head.length);
|
|
3676
|
+
return out;
|
|
3677
|
+
}
|
|
3678
|
+
function json(value) {
|
|
3679
|
+
return respond(200, `${JSON.stringify(value, null, 2)}
|
|
3680
|
+
`, "application/json; charset=UTF-8");
|
|
3681
|
+
}
|
|
3682
|
+
var CdpFacade = class {
|
|
3683
|
+
options;
|
|
3684
|
+
listener;
|
|
3685
|
+
stop = new AbortController();
|
|
3686
|
+
connections = /* @__PURE__ */ new Set();
|
|
3687
|
+
closed;
|
|
3688
|
+
constructor(options) {
|
|
3689
|
+
this.options = options;
|
|
3690
|
+
this.listener = options.net.listen({
|
|
3691
|
+
family: "inet",
|
|
3692
|
+
host: "127.0.0.1",
|
|
3693
|
+
port: options.port ?? CDP_PORT
|
|
3694
|
+
});
|
|
3695
|
+
this.closed = this.acceptLoop();
|
|
3696
|
+
}
|
|
3697
|
+
close() {
|
|
3698
|
+
if (this.stop.signal.aborted) return;
|
|
3699
|
+
this.stop.abort();
|
|
3700
|
+
this.listener.close();
|
|
3701
|
+
for (const conn of this.connections) conn.close();
|
|
3702
|
+
}
|
|
3703
|
+
async acceptLoop() {
|
|
3704
|
+
const served = /* @__PURE__ */ new Set();
|
|
3705
|
+
for (; ; ) {
|
|
3706
|
+
let conn;
|
|
3707
|
+
try {
|
|
3708
|
+
conn = await this.listener.accept(this.stop.signal);
|
|
3709
|
+
} catch {
|
|
3710
|
+
break;
|
|
3711
|
+
}
|
|
3712
|
+
const done4 = this.serve(conn).finally(() => served.delete(done4));
|
|
3713
|
+
served.add(done4);
|
|
3714
|
+
}
|
|
3715
|
+
await Promise.allSettled([...served]);
|
|
3716
|
+
}
|
|
3717
|
+
async serve(conn) {
|
|
3718
|
+
this.connections.add(conn);
|
|
3719
|
+
const incoming = new Incoming(conn);
|
|
3720
|
+
try {
|
|
3721
|
+
const head = await incoming.head(MAX_HEAD, this.stop.signal);
|
|
3722
|
+
if (head) await this.route(conn, incoming, parseRequestHead(head));
|
|
3723
|
+
} catch (e) {
|
|
3724
|
+
if (e instanceof HttpError) await this.write(conn, respond(e.status, `${e.message}
|
|
3725
|
+
`));
|
|
3726
|
+
} finally {
|
|
3727
|
+
this.connections.delete(conn);
|
|
3728
|
+
conn.close();
|
|
3729
|
+
}
|
|
3730
|
+
}
|
|
3731
|
+
async write(conn, bytes) {
|
|
3732
|
+
try {
|
|
3733
|
+
await conn.write(bytes, this.stop.signal);
|
|
3734
|
+
} catch {
|
|
3735
|
+
}
|
|
3736
|
+
}
|
|
3737
|
+
async route(conn, incoming, req) {
|
|
3738
|
+
const url = new URL(req.target, "http://127.0.0.1");
|
|
3739
|
+
const runtime = url.searchParams.get("runtime") ?? void 0;
|
|
3740
|
+
const host = req.headers.find(([n]) => n.toLowerCase() === "host")?.[1] ?? "127.0.0.1";
|
|
3741
|
+
if (req.method !== "GET") throw new HttpError(405, `${req.method} is not supported here`);
|
|
3742
|
+
const { hosts } = this.options;
|
|
3743
|
+
if (url.pathname === hosts.path && isUpgrade(req.headers)) {
|
|
3744
|
+
return this.upgrade(conn, incoming, req, runtime);
|
|
3745
|
+
}
|
|
3746
|
+
const path = url.pathname.replace(/\/+$/, "");
|
|
3747
|
+
if (path !== "/json/version" && path !== "/json/list" && path !== "/json") {
|
|
3748
|
+
throw new HttpError(404, "not found: try /json/version");
|
|
3749
|
+
}
|
|
3750
|
+
if (!this.options.hosts.available(runtime)) {
|
|
3751
|
+
throw new HttpError(503, NO_CDP_HOST);
|
|
3752
|
+
}
|
|
3753
|
+
if (path === "/json/version") {
|
|
3754
|
+
await this.write(
|
|
3755
|
+
conn,
|
|
3756
|
+
json({
|
|
3757
|
+
Browser: "slicc-kernel",
|
|
3758
|
+
"Protocol-Version": "1.3",
|
|
3759
|
+
webSocketDebuggerUrl: `ws://${host}${hosts.path}${url.search}`
|
|
3760
|
+
})
|
|
3761
|
+
);
|
|
3762
|
+
return;
|
|
3763
|
+
}
|
|
3764
|
+
await this.write(conn, json(await this.list(runtime)));
|
|
3765
|
+
}
|
|
3766
|
+
async list(runtime) {
|
|
3767
|
+
const conn = await this.host(runtime);
|
|
3768
|
+
try {
|
|
3769
|
+
const reply = await new Promise(
|
|
3770
|
+
(resolve, reject) => {
|
|
3771
|
+
const timer = setTimeout(
|
|
3772
|
+
() => reject(new HttpError(504, "the CDP host did not answer Target.getTargets")),
|
|
3773
|
+
this.options.listTimeout ?? LIST_TIMEOUT
|
|
3774
|
+
);
|
|
3775
|
+
conn.onclose = (reason) => {
|
|
3776
|
+
clearTimeout(timer);
|
|
3777
|
+
reject(new HttpError(502, `the CDP host closed: ${reason ?? "no reason"}`));
|
|
3778
|
+
};
|
|
3779
|
+
conn.onmessage = (message2) => {
|
|
3780
|
+
const parsed = JSON.parse(message2);
|
|
3781
|
+
if (parsed.id !== 1) return;
|
|
3782
|
+
clearTimeout(timer);
|
|
3783
|
+
resolve(parsed);
|
|
3784
|
+
};
|
|
3785
|
+
conn.send(JSON.stringify({ id: 1, method: "Target.getTargets" }));
|
|
3786
|
+
}
|
|
3787
|
+
);
|
|
3788
|
+
if (!reply.result) {
|
|
3789
|
+
throw new HttpError(502, `Target.getTargets failed: ${JSON.stringify(reply.error)}`);
|
|
3790
|
+
}
|
|
3791
|
+
return (reply.result.targetInfos ?? []).map((t) => ({
|
|
3792
|
+
description: "",
|
|
3793
|
+
id: t.targetId,
|
|
3794
|
+
title: t.title,
|
|
3795
|
+
type: t.type,
|
|
3796
|
+
url: t.url
|
|
3797
|
+
}));
|
|
3798
|
+
} finally {
|
|
3799
|
+
conn.close();
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3802
|
+
host(runtime) {
|
|
3803
|
+
return this.options.hosts.connect(runtime ? { runtime } : {});
|
|
3804
|
+
}
|
|
3805
|
+
async upgrade(conn, incoming, req, runtime) {
|
|
3806
|
+
const accept2 = await acceptKey(req.headers);
|
|
3807
|
+
const host = await this.host(runtime);
|
|
3808
|
+
await new Relay(conn, incoming, host, this.options.maxMessage ?? MAX_MESSAGE, this.stop.signal).start(switchingHead(accept2)).finally(() => host.close());
|
|
3809
|
+
}
|
|
3810
|
+
};
|
|
3811
|
+
var Relay = class {
|
|
3812
|
+
writes = Promise.resolve();
|
|
3813
|
+
ended = false;
|
|
3814
|
+
conn;
|
|
3815
|
+
incoming;
|
|
3816
|
+
host;
|
|
3817
|
+
max;
|
|
3818
|
+
stop;
|
|
3819
|
+
constructor(conn, incoming, host, max, stop) {
|
|
3820
|
+
this.conn = conn;
|
|
3821
|
+
this.incoming = incoming;
|
|
3822
|
+
this.host = host;
|
|
3823
|
+
this.max = max;
|
|
3824
|
+
this.stop = stop;
|
|
3825
|
+
}
|
|
3826
|
+
send(bytes) {
|
|
3827
|
+
this.writes = this.writes.then(() => this.conn.write(bytes, this.stop)).catch(() => void 0);
|
|
3828
|
+
return this.writes;
|
|
3829
|
+
}
|
|
3830
|
+
end(code, reason) {
|
|
3831
|
+
if (this.ended) return this.writes;
|
|
3832
|
+
this.ended = true;
|
|
3833
|
+
return this.send(closeFrame(code, reason));
|
|
3834
|
+
}
|
|
3835
|
+
async start(head) {
|
|
3836
|
+
await this.send(head);
|
|
3837
|
+
const hostGone = new Promise((resolve) => {
|
|
3838
|
+
this.host.onclose = (reason) => {
|
|
3839
|
+
void this.end(reason ? CLOSE.error : CLOSE.normal, reason ?? "").then(() => resolve());
|
|
3840
|
+
};
|
|
3841
|
+
});
|
|
3842
|
+
this.host.onmessage = (message2) => {
|
|
3843
|
+
if (this.ended) return;
|
|
3844
|
+
const bytes = new TextEncoder().encode(message2);
|
|
3845
|
+
if (bytes.length > this.max)
|
|
3846
|
+
void this.end(CLOSE.tooBig, `a message is over ${this.max} bytes`);
|
|
3847
|
+
else void this.send(frame(OP_TEXT, bytes));
|
|
3848
|
+
};
|
|
3849
|
+
await Promise.race([this.pump(), hostGone]);
|
|
3850
|
+
await this.writes;
|
|
3851
|
+
}
|
|
3852
|
+
async pump() {
|
|
3853
|
+
const reader = new WsReader(this.incoming, this.max);
|
|
3854
|
+
try {
|
|
3855
|
+
for (; ; ) {
|
|
3856
|
+
const event = await reader.next(this.stop);
|
|
3857
|
+
if (!event) return;
|
|
3858
|
+
if (event.kind === "text") this.host.send(event.text);
|
|
3859
|
+
else if (event.kind === "ping") void this.send(pongFrame(event.payload));
|
|
3860
|
+
else {
|
|
3861
|
+
await this.end(event.code === 1005 ? CLOSE.normal : event.code, "");
|
|
3862
|
+
return;
|
|
3863
|
+
}
|
|
3864
|
+
}
|
|
3865
|
+
} catch (e) {
|
|
3866
|
+
if (e instanceof WsError) await this.end(e.code, e.message);
|
|
3867
|
+
}
|
|
3868
|
+
}
|
|
3869
|
+
};
|
|
3870
|
+
function enableCdp(net, hosts) {
|
|
3871
|
+
let facade;
|
|
3872
|
+
net.kernelOnly(CDP_PORT, true);
|
|
3873
|
+
net.activate({ family: "inet", host: "127.0.0.1", port: CDP_PORT }, () => {
|
|
3874
|
+
const started = new CdpFacade({ net, hosts });
|
|
3875
|
+
facade = started;
|
|
3876
|
+
void started.closed.then(() => {
|
|
3877
|
+
if (facade === started) facade = void 0;
|
|
3878
|
+
});
|
|
3879
|
+
});
|
|
3880
|
+
return () => facade;
|
|
3881
|
+
}
|
|
3882
|
+
|
|
3883
|
+
// src/cdp/hosts.ts
|
|
3884
|
+
var CdpHosts = class {
|
|
3885
|
+
embedder;
|
|
3886
|
+
clients = [];
|
|
3887
|
+
closed = false;
|
|
3888
|
+
path = `/devtools/browser/${crypto.randomUUID()}`;
|
|
3889
|
+
url = `ws://127.0.0.1:${CDP_PORT}${this.path}`;
|
|
3890
|
+
constructor(hook) {
|
|
3891
|
+
this.embedder = hook ? { hook, runtime: void 0, open: /* @__PURE__ */ new Set() } : void 0;
|
|
3892
|
+
}
|
|
3893
|
+
register(hook, runtime) {
|
|
3894
|
+
const entry = { hook, runtime, open: /* @__PURE__ */ new Set() };
|
|
3895
|
+
this.clients.push(entry);
|
|
3896
|
+
return () => {
|
|
3897
|
+
const at = this.clients.indexOf(entry);
|
|
3898
|
+
if (at >= 0) this.clients.splice(at, 1);
|
|
3899
|
+
closeAll(entry);
|
|
3900
|
+
};
|
|
3901
|
+
}
|
|
3902
|
+
pick(runtime) {
|
|
3903
|
+
if (this.closed) return void 0;
|
|
3904
|
+
const named = runtime ? this.clients.findLast((c) => c.runtime === runtime) : void 0;
|
|
3905
|
+
return named ?? this.embedder ?? this.clients.at(-1);
|
|
3906
|
+
}
|
|
3907
|
+
available(runtime) {
|
|
3908
|
+
return this.pick(runtime) !== void 0;
|
|
3909
|
+
}
|
|
3910
|
+
async connect(request2) {
|
|
3911
|
+
const entry = this.pick(request2.runtime);
|
|
3912
|
+
if (!entry) throw new HttpError(503, NO_CDP_HOST);
|
|
3913
|
+
let conn;
|
|
3914
|
+
try {
|
|
3915
|
+
conn = await entry.hook(request2.runtime ? { runtime: request2.runtime } : {});
|
|
3916
|
+
} catch (e) {
|
|
3917
|
+
throw new HttpError(502, `CDP host: ${e instanceof Error ? e.message : String(e)}`);
|
|
3918
|
+
}
|
|
3919
|
+
if (this.closed || !(entry === this.embedder || this.clients.includes(entry))) {
|
|
3920
|
+
conn.close();
|
|
3921
|
+
throw new HttpError(503, NO_CDP_HOST);
|
|
3922
|
+
}
|
|
3923
|
+
entry.open.add(conn);
|
|
3924
|
+
const close = conn.close.bind(conn);
|
|
3925
|
+
conn.close = () => {
|
|
3926
|
+
if (entry.open.delete(conn)) close();
|
|
3927
|
+
};
|
|
3928
|
+
return conn;
|
|
3929
|
+
}
|
|
3930
|
+
close() {
|
|
3931
|
+
this.closed = true;
|
|
3932
|
+
for (const entry of [this.embedder, ...this.clients]) if (entry) closeAll(entry);
|
|
3933
|
+
}
|
|
3934
|
+
};
|
|
3935
|
+
function closeAll(entry) {
|
|
3936
|
+
for (const conn of [...entry.open]) {
|
|
3937
|
+
conn.close();
|
|
3938
|
+
conn.onclose?.("the CDP host is gone");
|
|
3939
|
+
}
|
|
3940
|
+
}
|
|
3941
|
+
|
|
3391
3942
|
// src/commands.ts
|
|
3392
3943
|
var NAME = /^[A-Za-z0-9._+-]+$/;
|
|
3393
3944
|
var ENV_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
@@ -4128,6 +4679,7 @@ var MountTable = class {
|
|
|
4128
4679
|
mounts = /* @__PURE__ */ new Map();
|
|
4129
4680
|
devices = 0;
|
|
4130
4681
|
pending = /* @__PURE__ */ new Set();
|
|
4682
|
+
notes = /* @__PURE__ */ new Map();
|
|
4131
4683
|
deps;
|
|
4132
4684
|
constructor(deps) {
|
|
4133
4685
|
this.deps = deps;
|
|
@@ -4136,7 +4688,7 @@ var MountTable = class {
|
|
|
4136
4688
|
return this.mounts.get(target);
|
|
4137
4689
|
}
|
|
4138
4690
|
list() {
|
|
4139
|
-
|
|
4691
|
+
const mounted = [...this.mounts.values()].map(
|
|
4140
4692
|
({ type, source, target, options, state, error, present }) => ({
|
|
4141
4693
|
type,
|
|
4142
4694
|
source,
|
|
@@ -4146,6 +4698,19 @@ var MountTable = class {
|
|
|
4146
4698
|
...error ? { error } : {}
|
|
4147
4699
|
})
|
|
4148
4700
|
);
|
|
4701
|
+
const noted = [...this.notes.values()].filter((note) => !this.mounts.has(note.target));
|
|
4702
|
+
return [...mounted, ...noted.map((note) => ({ ...note, options: { ...note.options } }))];
|
|
4703
|
+
}
|
|
4704
|
+
note(spec, state, error) {
|
|
4705
|
+
const target = normalizePath(spec.target);
|
|
4706
|
+
this.notes.set(target, {
|
|
4707
|
+
type: spec.type,
|
|
4708
|
+
source: spec.source,
|
|
4709
|
+
target,
|
|
4710
|
+
options: { ...spec.options },
|
|
4711
|
+
state,
|
|
4712
|
+
...error ? { error } : {}
|
|
4713
|
+
});
|
|
4149
4714
|
}
|
|
4150
4715
|
locate(path) {
|
|
4151
4716
|
let best;
|
|
@@ -4222,13 +4787,19 @@ var MountTable = class {
|
|
|
4222
4787
|
};
|
|
4223
4788
|
mounted = mount;
|
|
4224
4789
|
this.mounts.set(target, mount);
|
|
4790
|
+
this.notes.delete(target);
|
|
4225
4791
|
return this.list().find((m) => m.target === target);
|
|
4226
4792
|
}
|
|
4793
|
+
unnote(target) {
|
|
4794
|
+
this.notes.delete(normalizePath(target));
|
|
4795
|
+
}
|
|
4227
4796
|
umount(target, detach = false) {
|
|
4228
4797
|
const at = normalizePath(target);
|
|
4229
4798
|
const mount = this.mounts.get(at);
|
|
4799
|
+
if (!mount && this.notes.delete(at)) return;
|
|
4230
4800
|
if (!mount) throw errnoError("EINVAL", `${at} is not mounted`);
|
|
4231
4801
|
if (!detach && this.deps.busy(at)) throw errnoError("EBUSY", `${at} has open files`);
|
|
4802
|
+
this.notes.delete(at);
|
|
4232
4803
|
this.mounts.delete(at);
|
|
4233
4804
|
mount.conn.close();
|
|
4234
4805
|
mount.dispose();
|
|
@@ -4691,7 +5262,7 @@ async function storageUsage() {
|
|
|
4691
5262
|
return { quota: estimate.quota, usage: estimate.usage ?? 0 };
|
|
4692
5263
|
}
|
|
4693
5264
|
var done = { ok: true, kind: "void" };
|
|
4694
|
-
function
|
|
5265
|
+
function json2(value) {
|
|
4695
5266
|
return { ok: true, kind: "json", json: value };
|
|
4696
5267
|
}
|
|
4697
5268
|
async function run(entry, path, req) {
|
|
@@ -4703,15 +5274,15 @@ async function run(entry, path, req) {
|
|
|
4703
5274
|
await fs.writeFile(path, req.body ?? new Uint8Array(0));
|
|
4704
5275
|
return done;
|
|
4705
5276
|
case "exists":
|
|
4706
|
-
return
|
|
5277
|
+
return json2(await fs.exists(path));
|
|
4707
5278
|
case "stat":
|
|
4708
|
-
return
|
|
5279
|
+
return json2(statJson(await fs.stat(path)));
|
|
4709
5280
|
case "lstat":
|
|
4710
|
-
return
|
|
5281
|
+
return json2(statJson(await fs.lstat(path)));
|
|
4711
5282
|
case "readdir":
|
|
4712
|
-
return
|
|
5283
|
+
return json2(await fs.readdir(path));
|
|
4713
5284
|
case "readdir-stat":
|
|
4714
|
-
return
|
|
5285
|
+
return json2(await readdirStat(fs, path));
|
|
4715
5286
|
case "mkdir":
|
|
4716
5287
|
await fs.mkdir(path, { recursive: true });
|
|
4717
5288
|
return done;
|
|
@@ -4737,7 +5308,7 @@ async function run(entry, path, req) {
|
|
|
4737
5308
|
await fs.symlink(req.arg2 ?? "", path);
|
|
4738
5309
|
return done;
|
|
4739
5310
|
case "readlink":
|
|
4740
|
-
return
|
|
5311
|
+
return json2(await fs.readlink(path));
|
|
4741
5312
|
case "chmod":
|
|
4742
5313
|
await fs.chmod(path, req.mode ?? 0);
|
|
4743
5314
|
return done;
|
|
@@ -4768,7 +5339,7 @@ async function dispatchSyncFs(req) {
|
|
|
4768
5339
|
const { fs, cwd } = entry;
|
|
4769
5340
|
try {
|
|
4770
5341
|
const path = fs.resolvePath(cwd, req.path);
|
|
4771
|
-
if (req.op === "statfs") return
|
|
5342
|
+
if (req.op === "statfs") return json2(await entry.statfs?.(path) ?? await storageUsage());
|
|
4772
5343
|
if (req.op === "hold") {
|
|
4773
5344
|
entry.hold?.(path, req.mode === 1);
|
|
4774
5345
|
return done;
|
|
@@ -4941,7 +5512,7 @@ var HOST_OPS = [
|
|
|
4941
5512
|
"async-cancel",
|
|
4942
5513
|
"async-close"
|
|
4943
5514
|
];
|
|
4944
|
-
var
|
|
5515
|
+
var json3 = (value) => ({ ok: true, kind: "json", json: value });
|
|
4945
5516
|
var done2 = { ok: true, kind: "void" };
|
|
4946
5517
|
var LockTable = class {
|
|
4947
5518
|
held = /* @__PURE__ */ new Map();
|
|
@@ -4990,7 +5561,7 @@ var AsyncOps = class {
|
|
|
4990
5561
|
}
|
|
4991
5562
|
resolve(value) {
|
|
4992
5563
|
const id = this.hold();
|
|
4993
|
-
this.complete(id,
|
|
5564
|
+
this.complete(id, json3(value));
|
|
4994
5565
|
return id;
|
|
4995
5566
|
}
|
|
4996
5567
|
hold() {
|
|
@@ -5072,15 +5643,15 @@ async function hostSyscall(req, ctx) {
|
|
|
5072
5643
|
return done2;
|
|
5073
5644
|
case "async-submit":
|
|
5074
5645
|
if (!ctx.valid(req.req)) throw new KernelError("EINVAL");
|
|
5075
|
-
return
|
|
5646
|
+
return json3(ctx.ops.submit(req.req));
|
|
5076
5647
|
case "async-wait":
|
|
5077
|
-
return
|
|
5648
|
+
return json3(await ctx.ops.wait(req.timeoutMs, ctx.blocking()));
|
|
5078
5649
|
case "async-take":
|
|
5079
5650
|
return ctx.ops.take(req.id);
|
|
5080
5651
|
case "async-resolve":
|
|
5081
|
-
return
|
|
5652
|
+
return json3(ctx.ops.resolve(req.value));
|
|
5082
5653
|
case "async-hold":
|
|
5083
|
-
return
|
|
5654
|
+
return json3(ctx.ops.hold());
|
|
5084
5655
|
case "async-cancel":
|
|
5085
5656
|
ctx.ops.cancel(req.id);
|
|
5086
5657
|
return done2;
|
|
@@ -5607,23 +6178,23 @@ function terminalOf(ctx, fd) {
|
|
|
5607
6178
|
if (!tty) throw new KernelError("ENOTTY");
|
|
5608
6179
|
return tty;
|
|
5609
6180
|
}
|
|
5610
|
-
var
|
|
6181
|
+
var json4 = (value) => ({ ok: true, kind: "json", json: value });
|
|
5611
6182
|
var done3 = { ok: true, kind: "void" };
|
|
5612
6183
|
function ptySyscall(req, ctx) {
|
|
5613
6184
|
switch (req.op) {
|
|
5614
6185
|
case "pty-open": {
|
|
5615
6186
|
if (!ctx.ptys) throw new KernelError("ENOENT");
|
|
5616
|
-
return
|
|
6187
|
+
return json4(ctx.fds.install(ctx.ptys.open().master, 3));
|
|
5617
6188
|
}
|
|
5618
6189
|
case "pty-slave-open": {
|
|
5619
6190
|
if (!ctx.ptys) throw new KernelError("ENOENT");
|
|
5620
6191
|
const pair = ctx.ptys.get(req.n);
|
|
5621
6192
|
const fd = ctx.fds.install(pair.openSlave(), 3);
|
|
5622
6193
|
if (!req.noctty) ctx.jobs?.acquireTerminal(ctx.pid, pair.slave);
|
|
5623
|
-
return
|
|
6194
|
+
return json4(fd);
|
|
5624
6195
|
}
|
|
5625
6196
|
case "pty-number":
|
|
5626
|
-
return
|
|
6197
|
+
return json4(masterOf(ctx, req.fd).index);
|
|
5627
6198
|
case "pty-lock":
|
|
5628
6199
|
masterOf(ctx, req.fd).locked = req.lock;
|
|
5629
6200
|
return done3;
|
|
@@ -5635,7 +6206,7 @@ function ptySyscall(req, ctx) {
|
|
|
5635
6206
|
return done3;
|
|
5636
6207
|
}
|
|
5637
6208
|
case "pty-list":
|
|
5638
|
-
return
|
|
6209
|
+
return json4(ctx.ptys?.numbers() ?? []);
|
|
5639
6210
|
case "pty-packet":
|
|
5640
6211
|
masterOf(ctx, req.fd).packet = req.on;
|
|
5641
6212
|
return done3;
|
|
@@ -5713,7 +6284,7 @@ function socketAt(fds, fd) {
|
|
|
5713
6284
|
if (!(file instanceof KernelSocket)) throw new KernelError("ENOTSOCK");
|
|
5714
6285
|
return file;
|
|
5715
6286
|
}
|
|
5716
|
-
var ok = (
|
|
6287
|
+
var ok = (json5) => json5 === void 0 ? { ok: true, kind: "void" } : { ok: true, kind: "json", json: json5 };
|
|
5717
6288
|
async function socketSyscall(req, proc) {
|
|
5718
6289
|
const { fds, net } = proc;
|
|
5719
6290
|
switch (req.op) {
|
|
@@ -7677,8 +8248,8 @@ function umountCall(target, flags) {
|
|
|
7677
8248
|
var FSTAB_PATH = "/etc/fstab";
|
|
7678
8249
|
var FSTAB_RETRIES = [1e3, 4e3, 16e3];
|
|
7679
8250
|
var FINAL = /* @__PURE__ */ new Set(["EINVAL", "EBUSY", "EPERM", "EACCES"]);
|
|
7680
|
-
function unescape(
|
|
7681
|
-
return
|
|
8251
|
+
function unescape(field2) {
|
|
8252
|
+
return field2.replace(
|
|
7682
8253
|
/\\([0-7]{3})/g,
|
|
7683
8254
|
(_, octal) => String.fromCharCode(Number.parseInt(octal, 8))
|
|
7684
8255
|
);
|
|
@@ -7717,28 +8288,34 @@ function wait(ms, signal) {
|
|
|
7717
8288
|
});
|
|
7718
8289
|
}
|
|
7719
8290
|
async function mountFstab(lines, mount, signal, retries = FSTAB_RETRIES, discard = () => {
|
|
8291
|
+
}, settled = () => {
|
|
7720
8292
|
}) {
|
|
7721
8293
|
return Promise.all(
|
|
7722
|
-
lines.map(async (
|
|
7723
|
-
|
|
7724
|
-
|
|
7725
|
-
|
|
7726
|
-
const entry = await mount(spec);
|
|
7727
|
-
if (!signal.aborted) return { line, target: spec.target, entry };
|
|
7728
|
-
discard(entry);
|
|
7729
|
-
failed = Object.assign(new Error("the kernel stopped"), { code: "ECANCELED" });
|
|
7730
|
-
break;
|
|
7731
|
-
} catch (err) {
|
|
7732
|
-
failed = err;
|
|
7733
|
-
if (FINAL.has(codeOf3(err)) || attempt === retries.length) break;
|
|
7734
|
-
await wait(retries[attempt], signal);
|
|
7735
|
-
}
|
|
7736
|
-
}
|
|
7737
|
-
const code = codeOf3(failed);
|
|
7738
|
-
return { line, target: spec.target, code, error: String(failed?.message ?? code) };
|
|
8294
|
+
lines.map(async (each) => {
|
|
8295
|
+
const result = await mountLine(each, mount, signal, retries, discard);
|
|
8296
|
+
settled(each, result);
|
|
8297
|
+
return result;
|
|
7739
8298
|
})
|
|
7740
8299
|
);
|
|
7741
8300
|
}
|
|
8301
|
+
async function mountLine({ line, spec, invalid }, mount, signal, retries, discard) {
|
|
8302
|
+
let failed = invalid;
|
|
8303
|
+
for (let attempt = 0; !invalid && attempt <= retries.length && !signal.aborted; attempt++) {
|
|
8304
|
+
try {
|
|
8305
|
+
const entry = await mount(spec);
|
|
8306
|
+
if (!signal.aborted) return { line, target: spec.target, entry };
|
|
8307
|
+
discard(entry);
|
|
8308
|
+
failed = Object.assign(new Error("the kernel stopped"), { code: "ECANCELED" });
|
|
8309
|
+
break;
|
|
8310
|
+
} catch (err) {
|
|
8311
|
+
failed = err;
|
|
8312
|
+
if (FINAL.has(codeOf3(err)) || attempt === retries.length) break;
|
|
8313
|
+
await wait(retries[attempt], signal);
|
|
8314
|
+
}
|
|
8315
|
+
}
|
|
8316
|
+
const code = codeOf3(failed);
|
|
8317
|
+
return { line, target: spec.target, code, error: String(failed?.message ?? code) };
|
|
8318
|
+
}
|
|
7742
8319
|
|
|
7743
8320
|
// src/mount/hostfs.ts
|
|
7744
8321
|
var HOSTFS_IO = 16 * 1024 * 1024;
|
|
@@ -8320,7 +8897,7 @@ function importedMemory(bytes) {
|
|
|
8320
8897
|
const count = u32();
|
|
8321
8898
|
for (let i = 0; i < count; i++) {
|
|
8322
8899
|
const module = name2();
|
|
8323
|
-
const
|
|
8900
|
+
const field2 = name2();
|
|
8324
8901
|
const kind = bytes[at++];
|
|
8325
8902
|
if (kind === 0) u32();
|
|
8326
8903
|
else if (kind === 1) {
|
|
@@ -8328,7 +8905,7 @@ function importedMemory(bytes) {
|
|
|
8328
8905
|
limits();
|
|
8329
8906
|
} else if (kind === 2) {
|
|
8330
8907
|
const l = limits();
|
|
8331
|
-
return { module, name:
|
|
8908
|
+
return { module, name: field2, ...l };
|
|
8332
8909
|
} else if (kind === 3) at += 2;
|
|
8333
8910
|
else if (kind === 4) {
|
|
8334
8911
|
at++;
|
|
@@ -8419,12 +8996,12 @@ function readImports(r, types) {
|
|
|
8419
8996
|
const foreign = {};
|
|
8420
8997
|
for (let i = r.u32(); i > 0; i--) {
|
|
8421
8998
|
const module = r.name();
|
|
8422
|
-
const
|
|
8999
|
+
const field2 = r.name();
|
|
8423
9000
|
const kind = r.byte();
|
|
8424
9001
|
if (kind !== 0) skipImport(r, kind);
|
|
8425
9002
|
else {
|
|
8426
9003
|
const type = types[r.u32()] ?? "other";
|
|
8427
|
-
if (!RESERVED_NAMESPACES.has(module)) (foreign[module] ??= {})[
|
|
9004
|
+
if (!RESERVED_NAMESPACES.has(module)) (foreign[module] ??= {})[field2] = type;
|
|
8428
9005
|
}
|
|
8429
9006
|
}
|
|
8430
9007
|
return foreign;
|
|
@@ -8531,6 +9108,7 @@ var Launcher = class {
|
|
|
8531
9108
|
locks = new LockTable();
|
|
8532
9109
|
ptys = new PtyTable((tty, sig) => this.jobs.signalOwnedForeground(tty, sig));
|
|
8533
9110
|
net = new LoopbackNet();
|
|
9111
|
+
cdp;
|
|
8534
9112
|
ca;
|
|
8535
9113
|
transport;
|
|
8536
9114
|
watchers = new FsWatchers();
|
|
@@ -8541,6 +9119,7 @@ var Launcher = class {
|
|
|
8541
9119
|
onMountPending;
|
|
8542
9120
|
removable = /* @__PURE__ */ new Map();
|
|
8543
9121
|
hostfs;
|
|
9122
|
+
fstabRetries;
|
|
8544
9123
|
inserted = /* @__PURE__ */ new Map();
|
|
8545
9124
|
openFiles = /* @__PURE__ */ new Set();
|
|
8546
9125
|
held = /* @__PURE__ */ new Set();
|
|
@@ -8562,10 +9141,12 @@ var Launcher = class {
|
|
|
8562
9141
|
this.media = options.media ?? memoryMedia();
|
|
8563
9142
|
this.onMountPending = options.onMountPending;
|
|
8564
9143
|
this.hostfs = options;
|
|
9144
|
+
this.fstabRetries = options.fstabRetries ?? FSTAB_RETRIES;
|
|
8565
9145
|
this.processMounts = options.processMounts ?? true;
|
|
8566
9146
|
this.pnpmHome = options.env?.PNPM_HOME ?? PNPM_HOME;
|
|
8567
9147
|
this.modulesDir = options.modules ?? "/node_modules";
|
|
8568
|
-
this.
|
|
9148
|
+
this.cdp = new CdpHosts(options.cdp);
|
|
9149
|
+
this.env = { ...networkEnv(), SLICC_CDP_URL: this.cdp.url, ...options.env };
|
|
8569
9150
|
this.ca = kernelCa(options.caStore ?? memoryCaStore());
|
|
8570
9151
|
this.transport = options.transport ?? missingTransport();
|
|
8571
9152
|
enableNetwork(this.net, {
|
|
@@ -8573,6 +9154,7 @@ var Launcher = class {
|
|
|
8573
9154
|
engine: kernelTlsEngine(packageTlsEngine(options.fs, this.modulesDir)),
|
|
8574
9155
|
ca: this.ca
|
|
8575
9156
|
});
|
|
9157
|
+
enableCdp(this.net, this.cdp);
|
|
8576
9158
|
this.fs = withCommandDirs(this.base, async () => new Set((await this.commands()).keys()));
|
|
8577
9159
|
this.watchers.watch([this.modulesDir, this.pnpmHome], { recursive: true }, () => {
|
|
8578
9160
|
this.catalog = void 0;
|
|
@@ -8995,12 +9577,22 @@ var Launcher = class {
|
|
|
8995
9577
|
}
|
|
8996
9578
|
async mountFstab() {
|
|
8997
9579
|
const text = await this.base.readFile(FSTAB_PATH).catch(() => "");
|
|
9580
|
+
const lines = parseFstab(text);
|
|
9581
|
+
for (const { spec } of lines) this.mounts.note(spec, "pending");
|
|
8998
9582
|
return mountFstab(
|
|
8999
|
-
|
|
9000
|
-
(spec) => this.mount(spec)
|
|
9583
|
+
lines,
|
|
9584
|
+
(spec) => this.mount(spec).catch((err) => {
|
|
9585
|
+
this.mounts.note(spec, "pending", err?.message ?? String(err));
|
|
9586
|
+
throw err;
|
|
9587
|
+
}),
|
|
9001
9588
|
this.booting.signal,
|
|
9002
|
-
|
|
9003
|
-
(entry) => this.umount(entry.target, true)
|
|
9589
|
+
this.fstabRetries,
|
|
9590
|
+
(entry) => this.umount(entry.target, true),
|
|
9591
|
+
({ spec }, result) => {
|
|
9592
|
+
if (!result.code || result.code === "ECANCELED" || this.booting.signal.aborted) {
|
|
9593
|
+
this.mounts.unnote(spec.target);
|
|
9594
|
+
} else this.mounts.note(spec, "failed", result.error);
|
|
9595
|
+
}
|
|
9004
9596
|
);
|
|
9005
9597
|
}
|
|
9006
9598
|
environment(cwd, extra) {
|
|
@@ -9121,6 +9713,14 @@ async function stores(deps, name2, media) {
|
|
|
9121
9713
|
...handles === false ? {} : { media: await deps.media?.(handles) }
|
|
9122
9714
|
};
|
|
9123
9715
|
}
|
|
9716
|
+
function cdpOption(req, port) {
|
|
9717
|
+
if (!req.cdp) return {};
|
|
9718
|
+
return {
|
|
9719
|
+
cdp: portCdpHook(
|
|
9720
|
+
(request2, cdp) => port.postMessage({ cdpOpen: request2, port: cdp }, [cdp])
|
|
9721
|
+
)
|
|
9722
|
+
};
|
|
9723
|
+
}
|
|
9124
9724
|
function serveKernel(port, deps) {
|
|
9125
9725
|
let launcher;
|
|
9126
9726
|
let remote;
|
|
@@ -9192,6 +9792,7 @@ function serveKernel(port, deps) {
|
|
|
9192
9792
|
...media ? { media } : {},
|
|
9193
9793
|
onMountPending: (medium) => port.postMessage({ medium }),
|
|
9194
9794
|
...req.hostfs ? { hostfs: askGrant } : {},
|
|
9795
|
+
...cdpOption(req, port),
|
|
9195
9796
|
...mountPolicy(req.processMounts),
|
|
9196
9797
|
caStore: caStore(deps.metadata ? caDb(req) : false)
|
|
9197
9798
|
});
|