@ai-ecoverse/slicc-kernel 1.81.0 → 1.83.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 +23 -1
- package/dist/index.js +44 -3
- package/dist/index.js.map +3 -3
- package/dist/kernel-worker.js +1203 -221
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +1174 -225
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js.map +2 -2
- package/package.json +3 -2
package/dist/node.js
CHANGED
|
@@ -28,7 +28,7 @@ function serveCdpPort(port, hook, request, onDone) {
|
|
|
28
28
|
}
|
|
29
29
|
});
|
|
30
30
|
port.start?.();
|
|
31
|
-
new Promise((
|
|
31
|
+
new Promise((resolve2) => resolve2(hook(request))).then(
|
|
32
32
|
(opened2) => {
|
|
33
33
|
conn = opened2;
|
|
34
34
|
if (done4) return opened2.close();
|
|
@@ -51,7 +51,7 @@ function serveCdpPort(port, hook, request, onDone) {
|
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
53
|
function portCdpHook(ask, gone) {
|
|
54
|
-
return (request) => new Promise((
|
|
54
|
+
return (request) => new Promise((resolve2, reject) => {
|
|
55
55
|
const { port1, port2 } = new MessageChannel();
|
|
56
56
|
let closed = false;
|
|
57
57
|
const conn = {
|
|
@@ -69,7 +69,7 @@ function portCdpHook(ask, gone) {
|
|
|
69
69
|
};
|
|
70
70
|
port1.addEventListener("message", ({ data }) => {
|
|
71
71
|
if ("m" in data) conn.onmessage?.(data.m);
|
|
72
|
-
else if ("open" in data)
|
|
72
|
+
else if ("open" in data) resolve2(conn);
|
|
73
73
|
else if ("error" in data) {
|
|
74
74
|
closed = true;
|
|
75
75
|
port1.close();
|
|
@@ -671,9 +671,9 @@ var KernelPipe = class {
|
|
|
671
671
|
let filled = 0;
|
|
672
672
|
for (const chunk2 of this.chunks) {
|
|
673
673
|
if (filled === out.length) break;
|
|
674
|
-
const
|
|
675
|
-
out.set(chunk2.subarray(0,
|
|
676
|
-
filled +=
|
|
674
|
+
const count2 = Math.min(chunk2.length, out.length - filled);
|
|
675
|
+
out.set(chunk2.subarray(0, count2), filled);
|
|
676
|
+
filled += count2;
|
|
677
677
|
}
|
|
678
678
|
return out;
|
|
679
679
|
}
|
|
@@ -704,25 +704,25 @@ var KernelPipe = class {
|
|
|
704
704
|
let filled = 0;
|
|
705
705
|
while (filled < n) {
|
|
706
706
|
const head = this.chunks[0];
|
|
707
|
-
const
|
|
708
|
-
out.set(head.subarray(0,
|
|
709
|
-
filled +=
|
|
710
|
-
if (
|
|
711
|
-
else this.chunks[0] = head.subarray(
|
|
707
|
+
const count2 = Math.min(head.length, n - filled);
|
|
708
|
+
out.set(head.subarray(0, count2), filled);
|
|
709
|
+
filled += count2;
|
|
710
|
+
if (count2 === head.length) this.chunks.shift();
|
|
711
|
+
else this.chunks[0] = head.subarray(count2);
|
|
712
712
|
}
|
|
713
713
|
this.size -= n;
|
|
714
714
|
return out;
|
|
715
715
|
}
|
|
716
716
|
changed(signal) {
|
|
717
717
|
if (signal?.aborted) return Promise.reject(new PipeError("EINTR"));
|
|
718
|
-
return new Promise((
|
|
718
|
+
return new Promise((resolve2, reject) => {
|
|
719
719
|
const onAbort = () => {
|
|
720
720
|
this.waiters = this.waiters.filter((w) => w !== waiter);
|
|
721
721
|
reject(new PipeError("EINTR"));
|
|
722
722
|
};
|
|
723
723
|
const waiter = () => {
|
|
724
724
|
signal?.removeEventListener("abort", onAbort);
|
|
725
|
-
|
|
725
|
+
resolve2();
|
|
726
726
|
};
|
|
727
727
|
this.waiters.push(waiter);
|
|
728
728
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
@@ -731,7 +731,7 @@ var KernelPipe = class {
|
|
|
731
731
|
wake() {
|
|
732
732
|
const waiters = this.waiters;
|
|
733
733
|
this.waiters = [];
|
|
734
|
-
for (const
|
|
734
|
+
for (const resolve2 of waiters) resolve2();
|
|
735
735
|
}
|
|
736
736
|
};
|
|
737
737
|
|
|
@@ -1192,14 +1192,14 @@ var Changes = class {
|
|
|
1192
1192
|
waiters = [];
|
|
1193
1193
|
wait(signal) {
|
|
1194
1194
|
if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
|
|
1195
|
-
return new Promise((
|
|
1195
|
+
return new Promise((resolve2, reject) => {
|
|
1196
1196
|
const onAbort = () => {
|
|
1197
1197
|
this.waiters = this.waiters.filter((w) => w !== waiter);
|
|
1198
1198
|
reject(new KernelError("EINTR"));
|
|
1199
1199
|
};
|
|
1200
1200
|
const waiter = () => {
|
|
1201
1201
|
signal?.removeEventListener("abort", onAbort);
|
|
1202
|
-
|
|
1202
|
+
resolve2();
|
|
1203
1203
|
};
|
|
1204
1204
|
this.waiters.push(waiter);
|
|
1205
1205
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
@@ -1208,7 +1208,7 @@ var Changes = class {
|
|
|
1208
1208
|
wake() {
|
|
1209
1209
|
const waiters = this.waiters;
|
|
1210
1210
|
this.waiters = [];
|
|
1211
|
-
for (const
|
|
1211
|
+
for (const resolve2 of waiters) resolve2();
|
|
1212
1212
|
}
|
|
1213
1213
|
};
|
|
1214
1214
|
function link(a, b) {
|
|
@@ -1376,7 +1376,7 @@ var KernelSocket = class _KernelSocket {
|
|
|
1376
1376
|
});
|
|
1377
1377
|
const { host, port } = addr;
|
|
1378
1378
|
const dialled = new Promise(
|
|
1379
|
-
(
|
|
1379
|
+
(resolve2) => resolve2(route.uplink.dial({ network: "tcp", host, port, signal: abort.signal }))
|
|
1380
1380
|
);
|
|
1381
1381
|
dialled.then(
|
|
1382
1382
|
(conn) => abort.signal.aborted && conn.close(),
|
|
@@ -1747,8 +1747,8 @@ var MessageQueue = class {
|
|
|
1747
1747
|
this.overflow = overflow;
|
|
1748
1748
|
this.limit = limit;
|
|
1749
1749
|
this.taken = taken;
|
|
1750
|
-
this.closed = new Promise((
|
|
1751
|
-
this.settle =
|
|
1750
|
+
this.closed = new Promise((resolve2) => {
|
|
1751
|
+
this.settle = resolve2;
|
|
1752
1752
|
});
|
|
1753
1753
|
}
|
|
1754
1754
|
push(message3) {
|
|
@@ -1787,8 +1787,8 @@ var MessageQueue = class {
|
|
|
1787
1787
|
continue;
|
|
1788
1788
|
}
|
|
1789
1789
|
if (this.ended) return;
|
|
1790
|
-
await new Promise((
|
|
1791
|
-
this.waiter =
|
|
1790
|
+
await new Promise((resolve2) => {
|
|
1791
|
+
this.waiter = resolve2;
|
|
1792
1792
|
});
|
|
1793
1793
|
}
|
|
1794
1794
|
}
|
|
@@ -1837,7 +1837,7 @@ function openWebSocket(Socket, request, withHeaders) {
|
|
|
1837
1837
|
}
|
|
1838
1838
|
socket.binaryType = "arraybuffer";
|
|
1839
1839
|
const queue = new MessageQueue(() => socket.close(1e3, "too many waiting messages"));
|
|
1840
|
-
return new Promise((
|
|
1840
|
+
return new Promise((resolve2, reject) => {
|
|
1841
1841
|
let open = false;
|
|
1842
1842
|
let settled = false;
|
|
1843
1843
|
const fail3 = (error) => {
|
|
@@ -1856,7 +1856,7 @@ function openWebSocket(Socket, request, withHeaders) {
|
|
|
1856
1856
|
open = true;
|
|
1857
1857
|
settled = true;
|
|
1858
1858
|
signal.removeEventListener("abort", abort);
|
|
1859
|
-
|
|
1859
|
+
resolve2({
|
|
1860
1860
|
protocol: socket.protocol,
|
|
1861
1861
|
get buffered() {
|
|
1862
1862
|
return socket.bufferedAmount;
|
|
@@ -2047,8 +2047,8 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
|
|
|
2047
2047
|
const relay = async (nid, socket) => {
|
|
2048
2048
|
let ending;
|
|
2049
2049
|
let late;
|
|
2050
|
-
const unanswered = new Promise((
|
|
2051
|
-
late = () =>
|
|
2050
|
+
const unanswered = new Promise((resolve2) => {
|
|
2051
|
+
late = () => resolve2("late");
|
|
2052
2052
|
});
|
|
2053
2053
|
const flow = {
|
|
2054
2054
|
inflight: 0,
|
|
@@ -2076,8 +2076,8 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
|
|
|
2076
2076
|
}
|
|
2077
2077
|
flow.inflight += payloadSize(data);
|
|
2078
2078
|
while (flow.inflight > INBOUND_LIMIT && !flow.closing) {
|
|
2079
|
-
await new Promise((
|
|
2080
|
-
flow.wake =
|
|
2079
|
+
await new Promise((resolve2) => {
|
|
2080
|
+
flow.wake = resolve2;
|
|
2081
2081
|
});
|
|
2082
2082
|
}
|
|
2083
2083
|
}
|
|
@@ -2096,7 +2096,7 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
|
|
|
2096
2096
|
if (!socket) return;
|
|
2097
2097
|
socket.send(call.data);
|
|
2098
2098
|
while (socket.buffered > SEND_BUFFER && sockets.has(call.nid)) {
|
|
2099
|
-
await new Promise((
|
|
2099
|
+
await new Promise((resolve2) => setTimeout(resolve2, 10));
|
|
2100
2100
|
}
|
|
2101
2101
|
peer.postMessage({ net: "ws-acked", nid: call.nid, n: payloadSize(call.data) });
|
|
2102
2102
|
};
|
|
@@ -3033,6 +3033,97 @@ var MemoryMeta = class {
|
|
|
3033
3033
|
}
|
|
3034
3034
|
};
|
|
3035
3035
|
|
|
3036
|
+
// src/fs/integrity.ts
|
|
3037
|
+
var KERNEL_CALLER = { pid: 0, uid: 0 };
|
|
3038
|
+
function integrityOptions(options) {
|
|
3039
|
+
if (options && typeof options.lookup !== "function") {
|
|
3040
|
+
throw new TypeError("protect.integrity.lookup must be a function");
|
|
3041
|
+
}
|
|
3042
|
+
integrityUnknown(options);
|
|
3043
|
+
}
|
|
3044
|
+
function integrityUnknown(options) {
|
|
3045
|
+
const unknown = options?.unknown ?? "allow";
|
|
3046
|
+
if (unknown !== "allow" && unknown !== "deny") {
|
|
3047
|
+
throw new TypeError("protect.integrity.unknown must be 'allow' or 'deny'");
|
|
3048
|
+
}
|
|
3049
|
+
return unknown;
|
|
3050
|
+
}
|
|
3051
|
+
function hex(digest) {
|
|
3052
|
+
return [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
3053
|
+
}
|
|
3054
|
+
async function sha256(bytes) {
|
|
3055
|
+
return hex(await crypto.subtle.digest("SHA-256", bytes));
|
|
3056
|
+
}
|
|
3057
|
+
var Integrity = class {
|
|
3058
|
+
prefixes;
|
|
3059
|
+
root;
|
|
3060
|
+
lookup;
|
|
3061
|
+
unknown;
|
|
3062
|
+
report;
|
|
3063
|
+
unverified = /* @__PURE__ */ new Set();
|
|
3064
|
+
constructor(prefixes2, root, options, report) {
|
|
3065
|
+
this.prefixes = prefixes2;
|
|
3066
|
+
this.root = root;
|
|
3067
|
+
this.lookup = options.lookup;
|
|
3068
|
+
this.unknown = integrityUnknown(options);
|
|
3069
|
+
this.report = report;
|
|
3070
|
+
}
|
|
3071
|
+
async real(path) {
|
|
3072
|
+
const at = normalizePath(path);
|
|
3073
|
+
const real = await this.root.realpath?.(at, true).catch(() => void 0) ?? at;
|
|
3074
|
+
return isProtected(this.prefixes, real) ? real : void 0;
|
|
3075
|
+
}
|
|
3076
|
+
async key(path) {
|
|
3077
|
+
const real = await this.real(path);
|
|
3078
|
+
if (real === void 0 || !this.root.version) return void 0;
|
|
3079
|
+
return `${real}\0${await this.root.version(real)}`;
|
|
3080
|
+
}
|
|
3081
|
+
async read(path, caller, plain) {
|
|
3082
|
+
return (await this.readKeyed(path, caller, plain)).bytes;
|
|
3083
|
+
}
|
|
3084
|
+
async readKeyed(path, caller, plain) {
|
|
3085
|
+
const real = await this.real(path);
|
|
3086
|
+
if (real === void 0) return { bytes: await plain() };
|
|
3087
|
+
const read = this.root.readVersioned ? await this.root.readVersioned(real) : { bytes: await this.root.readFileBuffer(real), version: void 0 };
|
|
3088
|
+
const { bytes } = read;
|
|
3089
|
+
const keyed = read.version === void 0 ? { bytes } : { bytes, key: `${real}\0${read.version}` };
|
|
3090
|
+
const actual = await sha256(bytes);
|
|
3091
|
+
const denial = { pid: caller.pid, uid: caller.uid, path: real, actual };
|
|
3092
|
+
const tagged = caller.tag !== void 0 ? { tag: caller.tag } : {};
|
|
3093
|
+
let expected;
|
|
3094
|
+
try {
|
|
3095
|
+
expected = await this.expected(real);
|
|
3096
|
+
} catch {
|
|
3097
|
+
this.say({ ...denial, ...tagged, op: "exec-integrity", errno: "EACCES" });
|
|
3098
|
+
throw fsError("EACCES", `integrity lookup failed: ${real}`);
|
|
3099
|
+
}
|
|
3100
|
+
if (expected === void 0) {
|
|
3101
|
+
if (this.unknown === "allow") {
|
|
3102
|
+
if (!this.unverified.has(real)) {
|
|
3103
|
+
this.unverified.add(real);
|
|
3104
|
+
this.say({ ...denial, ...tagged, op: "exec-unverified", errno: "ENODATA" });
|
|
3105
|
+
}
|
|
3106
|
+
return keyed;
|
|
3107
|
+
}
|
|
3108
|
+
} else if (expected === actual) {
|
|
3109
|
+
return keyed;
|
|
3110
|
+
}
|
|
3111
|
+
const known = expected !== void 0 ? { expected } : {};
|
|
3112
|
+
this.say({ ...denial, ...tagged, ...known, op: "exec-integrity", errno: "EACCES" });
|
|
3113
|
+
throw fsError("EACCES", `integrity check failed: ${real}`);
|
|
3114
|
+
}
|
|
3115
|
+
async expected(path) {
|
|
3116
|
+
const answer = await this.lookup(path);
|
|
3117
|
+
return typeof answer === "string" ? answer.toLowerCase() : void 0;
|
|
3118
|
+
}
|
|
3119
|
+
say(event) {
|
|
3120
|
+
try {
|
|
3121
|
+
this.report(event);
|
|
3122
|
+
} catch {
|
|
3123
|
+
}
|
|
3124
|
+
}
|
|
3125
|
+
};
|
|
3126
|
+
|
|
3036
3127
|
// src/fs/protect.ts
|
|
3037
3128
|
function writerTags(protection) {
|
|
3038
3129
|
const writers = protection?.writers ?? [];
|
|
@@ -3313,11 +3404,11 @@ var RemoteWatch = class {
|
|
|
3313
3404
|
const seq2 = ++this.seq;
|
|
3314
3405
|
this.channel.postMessage({ tab: this.tab, paths: this.mine, seq: seq2 });
|
|
3315
3406
|
if (this.peers.size === 0) return Promise.resolve();
|
|
3316
|
-
return new Promise((
|
|
3407
|
+
return new Promise((resolve2) => {
|
|
3317
3408
|
const done4 = () => {
|
|
3318
3409
|
clearTimeout(timer);
|
|
3319
3410
|
this.waiting.delete(seq2);
|
|
3320
|
-
|
|
3411
|
+
resolve2();
|
|
3321
3412
|
};
|
|
3322
3413
|
const timer = setTimeout(() => {
|
|
3323
3414
|
done4();
|
|
@@ -3413,7 +3504,7 @@ async function openSync(handle) {
|
|
|
3413
3504
|
const delay = SYNC_BACKOFF_MS[attempt];
|
|
3414
3505
|
if (!LOCKED.has(err?.name)) throw err;
|
|
3415
3506
|
if (delay === void 0) return void 0;
|
|
3416
|
-
await new Promise((
|
|
3507
|
+
await new Promise((resolve2) => setTimeout(resolve2, delay));
|
|
3417
3508
|
}
|
|
3418
3509
|
}
|
|
3419
3510
|
}
|
|
@@ -3866,7 +3957,7 @@ var OpfsFs = class {
|
|
|
3866
3957
|
const again = name2 === "NotReadableError" || locks && LOCKED.has(name2);
|
|
3867
3958
|
if (!again || attempt >= READ_ATTEMPTS)
|
|
3868
3959
|
throw translate(err, path, locks ? "write" : "read");
|
|
3869
|
-
await new Promise((
|
|
3960
|
+
await new Promise((resolve2) => setTimeout(resolve2, attempt * 10));
|
|
3870
3961
|
}
|
|
3871
3962
|
}
|
|
3872
3963
|
}
|
|
@@ -3885,6 +3976,29 @@ var OpfsFs = class {
|
|
|
3885
3976
|
false
|
|
3886
3977
|
);
|
|
3887
3978
|
}
|
|
3979
|
+
async readVersioned(path) {
|
|
3980
|
+
const handle = await this.existing(path);
|
|
3981
|
+
return this.retrying(
|
|
3982
|
+
path,
|
|
3983
|
+
async () => {
|
|
3984
|
+
const file = await handle.getFile();
|
|
3985
|
+
const bytes = new Uint8Array(await file.arrayBuffer());
|
|
3986
|
+
return { bytes, version: `${file.lastModified}:${file.size}` };
|
|
3987
|
+
},
|
|
3988
|
+
false
|
|
3989
|
+
);
|
|
3990
|
+
}
|
|
3991
|
+
async version(path) {
|
|
3992
|
+
const handle = await this.existing(path);
|
|
3993
|
+
return this.retrying(
|
|
3994
|
+
path,
|
|
3995
|
+
async () => {
|
|
3996
|
+
const file = await handle.getFile();
|
|
3997
|
+
return `${file.lastModified}:${file.size}`;
|
|
3998
|
+
},
|
|
3999
|
+
false
|
|
4000
|
+
);
|
|
4001
|
+
}
|
|
3888
4002
|
async pread(path, offset, length2) {
|
|
3889
4003
|
const handle = await this.existing(path);
|
|
3890
4004
|
return this.retrying(
|
|
@@ -3900,8 +4014,8 @@ var OpfsFs = class {
|
|
|
3900
4014
|
}
|
|
3901
4015
|
async truncate(path, size) {
|
|
3902
4016
|
const handle = await this.existing(path, true);
|
|
3903
|
-
const
|
|
3904
|
-
const target =
|
|
4017
|
+
const resolve2 = this.ranged || this.remote?.watched();
|
|
4018
|
+
const target = resolve2 ? (await this.locate(path, true)).path : path;
|
|
3905
4019
|
await this.retrying(path, () => positioned(handle, (io) => io.truncate(size)), true);
|
|
3906
4020
|
this.written(target);
|
|
3907
4021
|
}
|
|
@@ -4341,12 +4455,12 @@ var PathGate = class {
|
|
|
4341
4455
|
tails = /* @__PURE__ */ new Map();
|
|
4342
4456
|
async acquire(path) {
|
|
4343
4457
|
const prev = this.tails.get(path) ?? Promise.resolve();
|
|
4344
|
-
const { promise, resolve } = Promise.withResolvers();
|
|
4458
|
+
const { promise, resolve: resolve2 } = Promise.withResolvers();
|
|
4345
4459
|
const tail = prev.then(() => promise);
|
|
4346
4460
|
this.tails.set(path, tail);
|
|
4347
4461
|
await prev;
|
|
4348
4462
|
return () => {
|
|
4349
|
-
|
|
4463
|
+
resolve2();
|
|
4350
4464
|
if (this.tails.get(path) === tail) this.tails.delete(path);
|
|
4351
4465
|
};
|
|
4352
4466
|
}
|
|
@@ -5110,7 +5224,7 @@ var CdpFacade = class {
|
|
|
5110
5224
|
const conn = await this.host(runtime);
|
|
5111
5225
|
try {
|
|
5112
5226
|
const reply = await new Promise(
|
|
5113
|
-
(
|
|
5227
|
+
(resolve2, reject) => {
|
|
5114
5228
|
const timer = setTimeout(
|
|
5115
5229
|
() => reject(new HttpError(504, "the CDP host did not answer Target.getTargets")),
|
|
5116
5230
|
this.options.listTimeout ?? LIST_TIMEOUT
|
|
@@ -5123,7 +5237,7 @@ var CdpFacade = class {
|
|
|
5123
5237
|
const parsed2 = JSON.parse(message3);
|
|
5124
5238
|
if (parsed2.id !== 1) return;
|
|
5125
5239
|
clearTimeout(timer);
|
|
5126
|
-
|
|
5240
|
+
resolve2(parsed2);
|
|
5127
5241
|
};
|
|
5128
5242
|
conn.send(JSON.stringify({ id: 1, method: "Target.getTargets" }));
|
|
5129
5243
|
}
|
|
@@ -5183,9 +5297,9 @@ var Relay = class {
|
|
|
5183
5297
|
}
|
|
5184
5298
|
async start(head) {
|
|
5185
5299
|
await this.send(head);
|
|
5186
|
-
const hostGone = new Promise((
|
|
5300
|
+
const hostGone = new Promise((resolve2) => {
|
|
5187
5301
|
this.host.onclose = (reason) => {
|
|
5188
|
-
void this.end(reason ? CLOSE.error : CLOSE.normal, reason ?? "").then(() =>
|
|
5302
|
+
void this.end(reason ? CLOSE.error : CLOSE.normal, reason ?? "").then(() => resolve2());
|
|
5189
5303
|
};
|
|
5190
5304
|
});
|
|
5191
5305
|
this.host.onmessage = (message3) => {
|
|
@@ -5199,8 +5313,8 @@ var Relay = class {
|
|
|
5199
5313
|
await Promise.race([pumping, hostGone]);
|
|
5200
5314
|
await this.writes;
|
|
5201
5315
|
let timer;
|
|
5202
|
-
const waited = new Promise((
|
|
5203
|
-
timer = setTimeout(
|
|
5316
|
+
const waited = new Promise((resolve2) => {
|
|
5317
|
+
timer = setTimeout(resolve2, this.closeWaitMs);
|
|
5204
5318
|
});
|
|
5205
5319
|
await Promise.race([pumping, waited]);
|
|
5206
5320
|
clearTimeout(timer);
|
|
@@ -5539,7 +5653,7 @@ function owned(path, st) {
|
|
|
5539
5653
|
function unchanged(a, b, bytes) {
|
|
5540
5654
|
return a.ino === b.ino && a.mtime.getTime() === b.mtime.getTime() && a.size === b.size && bytes.length === b.size;
|
|
5541
5655
|
}
|
|
5542
|
-
async function
|
|
5656
|
+
async function sha2562(bytes) {
|
|
5543
5657
|
const digest = new Uint8Array(
|
|
5544
5658
|
await crypto.subtle.digest("SHA-256", bytes)
|
|
5545
5659
|
);
|
|
@@ -5595,7 +5709,7 @@ async function attest(fs, asked, o) {
|
|
|
5595
5709
|
const bytes = (await fs.readFileBuffer(path)).slice();
|
|
5596
5710
|
const after = await fs.lstat(path);
|
|
5597
5711
|
if (unchanged(before, after, bytes)) {
|
|
5598
|
-
return attested(path, after, bytes, await
|
|
5712
|
+
return attested(path, after, bytes, await sha2562(bytes), o, parents);
|
|
5599
5713
|
}
|
|
5600
5714
|
if (tries >= 1) refuse("EAGAIN", path, "changed while it was read");
|
|
5601
5715
|
}
|
|
@@ -5850,16 +5964,16 @@ var FreshModules = class {
|
|
|
5850
5964
|
if (head) await decide(path, head);
|
|
5851
5965
|
};
|
|
5852
5966
|
}
|
|
5853
|
-
const
|
|
5967
|
+
const bound2 = /* @__PURE__ */ new WeakMap();
|
|
5854
5968
|
return new Proxy(fs, {
|
|
5855
5969
|
get(target, key, receiver) {
|
|
5856
5970
|
if (Object.hasOwn(overrides, key)) return overrides[key];
|
|
5857
5971
|
const value = Reflect.get(target, key, receiver);
|
|
5858
5972
|
if (typeof value !== "function") return value;
|
|
5859
|
-
let method =
|
|
5973
|
+
let method = bound2.get(value);
|
|
5860
5974
|
if (method === void 0) {
|
|
5861
5975
|
method = value.bind(target);
|
|
5862
|
-
|
|
5976
|
+
bound2.set(value, method);
|
|
5863
5977
|
}
|
|
5864
5978
|
return method;
|
|
5865
5979
|
}
|
|
@@ -6188,7 +6302,7 @@ var ChildTable = class {
|
|
|
6188
6302
|
}
|
|
6189
6303
|
async nextChange(candidates, flags2, interrupt) {
|
|
6190
6304
|
let wake;
|
|
6191
|
-
const stateChange = new Promise((
|
|
6305
|
+
const stateChange = new Promise((resolve2) => wake = resolve2);
|
|
6192
6306
|
const watching = flags2.untraced || flags2.continued;
|
|
6193
6307
|
if (watching && wake) this.stateChanged.push(wake);
|
|
6194
6308
|
try {
|
|
@@ -6402,6 +6516,65 @@ var ClipboardBroker = class {
|
|
|
6402
6516
|
}
|
|
6403
6517
|
};
|
|
6404
6518
|
|
|
6519
|
+
// src/kernel/exec-ticket.ts
|
|
6520
|
+
var TICKET_ENV = "SLICC_SEALED_TICKET";
|
|
6521
|
+
var TICKET_TTL_MS = 3e4;
|
|
6522
|
+
var MAX_TICKETS = 256;
|
|
6523
|
+
var TICKET = /^[0-9a-f]{32}$/;
|
|
6524
|
+
async function argvDigest(argv) {
|
|
6525
|
+
const bytes = new TextEncoder().encode(JSON.stringify(argv));
|
|
6526
|
+
const hash = new Uint8Array(await crypto.subtle.digest("SHA-256", bytes));
|
|
6527
|
+
return [...hash].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
6528
|
+
}
|
|
6529
|
+
function isStrings(value) {
|
|
6530
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
6531
|
+
}
|
|
6532
|
+
function stringRecord(value) {
|
|
6533
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
6534
|
+
const entries = Object.entries(value);
|
|
6535
|
+
return entries.every(([, v]) => typeof v === "string") ? Object.fromEntries(entries) : void 0;
|
|
6536
|
+
}
|
|
6537
|
+
var TicketBook = class {
|
|
6538
|
+
booked = /* @__PURE__ */ new Map();
|
|
6539
|
+
now;
|
|
6540
|
+
normalise = async (path) => path;
|
|
6541
|
+
constructor(now = Date.now) {
|
|
6542
|
+
this.now = now;
|
|
6543
|
+
}
|
|
6544
|
+
get size() {
|
|
6545
|
+
return this.booked.size;
|
|
6546
|
+
}
|
|
6547
|
+
async record(req, reply, pid) {
|
|
6548
|
+
const { ticket, error } = reply;
|
|
6549
|
+
if (error !== void 0 || typeof ticket !== "string" || !TICKET.test(ticket)) return;
|
|
6550
|
+
const { path, argv, cwd } = req;
|
|
6551
|
+
const entry = {
|
|
6552
|
+
pid,
|
|
6553
|
+
path: await this.normalise(path),
|
|
6554
|
+
requested: path,
|
|
6555
|
+
requestedCwd: cwd,
|
|
6556
|
+
argv: await argvDigest(argv),
|
|
6557
|
+
cwd: await this.normalise(cwd),
|
|
6558
|
+
at: this.now()
|
|
6559
|
+
};
|
|
6560
|
+
this.prune();
|
|
6561
|
+
while (this.booked.size >= MAX_TICKETS)
|
|
6562
|
+
this.booked.delete(this.booked.keys().next().value);
|
|
6563
|
+
this.booked.set(ticket, entry);
|
|
6564
|
+
}
|
|
6565
|
+
take(ticket) {
|
|
6566
|
+
const booked = this.booked.get(ticket);
|
|
6567
|
+
this.booked.delete(ticket);
|
|
6568
|
+
return booked && this.now() - booked.at < TICKET_TTL_MS ? booked : void 0;
|
|
6569
|
+
}
|
|
6570
|
+
prune() {
|
|
6571
|
+
const now = this.now();
|
|
6572
|
+
for (const [ticket, booked] of this.booked) {
|
|
6573
|
+
if (now - booked.at >= TICKET_TTL_MS) this.booked.delete(ticket);
|
|
6574
|
+
}
|
|
6575
|
+
}
|
|
6576
|
+
};
|
|
6577
|
+
|
|
6405
6578
|
// src/mount/protocol.ts
|
|
6406
6579
|
var DRIVER_PROTOCOL = [1, 2];
|
|
6407
6580
|
function driverVersionError(theirs) {
|
|
@@ -6472,7 +6645,7 @@ var DriverConnection = class _DriverConnection {
|
|
|
6472
6645
|
call(call, transfer = []) {
|
|
6473
6646
|
if (this.failure) return Promise.reject(this.failure);
|
|
6474
6647
|
const id2 = ++this.nextId;
|
|
6475
|
-
return new Promise((
|
|
6648
|
+
return new Promise((resolve2, reject) => {
|
|
6476
6649
|
const timer = unref(
|
|
6477
6650
|
setTimeout(() => {
|
|
6478
6651
|
this.waiting.delete(id2);
|
|
@@ -6485,7 +6658,7 @@ var DriverConnection = class _DriverConnection {
|
|
|
6485
6658
|
this.onFail?.(error);
|
|
6486
6659
|
}, this.timeoutMs)
|
|
6487
6660
|
);
|
|
6488
|
-
this.waiting.set(id2, { resolve, reject, timer });
|
|
6661
|
+
this.waiting.set(id2, { resolve: resolve2, reject, timer });
|
|
6489
6662
|
this.port.postMessage({ ...call, id: id2 }, transfer);
|
|
6490
6663
|
});
|
|
6491
6664
|
}
|
|
@@ -6628,8 +6801,8 @@ var HeldPaths = class extends Map {
|
|
|
6628
6801
|
this.keptHandles.delete(handle);
|
|
6629
6802
|
if (kept) this.letGo(kept);
|
|
6630
6803
|
}
|
|
6631
|
-
const
|
|
6632
|
-
if (
|
|
6804
|
+
const count2 = (this.opens.get(key) ?? 0) + (on ? 1 : -1);
|
|
6805
|
+
if (count2 > 0) this.opens.set(key, count2);
|
|
6633
6806
|
else {
|
|
6634
6807
|
this.opens.delete(key);
|
|
6635
6808
|
for (const id2 of this.handlesAt(key)) this.handles.delete(id2);
|
|
@@ -6643,8 +6816,8 @@ var HeldPaths = class extends Map {
|
|
|
6643
6816
|
return this.lastHandle;
|
|
6644
6817
|
}
|
|
6645
6818
|
holdWrite(path, on) {
|
|
6646
|
-
const
|
|
6647
|
-
if (
|
|
6819
|
+
const count2 = (this.get(path) ?? 0) + (on ? 1 : -1);
|
|
6820
|
+
if (count2 > 0) this.set(path, count2);
|
|
6648
6821
|
else {
|
|
6649
6822
|
this.delete(path);
|
|
6650
6823
|
this.revoked.delete(path);
|
|
@@ -6666,18 +6839,18 @@ var HeldPaths = class extends Map {
|
|
|
6666
6839
|
for (const path of this.keys()) if (within4(path, prefix)) this.revoked.add(path);
|
|
6667
6840
|
}
|
|
6668
6841
|
renamed(from, to) {
|
|
6669
|
-
for (const [path,
|
|
6842
|
+
for (const [path, count2] of [...this]) {
|
|
6670
6843
|
if (!within4(path, from)) continue;
|
|
6671
6844
|
const moved2 = to + path.slice(from.length);
|
|
6672
6845
|
this.delete(path);
|
|
6673
|
-
this.set(moved2, (this.get(moved2) ?? 0) +
|
|
6846
|
+
this.set(moved2, (this.get(moved2) ?? 0) + count2);
|
|
6674
6847
|
if (this.revoked.delete(path)) this.revoked.add(moved2);
|
|
6675
6848
|
}
|
|
6676
|
-
for (const [path,
|
|
6849
|
+
for (const [path, count2] of [...this.opens]) {
|
|
6677
6850
|
if (!within4(path, from)) continue;
|
|
6678
6851
|
const moved2 = to + path.slice(from.length);
|
|
6679
6852
|
this.opens.delete(path);
|
|
6680
|
-
this.opens.set(moved2, (this.opens.get(moved2) ?? 0) +
|
|
6853
|
+
this.opens.set(moved2, (this.opens.get(moved2) ?? 0) + count2);
|
|
6681
6854
|
}
|
|
6682
6855
|
for (const [id2, key] of this.handles) {
|
|
6683
6856
|
if (within4(key, from)) this.handles.set(id2, to + key.slice(from.length));
|
|
@@ -8998,7 +9171,7 @@ var AsyncOps = class {
|
|
|
8998
9171
|
this.wake();
|
|
8999
9172
|
}
|
|
9000
9173
|
sleep(timeoutMs, signal) {
|
|
9001
|
-
return new Promise((
|
|
9174
|
+
return new Promise((resolve2, reject) => {
|
|
9002
9175
|
let timer;
|
|
9003
9176
|
const cleanup = () => {
|
|
9004
9177
|
clearTimeout(timer);
|
|
@@ -9007,7 +9180,7 @@ var AsyncOps = class {
|
|
|
9007
9180
|
};
|
|
9008
9181
|
const wake = () => {
|
|
9009
9182
|
cleanup();
|
|
9010
|
-
|
|
9183
|
+
resolve2(true);
|
|
9011
9184
|
};
|
|
9012
9185
|
const abort = () => {
|
|
9013
9186
|
cleanup();
|
|
@@ -9018,7 +9191,7 @@ var AsyncOps = class {
|
|
|
9018
9191
|
if (timeoutMs !== void 0) {
|
|
9019
9192
|
timer = setTimeout(() => {
|
|
9020
9193
|
cleanup();
|
|
9021
|
-
|
|
9194
|
+
resolve2(false);
|
|
9022
9195
|
}, timeoutMs);
|
|
9023
9196
|
}
|
|
9024
9197
|
});
|
|
@@ -9102,10 +9275,10 @@ var Budget = class {
|
|
|
9102
9275
|
async acquire(n, signal) {
|
|
9103
9276
|
while (this.free < n) {
|
|
9104
9277
|
signal.throwIfAborted();
|
|
9105
|
-
await new Promise((
|
|
9278
|
+
await new Promise((resolve2) => {
|
|
9106
9279
|
const done4 = () => {
|
|
9107
9280
|
signal.removeEventListener("abort", done4);
|
|
9108
|
-
|
|
9281
|
+
resolve2();
|
|
9109
9282
|
};
|
|
9110
9283
|
this.waiters.push(done4);
|
|
9111
9284
|
signal.addEventListener("abort", done4, { once: true });
|
|
@@ -9247,14 +9420,14 @@ async function watchHangup(conn, abort, until) {
|
|
|
9247
9420
|
}
|
|
9248
9421
|
}
|
|
9249
9422
|
function untilAborted(response, signal) {
|
|
9250
|
-
return new Promise((
|
|
9423
|
+
return new Promise((resolve2, reject) => {
|
|
9251
9424
|
const onAbort = () => {
|
|
9252
9425
|
reject(signal.reason ?? new KernelError("EINTR"));
|
|
9253
9426
|
response.then((late) => late.cancel()).catch(() => void 0);
|
|
9254
9427
|
};
|
|
9255
9428
|
if (signal.aborted) return onAbort();
|
|
9256
9429
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
9257
|
-
response.then(
|
|
9430
|
+
response.then(resolve2, reject).finally(() => signal.removeEventListener("abort", onAbort));
|
|
9258
9431
|
});
|
|
9259
9432
|
}
|
|
9260
9433
|
function plainResponse(status, message3) {
|
|
@@ -9287,8 +9460,8 @@ function wireCode(code) {
|
|
|
9287
9460
|
return sendableCode(code) ? code : 1011;
|
|
9288
9461
|
}
|
|
9289
9462
|
function quietly(ms) {
|
|
9290
|
-
return new Promise((
|
|
9291
|
-
AbortSignal.timeout(ms).addEventListener("abort", () =>
|
|
9463
|
+
return new Promise((resolve2) => {
|
|
9464
|
+
AbortSignal.timeout(ms).addEventListener("abort", () => resolve2(), { once: true });
|
|
9292
9465
|
});
|
|
9293
9466
|
}
|
|
9294
9467
|
var RealmProxy = class {
|
|
@@ -9386,7 +9559,7 @@ var RealmProxy = class {
|
|
|
9386
9559
|
return false;
|
|
9387
9560
|
}
|
|
9388
9561
|
const url = ctx.origin ? tunnelRequestUrl(req, ctx.origin) : requestUrl(req);
|
|
9389
|
-
const
|
|
9562
|
+
const keep2 = keepsAlive(req);
|
|
9390
9563
|
const framing = requestFraming(req.headers);
|
|
9391
9564
|
const reserve = framing.kind === "length" ? framing.length : framing.kind === "chunked" ? this.cap() : 0;
|
|
9392
9565
|
if (reserve > this.cap()) throw new HttpError(413, `request body over ${this.cap()} bytes`);
|
|
@@ -9409,7 +9582,7 @@ var RealmProxy = class {
|
|
|
9409
9582
|
} finally {
|
|
9410
9583
|
this.bodies.release(reserve);
|
|
9411
9584
|
}
|
|
9412
|
-
return await this.relay(conn, req.method, req.minor, response,
|
|
9585
|
+
return await this.relay(conn, req.method, req.minor, response, keep2);
|
|
9413
9586
|
} finally {
|
|
9414
9587
|
this.stop.signal.removeEventListener("abort", stopExchange);
|
|
9415
9588
|
abort.abort();
|
|
@@ -9457,8 +9630,8 @@ var RealmProxy = class {
|
|
|
9457
9630
|
bodiless: bodiless2
|
|
9458
9631
|
});
|
|
9459
9632
|
const chunked2 = !bodiless2 && minor >= 1;
|
|
9460
|
-
const
|
|
9461
|
-
headers.push(["Connection",
|
|
9633
|
+
const keep2 = keepAlive && (bodiless2 || chunked2);
|
|
9634
|
+
headers.push(["Connection", keep2 ? "keep-alive" : "close"]);
|
|
9462
9635
|
if (chunked2) headers.push(["Transfer-Encoding", "chunked"]);
|
|
9463
9636
|
const signal = this.stop.signal;
|
|
9464
9637
|
await conn.write(
|
|
@@ -9467,7 +9640,7 @@ var RealmProxy = class {
|
|
|
9467
9640
|
);
|
|
9468
9641
|
if (bodiless2) {
|
|
9469
9642
|
await response.cancel();
|
|
9470
|
-
return
|
|
9643
|
+
return keep2;
|
|
9471
9644
|
}
|
|
9472
9645
|
try {
|
|
9473
9646
|
for await (const piece of response.body) {
|
|
@@ -9478,7 +9651,7 @@ var RealmProxy = class {
|
|
|
9478
9651
|
return false;
|
|
9479
9652
|
}
|
|
9480
9653
|
if (chunked2) await conn.write(LAST_CHUNK, signal);
|
|
9481
|
-
return
|
|
9654
|
+
return keep2;
|
|
9482
9655
|
}
|
|
9483
9656
|
async connect(conn, incoming, req, idle) {
|
|
9484
9657
|
const target = tunnelTarget(req);
|
|
@@ -9701,10 +9874,10 @@ function header(der, at) {
|
|
|
9701
9874
|
let len = der[at + 1];
|
|
9702
9875
|
let start2 = at + 2;
|
|
9703
9876
|
if (len & 128) {
|
|
9704
|
-
const
|
|
9877
|
+
const count2 = len & 127;
|
|
9705
9878
|
len = 0;
|
|
9706
|
-
for (let i = 0; i <
|
|
9707
|
-
start2 +=
|
|
9879
|
+
for (let i = 0; i < count2; i++) len = len * 256 + der[start2 + i];
|
|
9880
|
+
start2 += count2;
|
|
9708
9881
|
}
|
|
9709
9882
|
if (start2 + len > der.length) throw new Error("truncated DER");
|
|
9710
9883
|
return [tag, start2, start2 + len];
|
|
@@ -10761,7 +10934,7 @@ var Resolver = class {
|
|
|
10761
10934
|
};
|
|
10762
10935
|
function waitFor(lookup2, signal) {
|
|
10763
10936
|
lookup2.waiters++;
|
|
10764
|
-
return new Promise((
|
|
10937
|
+
return new Promise((resolve2, reject) => {
|
|
10765
10938
|
const abort = () => {
|
|
10766
10939
|
if (--lookup2.waiters === 0) lookup2.abort.abort(new KernelError("EINTR"));
|
|
10767
10940
|
reject(new KernelError("EINTR"));
|
|
@@ -10771,7 +10944,7 @@ function waitFor(lookup2, signal) {
|
|
|
10771
10944
|
if (signal?.aborted) return;
|
|
10772
10945
|
lookup2.waiters--;
|
|
10773
10946
|
signal?.removeEventListener("abort", abort);
|
|
10774
|
-
|
|
10947
|
+
resolve2(value);
|
|
10775
10948
|
});
|
|
10776
10949
|
});
|
|
10777
10950
|
}
|
|
@@ -10952,8 +11125,8 @@ var KernelTty = class {
|
|
|
10952
11125
|
}
|
|
10953
11126
|
rubOut(n) {
|
|
10954
11127
|
const dropped2 = this.line.splice(this.line.length - n, n);
|
|
10955
|
-
const
|
|
10956
|
-
const cells =
|
|
11128
|
+
const utf83 = (this.termios.c_iflag & IUTF8) !== 0;
|
|
11129
|
+
const cells = utf83 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
|
|
10957
11130
|
if (cells > 0 && this.termios.c_lflag & ECHO) {
|
|
10958
11131
|
this.output(encoder3.encode("\b \b".repeat(cells)), false);
|
|
10959
11132
|
}
|
|
@@ -11023,14 +11196,14 @@ var KernelTty = class {
|
|
|
11023
11196
|
}
|
|
11024
11197
|
changed(signal) {
|
|
11025
11198
|
if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
|
|
11026
|
-
return new Promise((
|
|
11199
|
+
return new Promise((resolve2, reject) => {
|
|
11027
11200
|
const onAbort = () => {
|
|
11028
11201
|
this.waiters = this.waiters.filter((w) => w !== waiter);
|
|
11029
11202
|
reject(new KernelError("EINTR"));
|
|
11030
11203
|
};
|
|
11031
11204
|
const waiter = () => {
|
|
11032
11205
|
signal?.removeEventListener("abort", onAbort);
|
|
11033
|
-
|
|
11206
|
+
resolve2();
|
|
11034
11207
|
};
|
|
11035
11208
|
this.waiters.push(waiter);
|
|
11036
11209
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
@@ -11039,7 +11212,7 @@ var KernelTty = class {
|
|
|
11039
11212
|
wake() {
|
|
11040
11213
|
const waiters = this.waiters;
|
|
11041
11214
|
this.waiters = [];
|
|
11042
|
-
for (const
|
|
11215
|
+
for (const resolve2 of waiters) resolve2();
|
|
11043
11216
|
}
|
|
11044
11217
|
};
|
|
11045
11218
|
|
|
@@ -11138,14 +11311,14 @@ var PtyPair = class {
|
|
|
11138
11311
|
}
|
|
11139
11312
|
changed(signal) {
|
|
11140
11313
|
if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
|
|
11141
|
-
return new Promise((
|
|
11314
|
+
return new Promise((resolve2, reject) => {
|
|
11142
11315
|
const onAbort = () => {
|
|
11143
11316
|
this.waiters = this.waiters.filter((w) => w !== waiter);
|
|
11144
11317
|
reject(new KernelError("EINTR"));
|
|
11145
11318
|
};
|
|
11146
11319
|
const waiter = () => {
|
|
11147
11320
|
signal?.removeEventListener("abort", onAbort);
|
|
11148
|
-
|
|
11321
|
+
resolve2();
|
|
11149
11322
|
};
|
|
11150
11323
|
this.waiters.push(waiter);
|
|
11151
11324
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
@@ -11154,7 +11327,7 @@ var PtyPair = class {
|
|
|
11154
11327
|
wake() {
|
|
11155
11328
|
const waiters = this.waiters;
|
|
11156
11329
|
this.waiters = [];
|
|
11157
|
-
for (const
|
|
11330
|
+
for (const resolve2 of waiters) resolve2();
|
|
11158
11331
|
}
|
|
11159
11332
|
};
|
|
11160
11333
|
var PtyTable = class {
|
|
@@ -11219,7 +11392,8 @@ function ptySyscall(req, ctx) {
|
|
|
11219
11392
|
if (!ctx.ptys) throw new KernelError("ENOENT");
|
|
11220
11393
|
const pair = ctx.ptys.get(req.n);
|
|
11221
11394
|
ttyOpenable(ctx.guard, pair.slave);
|
|
11222
|
-
const
|
|
11395
|
+
const slave = pair.openSlave();
|
|
11396
|
+
const fd = ctx.fds.install(ctx.ttyFilter?.(slave) ?? slave, 3);
|
|
11223
11397
|
if (!req.noctty) ctx.jobs?.acquireTerminal(ctx.pid, pair.slave);
|
|
11224
11398
|
return json4(fd);
|
|
11225
11399
|
}
|
|
@@ -11292,8 +11466,8 @@ async function runChild(req, deps) {
|
|
|
11292
11466
|
};
|
|
11293
11467
|
const controls = [];
|
|
11294
11468
|
const capture = () => {
|
|
11295
|
-
const { promise, resolve } = Promise.withResolvers();
|
|
11296
|
-
const control = { shut: false, ended:
|
|
11469
|
+
const { promise, resolve: resolve2 } = Promise.withResolvers();
|
|
11470
|
+
const control = { shut: false, ended: resolve2, eof: promise };
|
|
11297
11471
|
controls.push(control);
|
|
11298
11472
|
return { capture: true, max, overflow: () => stop("ENOBUFS"), control };
|
|
11299
11473
|
};
|
|
@@ -11386,7 +11560,7 @@ async function waitReady(fds, read, write, deadline, interrupted2) {
|
|
|
11386
11560
|
return file.changed === void 0 ? [] : [file.changed(round.signal).catch(() => void 0)];
|
|
11387
11561
|
});
|
|
11388
11562
|
let timer;
|
|
11389
|
-
const expiry = deadline === Number.POSITIVE_INFINITY ? [] : [new Promise((
|
|
11563
|
+
const expiry = deadline === Number.POSITIVE_INFINITY ? [] : [new Promise((resolve2) => timer = setTimeout(resolve2, deadline - Date.now()))];
|
|
11390
11564
|
try {
|
|
11391
11565
|
await Promise.race([...changes, ...expiry, interrupted2]);
|
|
11392
11566
|
} finally {
|
|
@@ -11470,10 +11644,10 @@ async function socketSyscall(req, proc) {
|
|
|
11470
11644
|
}
|
|
11471
11645
|
}
|
|
11472
11646
|
function interruptible2(dialing, signal) {
|
|
11473
|
-
return new Promise((
|
|
11647
|
+
return new Promise((resolve2, reject) => {
|
|
11474
11648
|
const abort = () => reject(new KernelError("EINTR"));
|
|
11475
11649
|
signal.addEventListener("abort", abort, { once: true });
|
|
11476
|
-
dialing.then(
|
|
11650
|
+
dialing.then(resolve2, reject).finally(() => signal.removeEventListener("abort", abort));
|
|
11477
11651
|
});
|
|
11478
11652
|
}
|
|
11479
11653
|
function unnamed(domain) {
|
|
@@ -12320,7 +12494,7 @@ var WasmProcess = class {
|
|
|
12320
12494
|
if (this.stopped) return "stop";
|
|
12321
12495
|
this.stopped = sig;
|
|
12322
12496
|
this.stops++;
|
|
12323
|
-
this.resumed = new Promise((
|
|
12497
|
+
this.resumed = new Promise((resolve2) => this.wake = resolve2);
|
|
12324
12498
|
const blocked = this.interrupt;
|
|
12325
12499
|
this.interrupt = new AbortController();
|
|
12326
12500
|
blocked.abort();
|
|
@@ -12385,7 +12559,9 @@ var WasmProcess = class {
|
|
|
12385
12559
|
const { ptys, jobs } = this.options;
|
|
12386
12560
|
const owner = this.options.owners ? this.ttyOwner() : void 0;
|
|
12387
12561
|
const guard2 = this.guarded();
|
|
12388
|
-
|
|
12562
|
+
const { ttyFilter } = this.options;
|
|
12563
|
+
const ctx = { pid: this.pid, fds: this.fds, ptys, jobs, guard: guard2, owner, ttyFilter };
|
|
12564
|
+
return ptySyscall(req, ctx);
|
|
12389
12565
|
}
|
|
12390
12566
|
return await this.procSyscall(req, cancelled);
|
|
12391
12567
|
} catch (e) {
|
|
@@ -12560,7 +12736,8 @@ var WasmProcess = class {
|
|
|
12560
12736
|
const tty = name2 === void 0 ? this.controllingTerminal() : this.options.jobs?.terminalNamed(name2);
|
|
12561
12737
|
if (!tty) throw new KernelError("ENXIO");
|
|
12562
12738
|
if (name2 !== void 0) ttyOpenable(this.guarded(), tty);
|
|
12563
|
-
|
|
12739
|
+
const file = tty.file();
|
|
12740
|
+
return this.fds.install(this.options.ttyFilter?.(file) ?? file, 3);
|
|
12564
12741
|
}
|
|
12565
12742
|
openSealed() {
|
|
12566
12743
|
const broker = this.options.sealed;
|
|
@@ -12600,15 +12777,8 @@ var WasmProcess = class {
|
|
|
12600
12777
|
async sealedCaller() {
|
|
12601
12778
|
const shown = await this.options.identity?.();
|
|
12602
12779
|
const broker = this.options.sealed;
|
|
12603
|
-
const
|
|
12604
|
-
return
|
|
12605
|
-
pid: shown?.pid ?? this.pid,
|
|
12606
|
-
ppid: shown?.ppid ?? 0,
|
|
12607
|
-
uid: this.cred.euid,
|
|
12608
|
-
gid: this.cred.egid,
|
|
12609
|
-
...tag !== void 0 ? { tag } : {},
|
|
12610
|
-
users: broker.users
|
|
12611
|
-
};
|
|
12780
|
+
const ids = { pid: shown?.pid ?? this.pid, ppid: shown?.ppid ?? 0 };
|
|
12781
|
+
return broker.stamp(ids, this.cred, broker.tagOf(this.pid));
|
|
12612
12782
|
}
|
|
12613
12783
|
guarded() {
|
|
12614
12784
|
const { guard: guard2 } = this.options;
|
|
@@ -13116,13 +13286,13 @@ var WasmProcess = class {
|
|
|
13116
13286
|
while (this.dlLock.depth > 0 && this.dlLock.owner !== owner) {
|
|
13117
13287
|
if (this.dying) throw new KernelError("EINTR");
|
|
13118
13288
|
const interrupt = this.interrupt.signal;
|
|
13119
|
-
const gone = await new Promise((
|
|
13289
|
+
const gone = await new Promise((resolve2) => {
|
|
13120
13290
|
const waiter = {
|
|
13121
13291
|
owner,
|
|
13122
13292
|
wake: (ended) => {
|
|
13123
13293
|
this.dlLock.waiters.delete(waiter);
|
|
13124
13294
|
interrupt.removeEventListener("abort", recheck);
|
|
13125
|
-
|
|
13295
|
+
resolve2(ended);
|
|
13126
13296
|
}
|
|
13127
13297
|
};
|
|
13128
13298
|
const recheck = () => waiter.wake(false);
|
|
@@ -13378,6 +13548,7 @@ function processOptions(opts, header4, hooks, created3) {
|
|
|
13378
13548
|
...inherited(opts),
|
|
13379
13549
|
...opts.owners ? { owners: true } : {},
|
|
13380
13550
|
...opts.sealed ? { sealed: opts.sealed } : {},
|
|
13551
|
+
...opts.ttyFilter ? { ttyFilter: opts.ttyFilter } : {},
|
|
13381
13552
|
...opts.clipboard ? { clipboard: opts.clipboard, ...clipboardName(opts) } : {},
|
|
13382
13553
|
...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
|
|
13383
13554
|
...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3 } } : {},
|
|
@@ -13872,6 +14043,523 @@ function replayingHops(inner, heldMs = HELD_MS) {
|
|
|
13872
14043
|
};
|
|
13873
14044
|
}
|
|
13874
14045
|
|
|
14046
|
+
// node_modules/@ai-ecoverse/slicc-scrub/dist/index.js
|
|
14047
|
+
var FRAGMENT = 16;
|
|
14048
|
+
var MIN_VALUE = 8;
|
|
14049
|
+
var MIN_BASE64 = 8;
|
|
14050
|
+
var encoder4 = new TextEncoder();
|
|
14051
|
+
var STD = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
14052
|
+
function utf82(text) {
|
|
14053
|
+
return encoder4.encode(text);
|
|
14054
|
+
}
|
|
14055
|
+
function base642(bytes, padded = true) {
|
|
14056
|
+
let out = "";
|
|
14057
|
+
for (let i = 0; i < bytes.length; i += 3) {
|
|
14058
|
+
const a = bytes[i];
|
|
14059
|
+
const b = i + 1 < bytes.length ? bytes[i + 1] : 0;
|
|
14060
|
+
const c = i + 2 < bytes.length ? bytes[i + 2] : 0;
|
|
14061
|
+
const triple = a << 16 | b << 8 | c;
|
|
14062
|
+
const left = bytes.length - i;
|
|
14063
|
+
out += STD[triple >> 18 & 63] + STD[triple >> 12 & 63];
|
|
14064
|
+
out += left > 1 ? STD[triple >> 6 & 63] : padded ? "=" : "";
|
|
14065
|
+
out += left > 2 ? STD[triple & 63] : padded ? "=" : "";
|
|
14066
|
+
}
|
|
14067
|
+
return out;
|
|
14068
|
+
}
|
|
14069
|
+
function urlSafe(text) {
|
|
14070
|
+
return text.replace(/\+/g, "-").replace(/\//g, "_");
|
|
14071
|
+
}
|
|
14072
|
+
function hex2(bytes) {
|
|
14073
|
+
let out = "";
|
|
14074
|
+
for (const byte of bytes) out += byte.toString(16).padStart(2, "0");
|
|
14075
|
+
return out;
|
|
14076
|
+
}
|
|
14077
|
+
function embedded(bytes, offset) {
|
|
14078
|
+
const shifted = new Uint8Array(offset + bytes.length);
|
|
14079
|
+
shifted.set(bytes, offset);
|
|
14080
|
+
const encoded = base642(shifted);
|
|
14081
|
+
const first = Math.ceil(8 * offset / 6);
|
|
14082
|
+
const last = Math.floor(8 * (offset + bytes.length) / 6);
|
|
14083
|
+
return encoded.slice(first, last);
|
|
14084
|
+
}
|
|
14085
|
+
function unicode(value, upper) {
|
|
14086
|
+
let out = "";
|
|
14087
|
+
for (let i = 0; i < value.length; i++) {
|
|
14088
|
+
const unit = value.charCodeAt(i);
|
|
14089
|
+
if (unit === 34) out += '\\"';
|
|
14090
|
+
else if (unit === 92) out += "\\\\";
|
|
14091
|
+
else if (unit < 32 || unit > 126) {
|
|
14092
|
+
const code = unit.toString(16).padStart(4, "0");
|
|
14093
|
+
out += `\\u${upper ? code.toUpperCase() : code}`;
|
|
14094
|
+
} else out += value[i];
|
|
14095
|
+
}
|
|
14096
|
+
return out;
|
|
14097
|
+
}
|
|
14098
|
+
function percent(value) {
|
|
14099
|
+
let component;
|
|
14100
|
+
try {
|
|
14101
|
+
component = encodeURIComponent(value);
|
|
14102
|
+
} catch {
|
|
14103
|
+
return [];
|
|
14104
|
+
}
|
|
14105
|
+
const lower = component.replace(/%[0-9A-F]{2}/g, (escape) => escape.toLowerCase());
|
|
14106
|
+
const plus = component.replace(/%20/g, "+");
|
|
14107
|
+
let form = "";
|
|
14108
|
+
for (const byte of utf82(value)) {
|
|
14109
|
+
const char = String.fromCharCode(byte);
|
|
14110
|
+
if (/[A-Za-z0-9*\-._]/.test(char)) form += char;
|
|
14111
|
+
else if (byte === 32) form += "+";
|
|
14112
|
+
else form += `%${byte.toString(16).toUpperCase().padStart(2, "0")}`;
|
|
14113
|
+
}
|
|
14114
|
+
return [component, lower, plus, form];
|
|
14115
|
+
}
|
|
14116
|
+
function html(value, apostrophe) {
|
|
14117
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, apostrophe);
|
|
14118
|
+
}
|
|
14119
|
+
function encodings(bytes) {
|
|
14120
|
+
const standard = [base642(bytes), base642(bytes, false)];
|
|
14121
|
+
for (const offset of [0, 1, 2]) {
|
|
14122
|
+
const run2 = embedded(bytes, offset);
|
|
14123
|
+
if (run2.length >= MIN_BASE64) standard.push(run2);
|
|
14124
|
+
}
|
|
14125
|
+
return [...standard, ...standard.map(urlSafe)];
|
|
14126
|
+
}
|
|
14127
|
+
function shapes(value) {
|
|
14128
|
+
const raw = utf82(value);
|
|
14129
|
+
const seen = /* @__PURE__ */ new Set([value]);
|
|
14130
|
+
const out = [];
|
|
14131
|
+
if (raw.length < FRAGMENT) out.push({ bytes: raw, form: "raw" });
|
|
14132
|
+
for (let offset = 0; raw.length >= FRAGMENT && offset + FRAGMENT <= raw.length; offset++) {
|
|
14133
|
+
out.push({ bytes: raw.subarray(offset, offset + FRAGMENT), form: "fragment", offset });
|
|
14134
|
+
}
|
|
14135
|
+
const add = (text, form) => {
|
|
14136
|
+
if (seen.has(text)) return;
|
|
14137
|
+
seen.add(text);
|
|
14138
|
+
out.push({ bytes: utf82(text), form });
|
|
14139
|
+
};
|
|
14140
|
+
const json5 = JSON.stringify(value).slice(1, -1);
|
|
14141
|
+
for (const text of [
|
|
14142
|
+
json5,
|
|
14143
|
+
json5.replace(/\//g, "\\/"),
|
|
14144
|
+
unicode(value, false),
|
|
14145
|
+
unicode(value, true)
|
|
14146
|
+
]) {
|
|
14147
|
+
add(text, "json");
|
|
14148
|
+
}
|
|
14149
|
+
for (const text of percent(value)) add(text, "percent");
|
|
14150
|
+
for (const text of [html(value, "'"), html(value, "'")]) add(text, "html");
|
|
14151
|
+
for (const text of encodings(raw)) add(text, "base64");
|
|
14152
|
+
add(hex2(raw), "hex");
|
|
14153
|
+
add(hex2(raw).toUpperCase(), "hex");
|
|
14154
|
+
return out;
|
|
14155
|
+
}
|
|
14156
|
+
function wrappable(form) {
|
|
14157
|
+
return form === "base64" || form === "hex";
|
|
14158
|
+
}
|
|
14159
|
+
var Automaton = class {
|
|
14160
|
+
edges = /* @__PURE__ */ new Map();
|
|
14161
|
+
fail = [0];
|
|
14162
|
+
depth = [0];
|
|
14163
|
+
children = [0];
|
|
14164
|
+
out = [[]];
|
|
14165
|
+
dict = [-1];
|
|
14166
|
+
lengths = [];
|
|
14167
|
+
add(bytes) {
|
|
14168
|
+
let node = 0;
|
|
14169
|
+
for (const byte of bytes) {
|
|
14170
|
+
const key = node * 256 + byte;
|
|
14171
|
+
let next2 = this.edges.get(key);
|
|
14172
|
+
if (next2 === void 0) {
|
|
14173
|
+
next2 = this.depth.length;
|
|
14174
|
+
this.edges.set(key, next2);
|
|
14175
|
+
this.fail.push(0);
|
|
14176
|
+
this.depth.push(this.depth[node] + 1);
|
|
14177
|
+
this.children.push(0);
|
|
14178
|
+
this.children[node] += 1;
|
|
14179
|
+
this.out.push([]);
|
|
14180
|
+
this.dict.push(-1);
|
|
14181
|
+
}
|
|
14182
|
+
node = next2;
|
|
14183
|
+
}
|
|
14184
|
+
const id2 = this.lengths.length;
|
|
14185
|
+
this.lengths.push(bytes.length);
|
|
14186
|
+
this.out[node].push(id2);
|
|
14187
|
+
return id2;
|
|
14188
|
+
}
|
|
14189
|
+
build() {
|
|
14190
|
+
const queue = [];
|
|
14191
|
+
const byNode = /* @__PURE__ */ new Map();
|
|
14192
|
+
for (const [key, child] of this.edges) {
|
|
14193
|
+
const parent = Math.floor(key / 256);
|
|
14194
|
+
const list = byNode.get(parent) ?? [];
|
|
14195
|
+
list.push([key % 256, child]);
|
|
14196
|
+
byNode.set(parent, list);
|
|
14197
|
+
}
|
|
14198
|
+
for (const [, child] of byNode.get(0) ?? []) queue.push(child);
|
|
14199
|
+
for (let head = 0; head < queue.length; head++) {
|
|
14200
|
+
const node = queue[head];
|
|
14201
|
+
const fallback = this.fail[node];
|
|
14202
|
+
this.dict[node] = this.out[fallback].length > 0 ? fallback : this.dict[fallback];
|
|
14203
|
+
for (const [byte, child] of byNode.get(node) ?? []) {
|
|
14204
|
+
let state = fallback;
|
|
14205
|
+
let target = this.edges.get(state * 256 + byte);
|
|
14206
|
+
while (target === void 0 && state > 0) {
|
|
14207
|
+
state = this.fail[state];
|
|
14208
|
+
target = this.edges.get(state * 256 + byte);
|
|
14209
|
+
}
|
|
14210
|
+
this.fail[child] = target ?? 0;
|
|
14211
|
+
queue.push(child);
|
|
14212
|
+
}
|
|
14213
|
+
}
|
|
14214
|
+
}
|
|
14215
|
+
step(state, byte) {
|
|
14216
|
+
let current = state;
|
|
14217
|
+
for (; ; ) {
|
|
14218
|
+
const next2 = this.edges.get(current * 256 + byte);
|
|
14219
|
+
if (next2 !== void 0) return next2;
|
|
14220
|
+
if (current === 0) return 0;
|
|
14221
|
+
current = this.fail[current];
|
|
14222
|
+
}
|
|
14223
|
+
}
|
|
14224
|
+
matches(state, visit) {
|
|
14225
|
+
for (let node = state; node > 0; node = this.dict[node]) {
|
|
14226
|
+
for (const id2 of this.out[node]) visit(id2);
|
|
14227
|
+
}
|
|
14228
|
+
}
|
|
14229
|
+
partials(state, length2, visit) {
|
|
14230
|
+
for (let node = state; node > 0; node = this.fail[node]) {
|
|
14231
|
+
if (this.children[node] > 0) visit(length2 - this.depth[node]);
|
|
14232
|
+
}
|
|
14233
|
+
}
|
|
14234
|
+
};
|
|
14235
|
+
var RANK = {
|
|
14236
|
+
raw: 0,
|
|
14237
|
+
fragment: 1,
|
|
14238
|
+
json: 2,
|
|
14239
|
+
percent: 3,
|
|
14240
|
+
html: 4,
|
|
14241
|
+
base64: 5,
|
|
14242
|
+
hex: 6,
|
|
14243
|
+
wrapped: 7
|
|
14244
|
+
};
|
|
14245
|
+
var CR2 = 13;
|
|
14246
|
+
var LF2 = 10;
|
|
14247
|
+
var EMPTY2 = new Uint8Array(0);
|
|
14248
|
+
function register(automaton, sources, ids, bytes, source) {
|
|
14249
|
+
const key = Array.from(bytes).join(",");
|
|
14250
|
+
let id2 = ids.get(key);
|
|
14251
|
+
if (id2 === void 0) {
|
|
14252
|
+
id2 = automaton.add(bytes);
|
|
14253
|
+
ids.set(key, id2);
|
|
14254
|
+
sources[id2] = [];
|
|
14255
|
+
}
|
|
14256
|
+
sources[id2].push(source);
|
|
14257
|
+
}
|
|
14258
|
+
function prepare(secrets) {
|
|
14259
|
+
const main = new Automaton();
|
|
14260
|
+
const wrap = new Automaton();
|
|
14261
|
+
const sources = [];
|
|
14262
|
+
const wrapSources = [];
|
|
14263
|
+
const ids = /* @__PURE__ */ new Map();
|
|
14264
|
+
const wrapIds = /* @__PURE__ */ new Map();
|
|
14265
|
+
const values = secrets.map((secret) => utf82(secret.value));
|
|
14266
|
+
secrets.forEach((secret, index) => {
|
|
14267
|
+
if (typeof secret.value !== "string" || values[index].length < MIN_VALUE) {
|
|
14268
|
+
throw new RangeError(`the value of ${secret.name} is shorter than ${MIN_VALUE} bytes`);
|
|
14269
|
+
}
|
|
14270
|
+
for (const shape of shapes(secret.value)) {
|
|
14271
|
+
const source = { secret: index, form: shape.form, offset: shape.offset };
|
|
14272
|
+
register(main, sources, ids, shape.bytes, source);
|
|
14273
|
+
if (wrappable(shape.form)) register(wrap, wrapSources, wrapIds, shape.bytes, source);
|
|
14274
|
+
}
|
|
14275
|
+
});
|
|
14276
|
+
main.build();
|
|
14277
|
+
wrap.build();
|
|
14278
|
+
const replacements = secrets.map((secret) => [
|
|
14279
|
+
utf82(secret.placeholder),
|
|
14280
|
+
utf82(`[redacted:${secret.name}]`)
|
|
14281
|
+
]);
|
|
14282
|
+
return { secrets, values, main, wrap, sources, wrapSources, replacements };
|
|
14283
|
+
}
|
|
14284
|
+
function better(a, b) {
|
|
14285
|
+
if (!b) return true;
|
|
14286
|
+
if (a.end !== b.end) return a.end > b.end;
|
|
14287
|
+
if (a.rank !== b.rank) return a.rank < b.rank;
|
|
14288
|
+
return a.secret < b.secret;
|
|
14289
|
+
}
|
|
14290
|
+
function resolve(c, input, start2, end, source) {
|
|
14291
|
+
if (source.form !== "fragment") {
|
|
14292
|
+
return {
|
|
14293
|
+
start: start2,
|
|
14294
|
+
end,
|
|
14295
|
+
secret: source.secret,
|
|
14296
|
+
form: source.form,
|
|
14297
|
+
rank: RANK[source.form],
|
|
14298
|
+
growing: false
|
|
14299
|
+
};
|
|
14300
|
+
}
|
|
14301
|
+
const value = c.values[source.secret];
|
|
14302
|
+
let stop = end;
|
|
14303
|
+
let at = source.offset + FRAGMENT;
|
|
14304
|
+
while (stop < input.length && at < value.length && input[stop] === value[at]) {
|
|
14305
|
+
stop++;
|
|
14306
|
+
at++;
|
|
14307
|
+
}
|
|
14308
|
+
const form = source.offset === 0 && at === value.length ? "raw" : "fragment";
|
|
14309
|
+
const growing = stop === input.length && at < value.length;
|
|
14310
|
+
return { start: start2, end: stop, secret: source.secret, form, rank: RANK[form], growing };
|
|
14311
|
+
}
|
|
14312
|
+
function keep(best, candidate) {
|
|
14313
|
+
if (better(candidate, best.get(candidate.start))) best.set(candidate.start, candidate);
|
|
14314
|
+
}
|
|
14315
|
+
function scanMain(c, input, best, partials) {
|
|
14316
|
+
let state = 0;
|
|
14317
|
+
for (let i = 0; i < input.length; i++) {
|
|
14318
|
+
state = c.main.step(state, input[i]);
|
|
14319
|
+
c.main.matches(state, (id2) => {
|
|
14320
|
+
const start2 = i + 1 - c.main.lengths[id2];
|
|
14321
|
+
for (const source of c.sources[id2]) {
|
|
14322
|
+
const candidate = resolve(c, input, start2, i + 1, source);
|
|
14323
|
+
if (candidate.growing) partials.push(candidate.start);
|
|
14324
|
+
keep(best, candidate);
|
|
14325
|
+
}
|
|
14326
|
+
});
|
|
14327
|
+
}
|
|
14328
|
+
c.main.partials(state, input.length, (start2) => partials.push(start2));
|
|
14329
|
+
}
|
|
14330
|
+
function scanWrapped(c, input, best, partials) {
|
|
14331
|
+
const map = [];
|
|
14332
|
+
for (let i = 0; i < input.length; i++) if (input[i] !== CR2 && input[i] !== LF2) map.push(i);
|
|
14333
|
+
if (map.length === input.length) return;
|
|
14334
|
+
let state = 0;
|
|
14335
|
+
for (let v = 0; v < map.length; v++) {
|
|
14336
|
+
state = c.wrap.step(state, input[map[v]]);
|
|
14337
|
+
c.wrap.matches(state, (id2) => {
|
|
14338
|
+
const first = v + 1 - c.wrap.lengths[id2];
|
|
14339
|
+
const start2 = map[first];
|
|
14340
|
+
const end = map[v] + 1;
|
|
14341
|
+
const wrapped = end - start2 !== c.wrap.lengths[id2];
|
|
14342
|
+
for (const source of c.wrapSources[id2]) {
|
|
14343
|
+
const form = wrapped ? "wrapped" : source.form;
|
|
14344
|
+
keep(best, { start: start2, end, secret: source.secret, form, rank: RANK[form], growing: false });
|
|
14345
|
+
}
|
|
14346
|
+
});
|
|
14347
|
+
}
|
|
14348
|
+
c.wrap.partials(state, map.length, (start2) => partials.push(map[start2]));
|
|
14349
|
+
}
|
|
14350
|
+
function count(c, hits, candidate) {
|
|
14351
|
+
const name2 = c.secrets[candidate.secret].name;
|
|
14352
|
+
const key = `${name2}\0${candidate.form}`;
|
|
14353
|
+
const hit = hits.get(key) ?? { name: name2, form: candidate.form, count: 0 };
|
|
14354
|
+
hit.count += 1;
|
|
14355
|
+
hits.set(key, hit);
|
|
14356
|
+
}
|
|
14357
|
+
function concat5(parts2) {
|
|
14358
|
+
let length2 = 0;
|
|
14359
|
+
for (const part of parts2) length2 += part.length;
|
|
14360
|
+
const out = new Uint8Array(length2);
|
|
14361
|
+
let at = 0;
|
|
14362
|
+
for (const part of parts2) {
|
|
14363
|
+
out.set(part, at);
|
|
14364
|
+
at += part.length;
|
|
14365
|
+
}
|
|
14366
|
+
return out;
|
|
14367
|
+
}
|
|
14368
|
+
function pass(c, input, final) {
|
|
14369
|
+
const best = /* @__PURE__ */ new Map();
|
|
14370
|
+
const partials = [];
|
|
14371
|
+
scanMain(c, input, best, partials);
|
|
14372
|
+
scanWrapped(c, input, best, partials);
|
|
14373
|
+
const holds = final ? [] : [...new Set(partials)].sort((a, b) => a - b);
|
|
14374
|
+
const starts = [...best.keys()].sort((a, b) => a - b);
|
|
14375
|
+
const parts2 = [];
|
|
14376
|
+
const hits = /* @__PURE__ */ new Map();
|
|
14377
|
+
let pos = 0;
|
|
14378
|
+
let cut = input.length;
|
|
14379
|
+
let s = 0;
|
|
14380
|
+
let h = 0;
|
|
14381
|
+
for (; ; ) {
|
|
14382
|
+
while (s < starts.length && starts[s] < pos) s++;
|
|
14383
|
+
while (h < holds.length && holds[h] < pos) h++;
|
|
14384
|
+
const hold = h < holds.length ? holds[h] : Infinity;
|
|
14385
|
+
const next2 = s < starts.length ? starts[s] : Infinity;
|
|
14386
|
+
if (hold <= next2) {
|
|
14387
|
+
cut = Math.min(hold, input.length);
|
|
14388
|
+
break;
|
|
14389
|
+
}
|
|
14390
|
+
const candidate = best.get(next2);
|
|
14391
|
+
parts2.push(
|
|
14392
|
+
input.subarray(pos, next2),
|
|
14393
|
+
c.replacements[candidate.secret][candidate.form === "raw" ? 0 : 1]
|
|
14394
|
+
);
|
|
14395
|
+
count(c, hits, candidate);
|
|
14396
|
+
pos = candidate.end;
|
|
14397
|
+
}
|
|
14398
|
+
parts2.push(input.subarray(pos, cut));
|
|
14399
|
+
return { output: concat5(parts2), cut, hits };
|
|
14400
|
+
}
|
|
14401
|
+
function merge(into, from) {
|
|
14402
|
+
for (const [key, hit] of from) {
|
|
14403
|
+
const known = into.get(key);
|
|
14404
|
+
if (known) known.count += hit.count;
|
|
14405
|
+
else into.set(key, { ...hit });
|
|
14406
|
+
}
|
|
14407
|
+
}
|
|
14408
|
+
function compile(secrets, options = {}) {
|
|
14409
|
+
const c = prepare(secrets.map((secret) => ({ ...secret })));
|
|
14410
|
+
const decoder4 = new TextDecoder();
|
|
14411
|
+
const idleMs = options.idleMs ?? 250;
|
|
14412
|
+
const scrubBytes = (input) => {
|
|
14413
|
+
if (c.secrets.length === 0) return { bytes: input.slice(), hits: [] };
|
|
14414
|
+
const result = pass(c, input, true);
|
|
14415
|
+
return { bytes: result.output, hits: [...result.hits.values()] };
|
|
14416
|
+
};
|
|
14417
|
+
return {
|
|
14418
|
+
version: options.version ?? 0,
|
|
14419
|
+
idleMs,
|
|
14420
|
+
size: c.main.lengths.length,
|
|
14421
|
+
bytes: scrubBytes,
|
|
14422
|
+
text(input) {
|
|
14423
|
+
const { bytes, hits } = scrubBytes(utf82(input));
|
|
14424
|
+
return { text: decoder4.decode(bytes), hits };
|
|
14425
|
+
},
|
|
14426
|
+
stream() {
|
|
14427
|
+
let pending = EMPTY2;
|
|
14428
|
+
const hits = /* @__PURE__ */ new Map();
|
|
14429
|
+
const run2 = (final) => {
|
|
14430
|
+
if (c.secrets.length === 0 || pending.length === 0) {
|
|
14431
|
+
const out = pending;
|
|
14432
|
+
pending = EMPTY2;
|
|
14433
|
+
return out;
|
|
14434
|
+
}
|
|
14435
|
+
const result = pass(c, pending, final);
|
|
14436
|
+
merge(hits, result.hits);
|
|
14437
|
+
pending = pending.slice(result.cut);
|
|
14438
|
+
return result.output;
|
|
14439
|
+
};
|
|
14440
|
+
return {
|
|
14441
|
+
push(chunk2) {
|
|
14442
|
+
pending = concat5([pending, chunk2]);
|
|
14443
|
+
return run2(false);
|
|
14444
|
+
},
|
|
14445
|
+
idle() {
|
|
14446
|
+
return pending.length < FRAGMENT ? run2(true) : EMPTY2;
|
|
14447
|
+
},
|
|
14448
|
+
end() {
|
|
14449
|
+
return run2(true);
|
|
14450
|
+
},
|
|
14451
|
+
get hits() {
|
|
14452
|
+
return [...hits.values()];
|
|
14453
|
+
}
|
|
14454
|
+
};
|
|
14455
|
+
}
|
|
14456
|
+
};
|
|
14457
|
+
}
|
|
14458
|
+
|
|
14459
|
+
// src/kernel/scrub-file.ts
|
|
14460
|
+
var SCRUB_IDLE_MS = 50;
|
|
14461
|
+
var encoder5 = new TextEncoder();
|
|
14462
|
+
function scrubberOf(secrets) {
|
|
14463
|
+
const usable = secrets.filter((s) => encoder5.encode(s.value).length >= MIN_VALUE);
|
|
14464
|
+
return usable.length > 0 ? compile(usable, { idleMs: SCRUB_IDLE_MS }) : void 0;
|
|
14465
|
+
}
|
|
14466
|
+
async function writeAll2(file, bytes, signal) {
|
|
14467
|
+
const write = file.write;
|
|
14468
|
+
for (let at = 0; at < bytes.length; ) {
|
|
14469
|
+
at += await write.call(file, bytes.subarray(at), at === 0 ? signal : void 0);
|
|
14470
|
+
}
|
|
14471
|
+
}
|
|
14472
|
+
function bound(file, key) {
|
|
14473
|
+
const method = file[key];
|
|
14474
|
+
return typeof method === "function" ? { [key]: method.bind(file) } : {};
|
|
14475
|
+
}
|
|
14476
|
+
function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
14477
|
+
const file = inner.file;
|
|
14478
|
+
const stream2 = scrubber.stream();
|
|
14479
|
+
let queue = Promise.resolve();
|
|
14480
|
+
let timer;
|
|
14481
|
+
let failed3;
|
|
14482
|
+
const raise = () => {
|
|
14483
|
+
if (failed3 === void 0) return;
|
|
14484
|
+
const err = failed3;
|
|
14485
|
+
failed3 = void 0;
|
|
14486
|
+
throw err;
|
|
14487
|
+
};
|
|
14488
|
+
const enqueue = (bytes, signal) => {
|
|
14489
|
+
const next2 = queue.then(() => bytes.length > 0 ? writeAll2(file, bytes, signal) : void 0);
|
|
14490
|
+
queue = next2.catch(() => void 0);
|
|
14491
|
+
return next2;
|
|
14492
|
+
};
|
|
14493
|
+
const release = () => {
|
|
14494
|
+
timer = void 0;
|
|
14495
|
+
enqueue(stream2.idle()).catch((err) => {
|
|
14496
|
+
failed3 = err;
|
|
14497
|
+
});
|
|
14498
|
+
};
|
|
14499
|
+
return new OpenFile({
|
|
14500
|
+
...bound(file, "read"),
|
|
14501
|
+
...bound(file, "peek"),
|
|
14502
|
+
...bound(file, "poll"),
|
|
14503
|
+
...bound(file, "changed"),
|
|
14504
|
+
...bound(file, "stat"),
|
|
14505
|
+
...file.tty ? { tty: file.tty } : {},
|
|
14506
|
+
...file.clipboard ? { clipboard: file.clipboard } : {},
|
|
14507
|
+
...file.write ? {
|
|
14508
|
+
write: async (bytes, signal) => {
|
|
14509
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
14510
|
+
raise();
|
|
14511
|
+
await enqueue(stream2.push(bytes), signal);
|
|
14512
|
+
timer = setTimeout(release, idleMs);
|
|
14513
|
+
return bytes.length;
|
|
14514
|
+
}
|
|
14515
|
+
} : {},
|
|
14516
|
+
flush: async () => {
|
|
14517
|
+
await queue;
|
|
14518
|
+
raise();
|
|
14519
|
+
await file.flush?.();
|
|
14520
|
+
},
|
|
14521
|
+
close: async () => {
|
|
14522
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
14523
|
+
let failure;
|
|
14524
|
+
try {
|
|
14525
|
+
await enqueue(stream2.end());
|
|
14526
|
+
raise();
|
|
14527
|
+
} catch (err) {
|
|
14528
|
+
failure = { err };
|
|
14529
|
+
}
|
|
14530
|
+
await inner.release();
|
|
14531
|
+
if (failure) throw failure.err;
|
|
14532
|
+
}
|
|
14533
|
+
});
|
|
14534
|
+
}
|
|
14535
|
+
function scrubbedAll(file, scrubbers) {
|
|
14536
|
+
return scrubbers.reduce((inner, scrubber) => scrubbed(inner, scrubber), file);
|
|
14537
|
+
}
|
|
14538
|
+
function stdioFiles(fds) {
|
|
14539
|
+
const files = /* @__PURE__ */ new Set();
|
|
14540
|
+
for (const fd of [1, 2]) {
|
|
14541
|
+
try {
|
|
14542
|
+
files.add(fds.get(fd));
|
|
14543
|
+
} catch {
|
|
14544
|
+
}
|
|
14545
|
+
}
|
|
14546
|
+
return files;
|
|
14547
|
+
}
|
|
14548
|
+
function scrubStdio(fds, scrubber) {
|
|
14549
|
+
const wrapped = /* @__PURE__ */ new Map();
|
|
14550
|
+
const stdio = stdioFiles(fds);
|
|
14551
|
+
for (const fd of fds.numbers()) {
|
|
14552
|
+
const original = fds.get(fd);
|
|
14553
|
+
if (!stdio.has(original) && !original.file.tty) continue;
|
|
14554
|
+
const known = wrapped.get(original);
|
|
14555
|
+
const flags2 = fds.statusFlags(fd);
|
|
14556
|
+
const wrapper = known ? known.retain() : scrubbed(original.retain(), scrubber);
|
|
14557
|
+
wrapped.set(original, wrapper);
|
|
14558
|
+
fds.installAt(fd, wrapper);
|
|
14559
|
+
if (flags2 !== void 0) fds.setStatusFlags(fd, flags2);
|
|
14560
|
+
}
|
|
14561
|
+
}
|
|
14562
|
+
|
|
13875
14563
|
// src/kernel/sealed.ts
|
|
13876
14564
|
var SEALED_MAX_BYTES = 64 * 1024;
|
|
13877
14565
|
var SEALED_TIMEOUT_MS = 1e4;
|
|
@@ -13885,7 +14573,7 @@ var SEALED_OPS = /* @__PURE__ */ new Set([
|
|
|
13885
14573
|
"cancel",
|
|
13886
14574
|
"materialize"
|
|
13887
14575
|
]);
|
|
13888
|
-
var
|
|
14576
|
+
var encoder6 = new TextEncoder();
|
|
13889
14577
|
var decoder3 = new TextDecoder("utf-8", { fatal: true });
|
|
13890
14578
|
function parseSealed(bytes) {
|
|
13891
14579
|
try {
|
|
@@ -13901,11 +14589,16 @@ function parseSealed(bytes) {
|
|
|
13901
14589
|
const req = parsed2;
|
|
13902
14590
|
if (req.v !== 1 || typeof req.op !== "string" || !SEALED_OPS.has(req.op))
|
|
13903
14591
|
throw new KernelError("EINVAL");
|
|
14592
|
+
if (req.op === "materialize" && !materializable(req)) throw new KernelError("EINVAL");
|
|
13904
14593
|
return req;
|
|
13905
14594
|
} finally {
|
|
13906
14595
|
bytes.fill(0);
|
|
13907
14596
|
}
|
|
13908
14597
|
}
|
|
14598
|
+
function materializable(req) {
|
|
14599
|
+
if (req.shellGrants !== true && !isStrings(req.names)) return false;
|
|
14600
|
+
return isStrings(req.argv) && typeof req.path === "string" && typeof req.cwd === "string";
|
|
14601
|
+
}
|
|
13909
14602
|
function errorReply(code, message3) {
|
|
13910
14603
|
return { v: 1, error: { code, message: message3 } };
|
|
13911
14604
|
}
|
|
@@ -13949,12 +14642,14 @@ var SealedChannel = class {
|
|
|
13949
14642
|
const timer = setTimeout(() => control.abort("timeout"), this.broker.timeoutMs);
|
|
13950
14643
|
const req = { ...fields, v: 1, op: fields.op, caller };
|
|
13951
14644
|
const flight = { control, done: Promise.resolve() };
|
|
13952
|
-
flight.done = this.answer(req, control.signal).then((reply) => {
|
|
14645
|
+
flight.done = this.answer(req, control.signal).then(async (reply) => {
|
|
13953
14646
|
clearTimeout(timer);
|
|
13954
14647
|
gone.removeEventListener("abort", withdraw);
|
|
14648
|
+
if (reply && req.op === "materialize")
|
|
14649
|
+
await this.broker.tickets.record(req, reply, caller.pid);
|
|
13955
14650
|
if (this.inflight !== flight) return;
|
|
13956
14651
|
this.inflight = void 0;
|
|
13957
|
-
if (reply) this.reply =
|
|
14652
|
+
if (reply) this.reply = encoder6.encode(serialized(reply));
|
|
13958
14653
|
});
|
|
13959
14654
|
this.inflight = flight;
|
|
13960
14655
|
return length2;
|
|
@@ -13962,8 +14657,8 @@ var SealedChannel = class {
|
|
|
13962
14657
|
async answer(req, signal) {
|
|
13963
14658
|
const hook = this.broker.hook;
|
|
13964
14659
|
if (!hook) return errorReply("ENOTCONN", "no vault is connected");
|
|
13965
|
-
const stopped = new Promise((
|
|
13966
|
-
signal.addEventListener("abort", () =>
|
|
14660
|
+
const stopped = new Promise((resolve2) => {
|
|
14661
|
+
signal.addEventListener("abort", () => resolve2(void 0), { once: true });
|
|
13967
14662
|
});
|
|
13968
14663
|
if (signal.aborted) return void 0;
|
|
13969
14664
|
const asked = Promise.resolve().then(() => hook(req, { signal })).then(
|
|
@@ -14018,17 +14713,60 @@ var SealedChannel = class {
|
|
|
14018
14713
|
this.reply = void 0;
|
|
14019
14714
|
}
|
|
14020
14715
|
};
|
|
14716
|
+
function swapOf(reply) {
|
|
14717
|
+
if (!isRecord(reply) || reply.error !== void 0) return void 0;
|
|
14718
|
+
const values = stringRecord(reply.values);
|
|
14719
|
+
const placeholders = stringRecord(reply.placeholders);
|
|
14720
|
+
return values && placeholders ? { values, placeholders } : void 0;
|
|
14721
|
+
}
|
|
14021
14722
|
var SealedBroker = class {
|
|
14022
14723
|
hook;
|
|
14023
14724
|
users;
|
|
14024
14725
|
timeoutMs;
|
|
14025
14726
|
tagOf;
|
|
14727
|
+
tickets = new TicketBook();
|
|
14728
|
+
userOf = async () => void 0;
|
|
14026
14729
|
constructor(hook, users, timeoutMs = SEALED_TIMEOUT_MS, tagOf = () => void 0) {
|
|
14027
14730
|
this.hook = hook;
|
|
14028
14731
|
this.users = users;
|
|
14029
14732
|
this.timeoutMs = timeoutMs;
|
|
14030
14733
|
this.tagOf = tagOf;
|
|
14031
14734
|
}
|
|
14735
|
+
async stamp(ids, cred, tag) {
|
|
14736
|
+
const user = await this.userOf(cred.euid).catch(() => void 0);
|
|
14737
|
+
return {
|
|
14738
|
+
pid: ids.pid,
|
|
14739
|
+
ppid: ids.ppid,
|
|
14740
|
+
uid: cred.euid,
|
|
14741
|
+
gid: cred.egid,
|
|
14742
|
+
...user !== void 0 ? { user } : {},
|
|
14743
|
+
groups: [...cred.groups],
|
|
14744
|
+
users: this.users,
|
|
14745
|
+
...tag !== void 0 ? { tag } : {}
|
|
14746
|
+
};
|
|
14747
|
+
}
|
|
14748
|
+
async consume(fields) {
|
|
14749
|
+
const { hook } = this;
|
|
14750
|
+
if (!hook) return void 0;
|
|
14751
|
+
const control = new AbortController();
|
|
14752
|
+
const timer = setTimeout(() => control.abort("timeout"), this.timeoutMs);
|
|
14753
|
+
const stopped = new Promise((resolve2) => {
|
|
14754
|
+
control.signal.addEventListener("abort", () => resolve2(void 0), { once: true });
|
|
14755
|
+
});
|
|
14756
|
+
const req = {
|
|
14757
|
+
...fields,
|
|
14758
|
+
v: 1,
|
|
14759
|
+
op: "materializeConsume",
|
|
14760
|
+
caller: fields.stamp
|
|
14761
|
+
};
|
|
14762
|
+
try {
|
|
14763
|
+
const asked = Promise.resolve().then(() => hook(req, { signal: control.signal })).then(swapOf, () => void 0);
|
|
14764
|
+
return await Promise.race([asked, stopped]);
|
|
14765
|
+
} finally {
|
|
14766
|
+
clearTimeout(timer);
|
|
14767
|
+
control.abort("done");
|
|
14768
|
+
}
|
|
14769
|
+
}
|
|
14032
14770
|
open() {
|
|
14033
14771
|
const channel = new SealedChannel(this);
|
|
14034
14772
|
return new OpenFile({
|
|
@@ -14527,11 +15265,11 @@ function codeOf3(err) {
|
|
|
14527
15265
|
}
|
|
14528
15266
|
function wait(ms, signal) {
|
|
14529
15267
|
if (signal.aborted) return Promise.resolve();
|
|
14530
|
-
return new Promise((
|
|
15268
|
+
return new Promise((resolve2) => {
|
|
14531
15269
|
const done4 = () => {
|
|
14532
15270
|
clearTimeout(timer);
|
|
14533
15271
|
signal.removeEventListener("abort", done4);
|
|
14534
|
-
|
|
15272
|
+
resolve2();
|
|
14535
15273
|
};
|
|
14536
15274
|
const timer = setTimeout(done4, ms);
|
|
14537
15275
|
signal.addEventListener("abort", done4, { once: true });
|
|
@@ -14858,11 +15596,11 @@ async function resize(h, path, size) {
|
|
|
14858
15596
|
}
|
|
14859
15597
|
}
|
|
14860
15598
|
function sleep(ms, signal) {
|
|
14861
|
-
return new Promise((
|
|
15599
|
+
return new Promise((resolve2) => {
|
|
14862
15600
|
const done4 = () => {
|
|
14863
15601
|
clearTimeout(timer);
|
|
14864
15602
|
signal.removeEventListener("abort", done4);
|
|
14865
|
-
|
|
15603
|
+
resolve2();
|
|
14866
15604
|
};
|
|
14867
15605
|
const timer = setTimeout(done4, ms);
|
|
14868
15606
|
signal.addEventListener("abort", done4);
|
|
@@ -14945,7 +15683,7 @@ var PORT_DEAD_MS = 1e4;
|
|
|
14945
15683
|
var PORT_LINGER_MS = 1e3;
|
|
14946
15684
|
function timed(transport, ms) {
|
|
14947
15685
|
const inflight = /* @__PURE__ */ new Set();
|
|
14948
|
-
const guard2 = (name2, run2, late, expire) => new Promise((
|
|
15686
|
+
const guard2 = (name2, run2, late, expire) => new Promise((resolve2, reject) => {
|
|
14949
15687
|
let expired = false;
|
|
14950
15688
|
const running = run2();
|
|
14951
15689
|
inflight.add(running);
|
|
@@ -14962,7 +15700,7 @@ function timed(transport, ms) {
|
|
|
14962
15700
|
(value) => {
|
|
14963
15701
|
clearTimeout(timer);
|
|
14964
15702
|
if (expired) late?.(value);
|
|
14965
|
-
|
|
15703
|
+
resolve2(value);
|
|
14966
15704
|
},
|
|
14967
15705
|
(err) => {
|
|
14968
15706
|
clearTimeout(timer);
|
|
@@ -15097,7 +15835,7 @@ var PortTransport = class {
|
|
|
15097
15835
|
ask(message3, bytes = 0, transfer = []) {
|
|
15098
15836
|
const id2 = ++this.next;
|
|
15099
15837
|
if (this.dead) return Promise.reject(fsError2("ENOTCONN", this.dead));
|
|
15100
|
-
return new Promise((
|
|
15838
|
+
return new Promise((resolve2, reject) => {
|
|
15101
15839
|
const timer = setTimeout(
|
|
15102
15840
|
() => {
|
|
15103
15841
|
this.waiting.delete(id2);
|
|
@@ -15111,7 +15849,7 @@ var PortTransport = class {
|
|
|
15111
15849
|
clearTimeout(timer);
|
|
15112
15850
|
this.waiting.delete(id2);
|
|
15113
15851
|
if (reply.error) reject(fsError2(reply.error.code ?? "EIO", reply.error.message ?? ""));
|
|
15114
|
-
else
|
|
15852
|
+
else resolve2(reply);
|
|
15115
15853
|
});
|
|
15116
15854
|
try {
|
|
15117
15855
|
this.port.postMessage({ id: id2, ...message3 }, transfer);
|
|
@@ -15140,7 +15878,7 @@ var PortTransport = class {
|
|
|
15140
15878
|
this.fail("the folder's watch ended");
|
|
15141
15879
|
return Promise.resolve();
|
|
15142
15880
|
}
|
|
15143
|
-
return new Promise((
|
|
15881
|
+
return new Promise((resolve2, reject) => {
|
|
15144
15882
|
const finish = () => {
|
|
15145
15883
|
this.waiting.delete(id2);
|
|
15146
15884
|
signal.removeEventListener("abort", stop);
|
|
@@ -15151,7 +15889,7 @@ var PortTransport = class {
|
|
|
15151
15889
|
this.port.postMessage({ id: id2, op: "unwatch" });
|
|
15152
15890
|
} catch {
|
|
15153
15891
|
}
|
|
15154
|
-
|
|
15892
|
+
resolve2();
|
|
15155
15893
|
};
|
|
15156
15894
|
this.watchBeat();
|
|
15157
15895
|
this.waiting.set(id2, (reply) => {
|
|
@@ -15159,7 +15897,7 @@ var PortTransport = class {
|
|
|
15159
15897
|
if (reply.event) return onEvent(reply.event);
|
|
15160
15898
|
finish();
|
|
15161
15899
|
if (reply.error) reject(fsError2(reply.error.code ?? "ENOTCONN", reply.error.message ?? ""));
|
|
15162
|
-
else
|
|
15900
|
+
else resolve2();
|
|
15163
15901
|
});
|
|
15164
15902
|
signal.addEventListener("abort", stop, { once: true });
|
|
15165
15903
|
try {
|
|
@@ -15375,10 +16113,10 @@ var ChunkedBytes = class {
|
|
|
15375
16113
|
}
|
|
15376
16114
|
truncate(size) {
|
|
15377
16115
|
if (size < this.size) {
|
|
15378
|
-
const
|
|
15379
|
-
this.chunks.length = Math.min(this.chunks.length,
|
|
15380
|
-
const tail = this.chunks[
|
|
15381
|
-
const from = size - (
|
|
16116
|
+
const keep2 = Math.ceil(size / CHUNK3);
|
|
16117
|
+
this.chunks.length = Math.min(this.chunks.length, keep2);
|
|
16118
|
+
const tail = this.chunks[keep2 - 1];
|
|
16119
|
+
const from = size - (keep2 - 1) * CHUNK3;
|
|
15382
16120
|
if (tail && from < tail.length) tail.fill(0, from);
|
|
15383
16121
|
}
|
|
15384
16122
|
this.size = size;
|
|
@@ -15623,8 +16361,8 @@ function importedMemory(bytes) {
|
|
|
15623
16361
|
at = end;
|
|
15624
16362
|
continue;
|
|
15625
16363
|
}
|
|
15626
|
-
const
|
|
15627
|
-
for (let i = 0; i <
|
|
16364
|
+
const count2 = u32();
|
|
16365
|
+
for (let i = 0; i < count2; i++) {
|
|
15628
16366
|
const module = name2();
|
|
15629
16367
|
const field3 = name2();
|
|
15630
16368
|
const kind = bytes[at++];
|
|
@@ -16053,7 +16791,7 @@ ${lines2}
|
|
|
16053
16791
|
const withdraw = () => control.abort("withdrawn");
|
|
16054
16792
|
requester.addEventListener("abort", withdraw, { once: true });
|
|
16055
16793
|
const aborted2 = new Promise(
|
|
16056
|
-
(
|
|
16794
|
+
(resolve2) => control.signal.addEventListener("abort", () => resolve2("aborted"), { once: true })
|
|
16057
16795
|
);
|
|
16058
16796
|
try {
|
|
16059
16797
|
const answered = Promise.resolve().then(() => approve(req, { signal: control.signal })).catch(() => "deny");
|
|
@@ -16179,7 +16917,7 @@ var LAYOUT = [
|
|
|
16179
16917
|
];
|
|
16180
16918
|
var LOAD_SAMPLE_MS = 5e3;
|
|
16181
16919
|
var LOAD_WINDOWS_MS = [6e4, 3e5, 9e5];
|
|
16182
|
-
var
|
|
16920
|
+
var encoder7 = new TextEncoder();
|
|
16183
16921
|
var USERS_MODES = ["off", "report", "enforce"];
|
|
16184
16922
|
function usersMode(mode = "off") {
|
|
16185
16923
|
if (USERS_MODES.includes(mode)) return mode;
|
|
@@ -16210,6 +16948,30 @@ function shebang(head) {
|
|
|
16210
16948
|
if (head[0] !== 35 || head[1] !== 33) return void 0;
|
|
16211
16949
|
return new TextDecoder().decode(head.subarray(0, SHEBANG_MAX)).slice(2).split("\n")[0].trim().split(/[ \t]+/);
|
|
16212
16950
|
}
|
|
16951
|
+
async function rootOnly(fs, path) {
|
|
16952
|
+
const names = chain(path);
|
|
16953
|
+
for (const [i, at] of names.entries()) {
|
|
16954
|
+
const st = await fs.lstat(at).catch(() => void 0);
|
|
16955
|
+
const kind = i === names.length - 1 ? st?.isFile : st?.isDirectory;
|
|
16956
|
+
if (!kind || st?.uid !== 0 || (st.mode & 18) !== 0) return false;
|
|
16957
|
+
}
|
|
16958
|
+
return true;
|
|
16959
|
+
}
|
|
16960
|
+
function plainEnv(words2) {
|
|
16961
|
+
const next2 = words2[1];
|
|
16962
|
+
if (baseName(words2[0] ?? "") !== "env" || next2 === void 0) return false;
|
|
16963
|
+
return next2 === "-S" || !next2.startsWith("-") && !next2.includes("=");
|
|
16964
|
+
}
|
|
16965
|
+
function shebangCommand(words2) {
|
|
16966
|
+
if (!plainEnv(words2)) {
|
|
16967
|
+
const arg2 = words2.slice(1).join(" ");
|
|
16968
|
+
return { interp: words2[0], args: arg2 ? [arg2] : [] };
|
|
16969
|
+
}
|
|
16970
|
+
const split2 = words2[1] === "-S";
|
|
16971
|
+
const [interp, ...rest2] = words2.slice(split2 ? 2 : 1);
|
|
16972
|
+
const arg = rest2.join(" ");
|
|
16973
|
+
return { interp, args: split2 ? rest2 : arg ? [arg] : [] };
|
|
16974
|
+
}
|
|
16213
16975
|
function interpreter(head) {
|
|
16214
16976
|
const words2 = shebang(head);
|
|
16215
16977
|
if (!words2) return "node";
|
|
@@ -16263,7 +17025,7 @@ function childHandle(handle) {
|
|
|
16263
17025
|
onState: (listener) => handle.onState(listener)
|
|
16264
17026
|
};
|
|
16265
17027
|
}
|
|
16266
|
-
function
|
|
17028
|
+
function concat6(chunks2) {
|
|
16267
17029
|
const out = new Uint8Array(chunks2.reduce((n, chunk2) => n + chunk2.length, 0));
|
|
16268
17030
|
let offset = 0;
|
|
16269
17031
|
for (const chunk2 of chunks2) {
|
|
@@ -16295,6 +17057,13 @@ var Launcher = class {
|
|
|
16295
17057
|
unwatchScopes = () => void 0;
|
|
16296
17058
|
warned = /* @__PURE__ */ new Set();
|
|
16297
17059
|
compiled = /* @__PURE__ */ new Map();
|
|
17060
|
+
texts = /* @__PURE__ */ new Map();
|
|
17061
|
+
integrity;
|
|
17062
|
+
scannedFs;
|
|
17063
|
+
get scanned() {
|
|
17064
|
+
this.scannedFs ??= this.verifiedFs(this.base);
|
|
17065
|
+
return this.scannedFs;
|
|
17066
|
+
}
|
|
16298
17067
|
processes = /* @__PURE__ */ new Map();
|
|
16299
17068
|
zombies = /* @__PURE__ */ new Set();
|
|
16300
17069
|
orphans = /* @__PURE__ */ new Set();
|
|
@@ -16342,6 +17111,7 @@ var Launcher = class {
|
|
|
16342
17111
|
usersMode;
|
|
16343
17112
|
dac;
|
|
16344
17113
|
sealed;
|
|
17114
|
+
scrubs = /* @__PURE__ */ new Map();
|
|
16345
17115
|
clipboard;
|
|
16346
17116
|
rootFs;
|
|
16347
17117
|
fileWatches;
|
|
@@ -16365,6 +17135,8 @@ var Launcher = class {
|
|
|
16365
17135
|
void 0,
|
|
16366
17136
|
(pid) => this.tagOf(pid)
|
|
16367
17137
|
);
|
|
17138
|
+
this.sealed.tickets.normalise = (path) => physicalPathAsync(this.base, path);
|
|
17139
|
+
this.sealed.userOf = async (uid) => (await this.users.lookup(uid))?.name;
|
|
16368
17140
|
if (this.owners) options.fs.trackOwners?.();
|
|
16369
17141
|
this.mounts = new MountTable({
|
|
16370
17142
|
open: (type, spec) => this.driver(type, spec),
|
|
@@ -16398,7 +17170,7 @@ var Launcher = class {
|
|
|
16398
17170
|
if (options.uplink) this.net.useUplink({ uplink: options.uplink, routes: this.routes });
|
|
16399
17171
|
enableNetwork(this.net, {
|
|
16400
17172
|
transport: this.transport,
|
|
16401
|
-
engine: kernelTlsEngine(packageTlsEngine(options.fs, this.modulesDir)),
|
|
17173
|
+
engine: kernelTlsEngine(packageTlsEngine(this.verifiedFs(options.fs), this.modulesDir)),
|
|
16402
17174
|
ca: this.ca
|
|
16403
17175
|
});
|
|
16404
17176
|
enableCdp(this.net, this.cdp);
|
|
@@ -16433,6 +17205,16 @@ var Launcher = class {
|
|
|
16433
17205
|
this.prefixes = protectedPrefixes(options.protect?.paths ?? []);
|
|
16434
17206
|
this.processFs = this.prefixes.length > 0 ? new ProtectedFs(this.prefixes).view(this.fs) : this.fs;
|
|
16435
17207
|
options.fs.protect?.(this.prefixes);
|
|
17208
|
+
const integrity = options.protect?.integrity;
|
|
17209
|
+
integrityOptions(integrity);
|
|
17210
|
+
if (integrity && this.prefixes.length > 0) {
|
|
17211
|
+
this.integrity = new Integrity(
|
|
17212
|
+
this.prefixes,
|
|
17213
|
+
options.fs,
|
|
17214
|
+
integrity,
|
|
17215
|
+
(event) => options.onDenial?.(event)
|
|
17216
|
+
);
|
|
17217
|
+
}
|
|
16436
17218
|
this.users = new UserDb(this.fs, this.owners);
|
|
16437
17219
|
this.sudoer = new Sudo({
|
|
16438
17220
|
fs: this.fs,
|
|
@@ -16478,7 +17260,7 @@ var Launcher = class {
|
|
|
16478
17260
|
commands(scope = "") {
|
|
16479
17261
|
let found = this.catalogs.get(scope);
|
|
16480
17262
|
if (!found) {
|
|
16481
|
-
found = this.roots(scope).then((roots) => scanCommands(this.
|
|
17263
|
+
found = this.roots(scope).then((roots) => scanCommands(this.scanned, roots)).then((commands) => commands.set(SUDO_COMMAND.name, SUDO_COMMAND));
|
|
16482
17264
|
this.catalogs.set(scope, found);
|
|
16483
17265
|
}
|
|
16484
17266
|
return found;
|
|
@@ -16486,7 +17268,7 @@ var Launcher = class {
|
|
|
16486
17268
|
ownCommands(scope) {
|
|
16487
17269
|
let found = this.ownCatalogs.get(scope);
|
|
16488
17270
|
if (!found) {
|
|
16489
|
-
found = pnpmGlobalRoots(this.base, scope).then((roots) => scanCommands(this.
|
|
17271
|
+
found = pnpmGlobalRoots(this.base, scope).then((roots) => scanCommands(this.scanned, roots)).then((commands) => new Set(commands.keys()));
|
|
16490
17272
|
this.ownCatalogs.set(scope, found);
|
|
16491
17273
|
}
|
|
16492
17274
|
return found;
|
|
@@ -16495,7 +17277,7 @@ var Launcher = class {
|
|
|
16495
17277
|
let found = this.binfmts.get(scope);
|
|
16496
17278
|
if (!found) {
|
|
16497
17279
|
found = this.roots(scope).then(
|
|
16498
|
-
(roots) => scanBinfmts(this.
|
|
17280
|
+
(roots) => scanBinfmts(this.scanned, roots, (message3) => {
|
|
16499
17281
|
if (this.warned.has(message3)) return;
|
|
16500
17282
|
this.warned.add(message3);
|
|
16501
17283
|
console.warn(message3);
|
|
@@ -16603,21 +17385,26 @@ var Launcher = class {
|
|
|
16603
17385
|
const binfmt = isWasm(head) ? void 0 : binfmtOf(await this.interpreters(scope), script);
|
|
16604
17386
|
const words2 = shebang(head) ?? (binfmt ? [binfmt] : void 0);
|
|
16605
17387
|
if (!words2) return void 0;
|
|
16606
|
-
|
|
16607
|
-
const [
|
|
16608
|
-
|
|
16609
|
-
const
|
|
16610
|
-
const found = interp ? await this.resolve(interp, interp, cwd, scope) : void 0;
|
|
17388
|
+
const { interp, args } = shebangCommand(words2);
|
|
17389
|
+
const passed = [...args, command ? script : file, ...argv.slice(1)];
|
|
17390
|
+
if (!interp) return void 0;
|
|
17391
|
+
const found = await this.resolve(interp, interp, cwd, scope);
|
|
16611
17392
|
if (!found) {
|
|
16612
|
-
if (
|
|
17393
|
+
if (depth >= MAX_INTERPRETERS || !await this.scriptCommand(interp, cwd, scope)) {
|
|
16613
17394
|
return void 0;
|
|
16614
17395
|
}
|
|
16615
17396
|
const inner = await this.interpreted(interp, [interp, ...passed], cwd, scope, depth + 1);
|
|
16616
17397
|
const via = [interp, ...inner?.via ?? []];
|
|
16617
|
-
|
|
17398
|
+
const sources = [script, ...inner?.sources ?? []];
|
|
17399
|
+
return inner && { ...inner, target: withScriptEnv(inner.target, command), via, sources };
|
|
16618
17400
|
}
|
|
16619
17401
|
const target = withScriptEnv(found, command);
|
|
16620
|
-
return {
|
|
17402
|
+
return {
|
|
17403
|
+
target,
|
|
17404
|
+
args: [...target.prefix ?? [], ...passed],
|
|
17405
|
+
via: [interp],
|
|
17406
|
+
sources: [script]
|
|
17407
|
+
};
|
|
16621
17408
|
}
|
|
16622
17409
|
async plan(file, argv, cwd, scope, argv0) {
|
|
16623
17410
|
const found = await this.resolve(file, argv[0] ?? file, cwd, scope);
|
|
@@ -16643,13 +17430,106 @@ var Launcher = class {
|
|
|
16643
17430
|
await need(g, "exec", this.fs.resolvePath(req.cwd, file), want);
|
|
16644
17431
|
}
|
|
16645
17432
|
}
|
|
17433
|
+
async checkedPlan(req, { ppid, cred, scope }, fds) {
|
|
17434
|
+
const planned = await this.plan(req.file, req.argv, req.cwd, scope, req.argv0);
|
|
17435
|
+
try {
|
|
17436
|
+
await this.mayExec(cred, { ...req, pid: ppid }, planned);
|
|
17437
|
+
} catch (err) {
|
|
17438
|
+
await fds.closeAll();
|
|
17439
|
+
throw err.code === "EACCES" ? new SpawnError("EACCES") : err;
|
|
17440
|
+
}
|
|
17441
|
+
if (planned) return planned;
|
|
17442
|
+
await fds.closeAll();
|
|
17443
|
+
throw new SpawnError(await this.unrunnable(req.file, req.cwd, scope));
|
|
17444
|
+
}
|
|
17445
|
+
async ticketFile(req, fds) {
|
|
17446
|
+
if (req.file.startsWith("/")) return physicalPathAsync(this.base, req.file);
|
|
17447
|
+
await fds.closeAll();
|
|
17448
|
+
throw new SpawnError("EACCES");
|
|
17449
|
+
}
|
|
17450
|
+
takeTicket(req) {
|
|
17451
|
+
if (!(TICKET_ENV in req.env)) return void 0;
|
|
17452
|
+
return this.sealed.tickets.take(req.env[TICKET_ENV] ?? "") ?? null;
|
|
17453
|
+
}
|
|
17454
|
+
async redeemed(req, who, planned, fds) {
|
|
17455
|
+
if (who.booked === void 0) return { env: req.env };
|
|
17456
|
+
try {
|
|
17457
|
+
const redeemed = await this.redeem(req, who, planned);
|
|
17458
|
+
if (redeemed.scrub) scrubStdio(fds, redeemed.scrub);
|
|
17459
|
+
return redeemed;
|
|
17460
|
+
} catch {
|
|
17461
|
+
await fds.closeAll();
|
|
17462
|
+
throw new SpawnError("EACCES");
|
|
17463
|
+
}
|
|
17464
|
+
}
|
|
17465
|
+
async redeem(req, { ppid, cred, scope, booked }, planned) {
|
|
17466
|
+
const { [TICKET_ENV]: ticket = "", ...env } = req.env;
|
|
17467
|
+
const caller = this.jobs.shown(ppid);
|
|
17468
|
+
const named = typeof req.argv0 === "string" && !planned.script && !planned.target.fixed;
|
|
17469
|
+
const argv = named ? [req.argv0, ...req.argv.slice(1)] : req.argv;
|
|
17470
|
+
const exact = booked?.pid === caller && req.file === booked.path && await physicalPathAsync(this.base, req.cwd) === booked.cwd && await argvDigest(argv) === booked.argv;
|
|
17471
|
+
if (!exact) throw new SpawnError("EACCES");
|
|
17472
|
+
const { requested: path, requestedCwd: cwd, path: resolved2 } = booked;
|
|
17473
|
+
const pid = this.nextPid++;
|
|
17474
|
+
const ids = { pid: req.exec ? caller : pid, ppid: caller };
|
|
17475
|
+
const stamp = await this.sealed.stamp(ids, cred, this.tagOf(ppid));
|
|
17476
|
+
const interpreters = await this.interpreterChain(planned, booked.cwd, scope);
|
|
17477
|
+
const trusted = this.usersMode === "enforce" && await this.trustedFiles(planned, req.file, booked.cwd, scope);
|
|
17478
|
+
const swap = await this.sealed.consume({
|
|
17479
|
+
ticket,
|
|
17480
|
+
stamp,
|
|
17481
|
+
path,
|
|
17482
|
+
resolved: resolved2,
|
|
17483
|
+
argv,
|
|
17484
|
+
cwd,
|
|
17485
|
+
interpreters,
|
|
17486
|
+
trusted
|
|
17487
|
+
});
|
|
17488
|
+
if (!swap) throw new SpawnError("EACCES");
|
|
17489
|
+
const secrets = [];
|
|
17490
|
+
for (const [name2, placeholder] of Object.entries(swap.placeholders)) {
|
|
17491
|
+
const value = swap.values[name2];
|
|
17492
|
+
if (value === void 0 || env[name2] !== placeholder) continue;
|
|
17493
|
+
env[name2] = value;
|
|
17494
|
+
secrets.push({ name: name2, value, placeholder });
|
|
17495
|
+
}
|
|
17496
|
+
const scrub = scrubberOf(secrets);
|
|
17497
|
+
return { env, pid, cwd: booked.cwd, ...scrub ? { scrub } : {} };
|
|
17498
|
+
}
|
|
17499
|
+
async interpreterChain(planned, cwd, scope) {
|
|
17500
|
+
const out = [];
|
|
17501
|
+
for (const entry of planned.via ?? []) {
|
|
17502
|
+
if (entry.includes("/")) {
|
|
17503
|
+
out.push(await physicalPathAsync(this.base, this.fs.resolvePath(cwd, entry)));
|
|
17504
|
+
continue;
|
|
17505
|
+
}
|
|
17506
|
+
const script = (await this.scriptCommand(entry, cwd, scope))?.script;
|
|
17507
|
+
out.push(
|
|
17508
|
+
await physicalPathAsync(this.base, script ?? await this.commandFile(entry, cwd, scope))
|
|
17509
|
+
);
|
|
17510
|
+
}
|
|
17511
|
+
return out;
|
|
17512
|
+
}
|
|
17513
|
+
async commandFile(name2, cwd, scope) {
|
|
17514
|
+
const scoped = await this.resolve(name2, name2, cwd, scope);
|
|
17515
|
+
const system = scope === "" ? scoped : await this.resolve(name2, name2, cwd, "");
|
|
17516
|
+
if (!scoped || system?.glue === scoped.glue) return `/usr/bin/${name2}`;
|
|
17517
|
+
return scoped.glue;
|
|
17518
|
+
}
|
|
17519
|
+
async trustedFiles(planned, file, cwd, scope) {
|
|
17520
|
+
const files = await this.backingOf(planned, file, cwd, scope);
|
|
17521
|
+
for (const at of files) {
|
|
17522
|
+
if (!await rootOnly(this.base, await physicalPathAsync(this.base, at))) return false;
|
|
17523
|
+
}
|
|
17524
|
+
return true;
|
|
17525
|
+
}
|
|
16646
17526
|
async grantedCwd(caller, cwd) {
|
|
16647
17527
|
const real = await physicalPathAsync(this.base, cwd).catch(() => void 0);
|
|
16648
17528
|
return real === cwd && await this.sudoer.reachable(caller, cwd);
|
|
16649
17529
|
}
|
|
16650
17530
|
async binfmtManifests(scripts) {
|
|
16651
17531
|
const sources = /* @__PURE__ */ new Map();
|
|
16652
|
-
const binfmts = await scanBinfmts(this.
|
|
17532
|
+
const binfmts = await scanBinfmts(this.scanned, await this.roots(), void 0, sources);
|
|
16653
17533
|
const manifests = [];
|
|
16654
17534
|
for (const script of scripts) {
|
|
16655
17535
|
const head = await this.head(script);
|
|
@@ -16661,13 +17541,15 @@ var Launcher = class {
|
|
|
16661
17541
|
}
|
|
16662
17542
|
async backing(file, cwd) {
|
|
16663
17543
|
const planned = await this.plan(file, [file], cwd, "");
|
|
16664
|
-
|
|
17544
|
+
return planned && this.backingOf(planned, file, cwd, "");
|
|
17545
|
+
}
|
|
17546
|
+
async backingOf(planned, file, cwd, scope) {
|
|
16665
17547
|
const { glue, wasm, imports } = planned.target;
|
|
16666
17548
|
const files = [glue, wasm, ...imports ? [imports] : []];
|
|
16667
17549
|
const scripts = [];
|
|
16668
17550
|
for (const name2 of [file, ...planned.via ?? []]) {
|
|
16669
17551
|
if (name2.includes("/") && !COMMAND2.test(name2)) scripts.push(this.fs.resolvePath(cwd, name2));
|
|
16670
|
-
const script = (await this.scriptCommand(name2, cwd,
|
|
17552
|
+
const script = (await this.scriptCommand(name2, cwd, scope))?.script;
|
|
16671
17553
|
if (script) scripts.push(script);
|
|
16672
17554
|
}
|
|
16673
17555
|
files.push(...scripts, ...await this.binfmtManifests(scripts));
|
|
@@ -16684,25 +17566,72 @@ var Launcher = class {
|
|
|
16684
17566
|
if (!head || head[0] === 35 && head[1] === 33) return "ENOENT";
|
|
16685
17567
|
return binfmtOf(await this.interpreters(scope), path) ? "ENOENT" : "ENOEXEC";
|
|
16686
17568
|
}
|
|
16687
|
-
|
|
16688
|
-
|
|
16689
|
-
|
|
16690
|
-
|
|
16691
|
-
|
|
16692
|
-
|
|
16693
|
-
|
|
16694
|
-
|
|
16695
|
-
|
|
16696
|
-
|
|
16697
|
-
|
|
16698
|
-
|
|
16699
|
-
|
|
16700
|
-
|
|
16701
|
-
|
|
16702
|
-
|
|
16703
|
-
|
|
16704
|
-
|
|
16705
|
-
|
|
17569
|
+
verifiedFs(fs) {
|
|
17570
|
+
const read = (path) => this.verified(path, KERNEL_CALLER, fs);
|
|
17571
|
+
return Object.assign(Object.create(fs), {
|
|
17572
|
+
readFileBuffer: read,
|
|
17573
|
+
readFile: (path) => this.integrity ? this.text(path, KERNEL_CALLER) : fs.readFile(path)
|
|
17574
|
+
});
|
|
17575
|
+
}
|
|
17576
|
+
async cacheKey(path) {
|
|
17577
|
+
const verified = await this.integrity?.key(path);
|
|
17578
|
+
if (verified !== void 0) return verified;
|
|
17579
|
+
const st = await this.base.stat(path);
|
|
17580
|
+
return `${path}:${st.size}:${st.mtime.getTime()}`;
|
|
17581
|
+
}
|
|
17582
|
+
verified(path, caller, fs = this.base) {
|
|
17583
|
+
const plain = () => fs.readFileBuffer(path);
|
|
17584
|
+
return this.integrity ? this.integrity.read(path, caller, plain) : plain();
|
|
17585
|
+
}
|
|
17586
|
+
keyed(path, caller) {
|
|
17587
|
+
const plain = async () => this.base.readFileBuffer(path);
|
|
17588
|
+
return this.integrity ? this.integrity.readKeyed(path, caller, plain) : plain().then((bytes) => ({ bytes }));
|
|
17589
|
+
}
|
|
17590
|
+
cached(cache, key, load) {
|
|
17591
|
+
const hit = cache.get(key);
|
|
17592
|
+
if (hit !== void 0) return hit;
|
|
17593
|
+
const loading2 = load();
|
|
17594
|
+
const found = loading2.then(({ value }) => value);
|
|
17595
|
+
cache.set(key, found);
|
|
17596
|
+
loading2.then(
|
|
17597
|
+
(done4) => {
|
|
17598
|
+
if (done4.key === key || done4.key === void 0 && !key.includes("\0")) return;
|
|
17599
|
+
if (cache.get(key) === found) cache.delete(key);
|
|
17600
|
+
if (done4.key !== void 0) cache.set(done4.key, found);
|
|
17601
|
+
},
|
|
17602
|
+
() => cache.delete(key)
|
|
17603
|
+
);
|
|
17604
|
+
return found;
|
|
17605
|
+
}
|
|
17606
|
+
async module(path, caller) {
|
|
17607
|
+
const key = await this.cacheKey(path);
|
|
17608
|
+
return this.cached(this.compiled, key, async () => {
|
|
17609
|
+
const { bytes, key: read } = await this.keyed(path, caller);
|
|
17610
|
+
const memory = importedMemory(bytes);
|
|
17611
|
+
const foreign = foreignImports(bytes);
|
|
17612
|
+
const compiled = await WebAssembly.compile(bytes);
|
|
17613
|
+
return { value: { module: compiled, ...memory ? { memory } : {}, foreign }, key: read };
|
|
17614
|
+
});
|
|
17615
|
+
}
|
|
17616
|
+
async checkedSources(planned, caller) {
|
|
17617
|
+
const { target } = planned;
|
|
17618
|
+
const files = [target.wasm, target.glue, target.imports, ...planned.sources ?? []];
|
|
17619
|
+
const manifests = /* @__PURE__ */ new Set();
|
|
17620
|
+
for (const file of files) {
|
|
17621
|
+
const root = file === void 0 ? void 0 : PACKAGE_ROOT.exec(file)?.[1];
|
|
17622
|
+
if (root !== void 0) manifests.add(`${root}/package.json`);
|
|
17623
|
+
}
|
|
17624
|
+
for (const source of planned.sources ?? []) await this.text(source, caller);
|
|
17625
|
+
for (const manifest of manifests) {
|
|
17626
|
+
if (await this.base.exists(manifest)) await this.text(manifest, caller);
|
|
17627
|
+
}
|
|
17628
|
+
}
|
|
17629
|
+
async text(path, caller) {
|
|
17630
|
+
if (!this.integrity) return this.base.readFile(path);
|
|
17631
|
+
const key = await this.cacheKey(path);
|
|
17632
|
+
return this.cached(this.texts, key, async () => {
|
|
17633
|
+
const { bytes, key: read } = await this.keyed(path, caller);
|
|
17634
|
+
return { value: new TextDecoder().decode(bytes), key: read };
|
|
16706
17635
|
});
|
|
16707
17636
|
}
|
|
16708
17637
|
async names(path, module) {
|
|
@@ -16714,23 +17643,23 @@ var Launcher = class {
|
|
|
16714
17643
|
const optional = `${root}-names${beside.slice(root.length)}`;
|
|
16715
17644
|
return await this.base.exists(optional) ? optional : void 0;
|
|
16716
17645
|
}
|
|
16717
|
-
async program(target, env) {
|
|
17646
|
+
async program(target, env, caller) {
|
|
16718
17647
|
if (target.abi === "js" && target.wasm === SUDO_PATH) {
|
|
16719
17648
|
return { abi: "js", glue: SUDO_SOURCE, path: SUDO_PATH };
|
|
16720
17649
|
}
|
|
16721
17650
|
if (target.abi === "js") {
|
|
16722
|
-
return { abi: "js", glue: await this.
|
|
17651
|
+
return { abi: "js", glue: await this.text(target.wasm, caller), path: target.wasm };
|
|
16723
17652
|
}
|
|
16724
17653
|
if (target.abi !== "wasi") {
|
|
16725
17654
|
const [glue, { module: module2 }] = await Promise.all([
|
|
16726
|
-
this.
|
|
16727
|
-
this.module(target.wasm)
|
|
17655
|
+
this.text(target.glue, caller),
|
|
17656
|
+
this.module(target.wasm, caller)
|
|
16728
17657
|
]);
|
|
16729
17658
|
return { glue, module: module2 };
|
|
16730
17659
|
}
|
|
16731
|
-
const { module, memory, foreign } = await this.module(target.wasm);
|
|
17660
|
+
const { module, memory, foreign } = await this.module(target.wasm, caller);
|
|
16732
17661
|
const names = env.SLICC_WASM_BACKTRACE === "1" ? await this.names(target.wasm, module) : void 0;
|
|
16733
|
-
const imports = target.imports ? await this.
|
|
17662
|
+
const imports = target.imports ? await this.text(target.imports, caller) : void 0;
|
|
16734
17663
|
return {
|
|
16735
17664
|
abi: "wasi",
|
|
16736
17665
|
glue: "",
|
|
@@ -16749,10 +17678,13 @@ var Launcher = class {
|
|
|
16749
17678
|
const seen = { ...req.env, HOSTNAME: this.hostname, cwd };
|
|
16750
17679
|
const env = { ...expandDefaults(target.env, seen), ...req.env };
|
|
16751
17680
|
for (const key of target.unset ?? []) delete env[key];
|
|
17681
|
+
delete env[TICKET_ENV];
|
|
16752
17682
|
env.HOSTNAME = this.hostname;
|
|
16753
17683
|
let program;
|
|
16754
17684
|
try {
|
|
16755
|
-
|
|
17685
|
+
const caller = { pid: req.ppid ?? 0, uid: req.cred.euid, tag: req.tag };
|
|
17686
|
+
if (this.integrity) await this.checkedSources(planned, caller);
|
|
17687
|
+
program = await this.program(target, env, caller);
|
|
16756
17688
|
} catch (err) {
|
|
16757
17689
|
await req.fds.closeAll();
|
|
16758
17690
|
throw err;
|
|
@@ -16762,16 +17694,23 @@ var Launcher = class {
|
|
|
16762
17694
|
groupSession(pgid) {
|
|
16763
17695
|
return this.jobs.sessionOf(pgid);
|
|
16764
17696
|
}
|
|
17697
|
+
treeScrubs(pid, req) {
|
|
17698
|
+
const inherited2 = req.ppid !== void 0 ? this.scrubs.get(req.ppid) : void 0;
|
|
17699
|
+
const scrubs = req.scrub ? [...inherited2 ?? [], req.scrub] : inherited2;
|
|
17700
|
+
if (scrubs) this.scrubs.set(pid, scrubs);
|
|
17701
|
+
return scrubs;
|
|
17702
|
+
}
|
|
16765
17703
|
start(req) {
|
|
16766
17704
|
this.sampling();
|
|
16767
17705
|
if (req.pgid !== void 0 && this.jobs.sessionOf(req.pgid) === void 0) {
|
|
16768
17706
|
throw new SpawnError("ESRCH");
|
|
16769
17707
|
}
|
|
16770
|
-
const pid = this.nextPid++;
|
|
17708
|
+
const pid = req.pid ?? this.nextPid++;
|
|
17709
|
+
const scrubs = this.treeScrubs(pid, req);
|
|
16771
17710
|
const stack = this.stackFor(req.tag);
|
|
16772
17711
|
const terminal = req.fds.stdioTerminal();
|
|
16773
17712
|
const decided = req.decided ?? Promise.resolve(true);
|
|
16774
|
-
const
|
|
17713
|
+
const count2 = (byParent) => {
|
|
16775
17714
|
this.identities.asked++;
|
|
16776
17715
|
if (!byParent) this.identities.timedOut++;
|
|
16777
17716
|
};
|
|
@@ -16779,7 +17718,7 @@ var Launcher = class {
|
|
|
16779
17718
|
const handle = spawnWasmProcess({
|
|
16780
17719
|
pid,
|
|
16781
17720
|
identity: async () => {
|
|
16782
|
-
await (asked ??= decided.then(
|
|
17721
|
+
await (asked ??= decided.then(count2));
|
|
16783
17722
|
return this.identityOf(pid);
|
|
16784
17723
|
},
|
|
16785
17724
|
onSyscall: (call) => {
|
|
@@ -16793,6 +17732,7 @@ var Launcher = class {
|
|
|
16793
17732
|
...this.owners && req.program.abi === "wasi" ? { fresh: (path, umask) => this.freshModules.add(path, umask, pid) } : {},
|
|
16794
17733
|
...this.owners ? { owners: true } : {},
|
|
16795
17734
|
sealed: this.sealed,
|
|
17735
|
+
...scrubs ? { ttyFilter: (file) => scrubbedAll(file, scrubs) } : {},
|
|
16796
17736
|
clipboard: this.clipboard,
|
|
16797
17737
|
...this.dac ? { dac: this.dac } : {},
|
|
16798
17738
|
program: req.program,
|
|
@@ -16859,6 +17799,7 @@ var Launcher = class {
|
|
|
16859
17799
|
if (spent) addTimes(this.imageTimes, this.jobs.shown(pid), spent);
|
|
16860
17800
|
this.stopped.delete(pid);
|
|
16861
17801
|
this.processes.delete(pid);
|
|
17802
|
+
this.scrubs.delete(pid);
|
|
16862
17803
|
if (this.processes.size === 0) {
|
|
16863
17804
|
clearInterval(this.sampler);
|
|
16864
17805
|
this.sampler = void 0;
|
|
@@ -17030,6 +17971,7 @@ var Launcher = class {
|
|
|
17030
17971
|
}
|
|
17031
17972
|
spawner(ppid, report) {
|
|
17032
17973
|
return async (req, fds, grant) => {
|
|
17974
|
+
const booked = this.takeTicket(req);
|
|
17033
17975
|
if (grant && !await this.grantedCwd(grant.caller, req.cwd)) {
|
|
17034
17976
|
await fds.closeAll();
|
|
17035
17977
|
throw new SpawnError("EACCES");
|
|
@@ -17041,23 +17983,20 @@ var Launcher = class {
|
|
|
17041
17983
|
}
|
|
17042
17984
|
const cred = grant ? grant.cred : this.credOf(ppid);
|
|
17043
17985
|
const scope = grant ? "" : await this.scopeOf(cred.euid);
|
|
17044
|
-
const
|
|
17045
|
-
|
|
17046
|
-
|
|
17047
|
-
|
|
17048
|
-
|
|
17049
|
-
|
|
17050
|
-
|
|
17051
|
-
|
|
17052
|
-
await fds.closeAll();
|
|
17053
|
-
throw new SpawnError(await this.unrunnable(req.file, req.cwd, scope));
|
|
17054
|
-
}
|
|
17986
|
+
const file = booked === void 0 ? req.file : await this.ticketFile(req, fds);
|
|
17987
|
+
const planned = await this.checkedPlan({ ...req, file }, { ppid, cred, scope }, fds);
|
|
17988
|
+
const redeemed = await this.redeemed(
|
|
17989
|
+
{ ...req, file },
|
|
17990
|
+
{ ppid, cred, scope, booked },
|
|
17991
|
+
planned,
|
|
17992
|
+
fds
|
|
17993
|
+
);
|
|
17055
17994
|
const exec = req.exec ? { exec: true } : {};
|
|
17056
17995
|
const deciding = req.exec ? void 0 : this.undecided(ppid);
|
|
17057
17996
|
const handle = await this.launch(planned, {
|
|
17058
17997
|
...deciding ? { decided: deciding.promise } : {},
|
|
17059
|
-
env: req.env,
|
|
17060
17998
|
cwd: req.cwd,
|
|
17999
|
+
...redeemed,
|
|
17061
18000
|
fds,
|
|
17062
18001
|
report,
|
|
17063
18002
|
ppid,
|
|
@@ -17110,11 +18049,11 @@ var Launcher = class {
|
|
|
17110
18049
|
if (!waiting) return;
|
|
17111
18050
|
this.deciding.delete(ppid);
|
|
17112
18051
|
setTimeout(() => {
|
|
17113
|
-
for (const
|
|
18052
|
+
for (const resolve2 of waiting) resolve2(true);
|
|
17114
18053
|
}, 0);
|
|
17115
18054
|
}
|
|
17116
18055
|
forker(ppid, parent) {
|
|
17117
|
-
const { exec: _exec, pgid: _pgid, group: _group, ...image } = parent;
|
|
18056
|
+
const { exec: _exec, pgid: _pgid, group: _group, pid: _pid, scrub: _scrub, ...image } = parent;
|
|
17118
18057
|
return async (state, fds) => childHandle(
|
|
17119
18058
|
this.start({
|
|
17120
18059
|
...image,
|
|
@@ -17244,11 +18183,14 @@ var Launcher = class {
|
|
|
17244
18183
|
}
|
|
17245
18184
|
};
|
|
17246
18185
|
}
|
|
17247
|
-
const module = (await scanFilesystems(this.
|
|
18186
|
+
const module = (await scanFilesystems(this.scanned, await this.roots())).get(type);
|
|
17248
18187
|
if (!module) throw fsError("ENODEV", `unknown file system type ${type}`);
|
|
17249
18188
|
if (!this.createDriverWorker)
|
|
17250
18189
|
throw fsError("ENODEV", `this kernel cannot start ${type} drivers`);
|
|
17251
|
-
const
|
|
18190
|
+
const root = PACKAGE_ROOT.exec(module)?.[1];
|
|
18191
|
+
if (this.integrity && root !== void 0)
|
|
18192
|
+
await this.text(`${root}/package.json`, KERNEL_CALLER);
|
|
18193
|
+
const code = new TextDecoder().decode(await this.verified(module, KERNEL_CALLER));
|
|
17252
18194
|
const worker = this.createDriverWorker();
|
|
17253
18195
|
const driver = new MessageChannel();
|
|
17254
18196
|
const client = new MessageChannel();
|
|
@@ -17488,7 +18430,14 @@ var Launcher = class {
|
|
|
17488
18430
|
);
|
|
17489
18431
|
}
|
|
17490
18432
|
environment(cwd, extra, account) {
|
|
17491
|
-
|
|
18433
|
+
const env = {
|
|
18434
|
+
...this.env,
|
|
18435
|
+
...userEnv(account, this.pnpmHome),
|
|
18436
|
+
PWD: cwd,
|
|
18437
|
+
...extra
|
|
18438
|
+
};
|
|
18439
|
+
delete env[TICKET_ENV];
|
|
18440
|
+
return env;
|
|
17492
18441
|
}
|
|
17493
18442
|
async account(user) {
|
|
17494
18443
|
if (typeof user === "object") return user;
|
|
@@ -17584,7 +18533,7 @@ var Launcher = class {
|
|
|
17584
18533
|
},
|
|
17585
18534
|
account
|
|
17586
18535
|
);
|
|
17587
|
-
const report = (message3) => options.onData(
|
|
18536
|
+
const report = (message3) => options.onData(encoder7.encode(`${message3}\r
|
|
17588
18537
|
`));
|
|
17589
18538
|
const handle = await this.launch(planned, {
|
|
17590
18539
|
env,
|
|
@@ -17615,18 +18564,18 @@ var Launcher = class {
|
|
|
17615
18564
|
const env = this.environment(cwd, options.env, account);
|
|
17616
18565
|
const out = [];
|
|
17617
18566
|
const err = [];
|
|
17618
|
-
const
|
|
18567
|
+
const keep2 = options.collect !== false;
|
|
17619
18568
|
const collect = (chunks2, tee) => (bytes) => {
|
|
17620
|
-
if (
|
|
18569
|
+
if (keep2) chunks2.push(bytes);
|
|
17621
18570
|
tee?.(bytes);
|
|
17622
18571
|
};
|
|
17623
18572
|
const stdout = collect(out, options.onStdout);
|
|
17624
18573
|
const stderr = collect(err, options.onStderr);
|
|
17625
|
-
const report = (message3) => stderr(
|
|
18574
|
+
const report = (message3) => stderr(encoder7.encode(`${message3}
|
|
17626
18575
|
`));
|
|
17627
18576
|
if (!planned) {
|
|
17628
18577
|
report(`${file}: command not found`);
|
|
17629
|
-
return { status: NOT_FOUND, stdout:
|
|
18578
|
+
return { status: NOT_FOUND, stdout: concat6(out), stderr: concat6(err) };
|
|
17630
18579
|
}
|
|
17631
18580
|
const fds = new FdTable();
|
|
17632
18581
|
fds.installAt(0, options.stdin ? bytesSource(options.stdin) : deviceFile("null"));
|
|
@@ -17644,7 +18593,7 @@ var Launcher = class {
|
|
|
17644
18593
|
options.onStarted?.(handle.pid);
|
|
17645
18594
|
const status = await handle.exited;
|
|
17646
18595
|
const signal = handle.termsig();
|
|
17647
|
-
return { status, ...signal ? { signal } : {}, stdout:
|
|
18596
|
+
return { status, ...signal ? { signal } : {}, stdout: concat6(out), stderr: concat6(err) };
|
|
17648
18597
|
}
|
|
17649
18598
|
};
|
|
17650
18599
|
|
|
@@ -17713,10 +18662,10 @@ var Chunks = class {
|
|
|
17713
18662
|
this.grow(size);
|
|
17714
18663
|
return;
|
|
17715
18664
|
}
|
|
17716
|
-
const
|
|
17717
|
-
this.blobs.length =
|
|
17718
|
-
if (
|
|
17719
|
-
this.blobs[
|
|
18665
|
+
const keep2 = Math.ceil(size / CHUNK4);
|
|
18666
|
+
this.blobs.length = keep2;
|
|
18667
|
+
if (keep2 > 0)
|
|
18668
|
+
this.blobs[keep2 - 1] = this.blobs[keep2 - 1].slice(0, size - (keep2 - 1) * CHUNK4);
|
|
17720
18669
|
this.size = size;
|
|
17721
18670
|
}
|
|
17722
18671
|
};
|
|
@@ -17865,12 +18814,12 @@ function codeOf5(e) {
|
|
|
17865
18814
|
return typeof code === "string" ? code : void 0;
|
|
17866
18815
|
}
|
|
17867
18816
|
function probe(secure, host, port, ms) {
|
|
17868
|
-
return new Promise((
|
|
18817
|
+
return new Promise((resolve2) => {
|
|
17869
18818
|
const socket = secure ? tls({ host, port, ...isIP(host) ? {} : { servername: host } }) : tcp({ host, port });
|
|
17870
18819
|
const done4 = (code) => {
|
|
17871
18820
|
clearTimeout(timer);
|
|
17872
18821
|
socket.destroy();
|
|
17873
|
-
|
|
18822
|
+
resolve2(code);
|
|
17874
18823
|
};
|
|
17875
18824
|
const timer = setTimeout(() => done4("ETIMEDOUT"), ms);
|
|
17876
18825
|
socket.once(secure ? "secureConnect" : "connect", () => done4("EPROTO"));
|
|
@@ -17962,8 +18911,8 @@ var RemoteTransport = class {
|
|
|
17962
18911
|
}
|
|
17963
18912
|
ask(call) {
|
|
17964
18913
|
if (this.failure) return Promise.reject(this.failure);
|
|
17965
|
-
return new Promise((
|
|
17966
|
-
this.waiting.set(call.nid, { resolve, reject });
|
|
18914
|
+
return new Promise((resolve2, reject) => {
|
|
18915
|
+
this.waiting.set(call.nid, { resolve: resolve2, reject });
|
|
17967
18916
|
this.port.postMessage(call);
|
|
17968
18917
|
});
|
|
17969
18918
|
}
|
|
@@ -18143,7 +19092,7 @@ function dialStream(port, onClose) {
|
|
|
18143
19092
|
write(chunk2) {
|
|
18144
19093
|
if (closed) throw errorWithCode("EPIPE: the connection is closed");
|
|
18145
19094
|
const copy = fresh(chunk2);
|
|
18146
|
-
const acked = new Promise((
|
|
19095
|
+
const acked = new Promise((resolve2, reject) => acks.push({ resolve: resolve2, reject }));
|
|
18147
19096
|
port.postMessage(copy, [copy.buffer]);
|
|
18148
19097
|
return acked;
|
|
18149
19098
|
},
|
|
@@ -18163,7 +19112,7 @@ function dialStream(port, onClose) {
|
|
|
18163
19112
|
|
|
18164
19113
|
// src/client/loopback-fetch.ts
|
|
18165
19114
|
var OWN_HEADERS = /* @__PURE__ */ new Set(["host", "connection", "content-length", "transfer-encoding"]);
|
|
18166
|
-
var
|
|
19115
|
+
var encoder8 = new TextEncoder();
|
|
18167
19116
|
var latin12 = new TextDecoder("latin1");
|
|
18168
19117
|
var ByteSource = class {
|
|
18169
19118
|
buffered = new Uint8Array(0);
|
|
@@ -18181,7 +19130,7 @@ var ByteSource = class {
|
|
|
18181
19130
|
return true;
|
|
18182
19131
|
}
|
|
18183
19132
|
async until(marker) {
|
|
18184
|
-
const needle =
|
|
19133
|
+
const needle = encoder8.encode(marker);
|
|
18185
19134
|
for (; ; ) {
|
|
18186
19135
|
const at = indexOf(this.buffered, needle);
|
|
18187
19136
|
if (at >= 0) return this.take(at + needle.length);
|
|
@@ -18192,13 +19141,13 @@ var ByteSource = class {
|
|
|
18192
19141
|
if (this.buffered.length === 0 && !await this.more()) return void 0;
|
|
18193
19142
|
return this.take(Math.min(max, this.buffered.length));
|
|
18194
19143
|
}
|
|
18195
|
-
async exactly(
|
|
18196
|
-
while (this.buffered.length <
|
|
18197
|
-
return this.take(
|
|
19144
|
+
async exactly(count2) {
|
|
19145
|
+
while (this.buffered.length < count2) if (!await this.more()) return void 0;
|
|
19146
|
+
return this.take(count2);
|
|
18198
19147
|
}
|
|
18199
|
-
take(
|
|
18200
|
-
const out = this.buffered.slice(0,
|
|
18201
|
-
this.buffered = this.buffered.slice(
|
|
19148
|
+
take(count2) {
|
|
19149
|
+
const out = this.buffered.slice(0, count2);
|
|
19150
|
+
this.buffered = this.buffered.slice(count2);
|
|
18202
19151
|
return out;
|
|
18203
19152
|
}
|
|
18204
19153
|
};
|
|
@@ -18289,7 +19238,7 @@ async function requestBytes(request) {
|
|
|
18289
19238
|
});
|
|
18290
19239
|
if (body) lines2.push(`Content-Length: ${body.length}`);
|
|
18291
19240
|
lines2.push("Connection: close", "", "");
|
|
18292
|
-
const head =
|
|
19241
|
+
const head = encoder8.encode(lines2.join("\r\n"));
|
|
18293
19242
|
const out = new Uint8Array(head.length + (body?.length ?? 0));
|
|
18294
19243
|
out.set(head);
|
|
18295
19244
|
if (body) out.set(body, head.length);
|
|
@@ -18343,8 +19292,8 @@ var KernelCallError = class extends Error {
|
|
|
18343
19292
|
this.code = code;
|
|
18344
19293
|
}
|
|
18345
19294
|
};
|
|
18346
|
-
var
|
|
18347
|
-
var bytesOf2 = (data) => typeof data === "string" ?
|
|
19295
|
+
var encoder9 = new TextEncoder();
|
|
19296
|
+
var bytesOf2 = (data) => typeof data === "string" ? encoder9.encode(data) : data;
|
|
18348
19297
|
var UNAVAILABLE = {
|
|
18349
19298
|
manualRedirects: false,
|
|
18350
19299
|
encodedBodies: false,
|
|
@@ -18364,7 +19313,7 @@ async function holdLock(locks) {
|
|
|
18364
19313
|
await granted2.promise;
|
|
18365
19314
|
return { name: name2, release: () => held.resolve() };
|
|
18366
19315
|
}
|
|
18367
|
-
function
|
|
19316
|
+
function concat7(chunks2) {
|
|
18368
19317
|
const out = new Uint8Array(chunks2.reduce((n, c) => n + c.length, 0));
|
|
18369
19318
|
let at = 0;
|
|
18370
19319
|
for (const chunk2 of chunks2) {
|
|
@@ -18415,8 +19364,8 @@ var Channel = class {
|
|
|
18415
19364
|
const id2 = ++this.nextId;
|
|
18416
19365
|
const { failure } = this;
|
|
18417
19366
|
if (failure) return { id: id2, done: Promise.reject(failure) };
|
|
18418
|
-
const done4 = new Promise((
|
|
18419
|
-
this.pending.set(id2, { resolve, reject, ...handlers });
|
|
19367
|
+
const done4 = new Promise((resolve2, reject) => {
|
|
19368
|
+
this.pending.set(id2, { resolve: resolve2, reject, ...handlers });
|
|
18420
19369
|
this.port.postMessage({ ...call, id: id2 });
|
|
18421
19370
|
});
|
|
18422
19371
|
return { id: id2, done: done4 };
|
|
@@ -18554,7 +19503,7 @@ async function runOn(channel, argv, opts) {
|
|
|
18554
19503
|
onStderr: tee(2, onStderr)
|
|
18555
19504
|
});
|
|
18556
19505
|
const { status, signal } = await child.ended;
|
|
18557
|
-
const text = (fd) => new TextDecoder().decode(
|
|
19506
|
+
const text = (fd) => new TextDecoder().decode(concat7(chunks2[fd]));
|
|
18558
19507
|
return {
|
|
18559
19508
|
pid: child.pid,
|
|
18560
19509
|
status,
|
|
@@ -18833,10 +19782,10 @@ async function createNodeKernel(options = {}) {
|
|
|
18833
19782
|
...options.approve ? { approve: options.approve } : {}
|
|
18834
19783
|
});
|
|
18835
19784
|
await launcher.prepare();
|
|
18836
|
-
const guard2 = (work) => new Promise((
|
|
19785
|
+
const guard2 = (work) => new Promise((resolve2, reject) => {
|
|
18837
19786
|
if (terminated) return reject(new Error(TERMINATED));
|
|
18838
19787
|
pending.add(reject);
|
|
18839
|
-
work().then(
|
|
19788
|
+
work().then(resolve2, reject).finally(() => pending.delete(reject));
|
|
18840
19789
|
});
|
|
18841
19790
|
const decoder4 = new TextDecoder();
|
|
18842
19791
|
return {
|