@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.
@@ -131,8 +131,8 @@ function descendants(path) {
131
131
  return IDBKeyRange.bound(`${path}/`, `${path}0`, false, true);
132
132
  }
133
133
  function settle(transaction, value) {
134
- return new Promise((resolve, reject) => {
135
- transaction.oncomplete = () => resolve(value());
134
+ return new Promise((resolve2, reject) => {
135
+ transaction.oncomplete = () => resolve2(value());
136
136
  transaction.onabort = () => reject(transaction.error ?? new Error("metadata transaction aborted"));
137
137
  });
138
138
  }
@@ -249,14 +249,14 @@ var IndexedDbMeta = class {
249
249
  }
250
250
  };
251
251
  function openMeta(name2, factory) {
252
- return new Promise((resolve, reject) => {
252
+ return new Promise((resolve2, reject) => {
253
253
  const request2 = factory.open(name2, META_VERSION);
254
254
  request2.onupgradeneeded = (event) => {
255
255
  if (event.oldVersion < 1) {
256
256
  request2.result.createObjectStore(META_STORE, { keyPath: "path" }).createIndex("dir", "dir");
257
257
  }
258
258
  };
259
- request2.onsuccess = () => resolve(new IndexedDbMeta(request2.result));
259
+ request2.onsuccess = () => resolve2(new IndexedDbMeta(request2.result));
260
260
  request2.onerror = () => reject(request2.error);
261
261
  });
262
262
  }
@@ -740,11 +740,11 @@ var RemoteWatch = class {
740
740
  const seq2 = ++this.seq;
741
741
  this.channel.postMessage({ tab: this.tab, paths: this.mine, seq: seq2 });
742
742
  if (this.peers.size === 0) return Promise.resolve();
743
- return new Promise((resolve) => {
743
+ return new Promise((resolve2) => {
744
744
  const done4 = () => {
745
745
  clearTimeout(timer);
746
746
  this.waiting.delete(seq2);
747
- resolve();
747
+ resolve2();
748
748
  };
749
749
  const timer = setTimeout(() => {
750
750
  done4();
@@ -840,7 +840,7 @@ async function openSync(handle) {
840
840
  const delay = SYNC_BACKOFF_MS[attempt];
841
841
  if (!LOCKED.has(err?.name)) throw err;
842
842
  if (delay === void 0) return void 0;
843
- await new Promise((resolve) => setTimeout(resolve, delay));
843
+ await new Promise((resolve2) => setTimeout(resolve2, delay));
844
844
  }
845
845
  }
846
846
  }
@@ -1297,7 +1297,7 @@ var OpfsFs = class {
1297
1297
  const again = name2 === "NotReadableError" || locks && LOCKED.has(name2);
1298
1298
  if (!again || attempt >= READ_ATTEMPTS)
1299
1299
  throw translate(err, path, locks ? "write" : "read");
1300
- await new Promise((resolve) => setTimeout(resolve, attempt * 10));
1300
+ await new Promise((resolve2) => setTimeout(resolve2, attempt * 10));
1301
1301
  }
1302
1302
  }
1303
1303
  }
@@ -1354,8 +1354,8 @@ var OpfsFs = class {
1354
1354
  }
1355
1355
  async truncate(path, size) {
1356
1356
  const handle = await this.existing(path, true);
1357
- const resolve = this.ranged || this.remote?.watched();
1358
- const target = resolve ? (await this.locate(path, true)).path : path;
1357
+ const resolve2 = this.ranged || this.remote?.watched();
1358
+ const target = resolve2 ? (await this.locate(path, true)).path : path;
1359
1359
  await this.retrying(path, () => positioned(handle, (io) => io.truncate(size)), true);
1360
1360
  this.written(target);
1361
1361
  }
@@ -1779,8 +1779,8 @@ function memoryMedia() {
1779
1779
  };
1780
1780
  }
1781
1781
  function settle2(request2, value) {
1782
- return new Promise((resolve, reject) => {
1783
- request2.onsuccess = () => resolve(value());
1782
+ return new Promise((resolve2, reject) => {
1783
+ request2.onsuccess = () => resolve2(value());
1784
1784
  request2.onerror = () => reject(request2.error);
1785
1785
  });
1786
1786
  }
@@ -1818,7 +1818,7 @@ function pinScript(url) {
1818
1818
 
1819
1819
  // src/cdp/port.ts
1820
1820
  function portCdpHook(ask, gone) {
1821
- return (request2) => new Promise((resolve, reject) => {
1821
+ return (request2) => new Promise((resolve2, reject) => {
1822
1822
  const { port1, port2 } = new MessageChannel();
1823
1823
  let closed = false;
1824
1824
  const conn = {
@@ -1836,7 +1836,7 @@ function portCdpHook(ask, gone) {
1836
1836
  };
1837
1837
  port1.addEventListener("message", ({ data }) => {
1838
1838
  if ("m" in data) conn.onmessage?.(data.m);
1839
- else if ("open" in data) resolve(conn);
1839
+ else if ("open" in data) resolve2(conn);
1840
1840
  else if ("error" in data) {
1841
1841
  closed = true;
1842
1842
  port1.close();
@@ -2370,9 +2370,9 @@ var KernelPipe = class {
2370
2370
  let filled = 0;
2371
2371
  for (const chunk2 of this.chunks) {
2372
2372
  if (filled === out.length) break;
2373
- const count = Math.min(chunk2.length, out.length - filled);
2374
- out.set(chunk2.subarray(0, count), filled);
2375
- filled += count;
2373
+ const count2 = Math.min(chunk2.length, out.length - filled);
2374
+ out.set(chunk2.subarray(0, count2), filled);
2375
+ filled += count2;
2376
2376
  }
2377
2377
  return out;
2378
2378
  }
@@ -2403,25 +2403,25 @@ var KernelPipe = class {
2403
2403
  let filled = 0;
2404
2404
  while (filled < n) {
2405
2405
  const head = this.chunks[0];
2406
- const count = Math.min(head.length, n - filled);
2407
- out.set(head.subarray(0, count), filled);
2408
- filled += count;
2409
- if (count === head.length) this.chunks.shift();
2410
- else this.chunks[0] = head.subarray(count);
2406
+ const count2 = Math.min(head.length, n - filled);
2407
+ out.set(head.subarray(0, count2), filled);
2408
+ filled += count2;
2409
+ if (count2 === head.length) this.chunks.shift();
2410
+ else this.chunks[0] = head.subarray(count2);
2411
2411
  }
2412
2412
  this.size -= n;
2413
2413
  return out;
2414
2414
  }
2415
2415
  changed(signal) {
2416
2416
  if (signal?.aborted) return Promise.reject(new PipeError("EINTR"));
2417
- return new Promise((resolve, reject) => {
2417
+ return new Promise((resolve2, reject) => {
2418
2418
  const onAbort = () => {
2419
2419
  this.waiters = this.waiters.filter((w) => w !== waiter);
2420
2420
  reject(new PipeError("EINTR"));
2421
2421
  };
2422
2422
  const waiter = () => {
2423
2423
  signal?.removeEventListener("abort", onAbort);
2424
- resolve();
2424
+ resolve2();
2425
2425
  };
2426
2426
  this.waiters.push(waiter);
2427
2427
  signal?.addEventListener("abort", onAbort, { once: true });
@@ -2430,7 +2430,7 @@ var KernelPipe = class {
2430
2430
  wake() {
2431
2431
  const waiters = this.waiters;
2432
2432
  this.waiters = [];
2433
- for (const resolve of waiters) resolve();
2433
+ for (const resolve2 of waiters) resolve2();
2434
2434
  }
2435
2435
  };
2436
2436
 
@@ -2891,14 +2891,14 @@ var Changes = class {
2891
2891
  waiters = [];
2892
2892
  wait(signal) {
2893
2893
  if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
2894
- return new Promise((resolve, reject) => {
2894
+ return new Promise((resolve2, reject) => {
2895
2895
  const onAbort = () => {
2896
2896
  this.waiters = this.waiters.filter((w) => w !== waiter);
2897
2897
  reject(new KernelError("EINTR"));
2898
2898
  };
2899
2899
  const waiter = () => {
2900
2900
  signal?.removeEventListener("abort", onAbort);
2901
- resolve();
2901
+ resolve2();
2902
2902
  };
2903
2903
  this.waiters.push(waiter);
2904
2904
  signal?.addEventListener("abort", onAbort, { once: true });
@@ -2907,7 +2907,7 @@ var Changes = class {
2907
2907
  wake() {
2908
2908
  const waiters = this.waiters;
2909
2909
  this.waiters = [];
2910
- for (const resolve of waiters) resolve();
2910
+ for (const resolve2 of waiters) resolve2();
2911
2911
  }
2912
2912
  };
2913
2913
  function link(a, b) {
@@ -3075,7 +3075,7 @@ var KernelSocket = class _KernelSocket {
3075
3075
  });
3076
3076
  const { host, port } = addr;
3077
3077
  const dialled = new Promise(
3078
- (resolve) => resolve(route.uplink.dial({ network: "tcp", host, port, signal: abort.signal }))
3078
+ (resolve2) => resolve2(route.uplink.dial({ network: "tcp", host, port, signal: abort.signal }))
3079
3079
  );
3080
3080
  dialled.then(
3081
3081
  (conn) => abort.signal.aborted && conn.close(),
@@ -3425,8 +3425,8 @@ var MessageQueue = class {
3425
3425
  this.overflow = overflow;
3426
3426
  this.limit = limit;
3427
3427
  this.taken = taken;
3428
- this.closed = new Promise((resolve) => {
3429
- this.settle = resolve;
3428
+ this.closed = new Promise((resolve2) => {
3429
+ this.settle = resolve2;
3430
3430
  });
3431
3431
  }
3432
3432
  push(message3) {
@@ -3465,8 +3465,8 @@ var MessageQueue = class {
3465
3465
  continue;
3466
3466
  }
3467
3467
  if (this.ended) return;
3468
- await new Promise((resolve) => {
3469
- this.waiter = resolve;
3468
+ await new Promise((resolve2) => {
3469
+ this.waiter = resolve2;
3470
3470
  });
3471
3471
  }
3472
3472
  }
@@ -3557,8 +3557,8 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
3557
3557
  const relay = async (nid, socket) => {
3558
3558
  let ending;
3559
3559
  let late;
3560
- const unanswered = new Promise((resolve) => {
3561
- late = () => resolve("late");
3560
+ const unanswered = new Promise((resolve2) => {
3561
+ late = () => resolve2("late");
3562
3562
  });
3563
3563
  const flow = {
3564
3564
  inflight: 0,
@@ -3586,8 +3586,8 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
3586
3586
  }
3587
3587
  flow.inflight += payloadSize(data);
3588
3588
  while (flow.inflight > INBOUND_LIMIT && !flow.closing) {
3589
- await new Promise((resolve) => {
3590
- flow.wake = resolve;
3589
+ await new Promise((resolve2) => {
3590
+ flow.wake = resolve2;
3591
3591
  });
3592
3592
  }
3593
3593
  }
@@ -3606,7 +3606,7 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
3606
3606
  if (!socket) return;
3607
3607
  socket.send(call.data);
3608
3608
  while (socket.buffered > SEND_BUFFER && sockets.has(call.nid)) {
3609
- await new Promise((resolve) => setTimeout(resolve, 10));
3609
+ await new Promise((resolve2) => setTimeout(resolve2, 10));
3610
3610
  }
3611
3611
  peer.postMessage({ net: "ws-acked", nid: call.nid, n: payloadSize(call.data) });
3612
3612
  };
@@ -5113,10 +5113,10 @@ var Budget = class {
5113
5113
  async acquire(n, signal) {
5114
5114
  while (this.free < n) {
5115
5115
  signal.throwIfAborted();
5116
- await new Promise((resolve) => {
5116
+ await new Promise((resolve2) => {
5117
5117
  const done4 = () => {
5118
5118
  signal.removeEventListener("abort", done4);
5119
- resolve();
5119
+ resolve2();
5120
5120
  };
5121
5121
  this.waiters.push(done4);
5122
5122
  signal.addEventListener("abort", done4, { once: true });
@@ -5258,14 +5258,14 @@ async function watchHangup(conn, abort, until) {
5258
5258
  }
5259
5259
  }
5260
5260
  function untilAborted(response, signal) {
5261
- return new Promise((resolve, reject) => {
5261
+ return new Promise((resolve2, reject) => {
5262
5262
  const onAbort = () => {
5263
5263
  reject(signal.reason ?? new KernelError("EINTR"));
5264
5264
  response.then((late) => late.cancel()).catch(() => void 0);
5265
5265
  };
5266
5266
  if (signal.aborted) return onAbort();
5267
5267
  signal.addEventListener("abort", onAbort, { once: true });
5268
- response.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
5268
+ response.then(resolve2, reject).finally(() => signal.removeEventListener("abort", onAbort));
5269
5269
  });
5270
5270
  }
5271
5271
  function plainResponse(status, message3) {
@@ -5298,8 +5298,8 @@ function wireCode(code) {
5298
5298
  return sendableCode(code) ? code : 1011;
5299
5299
  }
5300
5300
  function quietly(ms) {
5301
- return new Promise((resolve) => {
5302
- AbortSignal.timeout(ms).addEventListener("abort", () => resolve(), { once: true });
5301
+ return new Promise((resolve2) => {
5302
+ AbortSignal.timeout(ms).addEventListener("abort", () => resolve2(), { once: true });
5303
5303
  });
5304
5304
  }
5305
5305
  var RealmProxy = class {
@@ -5397,7 +5397,7 @@ var RealmProxy = class {
5397
5397
  return false;
5398
5398
  }
5399
5399
  const url = ctx.origin ? tunnelRequestUrl(req, ctx.origin) : requestUrl(req);
5400
- const keep = keepsAlive(req);
5400
+ const keep2 = keepsAlive(req);
5401
5401
  const framing = requestFraming(req.headers);
5402
5402
  const reserve = framing.kind === "length" ? framing.length : framing.kind === "chunked" ? this.cap() : 0;
5403
5403
  if (reserve > this.cap()) throw new HttpError(413, `request body over ${this.cap()} bytes`);
@@ -5420,7 +5420,7 @@ var RealmProxy = class {
5420
5420
  } finally {
5421
5421
  this.bodies.release(reserve);
5422
5422
  }
5423
- return await this.relay(conn, req.method, req.minor, response, keep);
5423
+ return await this.relay(conn, req.method, req.minor, response, keep2);
5424
5424
  } finally {
5425
5425
  this.stop.signal.removeEventListener("abort", stopExchange);
5426
5426
  abort.abort();
@@ -5468,8 +5468,8 @@ var RealmProxy = class {
5468
5468
  bodiless: bodiless2
5469
5469
  });
5470
5470
  const chunked = !bodiless2 && minor >= 1;
5471
- const keep = keepAlive && (bodiless2 || chunked);
5472
- headers.push(["Connection", keep ? "keep-alive" : "close"]);
5471
+ const keep2 = keepAlive && (bodiless2 || chunked);
5472
+ headers.push(["Connection", keep2 ? "keep-alive" : "close"]);
5473
5473
  if (chunked) headers.push(["Transfer-Encoding", "chunked"]);
5474
5474
  const signal = this.stop.signal;
5475
5475
  await conn.write(
@@ -5478,7 +5478,7 @@ var RealmProxy = class {
5478
5478
  );
5479
5479
  if (bodiless2) {
5480
5480
  await response.cancel();
5481
- return keep;
5481
+ return keep2;
5482
5482
  }
5483
5483
  try {
5484
5484
  for await (const piece of response.body) {
@@ -5489,7 +5489,7 @@ var RealmProxy = class {
5489
5489
  return false;
5490
5490
  }
5491
5491
  if (chunked) await conn.write(LAST_CHUNK, signal);
5492
- return keep;
5492
+ return keep2;
5493
5493
  }
5494
5494
  async connect(conn, incoming, req, idle) {
5495
5495
  const target = tunnelTarget(req);
@@ -5712,10 +5712,10 @@ function header(der, at) {
5712
5712
  let len = der[at + 1];
5713
5713
  let start2 = at + 2;
5714
5714
  if (len & 128) {
5715
- const count = len & 127;
5715
+ const count2 = len & 127;
5716
5716
  len = 0;
5717
- for (let i = 0; i < count; i++) len = len * 256 + der[start2 + i];
5718
- start2 += count;
5717
+ for (let i = 0; i < count2; i++) len = len * 256 + der[start2 + i];
5718
+ start2 += count2;
5719
5719
  }
5720
5720
  if (start2 + len > der.length) throw new Error("truncated DER");
5721
5721
  return [tag, start2, start2 + len];
@@ -5833,18 +5833,18 @@ var LEAF_LIFETIME = 7 * DAY;
5833
5833
  var ECDSA = { name: "ECDSA", namedCurve: "P-256" };
5834
5834
  var SIGN = { name: "ECDSA", hash: "SHA-256" };
5835
5835
  function request(req) {
5836
- return new Promise((resolve, reject) => {
5837
- req.onsuccess = () => resolve(req.result);
5836
+ return new Promise((resolve2, reject) => {
5837
+ req.onsuccess = () => resolve2(req.result);
5838
5838
  req.onerror = () => reject(req.error);
5839
5839
  });
5840
5840
  }
5841
5841
  function indexedDbCaStore(dbName = "slicc-realm-ca") {
5842
5842
  let db;
5843
5843
  const open = () => {
5844
- db ??= new Promise((resolve, reject) => {
5844
+ db ??= new Promise((resolve2, reject) => {
5845
5845
  const req = indexedDB.open(dbName, 1);
5846
5846
  req.onupgradeneeded = () => req.result.createObjectStore("ca");
5847
- req.onsuccess = () => resolve(req.result);
5847
+ req.onsuccess = () => resolve2(req.result);
5848
5848
  req.onerror = () => reject(req.error);
5849
5849
  });
5850
5850
  db.catch(() => {
@@ -5868,8 +5868,8 @@ function indexedDbCaStore(dbName = "slicc-realm-ca") {
5868
5868
  if (kept === record) store.put(record, owner);
5869
5869
  else stored = kept;
5870
5870
  };
5871
- await new Promise((resolve, reject) => {
5872
- tx.oncomplete = () => resolve();
5871
+ await new Promise((resolve2, reject) => {
5872
+ tx.oncomplete = () => resolve2();
5873
5873
  tx.onerror = () => reject(tx.error);
5874
5874
  });
5875
5875
  return stored;
@@ -6398,8 +6398,8 @@ var RemoteTransport = class {
6398
6398
  }
6399
6399
  ask(call) {
6400
6400
  if (this.failure) return Promise.reject(this.failure);
6401
- return new Promise((resolve, reject) => {
6402
- this.waiting.set(call.nid, { resolve, reject });
6401
+ return new Promise((resolve2, reject) => {
6402
+ this.waiting.set(call.nid, { resolve: resolve2, reject });
6403
6403
  this.port.postMessage(call);
6404
6404
  });
6405
6405
  }
@@ -6804,7 +6804,7 @@ var CdpFacade = class {
6804
6804
  const conn = await this.host(runtime);
6805
6805
  try {
6806
6806
  const reply = await new Promise(
6807
- (resolve, reject) => {
6807
+ (resolve2, reject) => {
6808
6808
  const timer = setTimeout(
6809
6809
  () => reject(new HttpError(504, "the CDP host did not answer Target.getTargets")),
6810
6810
  this.options.listTimeout ?? LIST_TIMEOUT
@@ -6817,7 +6817,7 @@ var CdpFacade = class {
6817
6817
  const parsed2 = JSON.parse(message3);
6818
6818
  if (parsed2.id !== 1) return;
6819
6819
  clearTimeout(timer);
6820
- resolve(parsed2);
6820
+ resolve2(parsed2);
6821
6821
  };
6822
6822
  conn.send(JSON.stringify({ id: 1, method: "Target.getTargets" }));
6823
6823
  }
@@ -6877,9 +6877,9 @@ var Relay = class {
6877
6877
  }
6878
6878
  async start(head) {
6879
6879
  await this.send(head);
6880
- const hostGone = new Promise((resolve) => {
6880
+ const hostGone = new Promise((resolve2) => {
6881
6881
  this.host.onclose = (reason) => {
6882
- void this.end(reason ? CLOSE.error : CLOSE.normal, reason ?? "").then(() => resolve());
6882
+ void this.end(reason ? CLOSE.error : CLOSE.normal, reason ?? "").then(() => resolve2());
6883
6883
  };
6884
6884
  });
6885
6885
  this.host.onmessage = (message3) => {
@@ -6893,8 +6893,8 @@ var Relay = class {
6893
6893
  await Promise.race([pumping, hostGone]);
6894
6894
  await this.writes;
6895
6895
  let timer;
6896
- const waited = new Promise((resolve) => {
6897
- timer = setTimeout(resolve, this.closeWaitMs);
6896
+ const waited = new Promise((resolve2) => {
6897
+ timer = setTimeout(resolve2, this.closeWaitMs);
6898
6898
  });
6899
6899
  await Promise.race([pumping, waited]);
6900
6900
  clearTimeout(timer);
@@ -7487,9 +7487,13 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
7487
7487
  // src/fs/fresh-modules.ts
7488
7488
  var MAGIC = [0, 97, 115, 109];
7489
7489
  var MAX_FRESH = 65536;
7490
+ var DEFAULT_UMASK = 18;
7490
7491
  function isModule(bytes) {
7491
7492
  return MAGIC.every((b, i) => bytes[i] === b);
7492
7493
  }
7494
+ function within4(path, dir) {
7495
+ return path.startsWith(dir === "/" ? "/" : `${dir}/`);
7496
+ }
7493
7497
  var FreshModules = class {
7494
7498
  fresh = /* @__PURE__ */ new Map();
7495
7499
  add(path, umask, pid) {
@@ -7497,17 +7501,40 @@ var FreshModules = class {
7497
7501
  this.fresh.delete(key);
7498
7502
  const oldest = this.fresh.size >= MAX_FRESH ? this.fresh.keys().next().value : void 0;
7499
7503
  if (oldest !== void 0) this.fresh.delete(oldest);
7500
- this.fresh.set(key, { umask: umask & 511, pid, added: 0 });
7504
+ const mask = umask & 511;
7505
+ this.fresh.set(key, { umask: mask, pid, added: 0, setup: mask !== DEFAULT_UMASK });
7501
7506
  }
7502
7507
  forget(pid) {
7503
7508
  for (const [path, fresh2] of this.fresh) if (fresh2.pid === pid) this.fresh.delete(path);
7504
7509
  }
7510
+ drop(fresh2) {
7511
+ for (const [key, note] of [...this.fresh]) if (note === fresh2) this.fresh.delete(key);
7512
+ }
7513
+ moved(from, to) {
7514
+ const source = normalizePath(from);
7515
+ const target = normalizePath(to);
7516
+ if (source === target) return;
7517
+ for (const key of [...this.fresh.keys()]) {
7518
+ if (key === target || within4(key, target)) this.fresh.delete(key);
7519
+ }
7520
+ for (const [key, fresh2] of [...this.fresh]) {
7521
+ if (key !== source && !within4(key, source)) continue;
7522
+ this.fresh.delete(key);
7523
+ this.fresh.set(target + key.slice(source.length), fresh2);
7524
+ }
7525
+ }
7505
7526
  wrap(fs) {
7506
- const decide = async (given, bytes) => {
7527
+ const head = async (path) => {
7528
+ if (fs.pread) return fs.pread(path, 0, MAGIC.length);
7529
+ return (await fs.readFileBuffer(path)).subarray(0, MAGIC.length);
7530
+ };
7531
+ const decide = async (given, known) => {
7507
7532
  const path = normalizePath(given);
7508
7533
  const fresh2 = this.fresh.get(path);
7509
- if (!fresh2 || typeof bytes === "string" || bytes.length < MAGIC.length) return;
7534
+ if (!fresh2) return;
7535
+ const bytes = known && known.length >= MAGIC.length ? known : await head(path);
7510
7536
  const want = isModule(bytes) ? 73 & ~fresh2.umask : 0;
7537
+ fresh2.setup = false;
7511
7538
  if (want === fresh2.added) return;
7512
7539
  const mode = (await fs.stat(path)).mode & 4095;
7513
7540
  await fs.chmod(path, mode & ~fresh2.added | want);
@@ -7516,44 +7543,59 @@ var FreshModules = class {
7516
7543
  const overrides = {
7517
7544
  writeFile: async (path, content, owner) => {
7518
7545
  await fs.writeFile(path, content, owner);
7519
- await decide(path, content);
7546
+ if (this.fresh.has(normalizePath(path))) {
7547
+ await decide(
7548
+ path,
7549
+ typeof content === "string" ? new TextEncoder().encode(content) : content
7550
+ );
7551
+ }
7520
7552
  },
7521
7553
  chmod: async (path, mode) => {
7522
7554
  const fresh2 = this.fresh.get(normalizePath(path));
7523
- const creating = fresh2?.added === 0 && (mode & 4095) === (438 & ~fresh2.umask);
7524
- if (!creating) this.fresh.delete(normalizePath(path));
7555
+ if (fresh2?.setup && (mode & 4095) === (438 & ~fresh2.umask)) fresh2.setup = false;
7556
+ else if (fresh2) this.drop(fresh2);
7525
7557
  await fs.chmod(path, mode);
7526
7558
  },
7527
7559
  rm: async (path, options) => {
7528
- this.fresh.delete(normalizePath(path));
7529
7560
  await fs.rm(path, options);
7561
+ const at = normalizePath(path);
7562
+ for (const key of [...this.fresh.keys()]) {
7563
+ if (key === at || within4(key, at)) this.fresh.delete(key);
7564
+ }
7530
7565
  },
7531
7566
  rename: async (from, to) => {
7532
7567
  await fs.rename(from, to);
7533
- const fresh2 = this.fresh.get(normalizePath(from));
7534
- this.fresh.delete(normalizePath(from));
7535
- this.fresh.delete(normalizePath(to));
7568
+ this.moved(from, to);
7569
+ },
7570
+ link: async (from, to, follow2) => {
7571
+ await fs.link(from, to, follow2);
7572
+ const source = follow2 && fs.realpath ? await fs.realpath(from, true) : from;
7573
+ const fresh2 = this.fresh.get(normalizePath(source));
7536
7574
  if (fresh2) this.fresh.set(normalizePath(to), fresh2);
7537
7575
  }
7538
7576
  };
7539
7577
  const ranged2 = rangedOps(fs);
7540
7578
  if (ranged2) {
7541
7579
  overrides.pwrite = async (path, at, bytes, transfer) => {
7542
- const head = at === 0 && this.fresh.has(normalizePath(path)) ? bytes.slice(0, 4) : void 0;
7580
+ const touches2 = at < MAGIC.length && this.fresh.has(normalizePath(path));
7543
7581
  await ranged2.pwrite(path, at, bytes, transfer);
7544
- if (head) await decide(path, head);
7582
+ if (touches2) await decide(path);
7583
+ };
7584
+ overrides.truncate = async (path, size) => {
7585
+ await ranged2.truncate(path, size);
7586
+ if (size < MAGIC.length && this.fresh.has(normalizePath(path))) await decide(path);
7545
7587
  };
7546
7588
  }
7547
- const bound = /* @__PURE__ */ new WeakMap();
7589
+ const bound2 = /* @__PURE__ */ new WeakMap();
7548
7590
  return new Proxy(fs, {
7549
7591
  get(target, key, receiver) {
7550
7592
  if (Object.hasOwn(overrides, key)) return overrides[key];
7551
7593
  const value = Reflect.get(target, key, receiver);
7552
7594
  if (typeof value !== "function") return value;
7553
- let method = bound.get(value);
7595
+ let method = bound2.get(value);
7554
7596
  if (method === void 0) {
7555
7597
  method = value.bind(target);
7556
- bound.set(value, method);
7598
+ bound2.set(value, method);
7557
7599
  }
7558
7600
  return method;
7559
7601
  }
@@ -7613,7 +7655,7 @@ var UNLINKED_DIR = ".slicc-unlinked";
7613
7655
  function webLocks() {
7614
7656
  return globalThis.navigator?.locks;
7615
7657
  }
7616
- var within4 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
7658
+ var within5 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
7617
7659
  var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
7618
7660
  var lockName = (instance) => `slicc-unlinked:${instance}`;
7619
7661
  var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
@@ -7621,12 +7663,12 @@ var PathGate = class {
7621
7663
  tails = /* @__PURE__ */ new Map();
7622
7664
  async acquire(path) {
7623
7665
  const prev = this.tails.get(path) ?? Promise.resolve();
7624
- const { promise, resolve } = Promise.withResolvers();
7666
+ const { promise, resolve: resolve2 } = Promise.withResolvers();
7625
7667
  const tail = prev.then(() => promise);
7626
7668
  this.tails.set(path, tail);
7627
7669
  await prev;
7628
7670
  return () => {
7629
- resolve();
7671
+ resolve2();
7630
7672
  if (this.tails.get(path) === tail) this.tails.delete(path);
7631
7673
  };
7632
7674
  }
@@ -7713,7 +7755,7 @@ var UnlinkedKeeper = class {
7713
7755
  for (const holder of this.holders) {
7714
7756
  if (holder.owns(target)) continue;
7715
7757
  for (const path of holder.held()) {
7716
- if (!within4(path, target) || holder.isKept(path)) continue;
7758
+ if (!within5(path, target) || holder.isKept(path)) continue;
7717
7759
  held.set(path, [...held.get(path) ?? [], holder]);
7718
7760
  }
7719
7761
  }
@@ -8104,7 +8146,7 @@ var ChildTable = class {
8104
8146
  }
8105
8147
  async nextChange(candidates, flags2, interrupt) {
8106
8148
  let wake;
8107
- const stateChange = new Promise((resolve) => wake = resolve);
8149
+ const stateChange = new Promise((resolve2) => wake = resolve2);
8108
8150
  const watching = flags2.untraced || flags2.continued;
8109
8151
  if (watching && wake) this.stateChanged.push(wake);
8110
8152
  try {
@@ -8151,6 +8193,65 @@ var ChildTable = class {
8151
8193
  }
8152
8194
  };
8153
8195
 
8196
+ // src/kernel/exec-ticket.ts
8197
+ var TICKET_ENV = "SLICC_SEALED_TICKET";
8198
+ var TICKET_TTL_MS = 3e4;
8199
+ var MAX_TICKETS = 256;
8200
+ var TICKET = /^[0-9a-f]{32}$/;
8201
+ async function argvDigest(argv) {
8202
+ const bytes = new TextEncoder().encode(JSON.stringify(argv));
8203
+ const hash = new Uint8Array(await crypto.subtle.digest("SHA-256", bytes));
8204
+ return [...hash].map((b) => b.toString(16).padStart(2, "0")).join("");
8205
+ }
8206
+ function isStrings(value) {
8207
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
8208
+ }
8209
+ function stringRecord(value) {
8210
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
8211
+ const entries = Object.entries(value);
8212
+ return entries.every(([, v]) => typeof v === "string") ? Object.fromEntries(entries) : void 0;
8213
+ }
8214
+ var TicketBook = class {
8215
+ booked = /* @__PURE__ */ new Map();
8216
+ now;
8217
+ normalise = async (path) => path;
8218
+ constructor(now = Date.now) {
8219
+ this.now = now;
8220
+ }
8221
+ get size() {
8222
+ return this.booked.size;
8223
+ }
8224
+ async record(req, reply, pid) {
8225
+ const { ticket, error } = reply;
8226
+ if (error !== void 0 || typeof ticket !== "string" || !TICKET.test(ticket)) return;
8227
+ const { path, argv, cwd } = req;
8228
+ const entry = {
8229
+ pid,
8230
+ path: await this.normalise(path),
8231
+ requested: path,
8232
+ requestedCwd: cwd,
8233
+ argv: await argvDigest(argv),
8234
+ cwd: await this.normalise(cwd),
8235
+ at: this.now()
8236
+ };
8237
+ this.prune();
8238
+ while (this.booked.size >= MAX_TICKETS)
8239
+ this.booked.delete(this.booked.keys().next().value);
8240
+ this.booked.set(ticket, entry);
8241
+ }
8242
+ take(ticket) {
8243
+ const booked = this.booked.get(ticket);
8244
+ this.booked.delete(ticket);
8245
+ return booked && this.now() - booked.at < TICKET_TTL_MS ? booked : void 0;
8246
+ }
8247
+ prune() {
8248
+ const now = this.now();
8249
+ for (const [ticket, booked] of this.booked) {
8250
+ if (now - booked.at >= TICKET_TTL_MS) this.booked.delete(ticket);
8251
+ }
8252
+ }
8253
+ };
8254
+
8154
8255
  // src/mount/protocol.ts
8155
8256
  var DRIVER_PROTOCOL = [1, 2];
8156
8257
  function driverVersionError(theirs) {
@@ -8221,7 +8322,7 @@ var DriverConnection = class _DriverConnection {
8221
8322
  call(call, transfer = []) {
8222
8323
  if (this.failure) return Promise.reject(this.failure);
8223
8324
  const id2 = ++this.nextId;
8224
- return new Promise((resolve, reject) => {
8325
+ return new Promise((resolve2, reject) => {
8225
8326
  const timer = unref(
8226
8327
  setTimeout(() => {
8227
8328
  this.waiting.delete(id2);
@@ -8234,7 +8335,7 @@ var DriverConnection = class _DriverConnection {
8234
8335
  this.onFail?.(error);
8235
8336
  }, this.timeoutMs)
8236
8337
  );
8237
- this.waiting.set(id2, { resolve, reject, timer });
8338
+ this.waiting.set(id2, { resolve: resolve2, reject, timer });
8238
8339
  this.port.postMessage({ ...call, id: id2 }, transfer);
8239
8340
  });
8240
8341
  }
@@ -8277,7 +8378,7 @@ function parseSize(text) {
8277
8378
  if (!match) throw errnoError("EINVAL", `not a size: ${text}`);
8278
8379
  return Math.floor(Number(match[1]) * UNITS[match[2].toLowerCase()]);
8279
8380
  }
8280
- var within5 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
8381
+ var within6 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
8281
8382
  var HeldSet = class extends Set {
8282
8383
  idle;
8283
8384
  constructor(idle) {
@@ -8296,7 +8397,7 @@ var HeldSet = class extends Set {
8296
8397
  }
8297
8398
  };
8298
8399
  function heldUnder(held, target) {
8299
- for (const paths of held) for (const path of paths.keys()) if (within5(path, target)) return true;
8400
+ for (const paths of held) for (const path of paths.keys()) if (within6(path, target)) return true;
8300
8401
  return false;
8301
8402
  }
8302
8403
  var HeldPaths = class extends Map {
@@ -8377,8 +8478,8 @@ var HeldPaths = class extends Map {
8377
8478
  this.keptHandles.delete(handle);
8378
8479
  if (kept) this.letGo(kept);
8379
8480
  }
8380
- const count = (this.opens.get(key) ?? 0) + (on ? 1 : -1);
8381
- if (count > 0) this.opens.set(key, count);
8481
+ const count2 = (this.opens.get(key) ?? 0) + (on ? 1 : -1);
8482
+ if (count2 > 0) this.opens.set(key, count2);
8382
8483
  else {
8383
8484
  this.opens.delete(key);
8384
8485
  for (const id2 of this.handlesAt(key)) this.handles.delete(id2);
@@ -8392,8 +8493,8 @@ var HeldPaths = class extends Map {
8392
8493
  return this.lastHandle;
8393
8494
  }
8394
8495
  holdWrite(path, on) {
8395
- const count = (this.get(path) ?? 0) + (on ? 1 : -1);
8396
- if (count > 0) this.set(path, count);
8496
+ const count2 = (this.get(path) ?? 0) + (on ? 1 : -1);
8497
+ if (count2 > 0) this.set(path, count2);
8397
8498
  else {
8398
8499
  this.delete(path);
8399
8500
  this.revoked.delete(path);
@@ -8412,24 +8513,24 @@ var HeldPaths = class extends Map {
8412
8513
  return this.owning === path;
8413
8514
  }
8414
8515
  revoke(prefix) {
8415
- for (const path of this.keys()) if (within5(path, prefix)) this.revoked.add(path);
8516
+ for (const path of this.keys()) if (within6(path, prefix)) this.revoked.add(path);
8416
8517
  }
8417
8518
  renamed(from, to) {
8418
- for (const [path, count] of [...this]) {
8419
- if (!within5(path, from)) continue;
8519
+ for (const [path, count2] of [...this]) {
8520
+ if (!within6(path, from)) continue;
8420
8521
  const moved2 = to + path.slice(from.length);
8421
8522
  this.delete(path);
8422
- this.set(moved2, (this.get(moved2) ?? 0) + count);
8523
+ this.set(moved2, (this.get(moved2) ?? 0) + count2);
8423
8524
  if (this.revoked.delete(path)) this.revoked.add(moved2);
8424
8525
  }
8425
- for (const [path, count] of [...this.opens]) {
8426
- if (!within5(path, from)) continue;
8526
+ for (const [path, count2] of [...this.opens]) {
8527
+ if (!within6(path, from)) continue;
8427
8528
  const moved2 = to + path.slice(from.length);
8428
8529
  this.opens.delete(path);
8429
- this.opens.set(moved2, (this.opens.get(moved2) ?? 0) + count);
8530
+ this.opens.set(moved2, (this.opens.get(moved2) ?? 0) + count2);
8430
8531
  }
8431
8532
  for (const [id2, key] of this.handles) {
8432
- if (within5(key, from)) this.handles.set(id2, to + key.slice(from.length));
8533
+ if (within6(key, from)) this.handles.set(id2, to + key.slice(from.length));
8433
8534
  }
8434
8535
  }
8435
8536
  isRevoked(path) {
@@ -8536,7 +8637,7 @@ var MountTable = class {
8536
8637
  locate(path) {
8537
8638
  let best;
8538
8639
  for (const mount of this.mounts.values()) {
8539
- if (within5(path, mount.target) && (!best || mount.target.length > best.target.length))
8640
+ if (within6(path, mount.target) && (!best || mount.target.length > best.target.length))
8540
8641
  best = mount;
8541
8642
  }
8542
8643
  if (!best) return void 0;
@@ -8679,7 +8780,7 @@ var MountTable = class {
8679
8780
  async commitUnder(prefix) {
8680
8781
  const at = normalizePath(prefix);
8681
8782
  await Promise.all(
8682
- [...this.sessions.keys()].filter((p) => within5(p, at)).map((p) => this.commit(p))
8783
+ [...this.sessions.keys()].filter((p) => within6(p, at)).map((p) => this.commit(p))
8683
8784
  );
8684
8785
  }
8685
8786
  async commitOverlapping(real, offset, length2) {
@@ -8714,10 +8815,10 @@ var MountTable = class {
8714
8815
  }
8715
8816
  forget(mount, path) {
8716
8817
  mount.epoch++;
8717
- for (const key of [...mount.attrs.keys()]) if (within5(key, path)) mount.attrs.delete(key);
8818
+ for (const key of [...mount.attrs.keys()]) if (within6(key, path)) mount.attrs.delete(key);
8718
8819
  mount.lists.delete(path);
8719
8820
  mount.lists.delete(parentOf7(path));
8720
- for (const key of [...mount.lists.keys()]) if (within5(key, path)) mount.lists.delete(key);
8821
+ for (const key of [...mount.lists.keys()]) if (within6(key, path)) mount.lists.delete(key);
8721
8822
  }
8722
8823
  async getattr(mount, path, rel2, settled = false) {
8723
8824
  for (let round = 1; ; round++) {
@@ -9352,6 +9453,11 @@ function statJson(s) {
9352
9453
  async function readdirStat(fs, dir) {
9353
9454
  return (await fs.readdirStat(dir)).map(([name2, stat]) => [name2, stat && statJson(stat)]);
9354
9455
  }
9456
+ async function usageAt(entry, path) {
9457
+ if (!entry.statfs) return void 0;
9458
+ const real = await realTarget(entry.fs, path).catch(() => path);
9459
+ return entry.statfs(real);
9460
+ }
9355
9461
  async function storageUsage() {
9356
9462
  const estimate = await globalThis.navigator?.storage?.estimate?.();
9357
9463
  if (typeof estimate?.quota !== "number") return null;
@@ -9668,7 +9774,7 @@ async function dispatchOne(entry, req) {
9668
9774
  if (req.op === "hold") return await holdOp(entry, path, req);
9669
9775
  const refused2 = await guarded(entry, path, req);
9670
9776
  if (refused2) return refused2;
9671
- if (req.op === "statfs") return json2(await entry.statfs?.(path) ?? await storageUsage());
9777
+ if (req.op === "statfs") return json2(await usageAt(entry, path) ?? await storageUsage());
9672
9778
  if (entry.revoked?.(path)) return { ok: false, errno: "EIO", message: `${path} was unmounted` };
9673
9779
  if (req.op === "write" && req.mode === 1 || UNLINKED_OPS.has(req.op)) {
9674
9780
  const serve = async () => run(entry, await keptOr(entry, path, req.handle), req);
@@ -10720,7 +10826,7 @@ var AsyncOps = class {
10720
10826
  this.wake();
10721
10827
  }
10722
10828
  sleep(timeoutMs, signal) {
10723
- return new Promise((resolve, reject) => {
10829
+ return new Promise((resolve2, reject) => {
10724
10830
  let timer;
10725
10831
  const cleanup = () => {
10726
10832
  clearTimeout(timer);
@@ -10729,7 +10835,7 @@ var AsyncOps = class {
10729
10835
  };
10730
10836
  const wake = () => {
10731
10837
  cleanup();
10732
- resolve(true);
10838
+ resolve2(true);
10733
10839
  };
10734
10840
  const abort = () => {
10735
10841
  cleanup();
@@ -10740,7 +10846,7 @@ var AsyncOps = class {
10740
10846
  if (timeoutMs !== void 0) {
10741
10847
  timer = setTimeout(() => {
10742
10848
  cleanup();
10743
- resolve(false);
10849
+ resolve2(false);
10744
10850
  }, timeoutMs);
10745
10851
  }
10746
10852
  });
@@ -11162,7 +11268,7 @@ var Resolver = class {
11162
11268
  };
11163
11269
  function waitFor(lookup, signal) {
11164
11270
  lookup.waiters++;
11165
- return new Promise((resolve, reject) => {
11271
+ return new Promise((resolve2, reject) => {
11166
11272
  const abort = () => {
11167
11273
  if (--lookup.waiters === 0) lookup.abort.abort(new KernelError("EINTR"));
11168
11274
  reject(new KernelError("EINTR"));
@@ -11172,7 +11278,7 @@ function waitFor(lookup, signal) {
11172
11278
  if (signal?.aborted) return;
11173
11279
  lookup.waiters--;
11174
11280
  signal?.removeEventListener("abort", abort);
11175
- resolve(value);
11281
+ resolve2(value);
11176
11282
  });
11177
11283
  });
11178
11284
  }
@@ -11353,8 +11459,8 @@ var KernelTty = class {
11353
11459
  }
11354
11460
  rubOut(n) {
11355
11461
  const dropped2 = this.line.splice(this.line.length - n, n);
11356
- const utf82 = (this.termios.c_iflag & IUTF8) !== 0;
11357
- const cells = utf82 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
11462
+ const utf83 = (this.termios.c_iflag & IUTF8) !== 0;
11463
+ const cells = utf83 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
11358
11464
  if (cells > 0 && this.termios.c_lflag & ECHO) {
11359
11465
  this.output(encoder3.encode("\b \b".repeat(cells)), false);
11360
11466
  }
@@ -11424,14 +11530,14 @@ var KernelTty = class {
11424
11530
  }
11425
11531
  changed(signal) {
11426
11532
  if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
11427
- return new Promise((resolve, reject) => {
11533
+ return new Promise((resolve2, reject) => {
11428
11534
  const onAbort = () => {
11429
11535
  this.waiters = this.waiters.filter((w) => w !== waiter);
11430
11536
  reject(new KernelError("EINTR"));
11431
11537
  };
11432
11538
  const waiter = () => {
11433
11539
  signal?.removeEventListener("abort", onAbort);
11434
- resolve();
11540
+ resolve2();
11435
11541
  };
11436
11542
  this.waiters.push(waiter);
11437
11543
  signal?.addEventListener("abort", onAbort, { once: true });
@@ -11440,7 +11546,7 @@ var KernelTty = class {
11440
11546
  wake() {
11441
11547
  const waiters = this.waiters;
11442
11548
  this.waiters = [];
11443
- for (const resolve of waiters) resolve();
11549
+ for (const resolve2 of waiters) resolve2();
11444
11550
  }
11445
11551
  };
11446
11552
 
@@ -11539,14 +11645,14 @@ var PtyPair = class {
11539
11645
  }
11540
11646
  changed(signal) {
11541
11647
  if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
11542
- return new Promise((resolve, reject) => {
11648
+ return new Promise((resolve2, reject) => {
11543
11649
  const onAbort = () => {
11544
11650
  this.waiters = this.waiters.filter((w) => w !== waiter);
11545
11651
  reject(new KernelError("EINTR"));
11546
11652
  };
11547
11653
  const waiter = () => {
11548
11654
  signal?.removeEventListener("abort", onAbort);
11549
- resolve();
11655
+ resolve2();
11550
11656
  };
11551
11657
  this.waiters.push(waiter);
11552
11658
  signal?.addEventListener("abort", onAbort, { once: true });
@@ -11555,7 +11661,7 @@ var PtyPair = class {
11555
11661
  wake() {
11556
11662
  const waiters = this.waiters;
11557
11663
  this.waiters = [];
11558
- for (const resolve of waiters) resolve();
11664
+ for (const resolve2 of waiters) resolve2();
11559
11665
  }
11560
11666
  };
11561
11667
  var PtyTable = class {
@@ -11620,7 +11726,8 @@ function ptySyscall(req, ctx) {
11620
11726
  if (!ctx.ptys) throw new KernelError("ENOENT");
11621
11727
  const pair = ctx.ptys.get(req.n);
11622
11728
  ttyOpenable(ctx.guard, pair.slave);
11623
- const fd = ctx.fds.install(pair.openSlave(), 3);
11729
+ const slave = pair.openSlave();
11730
+ const fd = ctx.fds.install(ctx.ttyFilter?.(slave) ?? slave, 3);
11624
11731
  if (!req.noctty) ctx.jobs?.acquireTerminal(ctx.pid, pair.slave);
11625
11732
  return json4(fd);
11626
11733
  }
@@ -11693,8 +11800,8 @@ async function runChild(req, deps) {
11693
11800
  };
11694
11801
  const controls = [];
11695
11802
  const capture = () => {
11696
- const { promise, resolve } = Promise.withResolvers();
11697
- const control = { shut: false, ended: resolve, eof: promise };
11803
+ const { promise, resolve: resolve2 } = Promise.withResolvers();
11804
+ const control = { shut: false, ended: resolve2, eof: promise };
11698
11805
  controls.push(control);
11699
11806
  return { capture: true, max, overflow: () => stop("ENOBUFS"), control };
11700
11807
  };
@@ -11787,7 +11894,7 @@ async function waitReady(fds, read, write, deadline, interrupted2) {
11787
11894
  return file.changed === void 0 ? [] : [file.changed(round.signal).catch(() => void 0)];
11788
11895
  });
11789
11896
  let timer;
11790
- const expiry = deadline === Number.POSITIVE_INFINITY ? [] : [new Promise((resolve) => timer = setTimeout(resolve, deadline - Date.now()))];
11897
+ const expiry = deadline === Number.POSITIVE_INFINITY ? [] : [new Promise((resolve2) => timer = setTimeout(resolve2, deadline - Date.now()))];
11791
11898
  try {
11792
11899
  await Promise.race([...changes, ...expiry, interrupted2]);
11793
11900
  } finally {
@@ -11871,10 +11978,10 @@ async function socketSyscall(req, proc) {
11871
11978
  }
11872
11979
  }
11873
11980
  function interruptible2(dialing, signal) {
11874
- return new Promise((resolve, reject) => {
11981
+ return new Promise((resolve2, reject) => {
11875
11982
  const abort = () => reject(new KernelError("EINTR"));
11876
11983
  signal.addEventListener("abort", abort, { once: true });
11877
- dialing.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
11984
+ dialing.then(resolve2, reject).finally(() => signal.removeEventListener("abort", abort));
11878
11985
  });
11879
11986
  }
11880
11987
  function unnamed(domain) {
@@ -11901,7 +12008,7 @@ function isMissing(err) {
11901
12008
  if (err?.code === "ENOENT") return true;
11902
12009
  return err instanceof Error && err.message.startsWith("ENOENT");
11903
12010
  }
11904
- function within6(path, root) {
12011
+ function within7(path, root) {
11905
12012
  return path === root || path.startsWith(root.endsWith("/") ? root : `${root}/`);
11906
12013
  }
11907
12014
  var VfsNode = class {
@@ -12222,12 +12329,12 @@ var VfsNodes = class {
12222
12329
  return node;
12223
12330
  }
12224
12331
  holds(prefix) {
12225
- for (const path of this.byPath.keys()) if (within6(path, prefix)) return true;
12332
+ for (const path of this.byPath.keys()) if (within7(path, prefix)) return true;
12226
12333
  return false;
12227
12334
  }
12228
12335
  writes(prefix) {
12229
12336
  for (const [path, node] of this.byPath)
12230
- if (node.writers > 0 && within6(path, prefix)) return true;
12337
+ if (node.writers > 0 && within7(path, prefix)) return true;
12231
12338
  return false;
12232
12339
  }
12233
12340
  closed(node, writable2 = false) {
@@ -12271,11 +12378,11 @@ var VfsNodes = class {
12271
12378
  this.kept.delete(node);
12272
12379
  }
12273
12380
  async flush(path) {
12274
- for (const [p, node] of this.byPath) if (within6(p, path)) await node.serial(() => node.flush());
12381
+ for (const [p, node] of this.byPath) if (within7(p, path)) await node.serial(() => node.flush());
12275
12382
  }
12276
12383
  async unlinking(path) {
12277
12384
  for (const [held, node] of [...this.byPath]) {
12278
- if (!within6(held, path)) continue;
12385
+ if (!within7(held, path)) continue;
12279
12386
  await node.serial(async () => {
12280
12387
  await Promise.all([this.keepsOnDisk || node.keep(), node.limit()]);
12281
12388
  });
@@ -12283,14 +12390,14 @@ var VfsNodes = class {
12283
12390
  }
12284
12391
  revoke(prefix) {
12285
12392
  for (const [path, node] of [...this.byPath]) {
12286
- if (!within6(path, prefix)) continue;
12393
+ if (!within7(path, prefix)) continue;
12287
12394
  node.revoke();
12288
12395
  this.forget(node);
12289
12396
  }
12290
12397
  }
12291
12398
  unlinked(path) {
12292
12399
  for (const [held, node] of [...this.byPath]) {
12293
- if (!within6(held, path) || this.byPath.get(held) !== node) continue;
12400
+ if (!within7(held, path) || this.byPath.get(held) !== node) continue;
12294
12401
  if (!this.dropName(held, node)) this.orphan(node);
12295
12402
  }
12296
12403
  }
@@ -12298,8 +12405,8 @@ var VfsNodes = class {
12298
12405
  if (from === to) return;
12299
12406
  const moved2 = [];
12300
12407
  for (const [p, node] of [...this.byPath]) {
12301
- if (within6(p, from)) moved2.push([p, node]);
12302
- else if (within6(p, to) && this.byPath.get(p) === node && !this.dropName(p, node)) {
12408
+ if (within7(p, from)) moved2.push([p, node]);
12409
+ else if (within7(p, to) && this.byPath.get(p) === node && !this.dropName(p, node)) {
12303
12410
  this.orphan(node);
12304
12411
  }
12305
12412
  }
@@ -12578,7 +12685,7 @@ function isWasmSyscall(req) {
12578
12685
  var MAX_READ = 1024 * 1024;
12579
12686
  var WRITE_ACCESS = 3;
12580
12687
  var KEYBOARD = /* @__PURE__ */ new Set([SIG.INT, SIG.QUIT, SIG.TSTP]);
12581
- var DEFAULT_UMASK = 18;
12688
+ var DEFAULT_UMASK2 = 18;
12582
12689
  function words(value) {
12583
12690
  if (!Array.isArray(value) || !value.every((w) => typeof w === "string")) {
12584
12691
  throw new KernelError("EINVAL");
@@ -12606,7 +12713,7 @@ var WasmProcess = class {
12606
12713
  interrupt = new AbortController();
12607
12714
  gone = new AbortController();
12608
12715
  dying = false;
12609
- umask = DEFAULT_UMASK;
12716
+ umask = DEFAULT_UMASK2;
12610
12717
  cred;
12611
12718
  execChild;
12612
12719
  killed = false;
@@ -12628,7 +12735,7 @@ var WasmProcess = class {
12628
12735
  this.fds = fds;
12629
12736
  this.options = options;
12630
12737
  this.inherited = options.ignored ?? 0;
12631
- this.umask = options.umask ?? DEFAULT_UMASK;
12738
+ this.umask = options.umask ?? DEFAULT_UMASK2;
12632
12739
  this.cred = copyCred(options.cred ?? ROOT);
12633
12740
  this.children = new ChildTable(fds, options.spawner, options.forker);
12634
12741
  this.nodes = options.nodes ?? new VfsNodes(options.fs);
@@ -12721,7 +12828,7 @@ var WasmProcess = class {
12721
12828
  if (this.stopped) return "stop";
12722
12829
  this.stopped = sig;
12723
12830
  this.stops++;
12724
- this.resumed = new Promise((resolve) => this.wake = resolve);
12831
+ this.resumed = new Promise((resolve2) => this.wake = resolve2);
12725
12832
  const blocked = this.interrupt;
12726
12833
  this.interrupt = new AbortController();
12727
12834
  blocked.abort();
@@ -12786,7 +12893,9 @@ var WasmProcess = class {
12786
12893
  const { ptys, jobs } = this.options;
12787
12894
  const owner = this.options.owners ? this.ttyOwner() : void 0;
12788
12895
  const guard2 = this.guarded();
12789
- return ptySyscall(req, { pid: this.pid, fds: this.fds, ptys, jobs, guard: guard2, owner });
12896
+ const { ttyFilter } = this.options;
12897
+ const ctx = { pid: this.pid, fds: this.fds, ptys, jobs, guard: guard2, owner, ttyFilter };
12898
+ return ptySyscall(req, ctx);
12790
12899
  }
12791
12900
  return await this.procSyscall(req, cancelled);
12792
12901
  } catch (e) {
@@ -12961,7 +13070,8 @@ var WasmProcess = class {
12961
13070
  const tty = name2 === void 0 ? this.controllingTerminal() : this.options.jobs?.terminalNamed(name2);
12962
13071
  if (!tty) throw new KernelError("ENXIO");
12963
13072
  if (name2 !== void 0) ttyOpenable(this.guarded(), tty);
12964
- return this.fds.install(tty.file(), 3);
13073
+ const file = tty.file();
13074
+ return this.fds.install(this.options.ttyFilter?.(file) ?? file, 3);
12965
13075
  }
12966
13076
  openSealed() {
12967
13077
  const broker = this.options.sealed;
@@ -13001,15 +13111,8 @@ var WasmProcess = class {
13001
13111
  async sealedCaller() {
13002
13112
  const shown = await this.options.identity?.();
13003
13113
  const broker = this.options.sealed;
13004
- const tag = broker.tagOf(this.pid);
13005
- return {
13006
- pid: shown?.pid ?? this.pid,
13007
- ppid: shown?.ppid ?? 0,
13008
- uid: this.cred.euid,
13009
- gid: this.cred.egid,
13010
- ...tag !== void 0 ? { tag } : {},
13011
- users: broker.users
13012
- };
13114
+ const ids = { pid: shown?.pid ?? this.pid, ppid: shown?.ppid ?? 0 };
13115
+ return broker.stamp(ids, this.cred, broker.tagOf(this.pid));
13013
13116
  }
13014
13117
  guarded() {
13015
13118
  const { guard: guard2 } = this.options;
@@ -13517,13 +13620,13 @@ var WasmProcess = class {
13517
13620
  while (this.dlLock.depth > 0 && this.dlLock.owner !== owner) {
13518
13621
  if (this.dying) throw new KernelError("EINTR");
13519
13622
  const interrupt = this.interrupt.signal;
13520
- const gone = await new Promise((resolve) => {
13623
+ const gone = await new Promise((resolve2) => {
13521
13624
  const waiter = {
13522
13625
  owner,
13523
13626
  wake: (ended) => {
13524
13627
  this.dlLock.waiters.delete(waiter);
13525
13628
  interrupt.removeEventListener("abort", recheck);
13526
- resolve(ended);
13629
+ resolve2(ended);
13527
13630
  }
13528
13631
  };
13529
13632
  const recheck = () => waiter.wake(false);
@@ -13779,6 +13882,7 @@ function processOptions(opts, header3, hooks, created3) {
13779
13882
  ...inherited(opts),
13780
13883
  ...opts.owners ? { owners: true } : {},
13781
13884
  ...opts.sealed ? { sealed: opts.sealed } : {},
13885
+ ...opts.ttyFilter ? { ttyFilter: opts.ttyFilter } : {},
13782
13886
  ...opts.clipboard ? { clipboard: opts.clipboard, ...clipboardName(opts) } : {},
13783
13887
  ...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
13784
13888
  ...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3 } } : {},
@@ -14273,6 +14377,523 @@ function replayingHops(inner, heldMs = HELD_MS) {
14273
14377
  };
14274
14378
  }
14275
14379
 
14380
+ // node_modules/@ai-ecoverse/slicc-scrub/dist/index.js
14381
+ var FRAGMENT = 16;
14382
+ var MIN_VALUE = 8;
14383
+ var MIN_BASE64 = 8;
14384
+ var encoder4 = new TextEncoder();
14385
+ var STD = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
14386
+ function utf82(text) {
14387
+ return encoder4.encode(text);
14388
+ }
14389
+ function base642(bytes, padded = true) {
14390
+ let out = "";
14391
+ for (let i = 0; i < bytes.length; i += 3) {
14392
+ const a = bytes[i];
14393
+ const b = i + 1 < bytes.length ? bytes[i + 1] : 0;
14394
+ const c = i + 2 < bytes.length ? bytes[i + 2] : 0;
14395
+ const triple = a << 16 | b << 8 | c;
14396
+ const left = bytes.length - i;
14397
+ out += STD[triple >> 18 & 63] + STD[triple >> 12 & 63];
14398
+ out += left > 1 ? STD[triple >> 6 & 63] : padded ? "=" : "";
14399
+ out += left > 2 ? STD[triple & 63] : padded ? "=" : "";
14400
+ }
14401
+ return out;
14402
+ }
14403
+ function urlSafe(text) {
14404
+ return text.replace(/\+/g, "-").replace(/\//g, "_");
14405
+ }
14406
+ function hex2(bytes) {
14407
+ let out = "";
14408
+ for (const byte of bytes) out += byte.toString(16).padStart(2, "0");
14409
+ return out;
14410
+ }
14411
+ function embedded(bytes, offset) {
14412
+ const shifted = new Uint8Array(offset + bytes.length);
14413
+ shifted.set(bytes, offset);
14414
+ const encoded = base642(shifted);
14415
+ const first = Math.ceil(8 * offset / 6);
14416
+ const last = Math.floor(8 * (offset + bytes.length) / 6);
14417
+ return encoded.slice(first, last);
14418
+ }
14419
+ function unicode(value, upper) {
14420
+ let out = "";
14421
+ for (let i = 0; i < value.length; i++) {
14422
+ const unit = value.charCodeAt(i);
14423
+ if (unit === 34) out += '\\"';
14424
+ else if (unit === 92) out += "\\\\";
14425
+ else if (unit < 32 || unit > 126) {
14426
+ const code = unit.toString(16).padStart(4, "0");
14427
+ out += `\\u${upper ? code.toUpperCase() : code}`;
14428
+ } else out += value[i];
14429
+ }
14430
+ return out;
14431
+ }
14432
+ function percent(value) {
14433
+ let component;
14434
+ try {
14435
+ component = encodeURIComponent(value);
14436
+ } catch {
14437
+ return [];
14438
+ }
14439
+ const lower = component.replace(/%[0-9A-F]{2}/g, (escape) => escape.toLowerCase());
14440
+ const plus = component.replace(/%20/g, "+");
14441
+ let form = "";
14442
+ for (const byte of utf82(value)) {
14443
+ const char = String.fromCharCode(byte);
14444
+ if (/[A-Za-z0-9*\-._]/.test(char)) form += char;
14445
+ else if (byte === 32) form += "+";
14446
+ else form += `%${byte.toString(16).toUpperCase().padStart(2, "0")}`;
14447
+ }
14448
+ return [component, lower, plus, form];
14449
+ }
14450
+ function html(value, apostrophe) {
14451
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, apostrophe);
14452
+ }
14453
+ function encodings(bytes) {
14454
+ const standard = [base642(bytes), base642(bytes, false)];
14455
+ for (const offset of [0, 1, 2]) {
14456
+ const run2 = embedded(bytes, offset);
14457
+ if (run2.length >= MIN_BASE64) standard.push(run2);
14458
+ }
14459
+ return [...standard, ...standard.map(urlSafe)];
14460
+ }
14461
+ function shapes(value) {
14462
+ const raw = utf82(value);
14463
+ const seen = /* @__PURE__ */ new Set([value]);
14464
+ const out = [];
14465
+ if (raw.length < FRAGMENT) out.push({ bytes: raw, form: "raw" });
14466
+ for (let offset = 0; raw.length >= FRAGMENT && offset + FRAGMENT <= raw.length; offset++) {
14467
+ out.push({ bytes: raw.subarray(offset, offset + FRAGMENT), form: "fragment", offset });
14468
+ }
14469
+ const add = (text, form) => {
14470
+ if (seen.has(text)) return;
14471
+ seen.add(text);
14472
+ out.push({ bytes: utf82(text), form });
14473
+ };
14474
+ const json5 = JSON.stringify(value).slice(1, -1);
14475
+ for (const text of [
14476
+ json5,
14477
+ json5.replace(/\//g, "\\/"),
14478
+ unicode(value, false),
14479
+ unicode(value, true)
14480
+ ]) {
14481
+ add(text, "json");
14482
+ }
14483
+ for (const text of percent(value)) add(text, "percent");
14484
+ for (const text of [html(value, "&#39;"), html(value, "&#x27;")]) add(text, "html");
14485
+ for (const text of encodings(raw)) add(text, "base64");
14486
+ add(hex2(raw), "hex");
14487
+ add(hex2(raw).toUpperCase(), "hex");
14488
+ return out;
14489
+ }
14490
+ function wrappable(form) {
14491
+ return form === "base64" || form === "hex";
14492
+ }
14493
+ var Automaton = class {
14494
+ edges = /* @__PURE__ */ new Map();
14495
+ fail = [0];
14496
+ depth = [0];
14497
+ children = [0];
14498
+ out = [[]];
14499
+ dict = [-1];
14500
+ lengths = [];
14501
+ add(bytes) {
14502
+ let node = 0;
14503
+ for (const byte of bytes) {
14504
+ const key = node * 256 + byte;
14505
+ let next2 = this.edges.get(key);
14506
+ if (next2 === void 0) {
14507
+ next2 = this.depth.length;
14508
+ this.edges.set(key, next2);
14509
+ this.fail.push(0);
14510
+ this.depth.push(this.depth[node] + 1);
14511
+ this.children.push(0);
14512
+ this.children[node] += 1;
14513
+ this.out.push([]);
14514
+ this.dict.push(-1);
14515
+ }
14516
+ node = next2;
14517
+ }
14518
+ const id2 = this.lengths.length;
14519
+ this.lengths.push(bytes.length);
14520
+ this.out[node].push(id2);
14521
+ return id2;
14522
+ }
14523
+ build() {
14524
+ const queue = [];
14525
+ const byNode = /* @__PURE__ */ new Map();
14526
+ for (const [key, child] of this.edges) {
14527
+ const parent = Math.floor(key / 256);
14528
+ const list = byNode.get(parent) ?? [];
14529
+ list.push([key % 256, child]);
14530
+ byNode.set(parent, list);
14531
+ }
14532
+ for (const [, child] of byNode.get(0) ?? []) queue.push(child);
14533
+ for (let head = 0; head < queue.length; head++) {
14534
+ const node = queue[head];
14535
+ const fallback = this.fail[node];
14536
+ this.dict[node] = this.out[fallback].length > 0 ? fallback : this.dict[fallback];
14537
+ for (const [byte, child] of byNode.get(node) ?? []) {
14538
+ let state = fallback;
14539
+ let target = this.edges.get(state * 256 + byte);
14540
+ while (target === void 0 && state > 0) {
14541
+ state = this.fail[state];
14542
+ target = this.edges.get(state * 256 + byte);
14543
+ }
14544
+ this.fail[child] = target ?? 0;
14545
+ queue.push(child);
14546
+ }
14547
+ }
14548
+ }
14549
+ step(state, byte) {
14550
+ let current = state;
14551
+ for (; ; ) {
14552
+ const next2 = this.edges.get(current * 256 + byte);
14553
+ if (next2 !== void 0) return next2;
14554
+ if (current === 0) return 0;
14555
+ current = this.fail[current];
14556
+ }
14557
+ }
14558
+ matches(state, visit) {
14559
+ for (let node = state; node > 0; node = this.dict[node]) {
14560
+ for (const id2 of this.out[node]) visit(id2);
14561
+ }
14562
+ }
14563
+ partials(state, length2, visit) {
14564
+ for (let node = state; node > 0; node = this.fail[node]) {
14565
+ if (this.children[node] > 0) visit(length2 - this.depth[node]);
14566
+ }
14567
+ }
14568
+ };
14569
+ var RANK = {
14570
+ raw: 0,
14571
+ fragment: 1,
14572
+ json: 2,
14573
+ percent: 3,
14574
+ html: 4,
14575
+ base64: 5,
14576
+ hex: 6,
14577
+ wrapped: 7
14578
+ };
14579
+ var CR2 = 13;
14580
+ var LF2 = 10;
14581
+ var EMPTY2 = new Uint8Array(0);
14582
+ function register(automaton, sources, ids, bytes, source) {
14583
+ const key = Array.from(bytes).join(",");
14584
+ let id2 = ids.get(key);
14585
+ if (id2 === void 0) {
14586
+ id2 = automaton.add(bytes);
14587
+ ids.set(key, id2);
14588
+ sources[id2] = [];
14589
+ }
14590
+ sources[id2].push(source);
14591
+ }
14592
+ function prepare(secrets) {
14593
+ const main = new Automaton();
14594
+ const wrap = new Automaton();
14595
+ const sources = [];
14596
+ const wrapSources = [];
14597
+ const ids = /* @__PURE__ */ new Map();
14598
+ const wrapIds = /* @__PURE__ */ new Map();
14599
+ const values = secrets.map((secret) => utf82(secret.value));
14600
+ secrets.forEach((secret, index) => {
14601
+ if (typeof secret.value !== "string" || values[index].length < MIN_VALUE) {
14602
+ throw new RangeError(`the value of ${secret.name} is shorter than ${MIN_VALUE} bytes`);
14603
+ }
14604
+ for (const shape of shapes(secret.value)) {
14605
+ const source = { secret: index, form: shape.form, offset: shape.offset };
14606
+ register(main, sources, ids, shape.bytes, source);
14607
+ if (wrappable(shape.form)) register(wrap, wrapSources, wrapIds, shape.bytes, source);
14608
+ }
14609
+ });
14610
+ main.build();
14611
+ wrap.build();
14612
+ const replacements = secrets.map((secret) => [
14613
+ utf82(secret.placeholder),
14614
+ utf82(`[redacted:${secret.name}]`)
14615
+ ]);
14616
+ return { secrets, values, main, wrap, sources, wrapSources, replacements };
14617
+ }
14618
+ function better(a, b) {
14619
+ if (!b) return true;
14620
+ if (a.end !== b.end) return a.end > b.end;
14621
+ if (a.rank !== b.rank) return a.rank < b.rank;
14622
+ return a.secret < b.secret;
14623
+ }
14624
+ function resolve(c, input, start2, end, source) {
14625
+ if (source.form !== "fragment") {
14626
+ return {
14627
+ start: start2,
14628
+ end,
14629
+ secret: source.secret,
14630
+ form: source.form,
14631
+ rank: RANK[source.form],
14632
+ growing: false
14633
+ };
14634
+ }
14635
+ const value = c.values[source.secret];
14636
+ let stop = end;
14637
+ let at = source.offset + FRAGMENT;
14638
+ while (stop < input.length && at < value.length && input[stop] === value[at]) {
14639
+ stop++;
14640
+ at++;
14641
+ }
14642
+ const form = source.offset === 0 && at === value.length ? "raw" : "fragment";
14643
+ const growing = stop === input.length && at < value.length;
14644
+ return { start: start2, end: stop, secret: source.secret, form, rank: RANK[form], growing };
14645
+ }
14646
+ function keep(best, candidate) {
14647
+ if (better(candidate, best.get(candidate.start))) best.set(candidate.start, candidate);
14648
+ }
14649
+ function scanMain(c, input, best, partials) {
14650
+ let state = 0;
14651
+ for (let i = 0; i < input.length; i++) {
14652
+ state = c.main.step(state, input[i]);
14653
+ c.main.matches(state, (id2) => {
14654
+ const start2 = i + 1 - c.main.lengths[id2];
14655
+ for (const source of c.sources[id2]) {
14656
+ const candidate = resolve(c, input, start2, i + 1, source);
14657
+ if (candidate.growing) partials.push(candidate.start);
14658
+ keep(best, candidate);
14659
+ }
14660
+ });
14661
+ }
14662
+ c.main.partials(state, input.length, (start2) => partials.push(start2));
14663
+ }
14664
+ function scanWrapped(c, input, best, partials) {
14665
+ const map = [];
14666
+ for (let i = 0; i < input.length; i++) if (input[i] !== CR2 && input[i] !== LF2) map.push(i);
14667
+ if (map.length === input.length) return;
14668
+ let state = 0;
14669
+ for (let v = 0; v < map.length; v++) {
14670
+ state = c.wrap.step(state, input[map[v]]);
14671
+ c.wrap.matches(state, (id2) => {
14672
+ const first = v + 1 - c.wrap.lengths[id2];
14673
+ const start2 = map[first];
14674
+ const end = map[v] + 1;
14675
+ const wrapped = end - start2 !== c.wrap.lengths[id2];
14676
+ for (const source of c.wrapSources[id2]) {
14677
+ const form = wrapped ? "wrapped" : source.form;
14678
+ keep(best, { start: start2, end, secret: source.secret, form, rank: RANK[form], growing: false });
14679
+ }
14680
+ });
14681
+ }
14682
+ c.wrap.partials(state, map.length, (start2) => partials.push(map[start2]));
14683
+ }
14684
+ function count(c, hits, candidate) {
14685
+ const name2 = c.secrets[candidate.secret].name;
14686
+ const key = `${name2}\0${candidate.form}`;
14687
+ const hit = hits.get(key) ?? { name: name2, form: candidate.form, count: 0 };
14688
+ hit.count += 1;
14689
+ hits.set(key, hit);
14690
+ }
14691
+ function concat5(parts2) {
14692
+ let length2 = 0;
14693
+ for (const part of parts2) length2 += part.length;
14694
+ const out = new Uint8Array(length2);
14695
+ let at = 0;
14696
+ for (const part of parts2) {
14697
+ out.set(part, at);
14698
+ at += part.length;
14699
+ }
14700
+ return out;
14701
+ }
14702
+ function pass(c, input, final) {
14703
+ const best = /* @__PURE__ */ new Map();
14704
+ const partials = [];
14705
+ scanMain(c, input, best, partials);
14706
+ scanWrapped(c, input, best, partials);
14707
+ const holds = final ? [] : [...new Set(partials)].sort((a, b) => a - b);
14708
+ const starts = [...best.keys()].sort((a, b) => a - b);
14709
+ const parts2 = [];
14710
+ const hits = /* @__PURE__ */ new Map();
14711
+ let pos = 0;
14712
+ let cut = input.length;
14713
+ let s = 0;
14714
+ let h = 0;
14715
+ for (; ; ) {
14716
+ while (s < starts.length && starts[s] < pos) s++;
14717
+ while (h < holds.length && holds[h] < pos) h++;
14718
+ const hold = h < holds.length ? holds[h] : Infinity;
14719
+ const next2 = s < starts.length ? starts[s] : Infinity;
14720
+ if (hold <= next2) {
14721
+ cut = Math.min(hold, input.length);
14722
+ break;
14723
+ }
14724
+ const candidate = best.get(next2);
14725
+ parts2.push(
14726
+ input.subarray(pos, next2),
14727
+ c.replacements[candidate.secret][candidate.form === "raw" ? 0 : 1]
14728
+ );
14729
+ count(c, hits, candidate);
14730
+ pos = candidate.end;
14731
+ }
14732
+ parts2.push(input.subarray(pos, cut));
14733
+ return { output: concat5(parts2), cut, hits };
14734
+ }
14735
+ function merge(into, from) {
14736
+ for (const [key, hit] of from) {
14737
+ const known = into.get(key);
14738
+ if (known) known.count += hit.count;
14739
+ else into.set(key, { ...hit });
14740
+ }
14741
+ }
14742
+ function compile(secrets, options = {}) {
14743
+ const c = prepare(secrets.map((secret) => ({ ...secret })));
14744
+ const decoder4 = new TextDecoder();
14745
+ const idleMs = options.idleMs ?? 250;
14746
+ const scrubBytes = (input) => {
14747
+ if (c.secrets.length === 0) return { bytes: input.slice(), hits: [] };
14748
+ const result = pass(c, input, true);
14749
+ return { bytes: result.output, hits: [...result.hits.values()] };
14750
+ };
14751
+ return {
14752
+ version: options.version ?? 0,
14753
+ idleMs,
14754
+ size: c.main.lengths.length,
14755
+ bytes: scrubBytes,
14756
+ text(input) {
14757
+ const { bytes, hits } = scrubBytes(utf82(input));
14758
+ return { text: decoder4.decode(bytes), hits };
14759
+ },
14760
+ stream() {
14761
+ let pending = EMPTY2;
14762
+ const hits = /* @__PURE__ */ new Map();
14763
+ const run2 = (final) => {
14764
+ if (c.secrets.length === 0 || pending.length === 0) {
14765
+ const out = pending;
14766
+ pending = EMPTY2;
14767
+ return out;
14768
+ }
14769
+ const result = pass(c, pending, final);
14770
+ merge(hits, result.hits);
14771
+ pending = pending.slice(result.cut);
14772
+ return result.output;
14773
+ };
14774
+ return {
14775
+ push(chunk2) {
14776
+ pending = concat5([pending, chunk2]);
14777
+ return run2(false);
14778
+ },
14779
+ idle() {
14780
+ return pending.length < FRAGMENT ? run2(true) : EMPTY2;
14781
+ },
14782
+ end() {
14783
+ return run2(true);
14784
+ },
14785
+ get hits() {
14786
+ return [...hits.values()];
14787
+ }
14788
+ };
14789
+ }
14790
+ };
14791
+ }
14792
+
14793
+ // src/kernel/scrub-file.ts
14794
+ var SCRUB_IDLE_MS = 50;
14795
+ var encoder5 = new TextEncoder();
14796
+ function scrubberOf(secrets) {
14797
+ const usable = secrets.filter((s) => encoder5.encode(s.value).length >= MIN_VALUE);
14798
+ return usable.length > 0 ? compile(usable, { idleMs: SCRUB_IDLE_MS }) : void 0;
14799
+ }
14800
+ async function writeAll2(file, bytes, signal) {
14801
+ const write = file.write;
14802
+ for (let at = 0; at < bytes.length; ) {
14803
+ at += await write.call(file, bytes.subarray(at), at === 0 ? signal : void 0);
14804
+ }
14805
+ }
14806
+ function bound(file, key) {
14807
+ const method = file[key];
14808
+ return typeof method === "function" ? { [key]: method.bind(file) } : {};
14809
+ }
14810
+ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
14811
+ const file = inner.file;
14812
+ const stream = scrubber.stream();
14813
+ let queue = Promise.resolve();
14814
+ let timer;
14815
+ let failed3;
14816
+ const raise = () => {
14817
+ if (failed3 === void 0) return;
14818
+ const err = failed3;
14819
+ failed3 = void 0;
14820
+ throw err;
14821
+ };
14822
+ const enqueue = (bytes, signal) => {
14823
+ const next2 = queue.then(() => bytes.length > 0 ? writeAll2(file, bytes, signal) : void 0);
14824
+ queue = next2.catch(() => void 0);
14825
+ return next2;
14826
+ };
14827
+ const release = () => {
14828
+ timer = void 0;
14829
+ enqueue(stream.idle()).catch((err) => {
14830
+ failed3 = err;
14831
+ });
14832
+ };
14833
+ return new OpenFile({
14834
+ ...bound(file, "read"),
14835
+ ...bound(file, "peek"),
14836
+ ...bound(file, "poll"),
14837
+ ...bound(file, "changed"),
14838
+ ...bound(file, "stat"),
14839
+ ...file.tty ? { tty: file.tty } : {},
14840
+ ...file.clipboard ? { clipboard: file.clipboard } : {},
14841
+ ...file.write ? {
14842
+ write: async (bytes, signal) => {
14843
+ if (timer !== void 0) clearTimeout(timer);
14844
+ raise();
14845
+ await enqueue(stream.push(bytes), signal);
14846
+ timer = setTimeout(release, idleMs);
14847
+ return bytes.length;
14848
+ }
14849
+ } : {},
14850
+ flush: async () => {
14851
+ await queue;
14852
+ raise();
14853
+ await file.flush?.();
14854
+ },
14855
+ close: async () => {
14856
+ if (timer !== void 0) clearTimeout(timer);
14857
+ let failure2;
14858
+ try {
14859
+ await enqueue(stream.end());
14860
+ raise();
14861
+ } catch (err) {
14862
+ failure2 = { err };
14863
+ }
14864
+ await inner.release();
14865
+ if (failure2) throw failure2.err;
14866
+ }
14867
+ });
14868
+ }
14869
+ function scrubbedAll(file, scrubbers) {
14870
+ return scrubbers.reduce((inner, scrubber) => scrubbed(inner, scrubber), file);
14871
+ }
14872
+ function stdioFiles(fds) {
14873
+ const files = /* @__PURE__ */ new Set();
14874
+ for (const fd of [1, 2]) {
14875
+ try {
14876
+ files.add(fds.get(fd));
14877
+ } catch {
14878
+ }
14879
+ }
14880
+ return files;
14881
+ }
14882
+ function scrubStdio(fds, scrubber) {
14883
+ const wrapped = /* @__PURE__ */ new Map();
14884
+ const stdio = stdioFiles(fds);
14885
+ for (const fd of fds.numbers()) {
14886
+ const original = fds.get(fd);
14887
+ if (!stdio.has(original) && !original.file.tty) continue;
14888
+ const known = wrapped.get(original);
14889
+ const flags2 = fds.statusFlags(fd);
14890
+ const wrapper = known ? known.retain() : scrubbed(original.retain(), scrubber);
14891
+ wrapped.set(original, wrapper);
14892
+ fds.installAt(fd, wrapper);
14893
+ if (flags2 !== void 0) fds.setStatusFlags(fd, flags2);
14894
+ }
14895
+ }
14896
+
14276
14897
  // src/kernel/sealed.ts
14277
14898
  var SEALED_MAX_BYTES = 64 * 1024;
14278
14899
  var SEALED_TIMEOUT_MS = 1e4;
@@ -14286,7 +14907,7 @@ var SEALED_OPS = /* @__PURE__ */ new Set([
14286
14907
  "cancel",
14287
14908
  "materialize"
14288
14909
  ]);
14289
- var encoder4 = new TextEncoder();
14910
+ var encoder6 = new TextEncoder();
14290
14911
  var decoder3 = new TextDecoder("utf-8", { fatal: true });
14291
14912
  function parseSealed(bytes) {
14292
14913
  try {
@@ -14302,11 +14923,16 @@ function parseSealed(bytes) {
14302
14923
  const req = parsed2;
14303
14924
  if (req.v !== 1 || typeof req.op !== "string" || !SEALED_OPS.has(req.op))
14304
14925
  throw new KernelError("EINVAL");
14926
+ if (req.op === "materialize" && !materializable(req)) throw new KernelError("EINVAL");
14305
14927
  return req;
14306
14928
  } finally {
14307
14929
  bytes.fill(0);
14308
14930
  }
14309
14931
  }
14932
+ function materializable(req) {
14933
+ if (req.shellGrants !== true && !isStrings(req.names)) return false;
14934
+ return isStrings(req.argv) && typeof req.path === "string" && typeof req.cwd === "string";
14935
+ }
14310
14936
  function errorReply(code, message3) {
14311
14937
  return { v: 1, error: { code, message: message3 } };
14312
14938
  }
@@ -14350,12 +14976,14 @@ var SealedChannel = class {
14350
14976
  const timer = setTimeout(() => control.abort("timeout"), this.broker.timeoutMs);
14351
14977
  const req = { ...fields, v: 1, op: fields.op, caller };
14352
14978
  const flight = { control, done: Promise.resolve() };
14353
- flight.done = this.answer(req, control.signal).then((reply) => {
14979
+ flight.done = this.answer(req, control.signal).then(async (reply) => {
14354
14980
  clearTimeout(timer);
14355
14981
  gone.removeEventListener("abort", withdraw);
14982
+ if (reply && req.op === "materialize")
14983
+ await this.broker.tickets.record(req, reply, caller.pid);
14356
14984
  if (this.inflight !== flight) return;
14357
14985
  this.inflight = void 0;
14358
- if (reply) this.reply = encoder4.encode(serialized(reply));
14986
+ if (reply) this.reply = encoder6.encode(serialized(reply));
14359
14987
  });
14360
14988
  this.inflight = flight;
14361
14989
  return length2;
@@ -14363,8 +14991,8 @@ var SealedChannel = class {
14363
14991
  async answer(req, signal) {
14364
14992
  const hook = this.broker.hook;
14365
14993
  if (!hook) return errorReply("ENOTCONN", "no vault is connected");
14366
- const stopped = new Promise((resolve) => {
14367
- signal.addEventListener("abort", () => resolve(void 0), { once: true });
14994
+ const stopped = new Promise((resolve2) => {
14995
+ signal.addEventListener("abort", () => resolve2(void 0), { once: true });
14368
14996
  });
14369
14997
  if (signal.aborted) return void 0;
14370
14998
  const asked = Promise.resolve().then(() => hook(req, { signal })).then(
@@ -14419,17 +15047,60 @@ var SealedChannel = class {
14419
15047
  this.reply = void 0;
14420
15048
  }
14421
15049
  };
15050
+ function swapOf(reply) {
15051
+ if (!isRecord(reply) || reply.error !== void 0) return void 0;
15052
+ const values = stringRecord(reply.values);
15053
+ const placeholders = stringRecord(reply.placeholders);
15054
+ return values && placeholders ? { values, placeholders } : void 0;
15055
+ }
14422
15056
  var SealedBroker = class {
14423
15057
  hook;
14424
15058
  users;
14425
15059
  timeoutMs;
14426
15060
  tagOf;
15061
+ tickets = new TicketBook();
15062
+ userOf = async () => void 0;
14427
15063
  constructor(hook, users, timeoutMs = SEALED_TIMEOUT_MS, tagOf = () => void 0) {
14428
15064
  this.hook = hook;
14429
15065
  this.users = users;
14430
15066
  this.timeoutMs = timeoutMs;
14431
15067
  this.tagOf = tagOf;
14432
15068
  }
15069
+ async stamp(ids, cred, tag) {
15070
+ const user = await this.userOf(cred.euid).catch(() => void 0);
15071
+ return {
15072
+ pid: ids.pid,
15073
+ ppid: ids.ppid,
15074
+ uid: cred.euid,
15075
+ gid: cred.egid,
15076
+ ...user !== void 0 ? { user } : {},
15077
+ groups: [...cred.groups],
15078
+ users: this.users,
15079
+ ...tag !== void 0 ? { tag } : {}
15080
+ };
15081
+ }
15082
+ async consume(fields) {
15083
+ const { hook } = this;
15084
+ if (!hook) return void 0;
15085
+ const control = new AbortController();
15086
+ const timer = setTimeout(() => control.abort("timeout"), this.timeoutMs);
15087
+ const stopped = new Promise((resolve2) => {
15088
+ control.signal.addEventListener("abort", () => resolve2(void 0), { once: true });
15089
+ });
15090
+ const req = {
15091
+ ...fields,
15092
+ v: 1,
15093
+ op: "materializeConsume",
15094
+ caller: fields.stamp
15095
+ };
15096
+ try {
15097
+ const asked = Promise.resolve().then(() => hook(req, { signal: control.signal })).then(swapOf, () => void 0);
15098
+ return await Promise.race([asked, stopped]);
15099
+ } finally {
15100
+ clearTimeout(timer);
15101
+ control.abort("done");
15102
+ }
15103
+ }
14433
15104
  open() {
14434
15105
  const channel = new SealedChannel(this);
14435
15106
  return new OpenFile({
@@ -14928,11 +15599,11 @@ function codeOf3(err) {
14928
15599
  }
14929
15600
  function wait(ms, signal) {
14930
15601
  if (signal.aborted) return Promise.resolve();
14931
- return new Promise((resolve) => {
15602
+ return new Promise((resolve2) => {
14932
15603
  const done4 = () => {
14933
15604
  clearTimeout(timer);
14934
15605
  signal.removeEventListener("abort", done4);
14935
- resolve();
15606
+ resolve2();
14936
15607
  };
14937
15608
  const timer = setTimeout(done4, ms);
14938
15609
  signal.addEventListener("abort", done4, { once: true });
@@ -15259,11 +15930,11 @@ async function resize(h, path, size) {
15259
15930
  }
15260
15931
  }
15261
15932
  function sleep(ms, signal) {
15262
- return new Promise((resolve) => {
15933
+ return new Promise((resolve2) => {
15263
15934
  const done4 = () => {
15264
15935
  clearTimeout(timer);
15265
15936
  signal.removeEventListener("abort", done4);
15266
- resolve();
15937
+ resolve2();
15267
15938
  };
15268
15939
  const timer = setTimeout(done4, ms);
15269
15940
  signal.addEventListener("abort", done4);
@@ -15346,7 +16017,7 @@ var PORT_DEAD_MS = 1e4;
15346
16017
  var PORT_LINGER_MS = 1e3;
15347
16018
  function timed(transport, ms) {
15348
16019
  const inflight = /* @__PURE__ */ new Set();
15349
- const guard2 = (name2, run2, late, expire) => new Promise((resolve, reject) => {
16020
+ const guard2 = (name2, run2, late, expire) => new Promise((resolve2, reject) => {
15350
16021
  let expired = false;
15351
16022
  const running = run2();
15352
16023
  inflight.add(running);
@@ -15363,7 +16034,7 @@ function timed(transport, ms) {
15363
16034
  (value) => {
15364
16035
  clearTimeout(timer);
15365
16036
  if (expired) late?.(value);
15366
- resolve(value);
16037
+ resolve2(value);
15367
16038
  },
15368
16039
  (err) => {
15369
16040
  clearTimeout(timer);
@@ -15498,7 +16169,7 @@ var PortTransport = class {
15498
16169
  ask(message3, bytes = 0, transfer = []) {
15499
16170
  const id2 = ++this.next;
15500
16171
  if (this.dead) return Promise.reject(fsError2("ENOTCONN", this.dead));
15501
- return new Promise((resolve, reject) => {
16172
+ return new Promise((resolve2, reject) => {
15502
16173
  const timer = setTimeout(
15503
16174
  () => {
15504
16175
  this.waiting.delete(id2);
@@ -15512,7 +16183,7 @@ var PortTransport = class {
15512
16183
  clearTimeout(timer);
15513
16184
  this.waiting.delete(id2);
15514
16185
  if (reply.error) reject(fsError2(reply.error.code ?? "EIO", reply.error.message ?? ""));
15515
- else resolve(reply);
16186
+ else resolve2(reply);
15516
16187
  });
15517
16188
  try {
15518
16189
  this.port.postMessage({ id: id2, ...message3 }, transfer);
@@ -15541,7 +16212,7 @@ var PortTransport = class {
15541
16212
  this.fail("the folder's watch ended");
15542
16213
  return Promise.resolve();
15543
16214
  }
15544
- return new Promise((resolve, reject) => {
16215
+ return new Promise((resolve2, reject) => {
15545
16216
  const finish = () => {
15546
16217
  this.waiting.delete(id2);
15547
16218
  signal.removeEventListener("abort", stop);
@@ -15552,7 +16223,7 @@ var PortTransport = class {
15552
16223
  this.port.postMessage({ id: id2, op: "unwatch" });
15553
16224
  } catch {
15554
16225
  }
15555
- resolve();
16226
+ resolve2();
15556
16227
  };
15557
16228
  this.watchBeat();
15558
16229
  this.waiting.set(id2, (reply) => {
@@ -15560,7 +16231,7 @@ var PortTransport = class {
15560
16231
  if (reply.event) return onEvent(reply.event);
15561
16232
  finish();
15562
16233
  if (reply.error) reject(fsError2(reply.error.code ?? "ENOTCONN", reply.error.message ?? ""));
15563
- else resolve();
16234
+ else resolve2();
15564
16235
  });
15565
16236
  signal.addEventListener("abort", stop, { once: true });
15566
16237
  try {
@@ -15673,10 +16344,10 @@ var ChunkedBytes = class {
15673
16344
  }
15674
16345
  truncate(size) {
15675
16346
  if (size < this.size) {
15676
- const keep = Math.ceil(size / CHUNK3);
15677
- this.chunks.length = Math.min(this.chunks.length, keep);
15678
- const tail = this.chunks[keep - 1];
15679
- const from = size - (keep - 1) * CHUNK3;
16347
+ const keep2 = Math.ceil(size / CHUNK3);
16348
+ this.chunks.length = Math.min(this.chunks.length, keep2);
16349
+ const tail = this.chunks[keep2 - 1];
16350
+ const from = size - (keep2 - 1) * CHUNK3;
15680
16351
  if (tail && from < tail.length) tail.fill(0, from);
15681
16352
  }
15682
16353
  this.size = size;
@@ -15921,8 +16592,8 @@ function importedMemory(bytes) {
15921
16592
  at = end;
15922
16593
  continue;
15923
16594
  }
15924
- const count = u32();
15925
- for (let i = 0; i < count; i++) {
16595
+ const count2 = u32();
16596
+ for (let i = 0; i < count2; i++) {
15926
16597
  const module = name2();
15927
16598
  const field3 = name2();
15928
16599
  const kind = bytes[at++];
@@ -16351,7 +17022,7 @@ ${lines2}
16351
17022
  const withdraw = () => control.abort("withdrawn");
16352
17023
  requester.addEventListener("abort", withdraw, { once: true });
16353
17024
  const aborted2 = new Promise(
16354
- (resolve) => control.signal.addEventListener("abort", () => resolve("aborted"), { once: true })
17025
+ (resolve2) => control.signal.addEventListener("abort", () => resolve2("aborted"), { once: true })
16355
17026
  );
16356
17027
  try {
16357
17028
  const answered2 = Promise.resolve().then(() => approve(req, { signal: control.signal })).catch(() => "deny");
@@ -16477,7 +17148,7 @@ var LAYOUT = [
16477
17148
  ];
16478
17149
  var LOAD_SAMPLE_MS = 5e3;
16479
17150
  var LOAD_WINDOWS_MS = [6e4, 3e5, 9e5];
16480
- var encoder5 = new TextEncoder();
17151
+ var encoder7 = new TextEncoder();
16481
17152
  var USERS_MODES = ["off", "report", "enforce"];
16482
17153
  function usersMode(mode = "off") {
16483
17154
  if (USERS_MODES.includes(mode)) return mode;
@@ -16508,6 +17179,30 @@ function shebang(head) {
16508
17179
  if (head[0] !== 35 || head[1] !== 33) return void 0;
16509
17180
  return new TextDecoder().decode(head.subarray(0, SHEBANG_MAX)).slice(2).split("\n")[0].trim().split(/[ \t]+/);
16510
17181
  }
17182
+ async function rootOnly(fs, path) {
17183
+ const names = chain(path);
17184
+ for (const [i, at] of names.entries()) {
17185
+ const st = await fs.lstat(at).catch(() => void 0);
17186
+ const kind = i === names.length - 1 ? st?.isFile : st?.isDirectory;
17187
+ if (!kind || st?.uid !== 0 || (st.mode & 18) !== 0) return false;
17188
+ }
17189
+ return true;
17190
+ }
17191
+ function plainEnv(words2) {
17192
+ const next2 = words2[1];
17193
+ if (baseName(words2[0] ?? "") !== "env" || next2 === void 0) return false;
17194
+ return next2 === "-S" || !next2.startsWith("-") && !next2.includes("=");
17195
+ }
17196
+ function shebangCommand(words2) {
17197
+ if (!plainEnv(words2)) {
17198
+ const arg2 = words2.slice(1).join(" ");
17199
+ return { interp: words2[0], args: arg2 ? [arg2] : [] };
17200
+ }
17201
+ const split2 = words2[1] === "-S";
17202
+ const [interp, ...rest2] = words2.slice(split2 ? 2 : 1);
17203
+ const arg = rest2.join(" ");
17204
+ return { interp, args: split2 ? rest2 : arg ? [arg] : [] };
17205
+ }
16511
17206
  function interpreter(head) {
16512
17207
  const words2 = shebang(head);
16513
17208
  if (!words2) return "node";
@@ -16561,7 +17256,7 @@ function childHandle(handle) {
16561
17256
  onState: (listener) => handle.onState(listener)
16562
17257
  };
16563
17258
  }
16564
- function concat5(chunks) {
17259
+ function concat6(chunks) {
16565
17260
  const out = new Uint8Array(chunks.reduce((n, chunk2) => n + chunk2.length, 0));
16566
17261
  let offset = 0;
16567
17262
  for (const chunk2 of chunks) {
@@ -16647,6 +17342,7 @@ var Launcher = class {
16647
17342
  usersMode;
16648
17343
  dac;
16649
17344
  sealed;
17345
+ scrubs = /* @__PURE__ */ new Map();
16650
17346
  clipboard;
16651
17347
  rootFs;
16652
17348
  fileWatches;
@@ -16670,6 +17366,8 @@ var Launcher = class {
16670
17366
  void 0,
16671
17367
  (pid) => this.tagOf(pid)
16672
17368
  );
17369
+ this.sealed.tickets.normalise = (path) => physicalPathAsync(this.base, path);
17370
+ this.sealed.userOf = async (uid) => (await this.users.lookup(uid))?.name;
16673
17371
  if (this.owners) options.fs.trackOwners?.();
16674
17372
  this.mounts = new MountTable({
16675
17373
  open: (type, spec) => this.driver(type, spec),
@@ -16918,13 +17616,12 @@ var Launcher = class {
16918
17616
  const binfmt = isWasm(head) ? void 0 : binfmtOf(await this.interpreters(scope), script);
16919
17617
  const words2 = shebang(head) ?? (binfmt ? [binfmt] : void 0);
16920
17618
  if (!words2) return void 0;
16921
- if (baseName(words2[0]) === "env") words2.shift();
16922
- const [interp, ...rest2] = words2;
16923
- const arg = rest2.join(" ");
16924
- const passed = [...arg ? [arg] : [], command ? script : file, ...argv.slice(1)];
16925
- const found = interp ? await this.resolve(interp, interp, cwd, scope) : void 0;
17619
+ const { interp, args } = shebangCommand(words2);
17620
+ const passed = [...args, command ? script : file, ...argv.slice(1)];
17621
+ if (!interp) return void 0;
17622
+ const found = await this.resolve(interp, interp, cwd, scope);
16926
17623
  if (!found) {
16927
- if (!interp || depth >= MAX_INTERPRETERS || !await this.scriptCommand(interp, cwd, scope)) {
17624
+ if (depth >= MAX_INTERPRETERS || !await this.scriptCommand(interp, cwd, scope)) {
16928
17625
  return void 0;
16929
17626
  }
16930
17627
  const inner = await this.interpreted(interp, [interp, ...passed], cwd, scope, depth + 1);
@@ -16964,6 +17661,99 @@ var Launcher = class {
16964
17661
  await need(g, "exec", this.fs.resolvePath(req.cwd, file), want);
16965
17662
  }
16966
17663
  }
17664
+ async checkedPlan(req, { ppid, cred, scope }, fds) {
17665
+ const planned = await this.plan(req.file, req.argv, req.cwd, scope, req.argv0);
17666
+ try {
17667
+ await this.mayExec(cred, { ...req, pid: ppid }, planned);
17668
+ } catch (err) {
17669
+ await fds.closeAll();
17670
+ throw err.code === "EACCES" ? new SpawnError("EACCES") : err;
17671
+ }
17672
+ if (planned) return planned;
17673
+ await fds.closeAll();
17674
+ throw new SpawnError(await this.unrunnable(req.file, req.cwd, scope));
17675
+ }
17676
+ async ticketFile(req, fds) {
17677
+ if (req.file.startsWith("/")) return physicalPathAsync(this.base, req.file);
17678
+ await fds.closeAll();
17679
+ throw new SpawnError("EACCES");
17680
+ }
17681
+ takeTicket(req) {
17682
+ if (!(TICKET_ENV in req.env)) return void 0;
17683
+ return this.sealed.tickets.take(req.env[TICKET_ENV] ?? "") ?? null;
17684
+ }
17685
+ async redeemed(req, who, planned, fds) {
17686
+ if (who.booked === void 0) return { env: req.env };
17687
+ try {
17688
+ const redeemed = await this.redeem(req, who, planned);
17689
+ if (redeemed.scrub) scrubStdio(fds, redeemed.scrub);
17690
+ return redeemed;
17691
+ } catch {
17692
+ await fds.closeAll();
17693
+ throw new SpawnError("EACCES");
17694
+ }
17695
+ }
17696
+ async redeem(req, { ppid, cred, scope, booked }, planned) {
17697
+ const { [TICKET_ENV]: ticket = "", ...env } = req.env;
17698
+ const caller = this.jobs.shown(ppid);
17699
+ const named = typeof req.argv0 === "string" && !planned.script && !planned.target.fixed;
17700
+ const argv = named ? [req.argv0, ...req.argv.slice(1)] : req.argv;
17701
+ const exact = booked?.pid === caller && req.file === booked.path && await physicalPathAsync(this.base, req.cwd) === booked.cwd && await argvDigest(argv) === booked.argv;
17702
+ if (!exact) throw new SpawnError("EACCES");
17703
+ const { requested: path, requestedCwd: cwd, path: resolved2 } = booked;
17704
+ const pid = this.nextPid++;
17705
+ const ids = { pid: req.exec ? caller : pid, ppid: caller };
17706
+ const stamp = await this.sealed.stamp(ids, cred, this.tagOf(ppid));
17707
+ const interpreters = await this.interpreterChain(planned, booked.cwd, scope);
17708
+ const trusted = this.usersMode === "enforce" && await this.trustedFiles(planned, req.file, booked.cwd, scope);
17709
+ const swap = await this.sealed.consume({
17710
+ ticket,
17711
+ stamp,
17712
+ path,
17713
+ resolved: resolved2,
17714
+ argv,
17715
+ cwd,
17716
+ interpreters,
17717
+ trusted
17718
+ });
17719
+ if (!swap) throw new SpawnError("EACCES");
17720
+ const secrets = [];
17721
+ for (const [name2, placeholder] of Object.entries(swap.placeholders)) {
17722
+ const value = swap.values[name2];
17723
+ if (value === void 0 || env[name2] !== placeholder) continue;
17724
+ env[name2] = value;
17725
+ secrets.push({ name: name2, value, placeholder });
17726
+ }
17727
+ const scrub = scrubberOf(secrets);
17728
+ return { env, pid, cwd: booked.cwd, ...scrub ? { scrub } : {} };
17729
+ }
17730
+ async interpreterChain(planned, cwd, scope) {
17731
+ const out = [];
17732
+ for (const entry of planned.via ?? []) {
17733
+ if (entry.includes("/")) {
17734
+ out.push(await physicalPathAsync(this.base, this.fs.resolvePath(cwd, entry)));
17735
+ continue;
17736
+ }
17737
+ const script = (await this.scriptCommand(entry, cwd, scope))?.script;
17738
+ out.push(
17739
+ await physicalPathAsync(this.base, script ?? await this.commandFile(entry, cwd, scope))
17740
+ );
17741
+ }
17742
+ return out;
17743
+ }
17744
+ async commandFile(name2, cwd, scope) {
17745
+ const scoped = await this.resolve(name2, name2, cwd, scope);
17746
+ const system = scope === "" ? scoped : await this.resolve(name2, name2, cwd, "");
17747
+ if (!scoped || system?.glue === scoped.glue) return `/usr/bin/${name2}`;
17748
+ return scoped.glue;
17749
+ }
17750
+ async trustedFiles(planned, file, cwd, scope) {
17751
+ const files = await this.backingOf(planned, file, cwd, scope);
17752
+ for (const at of files) {
17753
+ if (!await rootOnly(this.base, await physicalPathAsync(this.base, at))) return false;
17754
+ }
17755
+ return true;
17756
+ }
16967
17757
  async grantedCwd(caller, cwd) {
16968
17758
  const real = await physicalPathAsync(this.base, cwd).catch(() => void 0);
16969
17759
  return real === cwd && await this.sudoer.reachable(caller, cwd);
@@ -16982,13 +17772,15 @@ var Launcher = class {
16982
17772
  }
16983
17773
  async backing(file, cwd) {
16984
17774
  const planned = await this.plan(file, [file], cwd, "");
16985
- if (!planned) return void 0;
17775
+ return planned && this.backingOf(planned, file, cwd, "");
17776
+ }
17777
+ async backingOf(planned, file, cwd, scope) {
16986
17778
  const { glue, wasm, imports } = planned.target;
16987
17779
  const files = [glue, wasm, ...imports ? [imports] : []];
16988
17780
  const scripts = [];
16989
17781
  for (const name2 of [file, ...planned.via ?? []]) {
16990
17782
  if (name2.includes("/") && !COMMAND2.test(name2)) scripts.push(this.fs.resolvePath(cwd, name2));
16991
- const script = (await this.scriptCommand(name2, cwd, ""))?.script;
17783
+ const script = (await this.scriptCommand(name2, cwd, scope))?.script;
16992
17784
  if (script) scripts.push(script);
16993
17785
  }
16994
17786
  files.push(...scripts, ...await this.binfmtManifests(scripts));
@@ -17117,6 +17909,7 @@ var Launcher = class {
17117
17909
  const seen = { ...req.env, HOSTNAME: this.hostname, cwd };
17118
17910
  const env = { ...expandDefaults(target.env, seen), ...req.env };
17119
17911
  for (const key of target.unset ?? []) delete env[key];
17912
+ delete env[TICKET_ENV];
17120
17913
  env.HOSTNAME = this.hostname;
17121
17914
  let program;
17122
17915
  try {
@@ -17132,16 +17925,23 @@ var Launcher = class {
17132
17925
  groupSession(pgid) {
17133
17926
  return this.jobs.sessionOf(pgid);
17134
17927
  }
17928
+ treeScrubs(pid, req) {
17929
+ const inherited2 = req.ppid !== void 0 ? this.scrubs.get(req.ppid) : void 0;
17930
+ const scrubs = req.scrub ? [...inherited2 ?? [], req.scrub] : inherited2;
17931
+ if (scrubs) this.scrubs.set(pid, scrubs);
17932
+ return scrubs;
17933
+ }
17135
17934
  start(req) {
17136
17935
  this.sampling();
17137
17936
  if (req.pgid !== void 0 && this.jobs.sessionOf(req.pgid) === void 0) {
17138
17937
  throw new SpawnError("ESRCH");
17139
17938
  }
17140
- const pid = this.nextPid++;
17939
+ const pid = req.pid ?? this.nextPid++;
17940
+ const scrubs = this.treeScrubs(pid, req);
17141
17941
  const stack = this.stackFor(req.tag);
17142
17942
  const terminal = req.fds.stdioTerminal();
17143
17943
  const decided = req.decided ?? Promise.resolve(true);
17144
- const count = (byParent) => {
17944
+ const count2 = (byParent) => {
17145
17945
  this.identities.asked++;
17146
17946
  if (!byParent) this.identities.timedOut++;
17147
17947
  };
@@ -17149,7 +17949,7 @@ var Launcher = class {
17149
17949
  const handle = spawnWasmProcess({
17150
17950
  pid,
17151
17951
  identity: async () => {
17152
- await (asked ??= decided.then(count));
17952
+ await (asked ??= decided.then(count2));
17153
17953
  return this.identityOf(pid);
17154
17954
  },
17155
17955
  onSyscall: (call) => {
@@ -17163,6 +17963,7 @@ var Launcher = class {
17163
17963
  ...this.owners && req.program.abi === "wasi" ? { fresh: (path, umask) => this.freshModules.add(path, umask, pid) } : {},
17164
17964
  ...this.owners ? { owners: true } : {},
17165
17965
  sealed: this.sealed,
17966
+ ...scrubs ? { ttyFilter: (file) => scrubbedAll(file, scrubs) } : {},
17166
17967
  clipboard: this.clipboard,
17167
17968
  ...this.dac ? { dac: this.dac } : {},
17168
17969
  program: req.program,
@@ -17229,6 +18030,7 @@ var Launcher = class {
17229
18030
  if (spent) addTimes(this.imageTimes, this.jobs.shown(pid), spent);
17230
18031
  this.stopped.delete(pid);
17231
18032
  this.processes.delete(pid);
18033
+ this.scrubs.delete(pid);
17232
18034
  if (this.processes.size === 0) {
17233
18035
  clearInterval(this.sampler);
17234
18036
  this.sampler = void 0;
@@ -17400,6 +18202,7 @@ var Launcher = class {
17400
18202
  }
17401
18203
  spawner(ppid, report) {
17402
18204
  return async (req, fds, grant) => {
18205
+ const booked = this.takeTicket(req);
17403
18206
  if (grant && !await this.grantedCwd(grant.caller, req.cwd)) {
17404
18207
  await fds.closeAll();
17405
18208
  throw new SpawnError("EACCES");
@@ -17411,23 +18214,20 @@ var Launcher = class {
17411
18214
  }
17412
18215
  const cred = grant ? grant.cred : this.credOf(ppid);
17413
18216
  const scope = grant ? "" : await this.scopeOf(cred.euid);
17414
- const planned = await this.plan(req.file, req.argv, req.cwd, scope, req.argv0);
17415
- try {
17416
- await this.mayExec(cred, { ...req, pid: ppid }, planned);
17417
- } catch (err) {
17418
- await fds.closeAll();
17419
- throw err.code === "EACCES" ? new SpawnError("EACCES") : err;
17420
- }
17421
- if (!planned) {
17422
- await fds.closeAll();
17423
- throw new SpawnError(await this.unrunnable(req.file, req.cwd, scope));
17424
- }
18217
+ const file = booked === void 0 ? req.file : await this.ticketFile(req, fds);
18218
+ const planned = await this.checkedPlan({ ...req, file }, { ppid, cred, scope }, fds);
18219
+ const redeemed = await this.redeemed(
18220
+ { ...req, file },
18221
+ { ppid, cred, scope, booked },
18222
+ planned,
18223
+ fds
18224
+ );
17425
18225
  const exec = req.exec ? { exec: true } : {};
17426
18226
  const deciding = req.exec ? void 0 : this.undecided(ppid);
17427
18227
  const handle = await this.launch(planned, {
17428
18228
  ...deciding ? { decided: deciding.promise } : {},
17429
- env: req.env,
17430
18229
  cwd: req.cwd,
18230
+ ...redeemed,
17431
18231
  fds,
17432
18232
  report,
17433
18233
  ppid,
@@ -17480,11 +18280,11 @@ var Launcher = class {
17480
18280
  if (!waiting) return;
17481
18281
  this.deciding.delete(ppid);
17482
18282
  setTimeout(() => {
17483
- for (const resolve of waiting) resolve(true);
18283
+ for (const resolve2 of waiting) resolve2(true);
17484
18284
  }, 0);
17485
18285
  }
17486
18286
  forker(ppid, parent) {
17487
- const { exec: _exec, pgid: _pgid, group: _group, ...image } = parent;
18287
+ const { exec: _exec, pgid: _pgid, group: _group, pid: _pid, scrub: _scrub, ...image } = parent;
17488
18288
  return async (state, fds) => childHandle(
17489
18289
  this.start({
17490
18290
  ...image,
@@ -17518,7 +18318,7 @@ var Launcher = class {
17518
18318
  }
17519
18319
  }
17520
18320
  umaskOf(pid) {
17521
- return this.processes.get(pid)?.umask?.() ?? DEFAULT_UMASK;
18321
+ return this.processes.get(pid)?.umask?.() ?? DEFAULT_UMASK2;
17522
18322
  }
17523
18323
  imageOf(pid) {
17524
18324
  const members = this.jobs.list();
@@ -17861,7 +18661,14 @@ var Launcher = class {
17861
18661
  );
17862
18662
  }
17863
18663
  environment(cwd, extra, account) {
17864
- return { ...this.env, ...userEnv(account, this.pnpmHome), PWD: cwd, ...extra };
18664
+ const env = {
18665
+ ...this.env,
18666
+ ...userEnv(account, this.pnpmHome),
18667
+ PWD: cwd,
18668
+ ...extra
18669
+ };
18670
+ delete env[TICKET_ENV];
18671
+ return env;
17865
18672
  }
17866
18673
  async account(user) {
17867
18674
  if (typeof user === "object") return user;
@@ -17957,7 +18764,7 @@ var Launcher = class {
17957
18764
  },
17958
18765
  account
17959
18766
  );
17960
- const report = (message3) => options.onData(encoder5.encode(`${message3}\r
18767
+ const report = (message3) => options.onData(encoder7.encode(`${message3}\r
17961
18768
  `));
17962
18769
  const handle = await this.launch(planned, {
17963
18770
  env,
@@ -17988,18 +18795,18 @@ var Launcher = class {
17988
18795
  const env = this.environment(cwd, options.env, account);
17989
18796
  const out = [];
17990
18797
  const err = [];
17991
- const keep = options.collect !== false;
18798
+ const keep2 = options.collect !== false;
17992
18799
  const collect = (chunks, tee) => (bytes) => {
17993
- if (keep) chunks.push(bytes);
18800
+ if (keep2) chunks.push(bytes);
17994
18801
  tee?.(bytes);
17995
18802
  };
17996
18803
  const stdout = collect(out, options.onStdout);
17997
18804
  const stderr = collect(err, options.onStderr);
17998
- const report = (message3) => stderr(encoder5.encode(`${message3}
18805
+ const report = (message3) => stderr(encoder7.encode(`${message3}
17999
18806
  `));
18000
18807
  if (!planned) {
18001
18808
  report(`${file}: command not found`);
18002
- return { status: NOT_FOUND, stdout: concat5(out), stderr: concat5(err) };
18809
+ return { status: NOT_FOUND, stdout: concat6(out), stderr: concat6(err) };
18003
18810
  }
18004
18811
  const fds = new FdTable();
18005
18812
  fds.installAt(0, options.stdin ? bytesSource(options.stdin) : deviceFile("null"));
@@ -18017,7 +18824,7 @@ var Launcher = class {
18017
18824
  options.onStarted?.(handle.pid);
18018
18825
  const status = await handle.exited;
18019
18826
  const signal = handle.termsig();
18020
- return { status, ...signal ? { signal } : {}, stdout: concat5(out), stderr: concat5(err) };
18827
+ return { status, ...signal ? { signal } : {}, stdout: concat6(out), stderr: concat6(err) };
18021
18828
  }
18022
18829
  };
18023
18830
 
@@ -18118,14 +18925,14 @@ function dial(l, req) {
18118
18925
  function sudoApprovals(port) {
18119
18926
  const waiting = /* @__PURE__ */ new Map();
18120
18927
  return {
18121
- ask: (req, { signal }) => new Promise((resolve) => {
18122
- waiting.set(req.id, resolve);
18928
+ ask: (req, { signal }) => new Promise((resolve2) => {
18929
+ waiting.set(req.id, resolve2);
18123
18930
  signal.addEventListener(
18124
18931
  "abort",
18125
18932
  () => {
18126
18933
  waiting.delete(req.id);
18127
18934
  port.postMessage({ sudoAbort: { id: req.id, reason: String(signal.reason) } });
18128
- resolve("deny");
18935
+ resolve2("deny");
18129
18936
  },
18130
18937
  { once: true }
18131
18938
  );
@@ -18209,9 +19016,9 @@ function hostProtect(init, lookup) {
18209
19016
  function policyAsker(port) {
18210
19017
  const policies = /* @__PURE__ */ new Map();
18211
19018
  let nextPolicy = 0;
18212
- const askPolicy = (req) => new Promise((resolve) => {
19019
+ const askPolicy = (req) => new Promise((resolve2) => {
18213
19020
  const id2 = ++nextPolicy;
18214
- policies.set(id2, resolve);
19021
+ policies.set(id2, resolve2);
18215
19022
  port.postMessage({ mountPolicy: { id: id2, req } });
18216
19023
  });
18217
19024
  return {
@@ -18239,10 +19046,10 @@ function serveKernel(port, deps) {
18239
19046
  const locks = deps.locks === null ? void 0 : deps.locks ?? locksOf();
18240
19047
  let holding;
18241
19048
  function holdLock(locks2) {
18242
- holding ??= new Promise((resolve) => {
19049
+ holding ??= new Promise((resolve2) => {
18243
19050
  const name2 = `slicc-kernel:${crypto.randomUUID()}`;
18244
19051
  void locks2.request(name2, () => {
18245
- resolve(name2);
19052
+ resolve2(name2);
18246
19053
  return new Promise(() => {
18247
19054
  });
18248
19055
  });