@ai-ecoverse/slicc-kernel 1.82.0 → 1.84.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/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((resolve) => resolve(hook(request))).then(
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((resolve, reject) => {
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) resolve(conn);
72
+ else if ("open" in data) resolve2(conn);
73
73
  else if ("error" in data) {
74
74
  closed = true;
75
75
  port1.close();
@@ -448,8 +448,8 @@ async function inParent(g, op, path, old = false) {
448
448
  if (!old) return void await need(g, op, parentOf(path), W_OK | X_OK, DIR, path);
449
449
  const dir = await access(g.fs, g.cred, parentOf(path), W_OK | X_OK, DIR);
450
450
  if (dir.denied === void 0) return stickyEntry(g, op, path, dir);
451
- const lookup2 = dir.denied === dir.path && dir.perm && permits(g.cred, dir.perm, X_OK);
452
- if (!lookup2 || await present(g.fs, entryIn(dir, path), false)) deny(g, op, path);
451
+ const lookup3 = dir.denied === dir.path && dir.perm && permits(g.cred, dir.perm, X_OK);
452
+ if (!lookup3 || await present(g.fs, entryIn(dir, path), false)) deny(g, op, path);
453
453
  }
454
454
  async function makeIn(g, op, path) {
455
455
  for (let dir = parentOf(path); ; dir = parentOf(dir)) {
@@ -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 count = Math.min(chunk2.length, out.length - filled);
675
- out.set(chunk2.subarray(0, count), filled);
676
- filled += count;
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 count = Math.min(head.length, n - filled);
708
- out.set(head.subarray(0, count), filled);
709
- filled += count;
710
- if (count === head.length) this.chunks.shift();
711
- else this.chunks[0] = head.subarray(count);
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((resolve, reject) => {
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
- resolve();
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 resolve of waiters) resolve();
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((resolve, reject) => {
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
- resolve();
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 resolve of waiters) resolve();
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
- (resolve) => resolve(route.uplink.dial({ network: "tcp", host, port, signal: abort.signal }))
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((resolve) => {
1751
- this.settle = resolve;
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((resolve) => {
1791
- this.waiter = resolve;
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((resolve, reject) => {
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
- resolve({
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((resolve) => {
2051
- late = () => resolve("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((resolve) => {
2080
- flow.wake = resolve;
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((resolve) => setTimeout(resolve, 10));
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
  };
@@ -3404,11 +3404,11 @@ var RemoteWatch = class {
3404
3404
  const seq2 = ++this.seq;
3405
3405
  this.channel.postMessage({ tab: this.tab, paths: this.mine, seq: seq2 });
3406
3406
  if (this.peers.size === 0) return Promise.resolve();
3407
- return new Promise((resolve) => {
3407
+ return new Promise((resolve2) => {
3408
3408
  const done4 = () => {
3409
3409
  clearTimeout(timer);
3410
3410
  this.waiting.delete(seq2);
3411
- resolve();
3411
+ resolve2();
3412
3412
  };
3413
3413
  const timer = setTimeout(() => {
3414
3414
  done4();
@@ -3467,10 +3467,10 @@ function translate(err, path, op = "lookup", mapped = true) {
3467
3467
  var META_OPS = ["get", "update", "move", "remove", "links", "under", "all"];
3468
3468
  function sidecar(meta) {
3469
3469
  const pathOf = ([first]) => [first, "/"].flat().find((p) => typeof p === "string");
3470
- const guarded2 = (op) => (...args) => meta[op].apply(meta, args).catch((err) => {
3470
+ const guarded3 = (op) => (...args) => meta[op].apply(meta, args).catch((err) => {
3471
3471
  throw translate(err, pathOf(args), "sidecar", false);
3472
3472
  });
3473
- return Object.fromEntries(META_OPS.map((op) => [op, guarded2(op)]));
3473
+ return Object.fromEntries(META_OPS.map((op) => [op, guarded3(op)]));
3474
3474
  }
3475
3475
  function missing2(err) {
3476
3476
  const code = err?.code;
@@ -3504,7 +3504,7 @@ async function openSync(handle) {
3504
3504
  const delay = SYNC_BACKOFF_MS[attempt];
3505
3505
  if (!LOCKED.has(err?.name)) throw err;
3506
3506
  if (delay === void 0) return void 0;
3507
- await new Promise((resolve) => setTimeout(resolve, delay));
3507
+ await new Promise((resolve2) => setTimeout(resolve2, delay));
3508
3508
  }
3509
3509
  }
3510
3510
  }
@@ -3957,7 +3957,7 @@ var OpfsFs = class {
3957
3957
  const again = name2 === "NotReadableError" || locks && LOCKED.has(name2);
3958
3958
  if (!again || attempt >= READ_ATTEMPTS)
3959
3959
  throw translate(err, path, locks ? "write" : "read");
3960
- await new Promise((resolve) => setTimeout(resolve, attempt * 10));
3960
+ await new Promise((resolve2) => setTimeout(resolve2, attempt * 10));
3961
3961
  }
3962
3962
  }
3963
3963
  }
@@ -4014,8 +4014,8 @@ var OpfsFs = class {
4014
4014
  }
4015
4015
  async truncate(path, size) {
4016
4016
  const handle = await this.existing(path, true);
4017
- const resolve = this.ranged || this.remote?.watched();
4018
- const target = resolve ? (await this.locate(path, true)).path : path;
4017
+ const resolve2 = this.ranged || this.remote?.watched();
4018
+ const target = resolve2 ? (await this.locate(path, true)).path : path;
4019
4019
  await this.retrying(path, () => positioned(handle, (io) => io.truncate(size)), true);
4020
4020
  this.written(target);
4021
4021
  }
@@ -4455,12 +4455,12 @@ var PathGate = class {
4455
4455
  tails = /* @__PURE__ */ new Map();
4456
4456
  async acquire(path) {
4457
4457
  const prev = this.tails.get(path) ?? Promise.resolve();
4458
- const { promise, resolve } = Promise.withResolvers();
4458
+ const { promise, resolve: resolve2 } = Promise.withResolvers();
4459
4459
  const tail = prev.then(() => promise);
4460
4460
  this.tails.set(path, tail);
4461
4461
  await prev;
4462
4462
  return () => {
4463
- resolve();
4463
+ resolve2();
4464
4464
  if (this.tails.get(path) === tail) this.tails.delete(path);
4465
4465
  };
4466
4466
  }
@@ -5224,7 +5224,7 @@ var CdpFacade = class {
5224
5224
  const conn = await this.host(runtime);
5225
5225
  try {
5226
5226
  const reply = await new Promise(
5227
- (resolve, reject) => {
5227
+ (resolve2, reject) => {
5228
5228
  const timer = setTimeout(
5229
5229
  () => reject(new HttpError(504, "the CDP host did not answer Target.getTargets")),
5230
5230
  this.options.listTimeout ?? LIST_TIMEOUT
@@ -5237,7 +5237,7 @@ var CdpFacade = class {
5237
5237
  const parsed2 = JSON.parse(message3);
5238
5238
  if (parsed2.id !== 1) return;
5239
5239
  clearTimeout(timer);
5240
- resolve(parsed2);
5240
+ resolve2(parsed2);
5241
5241
  };
5242
5242
  conn.send(JSON.stringify({ id: 1, method: "Target.getTargets" }));
5243
5243
  }
@@ -5297,9 +5297,9 @@ var Relay = class {
5297
5297
  }
5298
5298
  async start(head) {
5299
5299
  await this.send(head);
5300
- const hostGone = new Promise((resolve) => {
5300
+ const hostGone = new Promise((resolve2) => {
5301
5301
  this.host.onclose = (reason) => {
5302
- void this.end(reason ? CLOSE.error : CLOSE.normal, reason ?? "").then(() => resolve());
5302
+ void this.end(reason ? CLOSE.error : CLOSE.normal, reason ?? "").then(() => resolve2());
5303
5303
  };
5304
5304
  });
5305
5305
  this.host.onmessage = (message3) => {
@@ -5313,8 +5313,8 @@ var Relay = class {
5313
5313
  await Promise.race([pumping, hostGone]);
5314
5314
  await this.writes;
5315
5315
  let timer;
5316
- const waited = new Promise((resolve) => {
5317
- timer = setTimeout(resolve, this.closeWaitMs);
5316
+ const waited = new Promise((resolve2) => {
5317
+ timer = setTimeout(resolve2, this.closeWaitMs);
5318
5318
  });
5319
5319
  await Promise.race([pumping, waited]);
5320
5320
  clearTimeout(timer);
@@ -5907,9 +5907,13 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
5907
5907
  // src/fs/fresh-modules.ts
5908
5908
  var MAGIC = [0, 97, 115, 109];
5909
5909
  var MAX_FRESH = 65536;
5910
+ var DEFAULT_UMASK = 18;
5910
5911
  function isModule(bytes) {
5911
5912
  return MAGIC.every((b, i) => bytes[i] === b);
5912
5913
  }
5914
+ function within4(path, dir) {
5915
+ return path.startsWith(dir === "/" ? "/" : `${dir}/`);
5916
+ }
5913
5917
  var FreshModules = class {
5914
5918
  fresh = /* @__PURE__ */ new Map();
5915
5919
  add(path, umask, pid) {
@@ -5917,17 +5921,40 @@ var FreshModules = class {
5917
5921
  this.fresh.delete(key);
5918
5922
  const oldest = this.fresh.size >= MAX_FRESH ? this.fresh.keys().next().value : void 0;
5919
5923
  if (oldest !== void 0) this.fresh.delete(oldest);
5920
- this.fresh.set(key, { umask: umask & 511, pid, added: 0 });
5924
+ const mask = umask & 511;
5925
+ this.fresh.set(key, { umask: mask, pid, added: 0, setup: mask !== DEFAULT_UMASK });
5921
5926
  }
5922
5927
  forget(pid) {
5923
5928
  for (const [path, fresh2] of this.fresh) if (fresh2.pid === pid) this.fresh.delete(path);
5924
5929
  }
5930
+ drop(fresh2) {
5931
+ for (const [key, note] of [...this.fresh]) if (note === fresh2) this.fresh.delete(key);
5932
+ }
5933
+ moved(from, to) {
5934
+ const source = normalizePath(from);
5935
+ const target = normalizePath(to);
5936
+ if (source === target) return;
5937
+ for (const key of [...this.fresh.keys()]) {
5938
+ if (key === target || within4(key, target)) this.fresh.delete(key);
5939
+ }
5940
+ for (const [key, fresh2] of [...this.fresh]) {
5941
+ if (key !== source && !within4(key, source)) continue;
5942
+ this.fresh.delete(key);
5943
+ this.fresh.set(target + key.slice(source.length), fresh2);
5944
+ }
5945
+ }
5925
5946
  wrap(fs) {
5926
- const decide = async (given, bytes) => {
5947
+ const head = async (path) => {
5948
+ if (fs.pread) return fs.pread(path, 0, MAGIC.length);
5949
+ return (await fs.readFileBuffer(path)).subarray(0, MAGIC.length);
5950
+ };
5951
+ const decide = async (given, known) => {
5927
5952
  const path = normalizePath(given);
5928
5953
  const fresh2 = this.fresh.get(path);
5929
- if (!fresh2 || typeof bytes === "string" || bytes.length < MAGIC.length) return;
5954
+ if (!fresh2) return;
5955
+ const bytes = known && known.length >= MAGIC.length ? known : await head(path);
5930
5956
  const want = isModule(bytes) ? 73 & ~fresh2.umask : 0;
5957
+ fresh2.setup = false;
5931
5958
  if (want === fresh2.added) return;
5932
5959
  const mode = (await fs.stat(path)).mode & 4095;
5933
5960
  await fs.chmod(path, mode & ~fresh2.added | want);
@@ -5936,44 +5963,59 @@ var FreshModules = class {
5936
5963
  const overrides = {
5937
5964
  writeFile: async (path, content, owner) => {
5938
5965
  await fs.writeFile(path, content, owner);
5939
- await decide(path, content);
5966
+ if (this.fresh.has(normalizePath(path))) {
5967
+ await decide(
5968
+ path,
5969
+ typeof content === "string" ? new TextEncoder().encode(content) : content
5970
+ );
5971
+ }
5940
5972
  },
5941
5973
  chmod: async (path, mode) => {
5942
5974
  const fresh2 = this.fresh.get(normalizePath(path));
5943
- const creating = fresh2?.added === 0 && (mode & 4095) === (438 & ~fresh2.umask);
5944
- if (!creating) this.fresh.delete(normalizePath(path));
5975
+ if (fresh2?.setup && (mode & 4095) === (438 & ~fresh2.umask)) fresh2.setup = false;
5976
+ else if (fresh2) this.drop(fresh2);
5945
5977
  await fs.chmod(path, mode);
5946
5978
  },
5947
5979
  rm: async (path, options) => {
5948
- this.fresh.delete(normalizePath(path));
5949
5980
  await fs.rm(path, options);
5981
+ const at = normalizePath(path);
5982
+ for (const key of [...this.fresh.keys()]) {
5983
+ if (key === at || within4(key, at)) this.fresh.delete(key);
5984
+ }
5950
5985
  },
5951
5986
  rename: async (from, to) => {
5952
5987
  await fs.rename(from, to);
5953
- const fresh2 = this.fresh.get(normalizePath(from));
5954
- this.fresh.delete(normalizePath(from));
5955
- this.fresh.delete(normalizePath(to));
5988
+ this.moved(from, to);
5989
+ },
5990
+ link: async (from, to, follow2) => {
5991
+ await fs.link(from, to, follow2);
5992
+ const source = follow2 && fs.realpath ? await fs.realpath(from, true) : from;
5993
+ const fresh2 = this.fresh.get(normalizePath(source));
5956
5994
  if (fresh2) this.fresh.set(normalizePath(to), fresh2);
5957
5995
  }
5958
5996
  };
5959
5997
  const ranged2 = rangedOps(fs);
5960
5998
  if (ranged2) {
5961
5999
  overrides.pwrite = async (path, at, bytes, transfer) => {
5962
- const head = at === 0 && this.fresh.has(normalizePath(path)) ? bytes.slice(0, 4) : void 0;
6000
+ const touches2 = at < MAGIC.length && this.fresh.has(normalizePath(path));
5963
6001
  await ranged2.pwrite(path, at, bytes, transfer);
5964
- if (head) await decide(path, head);
6002
+ if (touches2) await decide(path);
6003
+ };
6004
+ overrides.truncate = async (path, size) => {
6005
+ await ranged2.truncate(path, size);
6006
+ if (size < MAGIC.length && this.fresh.has(normalizePath(path))) await decide(path);
5965
6007
  };
5966
6008
  }
5967
- const bound = /* @__PURE__ */ new WeakMap();
6009
+ const bound2 = /* @__PURE__ */ new WeakMap();
5968
6010
  return new Proxy(fs, {
5969
6011
  get(target, key, receiver) {
5970
6012
  if (Object.hasOwn(overrides, key)) return overrides[key];
5971
6013
  const value = Reflect.get(target, key, receiver);
5972
6014
  if (typeof value !== "function") return value;
5973
- let method = bound.get(value);
6015
+ let method = bound2.get(value);
5974
6016
  if (method === void 0) {
5975
6017
  method = value.bind(target);
5976
- bound.set(value, method);
6018
+ bound2.set(value, method);
5977
6019
  }
5978
6020
  return method;
5979
6021
  }
@@ -6302,7 +6344,7 @@ var ChildTable = class {
6302
6344
  }
6303
6345
  async nextChange(candidates, flags2, interrupt) {
6304
6346
  let wake;
6305
- const stateChange = new Promise((resolve) => wake = resolve);
6347
+ const stateChange = new Promise((resolve2) => wake = resolve2);
6306
6348
  const watching = flags2.untraced || flags2.continued;
6307
6349
  if (watching && wake) this.stateChanged.push(wake);
6308
6350
  try {
@@ -6516,6 +6558,65 @@ var ClipboardBroker = class {
6516
6558
  }
6517
6559
  };
6518
6560
 
6561
+ // src/kernel/exec-ticket.ts
6562
+ var TICKET_ENV = "SLICC_SEALED_TICKET";
6563
+ var TICKET_TTL_MS = 3e4;
6564
+ var MAX_TICKETS = 256;
6565
+ var TICKET = /^[0-9a-f]{32}$/;
6566
+ async function argvDigest(argv) {
6567
+ const bytes = new TextEncoder().encode(JSON.stringify(argv));
6568
+ const hash = new Uint8Array(await crypto.subtle.digest("SHA-256", bytes));
6569
+ return [...hash].map((b) => b.toString(16).padStart(2, "0")).join("");
6570
+ }
6571
+ function isStrings(value) {
6572
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
6573
+ }
6574
+ function stringRecord(value) {
6575
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
6576
+ const entries = Object.entries(value);
6577
+ return entries.every(([, v]) => typeof v === "string") ? Object.fromEntries(entries) : void 0;
6578
+ }
6579
+ var TicketBook = class {
6580
+ booked = /* @__PURE__ */ new Map();
6581
+ now;
6582
+ normalise = async (path) => path;
6583
+ constructor(now = Date.now) {
6584
+ this.now = now;
6585
+ }
6586
+ get size() {
6587
+ return this.booked.size;
6588
+ }
6589
+ async record(req, reply, pid) {
6590
+ const { ticket, error } = reply;
6591
+ if (error !== void 0 || typeof ticket !== "string" || !TICKET.test(ticket)) return;
6592
+ const { path, argv, cwd } = req;
6593
+ const entry = {
6594
+ pid,
6595
+ path: await this.normalise(path),
6596
+ requested: path,
6597
+ requestedCwd: cwd,
6598
+ argv: await argvDigest(argv),
6599
+ cwd: await this.normalise(cwd),
6600
+ at: this.now()
6601
+ };
6602
+ this.prune();
6603
+ while (this.booked.size >= MAX_TICKETS)
6604
+ this.booked.delete(this.booked.keys().next().value);
6605
+ this.booked.set(ticket, entry);
6606
+ }
6607
+ take(ticket) {
6608
+ const booked = this.booked.get(ticket);
6609
+ this.booked.delete(ticket);
6610
+ return booked && this.now() - booked.at < TICKET_TTL_MS ? booked : void 0;
6611
+ }
6612
+ prune() {
6613
+ const now = this.now();
6614
+ for (const [ticket, booked] of this.booked) {
6615
+ if (now - booked.at >= TICKET_TTL_MS) this.booked.delete(ticket);
6616
+ }
6617
+ }
6618
+ };
6619
+
6519
6620
  // src/mount/protocol.ts
6520
6621
  var DRIVER_PROTOCOL = [1, 2];
6521
6622
  function driverVersionError(theirs) {
@@ -6586,7 +6687,7 @@ var DriverConnection = class _DriverConnection {
6586
6687
  call(call, transfer = []) {
6587
6688
  if (this.failure) return Promise.reject(this.failure);
6588
6689
  const id2 = ++this.nextId;
6589
- return new Promise((resolve, reject) => {
6690
+ return new Promise((resolve2, reject) => {
6590
6691
  const timer = unref(
6591
6692
  setTimeout(() => {
6592
6693
  this.waiting.delete(id2);
@@ -6599,7 +6700,7 @@ var DriverConnection = class _DriverConnection {
6599
6700
  this.onFail?.(error);
6600
6701
  }, this.timeoutMs)
6601
6702
  );
6602
- this.waiting.set(id2, { resolve, reject, timer });
6703
+ this.waiting.set(id2, { resolve: resolve2, reject, timer });
6603
6704
  this.port.postMessage({ ...call, id: id2 }, transfer);
6604
6705
  });
6605
6706
  }
@@ -6642,7 +6743,7 @@ function parseSize(text) {
6642
6743
  if (!match) throw errnoError("EINVAL", `not a size: ${text}`);
6643
6744
  return Math.floor(Number(match[1]) * UNITS[match[2].toLowerCase()]);
6644
6745
  }
6645
- var within4 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
6746
+ var within5 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
6646
6747
  var HeldSet = class extends Set {
6647
6748
  idle;
6648
6749
  constructor(idle) {
@@ -6661,7 +6762,7 @@ var HeldSet = class extends Set {
6661
6762
  }
6662
6763
  };
6663
6764
  function heldUnder(held, target) {
6664
- for (const paths of held) for (const path of paths.keys()) if (within4(path, target)) return true;
6765
+ for (const paths of held) for (const path of paths.keys()) if (within5(path, target)) return true;
6665
6766
  return false;
6666
6767
  }
6667
6768
  var HeldPaths = class extends Map {
@@ -6742,8 +6843,8 @@ var HeldPaths = class extends Map {
6742
6843
  this.keptHandles.delete(handle);
6743
6844
  if (kept) this.letGo(kept);
6744
6845
  }
6745
- const count = (this.opens.get(key) ?? 0) + (on ? 1 : -1);
6746
- if (count > 0) this.opens.set(key, count);
6846
+ const count2 = (this.opens.get(key) ?? 0) + (on ? 1 : -1);
6847
+ if (count2 > 0) this.opens.set(key, count2);
6747
6848
  else {
6748
6849
  this.opens.delete(key);
6749
6850
  for (const id2 of this.handlesAt(key)) this.handles.delete(id2);
@@ -6757,8 +6858,8 @@ var HeldPaths = class extends Map {
6757
6858
  return this.lastHandle;
6758
6859
  }
6759
6860
  holdWrite(path, on) {
6760
- const count = (this.get(path) ?? 0) + (on ? 1 : -1);
6761
- if (count > 0) this.set(path, count);
6861
+ const count2 = (this.get(path) ?? 0) + (on ? 1 : -1);
6862
+ if (count2 > 0) this.set(path, count2);
6762
6863
  else {
6763
6864
  this.delete(path);
6764
6865
  this.revoked.delete(path);
@@ -6777,24 +6878,24 @@ var HeldPaths = class extends Map {
6777
6878
  return this.owning === path;
6778
6879
  }
6779
6880
  revoke(prefix) {
6780
- for (const path of this.keys()) if (within4(path, prefix)) this.revoked.add(path);
6881
+ for (const path of this.keys()) if (within5(path, prefix)) this.revoked.add(path);
6781
6882
  }
6782
6883
  renamed(from, to) {
6783
- for (const [path, count] of [...this]) {
6784
- if (!within4(path, from)) continue;
6884
+ for (const [path, count2] of [...this]) {
6885
+ if (!within5(path, from)) continue;
6785
6886
  const moved2 = to + path.slice(from.length);
6786
6887
  this.delete(path);
6787
- this.set(moved2, (this.get(moved2) ?? 0) + count);
6888
+ this.set(moved2, (this.get(moved2) ?? 0) + count2);
6788
6889
  if (this.revoked.delete(path)) this.revoked.add(moved2);
6789
6890
  }
6790
- for (const [path, count] of [...this.opens]) {
6791
- if (!within4(path, from)) continue;
6891
+ for (const [path, count2] of [...this.opens]) {
6892
+ if (!within5(path, from)) continue;
6792
6893
  const moved2 = to + path.slice(from.length);
6793
6894
  this.opens.delete(path);
6794
- this.opens.set(moved2, (this.opens.get(moved2) ?? 0) + count);
6895
+ this.opens.set(moved2, (this.opens.get(moved2) ?? 0) + count2);
6795
6896
  }
6796
6897
  for (const [id2, key] of this.handles) {
6797
- if (within4(key, from)) this.handles.set(id2, to + key.slice(from.length));
6898
+ if (within5(key, from)) this.handles.set(id2, to + key.slice(from.length));
6798
6899
  }
6799
6900
  }
6800
6901
  isRevoked(path) {
@@ -6901,7 +7002,7 @@ var MountTable = class {
6901
7002
  locate(path) {
6902
7003
  let best;
6903
7004
  for (const mount of this.mounts.values()) {
6904
- if (within4(path, mount.target) && (!best || mount.target.length > best.target.length))
7005
+ if (within5(path, mount.target) && (!best || mount.target.length > best.target.length))
6905
7006
  best = mount;
6906
7007
  }
6907
7008
  if (!best) return void 0;
@@ -7044,7 +7145,7 @@ var MountTable = class {
7044
7145
  async commitUnder(prefix) {
7045
7146
  const at = normalizePath(prefix);
7046
7147
  await Promise.all(
7047
- [...this.sessions.keys()].filter((p) => within4(p, at)).map((p) => this.commit(p))
7148
+ [...this.sessions.keys()].filter((p) => within5(p, at)).map((p) => this.commit(p))
7048
7149
  );
7049
7150
  }
7050
7151
  async commitOverlapping(real, offset, length2) {
@@ -7079,10 +7180,10 @@ var MountTable = class {
7079
7180
  }
7080
7181
  forget(mount, path) {
7081
7182
  mount.epoch++;
7082
- for (const key of [...mount.attrs.keys()]) if (within4(key, path)) mount.attrs.delete(key);
7183
+ for (const key of [...mount.attrs.keys()]) if (within5(key, path)) mount.attrs.delete(key);
7083
7184
  mount.lists.delete(path);
7084
7185
  mount.lists.delete(parentOf7(path));
7085
- for (const key of [...mount.lists.keys()]) if (within4(key, path)) mount.lists.delete(key);
7186
+ for (const key of [...mount.lists.keys()]) if (within5(key, path)) mount.lists.delete(key);
7086
7187
  }
7087
7188
  async getattr(mount, path, rel2, settled = false) {
7088
7189
  for (let round = 1; ; round++) {
@@ -7717,6 +7818,11 @@ function statJson(s) {
7717
7818
  async function readdirStat(fs, dir) {
7718
7819
  return (await fs.readdirStat(dir)).map(([name2, stat]) => [name2, stat && statJson(stat)]);
7719
7820
  }
7821
+ async function usageAt(entry, path) {
7822
+ if (!entry.statfs) return void 0;
7823
+ const real = await realTarget(entry.fs, path).catch(() => path);
7824
+ return entry.statfs(real);
7825
+ }
7720
7826
  async function storageUsage() {
7721
7827
  const estimate = await globalThis.navigator?.storage?.estimate?.();
7722
7828
  if (typeof estimate?.quota !== "number") return null;
@@ -7937,8 +8043,8 @@ async function guarded(entry, path, req) {
7937
8043
  await guard(g, path, req, entry.cwd);
7938
8044
  return void 0;
7939
8045
  } catch (err) {
7940
- const refusal = err.code === "EACCES";
7941
- return req.op === "exists" && refusal ? json2(false) : toErrno(err);
8046
+ const refusal2 = err.code === "EACCES";
8047
+ return req.op === "exists" && refusal2 ? json2(false) : toErrno(err);
7942
8048
  }
7943
8049
  }
7944
8050
  async function createExclusive(fs, path, owner) {
@@ -8033,7 +8139,7 @@ async function dispatchOne(entry, req) {
8033
8139
  if (req.op === "hold") return await holdOp(entry, path, req);
8034
8140
  const refused2 = await guarded(entry, path, req);
8035
8141
  if (refused2) return refused2;
8036
- if (req.op === "statfs") return json2(await entry.statfs?.(path) ?? await storageUsage());
8142
+ if (req.op === "statfs") return json2(await usageAt(entry, path) ?? await storageUsage());
8037
8143
  if (entry.revoked?.(path)) return { ok: false, errno: "EIO", message: `${path} was unmounted` };
8038
8144
  if (req.op === "write" && req.mode === 1 || UNLINKED_OPS.has(req.op)) {
8039
8145
  const serve = async () => run(entry, await keptOr(entry, path, req.handle), req);
@@ -9112,7 +9218,7 @@ var AsyncOps = class {
9112
9218
  this.wake();
9113
9219
  }
9114
9220
  sleep(timeoutMs, signal) {
9115
- return new Promise((resolve, reject) => {
9221
+ return new Promise((resolve2, reject) => {
9116
9222
  let timer;
9117
9223
  const cleanup = () => {
9118
9224
  clearTimeout(timer);
@@ -9121,7 +9227,7 @@ var AsyncOps = class {
9121
9227
  };
9122
9228
  const wake = () => {
9123
9229
  cleanup();
9124
- resolve(true);
9230
+ resolve2(true);
9125
9231
  };
9126
9232
  const abort = () => {
9127
9233
  cleanup();
@@ -9132,7 +9238,7 @@ var AsyncOps = class {
9132
9238
  if (timeoutMs !== void 0) {
9133
9239
  timer = setTimeout(() => {
9134
9240
  cleanup();
9135
- resolve(false);
9241
+ resolve2(false);
9136
9242
  }, timeoutMs);
9137
9243
  }
9138
9244
  });
@@ -9216,10 +9322,10 @@ var Budget = class {
9216
9322
  async acquire(n, signal) {
9217
9323
  while (this.free < n) {
9218
9324
  signal.throwIfAborted();
9219
- await new Promise((resolve) => {
9325
+ await new Promise((resolve2) => {
9220
9326
  const done4 = () => {
9221
9327
  signal.removeEventListener("abort", done4);
9222
- resolve();
9328
+ resolve2();
9223
9329
  };
9224
9330
  this.waiters.push(done4);
9225
9331
  signal.addEventListener("abort", done4, { once: true });
@@ -9361,14 +9467,14 @@ async function watchHangup(conn, abort, until) {
9361
9467
  }
9362
9468
  }
9363
9469
  function untilAborted(response, signal) {
9364
- return new Promise((resolve, reject) => {
9470
+ return new Promise((resolve2, reject) => {
9365
9471
  const onAbort = () => {
9366
9472
  reject(signal.reason ?? new KernelError("EINTR"));
9367
9473
  response.then((late) => late.cancel()).catch(() => void 0);
9368
9474
  };
9369
9475
  if (signal.aborted) return onAbort();
9370
9476
  signal.addEventListener("abort", onAbort, { once: true });
9371
- response.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
9477
+ response.then(resolve2, reject).finally(() => signal.removeEventListener("abort", onAbort));
9372
9478
  });
9373
9479
  }
9374
9480
  function plainResponse(status, message3) {
@@ -9401,8 +9507,8 @@ function wireCode(code) {
9401
9507
  return sendableCode(code) ? code : 1011;
9402
9508
  }
9403
9509
  function quietly(ms) {
9404
- return new Promise((resolve) => {
9405
- AbortSignal.timeout(ms).addEventListener("abort", () => resolve(), { once: true });
9510
+ return new Promise((resolve2) => {
9511
+ AbortSignal.timeout(ms).addEventListener("abort", () => resolve2(), { once: true });
9406
9512
  });
9407
9513
  }
9408
9514
  var RealmProxy = class {
@@ -9500,7 +9606,7 @@ var RealmProxy = class {
9500
9606
  return false;
9501
9607
  }
9502
9608
  const url = ctx.origin ? tunnelRequestUrl(req, ctx.origin) : requestUrl(req);
9503
- const keep = keepsAlive(req);
9609
+ const keep2 = keepsAlive(req);
9504
9610
  const framing = requestFraming(req.headers);
9505
9611
  const reserve = framing.kind === "length" ? framing.length : framing.kind === "chunked" ? this.cap() : 0;
9506
9612
  if (reserve > this.cap()) throw new HttpError(413, `request body over ${this.cap()} bytes`);
@@ -9523,7 +9629,7 @@ var RealmProxy = class {
9523
9629
  } finally {
9524
9630
  this.bodies.release(reserve);
9525
9631
  }
9526
- return await this.relay(conn, req.method, req.minor, response, keep);
9632
+ return await this.relay(conn, req.method, req.minor, response, keep2);
9527
9633
  } finally {
9528
9634
  this.stop.signal.removeEventListener("abort", stopExchange);
9529
9635
  abort.abort();
@@ -9571,8 +9677,8 @@ var RealmProxy = class {
9571
9677
  bodiless: bodiless2
9572
9678
  });
9573
9679
  const chunked2 = !bodiless2 && minor >= 1;
9574
- const keep = keepAlive && (bodiless2 || chunked2);
9575
- headers.push(["Connection", keep ? "keep-alive" : "close"]);
9680
+ const keep2 = keepAlive && (bodiless2 || chunked2);
9681
+ headers.push(["Connection", keep2 ? "keep-alive" : "close"]);
9576
9682
  if (chunked2) headers.push(["Transfer-Encoding", "chunked"]);
9577
9683
  const signal = this.stop.signal;
9578
9684
  await conn.write(
@@ -9581,7 +9687,7 @@ var RealmProxy = class {
9581
9687
  );
9582
9688
  if (bodiless2) {
9583
9689
  await response.cancel();
9584
- return keep;
9690
+ return keep2;
9585
9691
  }
9586
9692
  try {
9587
9693
  for await (const piece of response.body) {
@@ -9592,7 +9698,7 @@ var RealmProxy = class {
9592
9698
  return false;
9593
9699
  }
9594
9700
  if (chunked2) await conn.write(LAST_CHUNK, signal);
9595
- return keep;
9701
+ return keep2;
9596
9702
  }
9597
9703
  async connect(conn, incoming, req, idle) {
9598
9704
  const target = tunnelTarget(req);
@@ -9815,10 +9921,10 @@ function header(der, at) {
9815
9921
  let len = der[at + 1];
9816
9922
  let start2 = at + 2;
9817
9923
  if (len & 128) {
9818
- const count = len & 127;
9924
+ const count2 = len & 127;
9819
9925
  len = 0;
9820
- for (let i = 0; i < count; i++) len = len * 256 + der[start2 + i];
9821
- start2 += count;
9926
+ for (let i = 0; i < count2; i++) len = len * 256 + der[start2 + i];
9927
+ start2 += count2;
9822
9928
  }
9823
9929
  if (start2 + len > der.length) throw new Error("truncated DER");
9824
9930
  return [tag, start2, start2 + len];
@@ -10717,7 +10823,7 @@ function parsePrefix(text) {
10717
10823
  if (length2 < 0 || length2 > max) return void 0;
10718
10824
  return { ...parsed2, length: length2 };
10719
10825
  }
10720
- function within5(prefix, addr) {
10826
+ function within6(prefix, addr) {
10721
10827
  if (prefix.v6 !== addr.v6) return false;
10722
10828
  const shift = BigInt((prefix.v6 ? 128 : 32) - prefix.length);
10723
10829
  return addr.bits >> shift === prefix.bits >> shift;
@@ -10757,9 +10863,9 @@ var Routes = class {
10757
10863
  if (!addr) return "unreachable";
10758
10864
  if (addr.v6 && addr.bits === 1n) return "loopback";
10759
10865
  if (!addr.v6 && addr.bits >> 24n === 127n) return "loopback";
10760
- if (RESERVED.some((p) => within5(p, addr))) return "reserved";
10866
+ if (RESERVED.some((p) => within6(p, addr))) return "reserved";
10761
10867
  if (addr.v6 && !this.ipv6) return "unreachable";
10762
- if (this.exit || this.table.some((p) => within5(p, addr))) return "uplink";
10868
+ if (this.exit || this.table.some((p) => within6(p, addr))) return "uplink";
10763
10869
  return "unreachable";
10764
10870
  }
10765
10871
  };
@@ -10825,14 +10931,14 @@ var Resolver = class {
10825
10931
  if (hit && hit.expires > this.now()) return hit.addresses;
10826
10932
  const signal = blocking?.();
10827
10933
  if (signal?.aborted) throw new KernelError("EINTR");
10828
- let lookup2 = this.pending.get(id2);
10829
- if (!lookup2) {
10934
+ let lookup3 = this.pending.get(id2);
10935
+ if (!lookup3) {
10830
10936
  const abort = new AbortController();
10831
- lookup2 = { work: this.lookup(key, asked, id2, abort.signal), abort, waiters: 0 };
10832
- this.pending.set(id2, lookup2);
10833
- void lookup2.work.then(() => this.pending.delete(id2));
10937
+ lookup3 = { work: this.lookup(key, asked, id2, abort.signal), abort, waiters: 0 };
10938
+ this.pending.set(id2, lookup3);
10939
+ void lookup3.work.then(() => this.pending.delete(id2));
10834
10940
  }
10835
- return waitFor(lookup2, signal);
10941
+ return waitFor(lookup3, signal);
10836
10942
  }
10837
10943
  async lookup(name2, family, id2, abandoned) {
10838
10944
  const uplink = this.uplink;
@@ -10873,19 +10979,19 @@ var Resolver = class {
10873
10979
  }
10874
10980
  }
10875
10981
  };
10876
- function waitFor(lookup2, signal) {
10877
- lookup2.waiters++;
10878
- return new Promise((resolve, reject) => {
10982
+ function waitFor(lookup3, signal) {
10983
+ lookup3.waiters++;
10984
+ return new Promise((resolve2, reject) => {
10879
10985
  const abort = () => {
10880
- if (--lookup2.waiters === 0) lookup2.abort.abort(new KernelError("EINTR"));
10986
+ if (--lookup3.waiters === 0) lookup3.abort.abort(new KernelError("EINTR"));
10881
10987
  reject(new KernelError("EINTR"));
10882
10988
  };
10883
10989
  signal?.addEventListener("abort", abort, { once: true });
10884
- void lookup2.work.then((value) => {
10990
+ void lookup3.work.then((value) => {
10885
10991
  if (signal?.aborted) return;
10886
- lookup2.waiters--;
10992
+ lookup3.waiters--;
10887
10993
  signal?.removeEventListener("abort", abort);
10888
- resolve(value);
10994
+ resolve2(value);
10889
10995
  });
10890
10996
  });
10891
10997
  }
@@ -11066,8 +11172,8 @@ var KernelTty = class {
11066
11172
  }
11067
11173
  rubOut(n) {
11068
11174
  const dropped2 = this.line.splice(this.line.length - n, n);
11069
- const utf82 = (this.termios.c_iflag & IUTF8) !== 0;
11070
- const cells = utf82 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
11175
+ const utf83 = (this.termios.c_iflag & IUTF8) !== 0;
11176
+ const cells = utf83 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
11071
11177
  if (cells > 0 && this.termios.c_lflag & ECHO) {
11072
11178
  this.output(encoder3.encode("\b \b".repeat(cells)), false);
11073
11179
  }
@@ -11137,14 +11243,14 @@ var KernelTty = class {
11137
11243
  }
11138
11244
  changed(signal) {
11139
11245
  if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
11140
- return new Promise((resolve, reject) => {
11246
+ return new Promise((resolve2, reject) => {
11141
11247
  const onAbort = () => {
11142
11248
  this.waiters = this.waiters.filter((w) => w !== waiter);
11143
11249
  reject(new KernelError("EINTR"));
11144
11250
  };
11145
11251
  const waiter = () => {
11146
11252
  signal?.removeEventListener("abort", onAbort);
11147
- resolve();
11253
+ resolve2();
11148
11254
  };
11149
11255
  this.waiters.push(waiter);
11150
11256
  signal?.addEventListener("abort", onAbort, { once: true });
@@ -11153,7 +11259,7 @@ var KernelTty = class {
11153
11259
  wake() {
11154
11260
  const waiters = this.waiters;
11155
11261
  this.waiters = [];
11156
- for (const resolve of waiters) resolve();
11262
+ for (const resolve2 of waiters) resolve2();
11157
11263
  }
11158
11264
  };
11159
11265
 
@@ -11252,14 +11358,14 @@ var PtyPair = class {
11252
11358
  }
11253
11359
  changed(signal) {
11254
11360
  if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
11255
- return new Promise((resolve, reject) => {
11361
+ return new Promise((resolve2, reject) => {
11256
11362
  const onAbort = () => {
11257
11363
  this.waiters = this.waiters.filter((w) => w !== waiter);
11258
11364
  reject(new KernelError("EINTR"));
11259
11365
  };
11260
11366
  const waiter = () => {
11261
11367
  signal?.removeEventListener("abort", onAbort);
11262
- resolve();
11368
+ resolve2();
11263
11369
  };
11264
11370
  this.waiters.push(waiter);
11265
11371
  signal?.addEventListener("abort", onAbort, { once: true });
@@ -11268,7 +11374,7 @@ var PtyPair = class {
11268
11374
  wake() {
11269
11375
  const waiters = this.waiters;
11270
11376
  this.waiters = [];
11271
- for (const resolve of waiters) resolve();
11377
+ for (const resolve2 of waiters) resolve2();
11272
11378
  }
11273
11379
  };
11274
11380
  var PtyTable = class {
@@ -11333,7 +11439,8 @@ function ptySyscall(req, ctx) {
11333
11439
  if (!ctx.ptys) throw new KernelError("ENOENT");
11334
11440
  const pair = ctx.ptys.get(req.n);
11335
11441
  ttyOpenable(ctx.guard, pair.slave);
11336
- const fd = ctx.fds.install(pair.openSlave(), 3);
11442
+ const slave = pair.openSlave();
11443
+ const fd = ctx.fds.install(ctx.ttyFilter?.(slave) ?? slave, 3);
11337
11444
  if (!req.noctty) ctx.jobs?.acquireTerminal(ctx.pid, pair.slave);
11338
11445
  return json4(fd);
11339
11446
  }
@@ -11406,8 +11513,8 @@ async function runChild(req, deps) {
11406
11513
  };
11407
11514
  const controls = [];
11408
11515
  const capture = () => {
11409
- const { promise, resolve } = Promise.withResolvers();
11410
- const control = { shut: false, ended: resolve, eof: promise };
11516
+ const { promise, resolve: resolve2 } = Promise.withResolvers();
11517
+ const control = { shut: false, ended: resolve2, eof: promise };
11411
11518
  controls.push(control);
11412
11519
  return { capture: true, max, overflow: () => stop("ENOBUFS"), control };
11413
11520
  };
@@ -11500,7 +11607,7 @@ async function waitReady(fds, read, write, deadline, interrupted2) {
11500
11607
  return file.changed === void 0 ? [] : [file.changed(round.signal).catch(() => void 0)];
11501
11608
  });
11502
11609
  let timer;
11503
- const expiry = deadline === Number.POSITIVE_INFINITY ? [] : [new Promise((resolve) => timer = setTimeout(resolve, deadline - Date.now()))];
11610
+ const expiry = deadline === Number.POSITIVE_INFINITY ? [] : [new Promise((resolve2) => timer = setTimeout(resolve2, deadline - Date.now()))];
11504
11611
  try {
11505
11612
  await Promise.race([...changes, ...expiry, interrupted2]);
11506
11613
  } finally {
@@ -11584,10 +11691,10 @@ async function socketSyscall(req, proc) {
11584
11691
  }
11585
11692
  }
11586
11693
  function interruptible2(dialing, signal) {
11587
- return new Promise((resolve, reject) => {
11694
+ return new Promise((resolve2, reject) => {
11588
11695
  const abort = () => reject(new KernelError("EINTR"));
11589
11696
  signal.addEventListener("abort", abort, { once: true });
11590
- dialing.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
11697
+ dialing.then(resolve2, reject).finally(() => signal.removeEventListener("abort", abort));
11591
11698
  });
11592
11699
  }
11593
11700
  function unnamed(domain) {
@@ -11614,7 +11721,7 @@ function isMissing(err) {
11614
11721
  if (err?.code === "ENOENT") return true;
11615
11722
  return err instanceof Error && err.message.startsWith("ENOENT");
11616
11723
  }
11617
- function within6(path, root) {
11724
+ function within7(path, root) {
11618
11725
  return path === root || path.startsWith(root.endsWith("/") ? root : `${root}/`);
11619
11726
  }
11620
11727
  var VfsNode = class {
@@ -11935,12 +12042,12 @@ var VfsNodes = class {
11935
12042
  return node;
11936
12043
  }
11937
12044
  holds(prefix) {
11938
- for (const path of this.byPath.keys()) if (within6(path, prefix)) return true;
12045
+ for (const path of this.byPath.keys()) if (within7(path, prefix)) return true;
11939
12046
  return false;
11940
12047
  }
11941
12048
  writes(prefix) {
11942
12049
  for (const [path, node] of this.byPath)
11943
- if (node.writers > 0 && within6(path, prefix)) return true;
12050
+ if (node.writers > 0 && within7(path, prefix)) return true;
11944
12051
  return false;
11945
12052
  }
11946
12053
  closed(node, writable2 = false) {
@@ -11984,11 +12091,11 @@ var VfsNodes = class {
11984
12091
  this.kept.delete(node);
11985
12092
  }
11986
12093
  async flush(path) {
11987
- for (const [p, node] of this.byPath) if (within6(p, path)) await node.serial(() => node.flush());
12094
+ for (const [p, node] of this.byPath) if (within7(p, path)) await node.serial(() => node.flush());
11988
12095
  }
11989
12096
  async unlinking(path) {
11990
12097
  for (const [held, node] of [...this.byPath]) {
11991
- if (!within6(held, path)) continue;
12098
+ if (!within7(held, path)) continue;
11992
12099
  await node.serial(async () => {
11993
12100
  await Promise.all([this.keepsOnDisk || node.keep(), node.limit()]);
11994
12101
  });
@@ -11996,14 +12103,14 @@ var VfsNodes = class {
11996
12103
  }
11997
12104
  revoke(prefix) {
11998
12105
  for (const [path, node] of [...this.byPath]) {
11999
- if (!within6(path, prefix)) continue;
12106
+ if (!within7(path, prefix)) continue;
12000
12107
  node.revoke();
12001
12108
  this.forget(node);
12002
12109
  }
12003
12110
  }
12004
12111
  unlinked(path) {
12005
12112
  for (const [held, node] of [...this.byPath]) {
12006
- if (!within6(held, path) || this.byPath.get(held) !== node) continue;
12113
+ if (!within7(held, path) || this.byPath.get(held) !== node) continue;
12007
12114
  if (!this.dropName(held, node)) this.orphan(node);
12008
12115
  }
12009
12116
  }
@@ -12011,8 +12118,8 @@ var VfsNodes = class {
12011
12118
  if (from === to) return;
12012
12119
  const moved2 = [];
12013
12120
  for (const [p, node] of [...this.byPath]) {
12014
- if (within6(p, from)) moved2.push([p, node]);
12015
- else if (within6(p, to) && this.byPath.get(p) === node && !this.dropName(p, node)) {
12121
+ if (within7(p, from)) moved2.push([p, node]);
12122
+ else if (within7(p, to) && this.byPath.get(p) === node && !this.dropName(p, node)) {
12016
12123
  this.orphan(node);
12017
12124
  }
12018
12125
  }
@@ -12291,7 +12398,7 @@ function isWasmSyscall(req) {
12291
12398
  var MAX_READ = 1024 * 1024;
12292
12399
  var WRITE_ACCESS = 3;
12293
12400
  var KEYBOARD = /* @__PURE__ */ new Set([SIG.INT, SIG.QUIT, SIG.TSTP]);
12294
- var DEFAULT_UMASK = 18;
12401
+ var DEFAULT_UMASK2 = 18;
12295
12402
  function words(value) {
12296
12403
  if (!Array.isArray(value) || !value.every((w) => typeof w === "string")) {
12297
12404
  throw new KernelError("EINVAL");
@@ -12319,7 +12426,7 @@ var WasmProcess = class {
12319
12426
  interrupt = new AbortController();
12320
12427
  gone = new AbortController();
12321
12428
  dying = false;
12322
- umask = DEFAULT_UMASK;
12429
+ umask = DEFAULT_UMASK2;
12323
12430
  cred;
12324
12431
  execChild;
12325
12432
  killed = false;
@@ -12341,7 +12448,7 @@ var WasmProcess = class {
12341
12448
  this.fds = fds;
12342
12449
  this.options = options;
12343
12450
  this.inherited = options.ignored ?? 0;
12344
- this.umask = options.umask ?? DEFAULT_UMASK;
12451
+ this.umask = options.umask ?? DEFAULT_UMASK2;
12345
12452
  this.cred = copyCred(options.cred ?? ROOT);
12346
12453
  this.children = new ChildTable(fds, options.spawner, options.forker);
12347
12454
  this.nodes = options.nodes ?? new VfsNodes(options.fs);
@@ -12434,7 +12541,7 @@ var WasmProcess = class {
12434
12541
  if (this.stopped) return "stop";
12435
12542
  this.stopped = sig;
12436
12543
  this.stops++;
12437
- this.resumed = new Promise((resolve) => this.wake = resolve);
12544
+ this.resumed = new Promise((resolve2) => this.wake = resolve2);
12438
12545
  const blocked = this.interrupt;
12439
12546
  this.interrupt = new AbortController();
12440
12547
  blocked.abort();
@@ -12499,7 +12606,9 @@ var WasmProcess = class {
12499
12606
  const { ptys, jobs } = this.options;
12500
12607
  const owner = this.options.owners ? this.ttyOwner() : void 0;
12501
12608
  const guard2 = this.guarded();
12502
- return ptySyscall(req, { pid: this.pid, fds: this.fds, ptys, jobs, guard: guard2, owner });
12609
+ const { ttyFilter } = this.options;
12610
+ const ctx = { pid: this.pid, fds: this.fds, ptys, jobs, guard: guard2, owner, ttyFilter };
12611
+ return ptySyscall(req, ctx);
12503
12612
  }
12504
12613
  return await this.procSyscall(req, cancelled);
12505
12614
  } catch (e) {
@@ -12674,7 +12783,8 @@ var WasmProcess = class {
12674
12783
  const tty = name2 === void 0 ? this.controllingTerminal() : this.options.jobs?.terminalNamed(name2);
12675
12784
  if (!tty) throw new KernelError("ENXIO");
12676
12785
  if (name2 !== void 0) ttyOpenable(this.guarded(), tty);
12677
- return this.fds.install(tty.file(), 3);
12786
+ const file = tty.file();
12787
+ return this.fds.install(this.options.ttyFilter?.(file) ?? file, 3);
12678
12788
  }
12679
12789
  openSealed() {
12680
12790
  const broker = this.options.sealed;
@@ -12714,15 +12824,8 @@ var WasmProcess = class {
12714
12824
  async sealedCaller() {
12715
12825
  const shown = await this.options.identity?.();
12716
12826
  const broker = this.options.sealed;
12717
- const tag = broker.tagOf(this.pid);
12718
- return {
12719
- pid: shown?.pid ?? this.pid,
12720
- ppid: shown?.ppid ?? 0,
12721
- uid: this.cred.euid,
12722
- gid: this.cred.egid,
12723
- ...tag !== void 0 ? { tag } : {},
12724
- users: broker.users
12725
- };
12827
+ const ids = { pid: shown?.pid ?? this.pid, ppid: shown?.ppid ?? 0 };
12828
+ return broker.stamp(ids, this.cred, broker.tagOf(this.pid));
12726
12829
  }
12727
12830
  guarded() {
12728
12831
  const { guard: guard2 } = this.options;
@@ -13230,13 +13333,13 @@ var WasmProcess = class {
13230
13333
  while (this.dlLock.depth > 0 && this.dlLock.owner !== owner) {
13231
13334
  if (this.dying) throw new KernelError("EINTR");
13232
13335
  const interrupt = this.interrupt.signal;
13233
- const gone = await new Promise((resolve) => {
13336
+ const gone = await new Promise((resolve2) => {
13234
13337
  const waiter = {
13235
13338
  owner,
13236
13339
  wake: (ended) => {
13237
13340
  this.dlLock.waiters.delete(waiter);
13238
13341
  interrupt.removeEventListener("abort", recheck);
13239
- resolve(ended);
13342
+ resolve2(ended);
13240
13343
  }
13241
13344
  };
13242
13345
  const recheck = () => waiter.wake(false);
@@ -13492,6 +13595,7 @@ function processOptions(opts, header4, hooks, created3) {
13492
13595
  ...inherited(opts),
13493
13596
  ...opts.owners ? { owners: true } : {},
13494
13597
  ...opts.sealed ? { sealed: opts.sealed } : {},
13598
+ ...opts.ttyFilter ? { ttyFilter: opts.ttyFilter } : {},
13495
13599
  ...opts.clipboard ? { clipboard: opts.clipboard, ...clipboardName(opts) } : {},
13496
13600
  ...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
13497
13601
  ...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3 } } : {},
@@ -13986,6 +14090,523 @@ function replayingHops(inner, heldMs = HELD_MS) {
13986
14090
  };
13987
14091
  }
13988
14092
 
14093
+ // node_modules/@ai-ecoverse/slicc-scrub/dist/index.js
14094
+ var FRAGMENT = 16;
14095
+ var MIN_VALUE = 8;
14096
+ var MIN_BASE64 = 8;
14097
+ var encoder4 = new TextEncoder();
14098
+ var STD = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
14099
+ function utf82(text) {
14100
+ return encoder4.encode(text);
14101
+ }
14102
+ function base642(bytes, padded = true) {
14103
+ let out = "";
14104
+ for (let i = 0; i < bytes.length; i += 3) {
14105
+ const a = bytes[i];
14106
+ const b = i + 1 < bytes.length ? bytes[i + 1] : 0;
14107
+ const c = i + 2 < bytes.length ? bytes[i + 2] : 0;
14108
+ const triple = a << 16 | b << 8 | c;
14109
+ const left = bytes.length - i;
14110
+ out += STD[triple >> 18 & 63] + STD[triple >> 12 & 63];
14111
+ out += left > 1 ? STD[triple >> 6 & 63] : padded ? "=" : "";
14112
+ out += left > 2 ? STD[triple & 63] : padded ? "=" : "";
14113
+ }
14114
+ return out;
14115
+ }
14116
+ function urlSafe(text) {
14117
+ return text.replace(/\+/g, "-").replace(/\//g, "_");
14118
+ }
14119
+ function hex2(bytes) {
14120
+ let out = "";
14121
+ for (const byte of bytes) out += byte.toString(16).padStart(2, "0");
14122
+ return out;
14123
+ }
14124
+ function embedded(bytes, offset) {
14125
+ const shifted = new Uint8Array(offset + bytes.length);
14126
+ shifted.set(bytes, offset);
14127
+ const encoded = base642(shifted);
14128
+ const first = Math.ceil(8 * offset / 6);
14129
+ const last = Math.floor(8 * (offset + bytes.length) / 6);
14130
+ return encoded.slice(first, last);
14131
+ }
14132
+ function unicode(value, upper) {
14133
+ let out = "";
14134
+ for (let i = 0; i < value.length; i++) {
14135
+ const unit = value.charCodeAt(i);
14136
+ if (unit === 34) out += '\\"';
14137
+ else if (unit === 92) out += "\\\\";
14138
+ else if (unit < 32 || unit > 126) {
14139
+ const code = unit.toString(16).padStart(4, "0");
14140
+ out += `\\u${upper ? code.toUpperCase() : code}`;
14141
+ } else out += value[i];
14142
+ }
14143
+ return out;
14144
+ }
14145
+ function percent(value) {
14146
+ let component;
14147
+ try {
14148
+ component = encodeURIComponent(value);
14149
+ } catch {
14150
+ return [];
14151
+ }
14152
+ const lower = component.replace(/%[0-9A-F]{2}/g, (escape) => escape.toLowerCase());
14153
+ const plus = component.replace(/%20/g, "+");
14154
+ let form = "";
14155
+ for (const byte of utf82(value)) {
14156
+ const char = String.fromCharCode(byte);
14157
+ if (/[A-Za-z0-9*\-._]/.test(char)) form += char;
14158
+ else if (byte === 32) form += "+";
14159
+ else form += `%${byte.toString(16).toUpperCase().padStart(2, "0")}`;
14160
+ }
14161
+ return [component, lower, plus, form];
14162
+ }
14163
+ function html(value, apostrophe) {
14164
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, apostrophe);
14165
+ }
14166
+ function encodings(bytes) {
14167
+ const standard = [base642(bytes), base642(bytes, false)];
14168
+ for (const offset of [0, 1, 2]) {
14169
+ const run2 = embedded(bytes, offset);
14170
+ if (run2.length >= MIN_BASE64) standard.push(run2);
14171
+ }
14172
+ return [...standard, ...standard.map(urlSafe)];
14173
+ }
14174
+ function shapes(value) {
14175
+ const raw = utf82(value);
14176
+ const seen = /* @__PURE__ */ new Set([value]);
14177
+ const out = [];
14178
+ if (raw.length < FRAGMENT) out.push({ bytes: raw, form: "raw" });
14179
+ for (let offset = 0; raw.length >= FRAGMENT && offset + FRAGMENT <= raw.length; offset++) {
14180
+ out.push({ bytes: raw.subarray(offset, offset + FRAGMENT), form: "fragment", offset });
14181
+ }
14182
+ const add = (text, form) => {
14183
+ if (seen.has(text)) return;
14184
+ seen.add(text);
14185
+ out.push({ bytes: utf82(text), form });
14186
+ };
14187
+ const json5 = JSON.stringify(value).slice(1, -1);
14188
+ for (const text of [
14189
+ json5,
14190
+ json5.replace(/\//g, "\\/"),
14191
+ unicode(value, false),
14192
+ unicode(value, true)
14193
+ ]) {
14194
+ add(text, "json");
14195
+ }
14196
+ for (const text of percent(value)) add(text, "percent");
14197
+ for (const text of [html(value, "&#39;"), html(value, "&#x27;")]) add(text, "html");
14198
+ for (const text of encodings(raw)) add(text, "base64");
14199
+ add(hex2(raw), "hex");
14200
+ add(hex2(raw).toUpperCase(), "hex");
14201
+ return out;
14202
+ }
14203
+ function wrappable(form) {
14204
+ return form === "base64" || form === "hex";
14205
+ }
14206
+ var Automaton = class {
14207
+ edges = /* @__PURE__ */ new Map();
14208
+ fail = [0];
14209
+ depth = [0];
14210
+ children = [0];
14211
+ out = [[]];
14212
+ dict = [-1];
14213
+ lengths = [];
14214
+ add(bytes) {
14215
+ let node = 0;
14216
+ for (const byte of bytes) {
14217
+ const key = node * 256 + byte;
14218
+ let next2 = this.edges.get(key);
14219
+ if (next2 === void 0) {
14220
+ next2 = this.depth.length;
14221
+ this.edges.set(key, next2);
14222
+ this.fail.push(0);
14223
+ this.depth.push(this.depth[node] + 1);
14224
+ this.children.push(0);
14225
+ this.children[node] += 1;
14226
+ this.out.push([]);
14227
+ this.dict.push(-1);
14228
+ }
14229
+ node = next2;
14230
+ }
14231
+ const id2 = this.lengths.length;
14232
+ this.lengths.push(bytes.length);
14233
+ this.out[node].push(id2);
14234
+ return id2;
14235
+ }
14236
+ build() {
14237
+ const queue = [];
14238
+ const byNode = /* @__PURE__ */ new Map();
14239
+ for (const [key, child] of this.edges) {
14240
+ const parent = Math.floor(key / 256);
14241
+ const list = byNode.get(parent) ?? [];
14242
+ list.push([key % 256, child]);
14243
+ byNode.set(parent, list);
14244
+ }
14245
+ for (const [, child] of byNode.get(0) ?? []) queue.push(child);
14246
+ for (let head = 0; head < queue.length; head++) {
14247
+ const node = queue[head];
14248
+ const fallback = this.fail[node];
14249
+ this.dict[node] = this.out[fallback].length > 0 ? fallback : this.dict[fallback];
14250
+ for (const [byte, child] of byNode.get(node) ?? []) {
14251
+ let state = fallback;
14252
+ let target = this.edges.get(state * 256 + byte);
14253
+ while (target === void 0 && state > 0) {
14254
+ state = this.fail[state];
14255
+ target = this.edges.get(state * 256 + byte);
14256
+ }
14257
+ this.fail[child] = target ?? 0;
14258
+ queue.push(child);
14259
+ }
14260
+ }
14261
+ }
14262
+ step(state, byte) {
14263
+ let current = state;
14264
+ for (; ; ) {
14265
+ const next2 = this.edges.get(current * 256 + byte);
14266
+ if (next2 !== void 0) return next2;
14267
+ if (current === 0) return 0;
14268
+ current = this.fail[current];
14269
+ }
14270
+ }
14271
+ matches(state, visit) {
14272
+ for (let node = state; node > 0; node = this.dict[node]) {
14273
+ for (const id2 of this.out[node]) visit(id2);
14274
+ }
14275
+ }
14276
+ partials(state, length2, visit) {
14277
+ for (let node = state; node > 0; node = this.fail[node]) {
14278
+ if (this.children[node] > 0) visit(length2 - this.depth[node]);
14279
+ }
14280
+ }
14281
+ };
14282
+ var RANK = {
14283
+ raw: 0,
14284
+ fragment: 1,
14285
+ json: 2,
14286
+ percent: 3,
14287
+ html: 4,
14288
+ base64: 5,
14289
+ hex: 6,
14290
+ wrapped: 7
14291
+ };
14292
+ var CR2 = 13;
14293
+ var LF2 = 10;
14294
+ var EMPTY2 = new Uint8Array(0);
14295
+ function register(automaton, sources, ids, bytes, source) {
14296
+ const key = Array.from(bytes).join(",");
14297
+ let id2 = ids.get(key);
14298
+ if (id2 === void 0) {
14299
+ id2 = automaton.add(bytes);
14300
+ ids.set(key, id2);
14301
+ sources[id2] = [];
14302
+ }
14303
+ sources[id2].push(source);
14304
+ }
14305
+ function prepare(secrets) {
14306
+ const main = new Automaton();
14307
+ const wrap = new Automaton();
14308
+ const sources = [];
14309
+ const wrapSources = [];
14310
+ const ids = /* @__PURE__ */ new Map();
14311
+ const wrapIds = /* @__PURE__ */ new Map();
14312
+ const values = secrets.map((secret) => utf82(secret.value));
14313
+ secrets.forEach((secret, index) => {
14314
+ if (typeof secret.value !== "string" || values[index].length < MIN_VALUE) {
14315
+ throw new RangeError(`the value of ${secret.name} is shorter than ${MIN_VALUE} bytes`);
14316
+ }
14317
+ for (const shape of shapes(secret.value)) {
14318
+ const source = { secret: index, form: shape.form, offset: shape.offset };
14319
+ register(main, sources, ids, shape.bytes, source);
14320
+ if (wrappable(shape.form)) register(wrap, wrapSources, wrapIds, shape.bytes, source);
14321
+ }
14322
+ });
14323
+ main.build();
14324
+ wrap.build();
14325
+ const replacements = secrets.map((secret) => [
14326
+ utf82(secret.placeholder),
14327
+ utf82(`[redacted:${secret.name}]`)
14328
+ ]);
14329
+ return { secrets, values, main, wrap, sources, wrapSources, replacements };
14330
+ }
14331
+ function better(a, b) {
14332
+ if (!b) return true;
14333
+ if (a.end !== b.end) return a.end > b.end;
14334
+ if (a.rank !== b.rank) return a.rank < b.rank;
14335
+ return a.secret < b.secret;
14336
+ }
14337
+ function resolve(c, input, start2, end, source) {
14338
+ if (source.form !== "fragment") {
14339
+ return {
14340
+ start: start2,
14341
+ end,
14342
+ secret: source.secret,
14343
+ form: source.form,
14344
+ rank: RANK[source.form],
14345
+ growing: false
14346
+ };
14347
+ }
14348
+ const value = c.values[source.secret];
14349
+ let stop = end;
14350
+ let at = source.offset + FRAGMENT;
14351
+ while (stop < input.length && at < value.length && input[stop] === value[at]) {
14352
+ stop++;
14353
+ at++;
14354
+ }
14355
+ const form = source.offset === 0 && at === value.length ? "raw" : "fragment";
14356
+ const growing = stop === input.length && at < value.length;
14357
+ return { start: start2, end: stop, secret: source.secret, form, rank: RANK[form], growing };
14358
+ }
14359
+ function keep(best, candidate) {
14360
+ if (better(candidate, best.get(candidate.start))) best.set(candidate.start, candidate);
14361
+ }
14362
+ function scanMain(c, input, best, partials) {
14363
+ let state = 0;
14364
+ for (let i = 0; i < input.length; i++) {
14365
+ state = c.main.step(state, input[i]);
14366
+ c.main.matches(state, (id2) => {
14367
+ const start2 = i + 1 - c.main.lengths[id2];
14368
+ for (const source of c.sources[id2]) {
14369
+ const candidate = resolve(c, input, start2, i + 1, source);
14370
+ if (candidate.growing) partials.push(candidate.start);
14371
+ keep(best, candidate);
14372
+ }
14373
+ });
14374
+ }
14375
+ c.main.partials(state, input.length, (start2) => partials.push(start2));
14376
+ }
14377
+ function scanWrapped(c, input, best, partials) {
14378
+ const map = [];
14379
+ for (let i = 0; i < input.length; i++) if (input[i] !== CR2 && input[i] !== LF2) map.push(i);
14380
+ if (map.length === input.length) return;
14381
+ let state = 0;
14382
+ for (let v = 0; v < map.length; v++) {
14383
+ state = c.wrap.step(state, input[map[v]]);
14384
+ c.wrap.matches(state, (id2) => {
14385
+ const first = v + 1 - c.wrap.lengths[id2];
14386
+ const start2 = map[first];
14387
+ const end = map[v] + 1;
14388
+ const wrapped = end - start2 !== c.wrap.lengths[id2];
14389
+ for (const source of c.wrapSources[id2]) {
14390
+ const form = wrapped ? "wrapped" : source.form;
14391
+ keep(best, { start: start2, end, secret: source.secret, form, rank: RANK[form], growing: false });
14392
+ }
14393
+ });
14394
+ }
14395
+ c.wrap.partials(state, map.length, (start2) => partials.push(map[start2]));
14396
+ }
14397
+ function count(c, hits, candidate) {
14398
+ const name2 = c.secrets[candidate.secret].name;
14399
+ const key = `${name2}\0${candidate.form}`;
14400
+ const hit = hits.get(key) ?? { name: name2, form: candidate.form, count: 0 };
14401
+ hit.count += 1;
14402
+ hits.set(key, hit);
14403
+ }
14404
+ function concat5(parts2) {
14405
+ let length2 = 0;
14406
+ for (const part of parts2) length2 += part.length;
14407
+ const out = new Uint8Array(length2);
14408
+ let at = 0;
14409
+ for (const part of parts2) {
14410
+ out.set(part, at);
14411
+ at += part.length;
14412
+ }
14413
+ return out;
14414
+ }
14415
+ function pass(c, input, final) {
14416
+ const best = /* @__PURE__ */ new Map();
14417
+ const partials = [];
14418
+ scanMain(c, input, best, partials);
14419
+ scanWrapped(c, input, best, partials);
14420
+ const holds = final ? [] : [...new Set(partials)].sort((a, b) => a - b);
14421
+ const starts = [...best.keys()].sort((a, b) => a - b);
14422
+ const parts2 = [];
14423
+ const hits = /* @__PURE__ */ new Map();
14424
+ let pos = 0;
14425
+ let cut = input.length;
14426
+ let s = 0;
14427
+ let h = 0;
14428
+ for (; ; ) {
14429
+ while (s < starts.length && starts[s] < pos) s++;
14430
+ while (h < holds.length && holds[h] < pos) h++;
14431
+ const hold = h < holds.length ? holds[h] : Infinity;
14432
+ const next2 = s < starts.length ? starts[s] : Infinity;
14433
+ if (hold <= next2) {
14434
+ cut = Math.min(hold, input.length);
14435
+ break;
14436
+ }
14437
+ const candidate = best.get(next2);
14438
+ parts2.push(
14439
+ input.subarray(pos, next2),
14440
+ c.replacements[candidate.secret][candidate.form === "raw" ? 0 : 1]
14441
+ );
14442
+ count(c, hits, candidate);
14443
+ pos = candidate.end;
14444
+ }
14445
+ parts2.push(input.subarray(pos, cut));
14446
+ return { output: concat5(parts2), cut, hits };
14447
+ }
14448
+ function merge(into, from) {
14449
+ for (const [key, hit] of from) {
14450
+ const known = into.get(key);
14451
+ if (known) known.count += hit.count;
14452
+ else into.set(key, { ...hit });
14453
+ }
14454
+ }
14455
+ function compile(secrets, options = {}) {
14456
+ const c = prepare(secrets.map((secret) => ({ ...secret })));
14457
+ const decoder4 = new TextDecoder();
14458
+ const idleMs = options.idleMs ?? 250;
14459
+ const scrubBytes = (input) => {
14460
+ if (c.secrets.length === 0) return { bytes: input.slice(), hits: [] };
14461
+ const result = pass(c, input, true);
14462
+ return { bytes: result.output, hits: [...result.hits.values()] };
14463
+ };
14464
+ return {
14465
+ version: options.version ?? 0,
14466
+ idleMs,
14467
+ size: c.main.lengths.length,
14468
+ bytes: scrubBytes,
14469
+ text(input) {
14470
+ const { bytes, hits } = scrubBytes(utf82(input));
14471
+ return { text: decoder4.decode(bytes), hits };
14472
+ },
14473
+ stream() {
14474
+ let pending = EMPTY2;
14475
+ const hits = /* @__PURE__ */ new Map();
14476
+ const run2 = (final) => {
14477
+ if (c.secrets.length === 0 || pending.length === 0) {
14478
+ const out = pending;
14479
+ pending = EMPTY2;
14480
+ return out;
14481
+ }
14482
+ const result = pass(c, pending, final);
14483
+ merge(hits, result.hits);
14484
+ pending = pending.slice(result.cut);
14485
+ return result.output;
14486
+ };
14487
+ return {
14488
+ push(chunk2) {
14489
+ pending = concat5([pending, chunk2]);
14490
+ return run2(false);
14491
+ },
14492
+ idle() {
14493
+ return pending.length < FRAGMENT ? run2(true) : EMPTY2;
14494
+ },
14495
+ end() {
14496
+ return run2(true);
14497
+ },
14498
+ get hits() {
14499
+ return [...hits.values()];
14500
+ }
14501
+ };
14502
+ }
14503
+ };
14504
+ }
14505
+
14506
+ // src/kernel/scrub-file.ts
14507
+ var SCRUB_IDLE_MS = 50;
14508
+ var encoder5 = new TextEncoder();
14509
+ function scrubberOf(secrets) {
14510
+ const usable = secrets.filter((s) => encoder5.encode(s.value).length >= MIN_VALUE);
14511
+ return usable.length > 0 ? compile(usable, { idleMs: SCRUB_IDLE_MS }) : void 0;
14512
+ }
14513
+ async function writeAll2(file, bytes, signal) {
14514
+ const write = file.write;
14515
+ for (let at = 0; at < bytes.length; ) {
14516
+ at += await write.call(file, bytes.subarray(at), at === 0 ? signal : void 0);
14517
+ }
14518
+ }
14519
+ function bound(file, key) {
14520
+ const method = file[key];
14521
+ return typeof method === "function" ? { [key]: method.bind(file) } : {};
14522
+ }
14523
+ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
14524
+ const file = inner.file;
14525
+ const stream2 = scrubber.stream();
14526
+ let queue = Promise.resolve();
14527
+ let timer;
14528
+ let failed3;
14529
+ const raise = () => {
14530
+ if (failed3 === void 0) return;
14531
+ const err = failed3;
14532
+ failed3 = void 0;
14533
+ throw err;
14534
+ };
14535
+ const enqueue = (bytes, signal) => {
14536
+ const next2 = queue.then(() => bytes.length > 0 ? writeAll2(file, bytes, signal) : void 0);
14537
+ queue = next2.catch(() => void 0);
14538
+ return next2;
14539
+ };
14540
+ const release = () => {
14541
+ timer = void 0;
14542
+ enqueue(stream2.idle()).catch((err) => {
14543
+ failed3 = err;
14544
+ });
14545
+ };
14546
+ return new OpenFile({
14547
+ ...bound(file, "read"),
14548
+ ...bound(file, "peek"),
14549
+ ...bound(file, "poll"),
14550
+ ...bound(file, "changed"),
14551
+ ...bound(file, "stat"),
14552
+ ...file.tty ? { tty: file.tty } : {},
14553
+ ...file.clipboard ? { clipboard: file.clipboard } : {},
14554
+ ...file.write ? {
14555
+ write: async (bytes, signal) => {
14556
+ if (timer !== void 0) clearTimeout(timer);
14557
+ raise();
14558
+ await enqueue(stream2.push(bytes), signal);
14559
+ timer = setTimeout(release, idleMs);
14560
+ return bytes.length;
14561
+ }
14562
+ } : {},
14563
+ flush: async () => {
14564
+ await queue;
14565
+ raise();
14566
+ await file.flush?.();
14567
+ },
14568
+ close: async () => {
14569
+ if (timer !== void 0) clearTimeout(timer);
14570
+ let failure;
14571
+ try {
14572
+ await enqueue(stream2.end());
14573
+ raise();
14574
+ } catch (err) {
14575
+ failure = { err };
14576
+ }
14577
+ await inner.release();
14578
+ if (failure) throw failure.err;
14579
+ }
14580
+ });
14581
+ }
14582
+ function scrubbedAll(file, scrubbers) {
14583
+ return scrubbers.reduce((inner, scrubber) => scrubbed(inner, scrubber), file);
14584
+ }
14585
+ function stdioFiles(fds) {
14586
+ const files = /* @__PURE__ */ new Set();
14587
+ for (const fd of [1, 2]) {
14588
+ try {
14589
+ files.add(fds.get(fd));
14590
+ } catch {
14591
+ }
14592
+ }
14593
+ return files;
14594
+ }
14595
+ function scrubStdio(fds, scrubber) {
14596
+ const wrapped = /* @__PURE__ */ new Map();
14597
+ const stdio = stdioFiles(fds);
14598
+ for (const fd of fds.numbers()) {
14599
+ const original = fds.get(fd);
14600
+ if (!stdio.has(original) && !original.file.tty) continue;
14601
+ const known = wrapped.get(original);
14602
+ const flags2 = fds.statusFlags(fd);
14603
+ const wrapper = known ? known.retain() : scrubbed(original.retain(), scrubber);
14604
+ wrapped.set(original, wrapper);
14605
+ fds.installAt(fd, wrapper);
14606
+ if (flags2 !== void 0) fds.setStatusFlags(fd, flags2);
14607
+ }
14608
+ }
14609
+
13989
14610
  // src/kernel/sealed.ts
13990
14611
  var SEALED_MAX_BYTES = 64 * 1024;
13991
14612
  var SEALED_TIMEOUT_MS = 1e4;
@@ -13999,7 +14620,7 @@ var SEALED_OPS = /* @__PURE__ */ new Set([
13999
14620
  "cancel",
14000
14621
  "materialize"
14001
14622
  ]);
14002
- var encoder4 = new TextEncoder();
14623
+ var encoder6 = new TextEncoder();
14003
14624
  var decoder3 = new TextDecoder("utf-8", { fatal: true });
14004
14625
  function parseSealed(bytes) {
14005
14626
  try {
@@ -14015,11 +14636,16 @@ function parseSealed(bytes) {
14015
14636
  const req = parsed2;
14016
14637
  if (req.v !== 1 || typeof req.op !== "string" || !SEALED_OPS.has(req.op))
14017
14638
  throw new KernelError("EINVAL");
14639
+ if (req.op === "materialize" && !materializable(req)) throw new KernelError("EINVAL");
14018
14640
  return req;
14019
14641
  } finally {
14020
14642
  bytes.fill(0);
14021
14643
  }
14022
14644
  }
14645
+ function materializable(req) {
14646
+ if (req.shellGrants !== true && !isStrings(req.names)) return false;
14647
+ return isStrings(req.argv) && typeof req.path === "string" && typeof req.cwd === "string";
14648
+ }
14023
14649
  function errorReply(code, message3) {
14024
14650
  return { v: 1, error: { code, message: message3 } };
14025
14651
  }
@@ -14063,12 +14689,14 @@ var SealedChannel = class {
14063
14689
  const timer = setTimeout(() => control.abort("timeout"), this.broker.timeoutMs);
14064
14690
  const req = { ...fields, v: 1, op: fields.op, caller };
14065
14691
  const flight = { control, done: Promise.resolve() };
14066
- flight.done = this.answer(req, control.signal).then((reply) => {
14692
+ flight.done = this.answer(req, control.signal).then(async (reply) => {
14067
14693
  clearTimeout(timer);
14068
14694
  gone.removeEventListener("abort", withdraw);
14695
+ if (reply && req.op === "materialize")
14696
+ await this.broker.tickets.record(req, reply, caller.pid);
14069
14697
  if (this.inflight !== flight) return;
14070
14698
  this.inflight = void 0;
14071
- if (reply) this.reply = encoder4.encode(serialized(reply));
14699
+ if (reply) this.reply = encoder6.encode(serialized(reply));
14072
14700
  });
14073
14701
  this.inflight = flight;
14074
14702
  return length2;
@@ -14076,8 +14704,8 @@ var SealedChannel = class {
14076
14704
  async answer(req, signal) {
14077
14705
  const hook = this.broker.hook;
14078
14706
  if (!hook) return errorReply("ENOTCONN", "no vault is connected");
14079
- const stopped = new Promise((resolve) => {
14080
- signal.addEventListener("abort", () => resolve(void 0), { once: true });
14707
+ const stopped = new Promise((resolve2) => {
14708
+ signal.addEventListener("abort", () => resolve2(void 0), { once: true });
14081
14709
  });
14082
14710
  if (signal.aborted) return void 0;
14083
14711
  const asked = Promise.resolve().then(() => hook(req, { signal })).then(
@@ -14132,17 +14760,60 @@ var SealedChannel = class {
14132
14760
  this.reply = void 0;
14133
14761
  }
14134
14762
  };
14763
+ function swapOf(reply) {
14764
+ if (!isRecord(reply) || reply.error !== void 0) return void 0;
14765
+ const values = stringRecord(reply.values);
14766
+ const placeholders = stringRecord(reply.placeholders);
14767
+ return values && placeholders ? { values, placeholders } : void 0;
14768
+ }
14135
14769
  var SealedBroker = class {
14136
14770
  hook;
14137
14771
  users;
14138
14772
  timeoutMs;
14139
14773
  tagOf;
14774
+ tickets = new TicketBook();
14775
+ userOf = async () => void 0;
14140
14776
  constructor(hook, users, timeoutMs = SEALED_TIMEOUT_MS, tagOf = () => void 0) {
14141
14777
  this.hook = hook;
14142
14778
  this.users = users;
14143
14779
  this.timeoutMs = timeoutMs;
14144
14780
  this.tagOf = tagOf;
14145
14781
  }
14782
+ async stamp(ids, cred, tag) {
14783
+ const user = await this.userOf(cred.euid).catch(() => void 0);
14784
+ return {
14785
+ pid: ids.pid,
14786
+ ppid: ids.ppid,
14787
+ uid: cred.euid,
14788
+ gid: cred.egid,
14789
+ ...user !== void 0 ? { user } : {},
14790
+ groups: [...cred.groups],
14791
+ users: this.users,
14792
+ ...tag !== void 0 ? { tag } : {}
14793
+ };
14794
+ }
14795
+ async consume(fields) {
14796
+ const { hook } = this;
14797
+ if (!hook) return void 0;
14798
+ const control = new AbortController();
14799
+ const timer = setTimeout(() => control.abort("timeout"), this.timeoutMs);
14800
+ const stopped = new Promise((resolve2) => {
14801
+ control.signal.addEventListener("abort", () => resolve2(void 0), { once: true });
14802
+ });
14803
+ const req = {
14804
+ ...fields,
14805
+ v: 1,
14806
+ op: "materializeConsume",
14807
+ caller: fields.stamp
14808
+ };
14809
+ try {
14810
+ const asked = Promise.resolve().then(() => hook(req, { signal: control.signal })).then(swapOf, () => void 0);
14811
+ return await Promise.race([asked, stopped]);
14812
+ } finally {
14813
+ clearTimeout(timer);
14814
+ control.abort("done");
14815
+ }
14816
+ }
14146
14817
  open() {
14147
14818
  const channel = new SealedChannel(this);
14148
14819
  return new OpenFile({
@@ -14641,11 +15312,11 @@ function codeOf3(err) {
14641
15312
  }
14642
15313
  function wait(ms, signal) {
14643
15314
  if (signal.aborted) return Promise.resolve();
14644
- return new Promise((resolve) => {
15315
+ return new Promise((resolve2) => {
14645
15316
  const done4 = () => {
14646
15317
  clearTimeout(timer);
14647
15318
  signal.removeEventListener("abort", done4);
14648
- resolve();
15319
+ resolve2();
14649
15320
  };
14650
15321
  const timer = setTimeout(done4, ms);
14651
15322
  signal.addEventListener("abort", done4, { once: true });
@@ -14972,11 +15643,11 @@ async function resize(h, path, size) {
14972
15643
  }
14973
15644
  }
14974
15645
  function sleep(ms, signal) {
14975
- return new Promise((resolve) => {
15646
+ return new Promise((resolve2) => {
14976
15647
  const done4 = () => {
14977
15648
  clearTimeout(timer);
14978
15649
  signal.removeEventListener("abort", done4);
14979
- resolve();
15650
+ resolve2();
14980
15651
  };
14981
15652
  const timer = setTimeout(done4, ms);
14982
15653
  signal.addEventListener("abort", done4);
@@ -15059,7 +15730,7 @@ var PORT_DEAD_MS = 1e4;
15059
15730
  var PORT_LINGER_MS = 1e3;
15060
15731
  function timed(transport, ms) {
15061
15732
  const inflight = /* @__PURE__ */ new Set();
15062
- const guard2 = (name2, run2, late, expire) => new Promise((resolve, reject) => {
15733
+ const guard2 = (name2, run2, late, expire) => new Promise((resolve2, reject) => {
15063
15734
  let expired = false;
15064
15735
  const running = run2();
15065
15736
  inflight.add(running);
@@ -15076,7 +15747,7 @@ function timed(transport, ms) {
15076
15747
  (value) => {
15077
15748
  clearTimeout(timer);
15078
15749
  if (expired) late?.(value);
15079
- resolve(value);
15750
+ resolve2(value);
15080
15751
  },
15081
15752
  (err) => {
15082
15753
  clearTimeout(timer);
@@ -15211,7 +15882,7 @@ var PortTransport = class {
15211
15882
  ask(message3, bytes = 0, transfer = []) {
15212
15883
  const id2 = ++this.next;
15213
15884
  if (this.dead) return Promise.reject(fsError2("ENOTCONN", this.dead));
15214
- return new Promise((resolve, reject) => {
15885
+ return new Promise((resolve2, reject) => {
15215
15886
  const timer = setTimeout(
15216
15887
  () => {
15217
15888
  this.waiting.delete(id2);
@@ -15225,7 +15896,7 @@ var PortTransport = class {
15225
15896
  clearTimeout(timer);
15226
15897
  this.waiting.delete(id2);
15227
15898
  if (reply.error) reject(fsError2(reply.error.code ?? "EIO", reply.error.message ?? ""));
15228
- else resolve(reply);
15899
+ else resolve2(reply);
15229
15900
  });
15230
15901
  try {
15231
15902
  this.port.postMessage({ id: id2, ...message3 }, transfer);
@@ -15254,7 +15925,7 @@ var PortTransport = class {
15254
15925
  this.fail("the folder's watch ended");
15255
15926
  return Promise.resolve();
15256
15927
  }
15257
- return new Promise((resolve, reject) => {
15928
+ return new Promise((resolve2, reject) => {
15258
15929
  const finish = () => {
15259
15930
  this.waiting.delete(id2);
15260
15931
  signal.removeEventListener("abort", stop);
@@ -15265,7 +15936,7 @@ var PortTransport = class {
15265
15936
  this.port.postMessage({ id: id2, op: "unwatch" });
15266
15937
  } catch {
15267
15938
  }
15268
- resolve();
15939
+ resolve2();
15269
15940
  };
15270
15941
  this.watchBeat();
15271
15942
  this.waiting.set(id2, (reply) => {
@@ -15273,7 +15944,7 @@ var PortTransport = class {
15273
15944
  if (reply.event) return onEvent(reply.event);
15274
15945
  finish();
15275
15946
  if (reply.error) reject(fsError2(reply.error.code ?? "ENOTCONN", reply.error.message ?? ""));
15276
- else resolve();
15947
+ else resolve2();
15277
15948
  });
15278
15949
  signal.addEventListener("abort", stop, { once: true });
15279
15950
  try {
@@ -15489,10 +16160,10 @@ var ChunkedBytes = class {
15489
16160
  }
15490
16161
  truncate(size) {
15491
16162
  if (size < this.size) {
15492
- const keep = Math.ceil(size / CHUNK3);
15493
- this.chunks.length = Math.min(this.chunks.length, keep);
15494
- const tail = this.chunks[keep - 1];
15495
- const from = size - (keep - 1) * CHUNK3;
16163
+ const keep2 = Math.ceil(size / CHUNK3);
16164
+ this.chunks.length = Math.min(this.chunks.length, keep2);
16165
+ const tail = this.chunks[keep2 - 1];
16166
+ const from = size - (keep2 - 1) * CHUNK3;
15496
16167
  if (tail && from < tail.length) tail.fill(0, from);
15497
16168
  }
15498
16169
  this.size = size;
@@ -15737,8 +16408,8 @@ function importedMemory(bytes) {
15737
16408
  at = end;
15738
16409
  continue;
15739
16410
  }
15740
- const count = u32();
15741
- for (let i = 0; i < count; i++) {
16411
+ const count2 = u32();
16412
+ for (let i = 0; i < count2; i++) {
15742
16413
  const module = name2();
15743
16414
  const field3 = name2();
15744
16415
  const kind = bytes[at++];
@@ -16167,7 +16838,7 @@ ${lines2}
16167
16838
  const withdraw = () => control.abort("withdrawn");
16168
16839
  requester.addEventListener("abort", withdraw, { once: true });
16169
16840
  const aborted2 = new Promise(
16170
- (resolve) => control.signal.addEventListener("abort", () => resolve("aborted"), { once: true })
16841
+ (resolve2) => control.signal.addEventListener("abort", () => resolve2("aborted"), { once: true })
16171
16842
  );
16172
16843
  try {
16173
16844
  const answered = Promise.resolve().then(() => approve(req, { signal: control.signal })).catch(() => "deny");
@@ -16293,7 +16964,7 @@ var LAYOUT = [
16293
16964
  ];
16294
16965
  var LOAD_SAMPLE_MS = 5e3;
16295
16966
  var LOAD_WINDOWS_MS = [6e4, 3e5, 9e5];
16296
- var encoder5 = new TextEncoder();
16967
+ var encoder7 = new TextEncoder();
16297
16968
  var USERS_MODES = ["off", "report", "enforce"];
16298
16969
  function usersMode(mode = "off") {
16299
16970
  if (USERS_MODES.includes(mode)) return mode;
@@ -16324,6 +16995,30 @@ function shebang(head) {
16324
16995
  if (head[0] !== 35 || head[1] !== 33) return void 0;
16325
16996
  return new TextDecoder().decode(head.subarray(0, SHEBANG_MAX)).slice(2).split("\n")[0].trim().split(/[ \t]+/);
16326
16997
  }
16998
+ async function rootOnly(fs, path) {
16999
+ const names = chain(path);
17000
+ for (const [i, at] of names.entries()) {
17001
+ const st = await fs.lstat(at).catch(() => void 0);
17002
+ const kind = i === names.length - 1 ? st?.isFile : st?.isDirectory;
17003
+ if (!kind || st?.uid !== 0 || (st.mode & 18) !== 0) return false;
17004
+ }
17005
+ return true;
17006
+ }
17007
+ function plainEnv(words2) {
17008
+ const next2 = words2[1];
17009
+ if (baseName(words2[0] ?? "") !== "env" || next2 === void 0) return false;
17010
+ return next2 === "-S" || !next2.startsWith("-") && !next2.includes("=");
17011
+ }
17012
+ function shebangCommand(words2) {
17013
+ if (!plainEnv(words2)) {
17014
+ const arg2 = words2.slice(1).join(" ");
17015
+ return { interp: words2[0], args: arg2 ? [arg2] : [] };
17016
+ }
17017
+ const split2 = words2[1] === "-S";
17018
+ const [interp, ...rest2] = words2.slice(split2 ? 2 : 1);
17019
+ const arg = rest2.join(" ");
17020
+ return { interp, args: split2 ? rest2 : arg ? [arg] : [] };
17021
+ }
16327
17022
  function interpreter(head) {
16328
17023
  const words2 = shebang(head);
16329
17024
  if (!words2) return "node";
@@ -16377,7 +17072,7 @@ function childHandle(handle) {
16377
17072
  onState: (listener) => handle.onState(listener)
16378
17073
  };
16379
17074
  }
16380
- function concat5(chunks2) {
17075
+ function concat6(chunks2) {
16381
17076
  const out = new Uint8Array(chunks2.reduce((n, chunk2) => n + chunk2.length, 0));
16382
17077
  let offset = 0;
16383
17078
  for (const chunk2 of chunks2) {
@@ -16463,6 +17158,7 @@ var Launcher = class {
16463
17158
  usersMode;
16464
17159
  dac;
16465
17160
  sealed;
17161
+ scrubs = /* @__PURE__ */ new Map();
16466
17162
  clipboard;
16467
17163
  rootFs;
16468
17164
  fileWatches;
@@ -16486,6 +17182,8 @@ var Launcher = class {
16486
17182
  void 0,
16487
17183
  (pid) => this.tagOf(pid)
16488
17184
  );
17185
+ this.sealed.tickets.normalise = (path) => physicalPathAsync(this.base, path);
17186
+ this.sealed.userOf = async (uid) => (await this.users.lookup(uid))?.name;
16489
17187
  if (this.owners) options.fs.trackOwners?.();
16490
17188
  this.mounts = new MountTable({
16491
17189
  open: (type, spec) => this.driver(type, spec),
@@ -16734,13 +17432,12 @@ var Launcher = class {
16734
17432
  const binfmt = isWasm(head) ? void 0 : binfmtOf(await this.interpreters(scope), script);
16735
17433
  const words2 = shebang(head) ?? (binfmt ? [binfmt] : void 0);
16736
17434
  if (!words2) return void 0;
16737
- if (baseName(words2[0]) === "env") words2.shift();
16738
- const [interp, ...rest2] = words2;
16739
- const arg = rest2.join(" ");
16740
- const passed = [...arg ? [arg] : [], command ? script : file, ...argv.slice(1)];
16741
- const found = interp ? await this.resolve(interp, interp, cwd, scope) : void 0;
17435
+ const { interp, args } = shebangCommand(words2);
17436
+ const passed = [...args, command ? script : file, ...argv.slice(1)];
17437
+ if (!interp) return void 0;
17438
+ const found = await this.resolve(interp, interp, cwd, scope);
16742
17439
  if (!found) {
16743
- if (!interp || depth >= MAX_INTERPRETERS || !await this.scriptCommand(interp, cwd, scope)) {
17440
+ if (depth >= MAX_INTERPRETERS || !await this.scriptCommand(interp, cwd, scope)) {
16744
17441
  return void 0;
16745
17442
  }
16746
17443
  const inner = await this.interpreted(interp, [interp, ...passed], cwd, scope, depth + 1);
@@ -16780,6 +17477,99 @@ var Launcher = class {
16780
17477
  await need(g, "exec", this.fs.resolvePath(req.cwd, file), want);
16781
17478
  }
16782
17479
  }
17480
+ async checkedPlan(req, { ppid, cred, scope }, fds) {
17481
+ const planned = await this.plan(req.file, req.argv, req.cwd, scope, req.argv0);
17482
+ try {
17483
+ await this.mayExec(cred, { ...req, pid: ppid }, planned);
17484
+ } catch (err) {
17485
+ await fds.closeAll();
17486
+ throw err.code === "EACCES" ? new SpawnError("EACCES") : err;
17487
+ }
17488
+ if (planned) return planned;
17489
+ await fds.closeAll();
17490
+ throw new SpawnError(await this.unrunnable(req.file, req.cwd, scope));
17491
+ }
17492
+ async ticketFile(req, fds) {
17493
+ if (req.file.startsWith("/")) return physicalPathAsync(this.base, req.file);
17494
+ await fds.closeAll();
17495
+ throw new SpawnError("EACCES");
17496
+ }
17497
+ takeTicket(req) {
17498
+ if (!(TICKET_ENV in req.env)) return void 0;
17499
+ return this.sealed.tickets.take(req.env[TICKET_ENV] ?? "") ?? null;
17500
+ }
17501
+ async redeemed(req, who, planned, fds) {
17502
+ if (who.booked === void 0) return { env: req.env };
17503
+ try {
17504
+ const redeemed = await this.redeem(req, who, planned);
17505
+ if (redeemed.scrub) scrubStdio(fds, redeemed.scrub);
17506
+ return redeemed;
17507
+ } catch {
17508
+ await fds.closeAll();
17509
+ throw new SpawnError("EACCES");
17510
+ }
17511
+ }
17512
+ async redeem(req, { ppid, cred, scope, booked }, planned) {
17513
+ const { [TICKET_ENV]: ticket = "", ...env } = req.env;
17514
+ const caller = this.jobs.shown(ppid);
17515
+ const named = typeof req.argv0 === "string" && !planned.script && !planned.target.fixed;
17516
+ const argv = named ? [req.argv0, ...req.argv.slice(1)] : req.argv;
17517
+ const exact = booked?.pid === caller && req.file === booked.path && await physicalPathAsync(this.base, req.cwd) === booked.cwd && await argvDigest(argv) === booked.argv;
17518
+ if (!exact) throw new SpawnError("EACCES");
17519
+ const { requested: path, requestedCwd: cwd, path: resolved2 } = booked;
17520
+ const pid = this.nextPid++;
17521
+ const ids = { pid: req.exec ? caller : pid, ppid: caller };
17522
+ const stamp = await this.sealed.stamp(ids, cred, this.tagOf(ppid));
17523
+ const interpreters = await this.interpreterChain(planned, booked.cwd, scope);
17524
+ const trusted = this.usersMode === "enforce" && await this.trustedFiles(planned, req.file, booked.cwd, scope);
17525
+ const swap = await this.sealed.consume({
17526
+ ticket,
17527
+ stamp,
17528
+ path,
17529
+ resolved: resolved2,
17530
+ argv,
17531
+ cwd,
17532
+ interpreters,
17533
+ trusted
17534
+ });
17535
+ if (!swap) throw new SpawnError("EACCES");
17536
+ const secrets = [];
17537
+ for (const [name2, placeholder] of Object.entries(swap.placeholders)) {
17538
+ const value = swap.values[name2];
17539
+ if (value === void 0 || env[name2] !== placeholder) continue;
17540
+ env[name2] = value;
17541
+ secrets.push({ name: name2, value, placeholder });
17542
+ }
17543
+ const scrub = scrubberOf(secrets);
17544
+ return { env, pid, cwd: booked.cwd, ...scrub ? { scrub } : {} };
17545
+ }
17546
+ async interpreterChain(planned, cwd, scope) {
17547
+ const out = [];
17548
+ for (const entry of planned.via ?? []) {
17549
+ if (entry.includes("/")) {
17550
+ out.push(await physicalPathAsync(this.base, this.fs.resolvePath(cwd, entry)));
17551
+ continue;
17552
+ }
17553
+ const script = (await this.scriptCommand(entry, cwd, scope))?.script;
17554
+ out.push(
17555
+ await physicalPathAsync(this.base, script ?? await this.commandFile(entry, cwd, scope))
17556
+ );
17557
+ }
17558
+ return out;
17559
+ }
17560
+ async commandFile(name2, cwd, scope) {
17561
+ const scoped = await this.resolve(name2, name2, cwd, scope);
17562
+ const system = scope === "" ? scoped : await this.resolve(name2, name2, cwd, "");
17563
+ if (!scoped || system?.glue === scoped.glue) return `/usr/bin/${name2}`;
17564
+ return scoped.glue;
17565
+ }
17566
+ async trustedFiles(planned, file, cwd, scope) {
17567
+ const files = await this.backingOf(planned, file, cwd, scope);
17568
+ for (const at of files) {
17569
+ if (!await rootOnly(this.base, await physicalPathAsync(this.base, at))) return false;
17570
+ }
17571
+ return true;
17572
+ }
16783
17573
  async grantedCwd(caller, cwd) {
16784
17574
  const real = await physicalPathAsync(this.base, cwd).catch(() => void 0);
16785
17575
  return real === cwd && await this.sudoer.reachable(caller, cwd);
@@ -16798,13 +17588,15 @@ var Launcher = class {
16798
17588
  }
16799
17589
  async backing(file, cwd) {
16800
17590
  const planned = await this.plan(file, [file], cwd, "");
16801
- if (!planned) return void 0;
17591
+ return planned && this.backingOf(planned, file, cwd, "");
17592
+ }
17593
+ async backingOf(planned, file, cwd, scope) {
16802
17594
  const { glue, wasm, imports } = planned.target;
16803
17595
  const files = [glue, wasm, ...imports ? [imports] : []];
16804
17596
  const scripts = [];
16805
17597
  for (const name2 of [file, ...planned.via ?? []]) {
16806
17598
  if (name2.includes("/") && !COMMAND2.test(name2)) scripts.push(this.fs.resolvePath(cwd, name2));
16807
- const script = (await this.scriptCommand(name2, cwd, ""))?.script;
17599
+ const script = (await this.scriptCommand(name2, cwd, scope))?.script;
16808
17600
  if (script) scripts.push(script);
16809
17601
  }
16810
17602
  files.push(...scripts, ...await this.binfmtManifests(scripts));
@@ -16933,6 +17725,7 @@ var Launcher = class {
16933
17725
  const seen = { ...req.env, HOSTNAME: this.hostname, cwd };
16934
17726
  const env = { ...expandDefaults(target.env, seen), ...req.env };
16935
17727
  for (const key of target.unset ?? []) delete env[key];
17728
+ delete env[TICKET_ENV];
16936
17729
  env.HOSTNAME = this.hostname;
16937
17730
  let program;
16938
17731
  try {
@@ -16948,16 +17741,23 @@ var Launcher = class {
16948
17741
  groupSession(pgid) {
16949
17742
  return this.jobs.sessionOf(pgid);
16950
17743
  }
17744
+ treeScrubs(pid, req) {
17745
+ const inherited2 = req.ppid !== void 0 ? this.scrubs.get(req.ppid) : void 0;
17746
+ const scrubs = req.scrub ? [...inherited2 ?? [], req.scrub] : inherited2;
17747
+ if (scrubs) this.scrubs.set(pid, scrubs);
17748
+ return scrubs;
17749
+ }
16951
17750
  start(req) {
16952
17751
  this.sampling();
16953
17752
  if (req.pgid !== void 0 && this.jobs.sessionOf(req.pgid) === void 0) {
16954
17753
  throw new SpawnError("ESRCH");
16955
17754
  }
16956
- const pid = this.nextPid++;
17755
+ const pid = req.pid ?? this.nextPid++;
17756
+ const scrubs = this.treeScrubs(pid, req);
16957
17757
  const stack = this.stackFor(req.tag);
16958
17758
  const terminal = req.fds.stdioTerminal();
16959
17759
  const decided = req.decided ?? Promise.resolve(true);
16960
- const count = (byParent) => {
17760
+ const count2 = (byParent) => {
16961
17761
  this.identities.asked++;
16962
17762
  if (!byParent) this.identities.timedOut++;
16963
17763
  };
@@ -16965,7 +17765,7 @@ var Launcher = class {
16965
17765
  const handle = spawnWasmProcess({
16966
17766
  pid,
16967
17767
  identity: async () => {
16968
- await (asked ??= decided.then(count));
17768
+ await (asked ??= decided.then(count2));
16969
17769
  return this.identityOf(pid);
16970
17770
  },
16971
17771
  onSyscall: (call) => {
@@ -16979,6 +17779,7 @@ var Launcher = class {
16979
17779
  ...this.owners && req.program.abi === "wasi" ? { fresh: (path, umask) => this.freshModules.add(path, umask, pid) } : {},
16980
17780
  ...this.owners ? { owners: true } : {},
16981
17781
  sealed: this.sealed,
17782
+ ...scrubs ? { ttyFilter: (file) => scrubbedAll(file, scrubs) } : {},
16982
17783
  clipboard: this.clipboard,
16983
17784
  ...this.dac ? { dac: this.dac } : {},
16984
17785
  program: req.program,
@@ -17045,6 +17846,7 @@ var Launcher = class {
17045
17846
  if (spent) addTimes(this.imageTimes, this.jobs.shown(pid), spent);
17046
17847
  this.stopped.delete(pid);
17047
17848
  this.processes.delete(pid);
17849
+ this.scrubs.delete(pid);
17048
17850
  if (this.processes.size === 0) {
17049
17851
  clearInterval(this.sampler);
17050
17852
  this.sampler = void 0;
@@ -17216,6 +18018,7 @@ var Launcher = class {
17216
18018
  }
17217
18019
  spawner(ppid, report) {
17218
18020
  return async (req, fds, grant) => {
18021
+ const booked = this.takeTicket(req);
17219
18022
  if (grant && !await this.grantedCwd(grant.caller, req.cwd)) {
17220
18023
  await fds.closeAll();
17221
18024
  throw new SpawnError("EACCES");
@@ -17227,23 +18030,20 @@ var Launcher = class {
17227
18030
  }
17228
18031
  const cred = grant ? grant.cred : this.credOf(ppid);
17229
18032
  const scope = grant ? "" : await this.scopeOf(cred.euid);
17230
- const planned = await this.plan(req.file, req.argv, req.cwd, scope, req.argv0);
17231
- try {
17232
- await this.mayExec(cred, { ...req, pid: ppid }, planned);
17233
- } catch (err) {
17234
- await fds.closeAll();
17235
- throw err.code === "EACCES" ? new SpawnError("EACCES") : err;
17236
- }
17237
- if (!planned) {
17238
- await fds.closeAll();
17239
- throw new SpawnError(await this.unrunnable(req.file, req.cwd, scope));
17240
- }
18033
+ const file = booked === void 0 ? req.file : await this.ticketFile(req, fds);
18034
+ const planned = await this.checkedPlan({ ...req, file }, { ppid, cred, scope }, fds);
18035
+ const redeemed = await this.redeemed(
18036
+ { ...req, file },
18037
+ { ppid, cred, scope, booked },
18038
+ planned,
18039
+ fds
18040
+ );
17241
18041
  const exec = req.exec ? { exec: true } : {};
17242
18042
  const deciding = req.exec ? void 0 : this.undecided(ppid);
17243
18043
  const handle = await this.launch(planned, {
17244
18044
  ...deciding ? { decided: deciding.promise } : {},
17245
- env: req.env,
17246
18045
  cwd: req.cwd,
18046
+ ...redeemed,
17247
18047
  fds,
17248
18048
  report,
17249
18049
  ppid,
@@ -17296,11 +18096,11 @@ var Launcher = class {
17296
18096
  if (!waiting) return;
17297
18097
  this.deciding.delete(ppid);
17298
18098
  setTimeout(() => {
17299
- for (const resolve of waiting) resolve(true);
18099
+ for (const resolve2 of waiting) resolve2(true);
17300
18100
  }, 0);
17301
18101
  }
17302
18102
  forker(ppid, parent) {
17303
- const { exec: _exec, pgid: _pgid, group: _group, ...image } = parent;
18103
+ const { exec: _exec, pgid: _pgid, group: _group, pid: _pid, scrub: _scrub, ...image } = parent;
17304
18104
  return async (state, fds) => childHandle(
17305
18105
  this.start({
17306
18106
  ...image,
@@ -17334,7 +18134,7 @@ var Launcher = class {
17334
18134
  }
17335
18135
  }
17336
18136
  umaskOf(pid) {
17337
- return this.processes.get(pid)?.umask?.() ?? DEFAULT_UMASK;
18137
+ return this.processes.get(pid)?.umask?.() ?? DEFAULT_UMASK2;
17338
18138
  }
17339
18139
  imageOf(pid) {
17340
18140
  const members = this.jobs.list();
@@ -17677,7 +18477,14 @@ var Launcher = class {
17677
18477
  );
17678
18478
  }
17679
18479
  environment(cwd, extra, account) {
17680
- return { ...this.env, ...userEnv(account, this.pnpmHome), PWD: cwd, ...extra };
18480
+ const env = {
18481
+ ...this.env,
18482
+ ...userEnv(account, this.pnpmHome),
18483
+ PWD: cwd,
18484
+ ...extra
18485
+ };
18486
+ delete env[TICKET_ENV];
18487
+ return env;
17681
18488
  }
17682
18489
  async account(user) {
17683
18490
  if (typeof user === "object") return user;
@@ -17773,7 +18580,7 @@ var Launcher = class {
17773
18580
  },
17774
18581
  account
17775
18582
  );
17776
- const report = (message3) => options.onData(encoder5.encode(`${message3}\r
18583
+ const report = (message3) => options.onData(encoder7.encode(`${message3}\r
17777
18584
  `));
17778
18585
  const handle = await this.launch(planned, {
17779
18586
  env,
@@ -17804,18 +18611,18 @@ var Launcher = class {
17804
18611
  const env = this.environment(cwd, options.env, account);
17805
18612
  const out = [];
17806
18613
  const err = [];
17807
- const keep = options.collect !== false;
18614
+ const keep2 = options.collect !== false;
17808
18615
  const collect = (chunks2, tee) => (bytes) => {
17809
- if (keep) chunks2.push(bytes);
18616
+ if (keep2) chunks2.push(bytes);
17810
18617
  tee?.(bytes);
17811
18618
  };
17812
18619
  const stdout = collect(out, options.onStdout);
17813
18620
  const stderr = collect(err, options.onStderr);
17814
- const report = (message3) => stderr(encoder5.encode(`${message3}
18621
+ const report = (message3) => stderr(encoder7.encode(`${message3}
17815
18622
  `));
17816
18623
  if (!planned) {
17817
18624
  report(`${file}: command not found`);
17818
- return { status: NOT_FOUND, stdout: concat5(out), stderr: concat5(err) };
18625
+ return { status: NOT_FOUND, stdout: concat6(out), stderr: concat6(err) };
17819
18626
  }
17820
18627
  const fds = new FdTable();
17821
18628
  fds.installAt(0, options.stdin ? bytesSource(options.stdin) : deviceFile("null"));
@@ -17833,10 +18640,115 @@ var Launcher = class {
17833
18640
  options.onStarted?.(handle.pid);
17834
18641
  const status = await handle.exited;
17835
18642
  const signal = handle.termsig();
17836
- return { status, ...signal ? { signal } : {}, stdout: concat5(out), stderr: concat5(err) };
18643
+ return { status, ...signal ? { signal } : {}, stdout: concat6(out), stderr: concat6(err) };
17837
18644
  }
17838
18645
  };
17839
18646
 
18647
+ // src/node/loopback.ts
18648
+ import { lookup } from "node:dns/promises";
18649
+ import { isIP } from "node:net";
18650
+ var DEVTOOLS_PORTS = [9222, 9223, 9224, 9225, 9226, 9227, 9228, 9229];
18651
+ var METADATA_V6 = [64768, 3778, 0, 0, 0, 0, 0, 596].join(":");
18652
+ var resolveHost = async (host) => (await lookup(host, { all: true, verbatim: true })).map((a) => a.address);
18653
+ function ipv4Place(address) {
18654
+ const [a, b] = address.split(".").map(Number);
18655
+ if (a === 127 || a === 0) return "loopback";
18656
+ if (a === 169 && b === 254) return "link-local";
18657
+ return void 0;
18658
+ }
18659
+ function placeOf(address) {
18660
+ const plain = address.replace(/^\[(.*)\]$/, "$1").toLowerCase();
18661
+ const kind = isIP(plain);
18662
+ if (kind === 4) return ipv4Place(plain);
18663
+ if (kind !== 6) return void 0;
18664
+ const words2 = hextets2(plain);
18665
+ const embedded2 = words2.slice(0, 5).every((w) => w === 0) && (words2[5] === 65535 || words2[5] === 0);
18666
+ if (embedded2 && (words2[5] === 65535 || words2[6] !== 0)) return ipv4Place(ipv4Of(words2));
18667
+ if (words2.slice(0, 7).every((w) => w === 0) && (words2[7] ?? 0) <= 1) return "loopback";
18668
+ if (((words2[0] ?? 0) & 65472) === 65152) return "link-local";
18669
+ if (words2.join(":") === METADATA_V6) return "link-local";
18670
+ return void 0;
18671
+ }
18672
+ function ipv4Of(words2) {
18673
+ const [high = 0, low = 0] = words2.slice(6);
18674
+ return [high >> 8, high & 255, low >> 8, low & 255].join(".");
18675
+ }
18676
+ function hextets2(address) {
18677
+ const dotted = /(\d+\.\d+\.\d+\.\d+)$/.exec(address)?.[1];
18678
+ let text = address;
18679
+ if (dotted) {
18680
+ const [a, b, c, d] = dotted.split(".").map(Number);
18681
+ text = `${address.slice(0, -dotted.length)}${(a << 8 | b).toString(16)}:${(c << 8 | d).toString(16)}`;
18682
+ }
18683
+ const [head = "", tail] = text.split("::");
18684
+ const part = (side) => side ? side.split(":").map((w) => Number.parseInt(w, 16)) : [];
18685
+ const left = part(head);
18686
+ const right = tail === void 0 ? [] : part(tail);
18687
+ const gap = Array(8 - left.length - right.length).fill(0);
18688
+ return [...left, ...gap, ...right];
18689
+ }
18690
+ function loopbackName2(host) {
18691
+ const name2 = host.toLowerCase().replace(/\.$/, "");
18692
+ return name2 === "localhost" || name2.endsWith(".localhost");
18693
+ }
18694
+ function portOf(url) {
18695
+ if (url.port) return Number(url.port);
18696
+ return url.protocol === "https:" || url.protocol === "wss:" ? 443 : 80;
18697
+ }
18698
+ function allows(policy, port) {
18699
+ if (Array.isArray(policy)) return policy.includes(port);
18700
+ return policy === true && !DEVTOOLS_PORTS.includes(port);
18701
+ }
18702
+ function refusal(url, address, place) {
18703
+ const where = `${address}:${portOf(url)}`;
18704
+ const why = place === "link-local" ? `nodeTransport refuses link-local ${where}` : `nodeTransport refuses the host's loopback ${where} (see nodeTransport({ hostLoopback }))`;
18705
+ return Object.assign(new Error(why), { status: 403, code: "EACCES" });
18706
+ }
18707
+ async function addressesOf(host, resolve2) {
18708
+ const plain = host.replace(/^\[(.*)\]$/, "$1");
18709
+ if (isIP(plain)) return [plain];
18710
+ if (loopbackName2(plain)) return ["127.0.0.1"];
18711
+ return resolve2(plain).catch(() => []);
18712
+ }
18713
+ async function checkDestination(target, policy, resolve2 = resolveHost) {
18714
+ const url = new URL(target);
18715
+ for (const address of await addressesOf(url.hostname, resolve2)) {
18716
+ const place = placeOf(address);
18717
+ if (!place) continue;
18718
+ if (place === "loopback" && allows(policy, portOf(url))) continue;
18719
+ throw refusal(url, address, place);
18720
+ }
18721
+ }
18722
+ function guarded2(check, signal) {
18723
+ if (!signal) return check;
18724
+ signal.throwIfAborted();
18725
+ let stop = () => {
18726
+ };
18727
+ const aborted2 = new Promise((_, reject) => {
18728
+ stop = () => reject(signal.reason);
18729
+ signal.addEventListener("abort", stop, { once: true });
18730
+ });
18731
+ return Promise.race([check, aborted2]).finally(() => signal.removeEventListener("abort", stop));
18732
+ }
18733
+ function guardedLoopback(transport, policy, resolve2) {
18734
+ const open = transport.websocket?.bind(transport);
18735
+ return {
18736
+ get traits() {
18737
+ return transport.traits;
18738
+ },
18739
+ fetch: async (request, owner) => {
18740
+ await guarded2(checkDestination(request.url, policy, resolve2), request.signal);
18741
+ return transport.fetch(request, owner);
18742
+ },
18743
+ ...open ? {
18744
+ websocket: async (request) => {
18745
+ await guarded2(checkDestination(request.url, policy, resolve2), request.signal);
18746
+ return open(request);
18747
+ }
18748
+ } : {}
18749
+ };
18750
+ }
18751
+
17840
18752
  // src/node/memory-root.ts
17841
18753
  var fail2 = (name2) => {
17842
18754
  throw new DOMException(name2, name2);
@@ -17902,10 +18814,10 @@ var Chunks = class {
17902
18814
  this.grow(size);
17903
18815
  return;
17904
18816
  }
17905
- const keep = Math.ceil(size / CHUNK4);
17906
- this.blobs.length = keep;
17907
- if (keep > 0)
17908
- this.blobs[keep - 1] = this.blobs[keep - 1].slice(0, size - (keep - 1) * CHUNK4);
18817
+ const keep2 = Math.ceil(size / CHUNK4);
18818
+ this.blobs.length = keep2;
18819
+ if (keep2 > 0)
18820
+ this.blobs[keep2 - 1] = this.blobs[keep2 - 1].slice(0, size - (keep2 - 1) * CHUNK4);
17909
18821
  this.size = size;
17910
18822
  }
17911
18823
  };
@@ -18043,8 +18955,8 @@ function isMemoryRoot(handle) {
18043
18955
  }
18044
18956
 
18045
18957
  // src/node/ws-diagnose.ts
18046
- import { lookup } from "node:dns/promises";
18047
- import { isIP, connect as tcp } from "node:net";
18958
+ import { lookup as lookup2 } from "node:dns/promises";
18959
+ import { isIP as isIP2, connect as tcp } from "node:net";
18048
18960
  import { connect as tls } from "node:tls";
18049
18961
  var PROBE_MS = 3e3;
18050
18962
  var PASSED = /* @__PURE__ */ new Set(["ECONNREFUSED", "EHOSTUNREACH", "ENETUNREACH", "ETIMEDOUT", "ECONNRESET"]);
@@ -18054,12 +18966,12 @@ function codeOf5(e) {
18054
18966
  return typeof code === "string" ? code : void 0;
18055
18967
  }
18056
18968
  function probe(secure, host, port, ms) {
18057
- return new Promise((resolve) => {
18058
- const socket = secure ? tls({ host, port, ...isIP(host) ? {} : { servername: host } }) : tcp({ host, port });
18969
+ return new Promise((resolve2) => {
18970
+ const socket = secure ? tls({ host, port, ...isIP2(host) ? {} : { servername: host } }) : tcp({ host, port });
18059
18971
  const done4 = (code) => {
18060
18972
  clearTimeout(timer);
18061
18973
  socket.destroy();
18062
- resolve(code);
18974
+ resolve2(code);
18063
18975
  };
18064
18976
  const timer = setTimeout(() => done4("ETIMEDOUT"), ms);
18065
18977
  socket.once(secure ? "secureConnect" : "connect", () => done4("EPROTO"));
@@ -18075,7 +18987,7 @@ async function diagnose(url, ms = PROBE_MS) {
18075
18987
  const secure = at.protocol === "wss:";
18076
18988
  const port = Number(at.port) || (secure ? 443 : 80);
18077
18989
  try {
18078
- await lookup(host);
18990
+ await lookup2(host);
18079
18991
  } catch (e) {
18080
18992
  return NO_NAME.has(codeOf5(e) ?? "") ? "EAI_NONAME" : void 0;
18081
18993
  }
@@ -18151,8 +19063,8 @@ var RemoteTransport = class {
18151
19063
  }
18152
19064
  ask(call) {
18153
19065
  if (this.failure) return Promise.reject(this.failure);
18154
- return new Promise((resolve, reject) => {
18155
- this.waiting.set(call.nid, { resolve, reject });
19066
+ return new Promise((resolve2, reject) => {
19067
+ this.waiting.set(call.nid, { resolve: resolve2, reject });
18156
19068
  this.port.postMessage(call);
18157
19069
  });
18158
19070
  }
@@ -18332,7 +19244,7 @@ function dialStream(port, onClose) {
18332
19244
  write(chunk2) {
18333
19245
  if (closed) throw errorWithCode("EPIPE: the connection is closed");
18334
19246
  const copy = fresh(chunk2);
18335
- const acked = new Promise((resolve, reject) => acks.push({ resolve, reject }));
19247
+ const acked = new Promise((resolve2, reject) => acks.push({ resolve: resolve2, reject }));
18336
19248
  port.postMessage(copy, [copy.buffer]);
18337
19249
  return acked;
18338
19250
  },
@@ -18352,7 +19264,7 @@ function dialStream(port, onClose) {
18352
19264
 
18353
19265
  // src/client/loopback-fetch.ts
18354
19266
  var OWN_HEADERS = /* @__PURE__ */ new Set(["host", "connection", "content-length", "transfer-encoding"]);
18355
- var encoder6 = new TextEncoder();
19267
+ var encoder8 = new TextEncoder();
18356
19268
  var latin12 = new TextDecoder("latin1");
18357
19269
  var ByteSource = class {
18358
19270
  buffered = new Uint8Array(0);
@@ -18370,7 +19282,7 @@ var ByteSource = class {
18370
19282
  return true;
18371
19283
  }
18372
19284
  async until(marker) {
18373
- const needle = encoder6.encode(marker);
19285
+ const needle = encoder8.encode(marker);
18374
19286
  for (; ; ) {
18375
19287
  const at = indexOf(this.buffered, needle);
18376
19288
  if (at >= 0) return this.take(at + needle.length);
@@ -18381,13 +19293,13 @@ var ByteSource = class {
18381
19293
  if (this.buffered.length === 0 && !await this.more()) return void 0;
18382
19294
  return this.take(Math.min(max, this.buffered.length));
18383
19295
  }
18384
- async exactly(count) {
18385
- while (this.buffered.length < count) if (!await this.more()) return void 0;
18386
- return this.take(count);
19296
+ async exactly(count2) {
19297
+ while (this.buffered.length < count2) if (!await this.more()) return void 0;
19298
+ return this.take(count2);
18387
19299
  }
18388
- take(count) {
18389
- const out = this.buffered.slice(0, count);
18390
- this.buffered = this.buffered.slice(count);
19300
+ take(count2) {
19301
+ const out = this.buffered.slice(0, count2);
19302
+ this.buffered = this.buffered.slice(count2);
18391
19303
  return out;
18392
19304
  }
18393
19305
  };
@@ -18478,7 +19390,7 @@ async function requestBytes(request) {
18478
19390
  });
18479
19391
  if (body) lines2.push(`Content-Length: ${body.length}`);
18480
19392
  lines2.push("Connection: close", "", "");
18481
- const head = encoder6.encode(lines2.join("\r\n"));
19393
+ const head = encoder8.encode(lines2.join("\r\n"));
18482
19394
  const out = new Uint8Array(head.length + (body?.length ?? 0));
18483
19395
  out.set(head);
18484
19396
  if (body) out.set(body, head.length);
@@ -18532,8 +19444,8 @@ var KernelCallError = class extends Error {
18532
19444
  this.code = code;
18533
19445
  }
18534
19446
  };
18535
- var encoder7 = new TextEncoder();
18536
- var bytesOf2 = (data) => typeof data === "string" ? encoder7.encode(data) : data;
19447
+ var encoder9 = new TextEncoder();
19448
+ var bytesOf2 = (data) => typeof data === "string" ? encoder9.encode(data) : data;
18537
19449
  var UNAVAILABLE = {
18538
19450
  manualRedirects: false,
18539
19451
  encodedBodies: false,
@@ -18553,7 +19465,7 @@ async function holdLock(locks) {
18553
19465
  await granted2.promise;
18554
19466
  return { name: name2, release: () => held.resolve() };
18555
19467
  }
18556
- function concat6(chunks2) {
19468
+ function concat7(chunks2) {
18557
19469
  const out = new Uint8Array(chunks2.reduce((n, c) => n + c.length, 0));
18558
19470
  let at = 0;
18559
19471
  for (const chunk2 of chunks2) {
@@ -18604,8 +19516,8 @@ var Channel = class {
18604
19516
  const id2 = ++this.nextId;
18605
19517
  const { failure } = this;
18606
19518
  if (failure) return { id: id2, done: Promise.reject(failure) };
18607
- const done4 = new Promise((resolve, reject) => {
18608
- this.pending.set(id2, { resolve, reject, ...handlers });
19519
+ const done4 = new Promise((resolve2, reject) => {
19520
+ this.pending.set(id2, { resolve: resolve2, reject, ...handlers });
18609
19521
  this.port.postMessage({ ...call, id: id2 });
18610
19522
  });
18611
19523
  return { id: id2, done: done4 };
@@ -18743,7 +19655,7 @@ async function runOn(channel, argv, opts) {
18743
19655
  onStderr: tee(2, onStderr)
18744
19656
  });
18745
19657
  const { status, signal } = await child.ended;
18746
- const text = (fd) => new TextDecoder().decode(concat6(chunks2[fd]));
19658
+ const text = (fd) => new TextDecoder().decode(concat7(chunks2[fd]));
18747
19659
  return {
18748
19660
  pid: child.pid,
18749
19661
  status,
@@ -18924,9 +19836,10 @@ function hostfsFetchOf(options) {
18924
19836
  }
18925
19837
  };
18926
19838
  }
18927
- function nodeTransport() {
19839
+ function nodeTransport(options = {}) {
18928
19840
  const transport = fetchTransport({ webSocket: true, webSocketHeaders: true, redirect: "manual" });
18929
- return diagnosing({ ...transport, traits: { ...transport.traits, crossOrigin: "any" } });
19841
+ const open = { ...transport, traits: { ...transport.traits, crossOrigin: "any" } };
19842
+ return diagnosing(guardedLoopback(open, options.hostLoopback ?? false));
18930
19843
  }
18931
19844
  var TERMINATED = "the kernel is terminated";
18932
19845
  var EXIT_GRACE_MS = 50;
@@ -19022,10 +19935,10 @@ async function createNodeKernel(options = {}) {
19022
19935
  ...options.approve ? { approve: options.approve } : {}
19023
19936
  });
19024
19937
  await launcher.prepare();
19025
- const guard2 = (work) => new Promise((resolve, reject) => {
19938
+ const guard2 = (work) => new Promise((resolve2, reject) => {
19026
19939
  if (terminated) return reject(new Error(TERMINATED));
19027
19940
  pending.add(reject);
19028
- work().then(resolve, reject).finally(() => pending.delete(reject));
19941
+ work().then(resolve2, reject).finally(() => pending.delete(reject));
19029
19942
  });
19030
19943
  const decoder4 = new TextDecoder();
19031
19944
  return {