@ai-ecoverse/slicc-kernel 1.103.4 → 1.106.2

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.
Files changed (37) hide show
  1. package/README.md +56 -15
  2. package/dist/driver-worker.js +36 -1
  3. package/dist/driver-worker.js.map +2 -2
  4. package/dist/index.js +89 -5
  5. package/dist/index.js.map +2 -2
  6. package/dist/kernel-worker.js +783 -289
  7. package/dist/kernel-worker.js.map +4 -4
  8. package/dist/meta.js.map +2 -2
  9. package/dist/node-driver-worker.js +36 -1
  10. package/dist/node-driver-worker.js.map +2 -2
  11. package/dist/node-process-worker.js +447 -224
  12. package/dist/node-process-worker.js.map +4 -4
  13. package/dist/node.js +831 -309
  14. package/dist/node.js.map +4 -4
  15. package/dist/process-worker.js +384 -161
  16. package/dist/process-worker.js.map +4 -4
  17. package/dist/types/commands.d.ts +6 -1
  18. package/dist/types/fs/command-stat.d.ts +3 -0
  19. package/dist/types/fs/commands.d.ts +7 -1
  20. package/dist/types/fs/engines.d.ts +5 -0
  21. package/dist/types/fs/types.d.ts +1 -0
  22. package/dist/types/kernel/host.d.ts +1 -0
  23. package/dist/types/kernel/net/http-syscalls.d.ts +10 -0
  24. package/dist/types/kernel/net/http1.d.ts +1 -0
  25. package/dist/types/kernel/net/remote-transport.d.ts +18 -0
  26. package/dist/types/kernel/net/transport.d.ts +2 -0
  27. package/dist/types/kernel/process.d.ts +8 -0
  28. package/dist/types/launcher.d.ts +4 -0
  29. package/dist/types/process/process-signals.d.ts +3 -0
  30. package/dist/types/process/wasi/wasi-cred.d.ts +11 -0
  31. package/dist/types/process/wasi/wasi-files.d.ts +1 -0
  32. package/dist/types/process/wasi/wasi-host.d.ts +9 -0
  33. package/dist/types/process/wasi/wasi-sigmask.d.ts +7 -1
  34. package/dist/types/process/wasi/wasix-epoll.d.ts +3 -0
  35. package/dist/types/refused-command.d.ts +4 -0
  36. package/dist/types/transport.d.ts +1 -0
  37. package/package.json +12 -12
@@ -58,9 +58,9 @@ var PathMap = class {
58
58
  }
59
59
  link(path) {
60
60
  for (let child = path, parent = parentOf(path); parent !== void 0; ) {
61
- const set2 = this.children.get(parent);
62
- if (set2?.has(child)) return;
63
- if (set2) set2.add(child);
61
+ const set3 = this.children.get(parent);
62
+ if (set3?.has(child)) return;
63
+ if (set3) set3.add(child);
64
64
  else this.children.set(parent, /* @__PURE__ */ new Set([child]));
65
65
  child = parent;
66
66
  parent = parentOf(parent);
@@ -69,9 +69,9 @@ var PathMap = class {
69
69
  prune(path) {
70
70
  for (let child = path, parent = parentOf(path); parent !== void 0; ) {
71
71
  if (this.values.has(child) || this.children.has(child)) return;
72
- const set2 = this.children.get(parent);
73
- set2?.delete(child);
74
- if (set2 && set2.size > 0) return;
72
+ const set3 = this.children.get(parent);
73
+ set3?.delete(child);
74
+ if (set3 && set3.size > 0) return;
75
75
  this.children.delete(parent);
76
76
  child = parent;
77
77
  parent = parentOf(parent);
@@ -208,15 +208,15 @@ var IndexedDbMeta = class {
208
208
  this.settleRemoves();
209
209
  const [transaction, store] = this.transaction("readwrite");
210
210
  const request2 = store.get(path);
211
- let next2;
211
+ let next3;
212
212
  request2.onsuccess = () => {
213
- next2 = change(request2.result);
214
- if (next2) store.put(next2);
213
+ next3 = change(request2.result);
214
+ if (next3) store.put(next3);
215
215
  else store.delete(path);
216
216
  };
217
217
  await settle(transaction, () => void 0);
218
218
  this.changed({ paths: [path] });
219
- this.cache.set(path, next2 ?? null);
219
+ this.cache.set(path, next3 ?? null);
220
220
  }
221
221
  async updateMany(changes) {
222
222
  const [transaction, store] = this.transaction("readwrite");
@@ -530,14 +530,14 @@ var Integrity = class {
530
530
  if (!this.root.version) return void 0;
531
531
  return `${real}\0${await this.root.version(real)}`;
532
532
  }
533
- async read(path, caller, plain, asked) {
534
- return (await this.readKeyed(path, caller, plain, asked)).bytes;
533
+ async read(path, caller, plain2, asked) {
534
+ return (await this.readKeyed(path, caller, plain2, asked)).bytes;
535
535
  }
536
- async readKeyed(path, caller, plain, asked) {
536
+ async readKeyed(path, caller, plain2, asked) {
537
537
  const found = await this.real(path, asked);
538
- if (found === void 0) return { bytes: await plain() };
538
+ if (found === void 0) return { bytes: await plain2() };
539
539
  const { real, name: name2 } = found;
540
- const read = name2 !== real ? { bytes: await plain(), version: void 0 } : this.root.readVersioned ? await this.root.readVersioned(real) : { bytes: await this.root.readFileBuffer(real), version: void 0 };
540
+ const read = name2 !== real ? { bytes: await plain2(), version: void 0 } : this.root.readVersioned ? await this.root.readVersioned(real) : { bytes: await this.root.readFileBuffer(real), version: void 0 };
541
541
  const { bytes } = read;
542
542
  const keyed = read.version === void 0 ? { bytes } : { bytes, key: `${real}\0${read.version}` };
543
543
  await this.verify(name2, bytes, caller, EXEC_OPS);
@@ -546,9 +546,9 @@ var Integrity = class {
546
546
  get readsChecked() {
547
547
  return this.sources.length > 0;
548
548
  }
549
- async readSource(path, caller, plain) {
549
+ async readSource(path, caller, plain2) {
550
550
  const found = await this.source(path);
551
- return found === void 0 ? plain() : this.checked(found, caller, plain);
551
+ return found === void 0 ? plain2() : this.checked(found, caller, plain2);
552
552
  }
553
553
  async openSource(path, caller, real) {
554
554
  const found = await this.source(path, real);
@@ -558,10 +558,10 @@ var Integrity = class {
558
558
  }
559
559
  await this.checked(found, caller, () => this.root.readFileBuffer(found.real));
560
560
  }
561
- async checked(found, caller, plain) {
561
+ async checked(found, caller, plain2) {
562
562
  const { real, name: name2 } = found;
563
563
  if (name2 !== real || !this.root.readVersioned) {
564
- const bytes2 = await plain();
564
+ const bytes2 = await plain2();
565
565
  await this.verify(name2, bytes2, caller, READ_OPS);
566
566
  return bytes2;
567
567
  }
@@ -1287,8 +1287,8 @@ var OpfsFs = class {
1287
1287
  this.refuse(found.path);
1288
1288
  const [stored] = found.link ? [found.link] : await this.meta.get([found.path]);
1289
1289
  const current = await this.ownerAt(found.path, stored);
1290
- const next2 = { uid: change.uid ?? current.uid, gid: change.gid ?? current.gid };
1291
- const handing = found.handle?.kind === "directory" && !sameOwner(next2, current);
1290
+ const next3 = { uid: change.uid ?? current.uid, gid: change.gid ?? current.gid };
1291
+ const handing = found.handle?.kind === "directory" && !sameOwner(next3, current);
1292
1292
  const heir = change.kind === "adopt" ? void 0 : stored?.heir ?? (handing ? [current.uid, current.gid] : void 0);
1293
1293
  const ctimeMs = Date.now();
1294
1294
  await this.meta.update(found.path, (entry) => {
@@ -1296,7 +1296,7 @@ var OpfsFs = class {
1296
1296
  return {
1297
1297
  ...rest2,
1298
1298
  path: found.path,
1299
- ...next2,
1299
+ ...next3,
1300
1300
  ...heir ? { heir } : {},
1301
1301
  ...change.mode !== void 0 ? { mode: change.mode & PERM_MASK } : {},
1302
1302
  ctimeMs
@@ -2342,11 +2342,11 @@ async function realDir(fs, dir, budget = { hops: 0 }) {
2342
2342
  at = parentOf4(at);
2343
2343
  continue;
2344
2344
  }
2345
- const next2 = at === "/" ? `/${part}` : `${at}/${part}`;
2346
- const link2 = await linkOf(fs, next2);
2345
+ const next3 = at === "/" ? `/${part}` : `${at}/${part}`;
2346
+ const link2 = await linkOf(fs, next3);
2347
2347
  if (link2 === void 0) {
2348
- if (queue.find((p) => p !== ".") === "..") await mustBeDir(fs, next2);
2349
- at = next2;
2348
+ if (queue.find((p) => p !== ".") === "..") await mustBeDir(fs, next3);
2349
+ at = next3;
2350
2350
  continue;
2351
2351
  }
2352
2352
  if (budget.hops >= MAX_LINKS3) throw fsError("ELOOP", dir);
@@ -2449,18 +2449,18 @@ async function walk(fs, cred, path, follow2, links) {
2449
2449
  at = parentOf4(at);
2450
2450
  continue;
2451
2451
  }
2452
- const next2 = joinName(at, part);
2453
- const perm = await perms.at(next2);
2452
+ const next3 = joinName(at, part);
2453
+ const perm = await perms.at(next3);
2454
2454
  const last = queue.length === 0;
2455
2455
  if (perm?.link !== void 0 && (!last || follow2)) {
2456
- if (!trustedLink(perm, links)) return { path, perm: void 0, denied: next2, link: true };
2457
- if (++hops > MAX_LINKS4) return { path: next2, perm: void 0 };
2456
+ if (!trustedLink(perm, links)) return { path, perm: void 0, denied: next3, link: true };
2457
+ if (++hops > MAX_LINKS4) return { path: next3, perm: void 0 };
2458
2458
  queue.unshift(...parts(perm.link));
2459
2459
  if (perm.link.startsWith("/")) at = "/";
2460
2460
  continue;
2461
2461
  }
2462
- if (last) return { path: next2, perm };
2463
- at = next2;
2462
+ if (last) return { path: next3, perm };
2463
+ at = next3;
2464
2464
  }
2465
2465
  return { path: at, perm: await perms.at(at) };
2466
2466
  }
@@ -2565,8 +2565,13 @@ function ttyOpenable(g, tty) {
2565
2565
  async function need(g, op, path, want, options = {}, shown = path) {
2566
2566
  const walked = await access(g.fs, g.cred, path, want, linked(g, options));
2567
2567
  if (walked.denied !== void 0) deny(g, op, walked.link ? walked.denied : shown);
2568
+ else if (want & R_OK && g.fs.commandTarget) await readsAs(g, op, walked.path, shown);
2568
2569
  return walked;
2569
2570
  }
2571
+ async function readsAs(g, op, path, shown) {
2572
+ const target = await g.fs.commandTarget?.(path);
2573
+ if (target !== void 0) await need(g, op, target, R_OK, { directory: false }, shown);
2574
+ }
2570
2575
  function entryIn(dir, path) {
2571
2576
  return joinName(dir.path, path.slice(path.lastIndexOf("/") + 1));
2572
2577
  }
@@ -2725,14 +2730,14 @@ function changeCred(cur, change) {
2725
2730
  throw fsError("EINVAL", "not a change");
2726
2731
  }
2727
2732
  const privileged = cur.euid === 0;
2728
- const next2 = copyCred(cur);
2733
+ const next3 = copyCred(cur);
2729
2734
  for (const keys of [UIDS, GIDS]) {
2730
2735
  const own = keys.map((key) => cur[key]);
2731
2736
  for (const key of keys) {
2732
2737
  const value = id(change[key]);
2733
2738
  if (value === void 0) continue;
2734
2739
  if (!privileged && !own.includes(value)) throw fsError("EPERM", `${key} ${value}`);
2735
- next2[key] = value;
2740
+ next3[key] = value;
2736
2741
  }
2737
2742
  }
2738
2743
  if (change.groups !== void 0) {
@@ -2742,9 +2747,9 @@ function changeCred(cur, change) {
2742
2747
  const groups = change.groups.map((g) => id(g) ?? Number.NaN);
2743
2748
  if (groups.some(Number.isNaN)) throw fsError("EINVAL", "not a group list");
2744
2749
  if (!privileged) throw fsError("EPERM", "setgroups");
2745
- next2.groups = groups;
2750
+ next3.groups = groups;
2746
2751
  }
2747
- return next2;
2752
+ return next3;
2748
2753
  }
2749
2754
  function maySignal(sender, target) {
2750
2755
  if (sender.euid === 0) return true;
@@ -3657,10 +3662,10 @@ var KernelSocket = class _KernelSocket {
3657
3662
  async accept(signal, nonblock = false) {
3658
3663
  for (; ; ) {
3659
3664
  if (this.state !== "listening") throw new KernelError("EINVAL");
3660
- const next2 = this.queue.shift();
3661
- if (next2) {
3665
+ const next3 = this.queue.shift();
3666
+ if (next3) {
3662
3667
  this.room.wake();
3663
- return next2;
3668
+ return next3;
3664
3669
  }
3665
3670
  if (nonblock) throw new KernelError("EAGAIN");
3666
3671
  await this.changes.wait(signal);
@@ -4179,12 +4184,12 @@ var MessageQueue = class {
4179
4184
  }
4180
4185
  async *[Symbol.asyncIterator]() {
4181
4186
  for (; ; ) {
4182
- const next2 = this.items.shift();
4183
- if (next2 !== void 0) {
4184
- const n = payloadSize(next2);
4187
+ const next3 = this.items.shift();
4188
+ if (next3 !== void 0) {
4189
+ const n = payloadSize(next3);
4185
4190
  this.size -= n;
4186
4191
  this.taken?.(n);
4187
- yield next2;
4192
+ yield next3;
4188
4193
  continue;
4189
4194
  }
4190
4195
  if (this.ended) return;
@@ -4200,6 +4205,29 @@ var SEND_BUFFER = 1024 * 1024;
4200
4205
  var CLOSE_WAIT_MS = 5e3;
4201
4206
  var NO_WEBSOCKET = "WebSocket not supported by this transport (fetchTransport needs { webSocket: true })";
4202
4207
  var MAX_REQUEST_BODY = 64 * 1024 * 1024;
4208
+ function uploadsFor(peer) {
4209
+ const pulls = /* @__PURE__ */ new Map();
4210
+ const of = (nid, signal) => ({
4211
+ async *[Symbol.asyncIterator]() {
4212
+ for (; ; ) {
4213
+ signal.throwIfAborted();
4214
+ const got = await new Promise((resolve2, reject) => {
4215
+ const stop = () => reject(signal.reason);
4216
+ signal.addEventListener("abort", stop, { once: true });
4217
+ pulls.set(nid, (call) => {
4218
+ signal.removeEventListener("abort", stop);
4219
+ resolve2(call);
4220
+ });
4221
+ peer.postMessage({ net: "body-pull", nid });
4222
+ }).finally(() => pulls.delete(nid));
4223
+ if (got.net === "body-end") return;
4224
+ if (got.net === "body-error") throw new Error(got.message);
4225
+ if (got.net === "body-chunk") yield got.bytes;
4226
+ }
4227
+ }
4228
+ });
4229
+ return { of, answer: (call) => pulls.get(call.nid)?.(call) };
4230
+ }
4203
4231
  function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
4204
4232
  const open = /* @__PURE__ */ new Map();
4205
4233
  const fail2 = (nid, e) => {
@@ -4213,6 +4241,7 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
4213
4241
  ...typeof code === "string" ? { code } : {}
4214
4242
  });
4215
4243
  };
4244
+ const uploads = uploadsFor(peer);
4216
4245
  const start2 = async (call) => {
4217
4246
  const abort = new AbortController();
4218
4247
  const entry = { abort };
@@ -4222,7 +4251,7 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
4222
4251
  url: call.url,
4223
4252
  method: call.method,
4224
4253
  headers: call.headers,
4225
- ...call.body ? { body: call.body } : {},
4254
+ ...call.streamed ? { bodyStream: uploads.of(call.nid, abort.signal) } : call.body ? { body: call.body } : {},
4226
4255
  signal: abort.signal
4227
4256
  });
4228
4257
  if (!open.has(call.nid)) {
@@ -4294,9 +4323,9 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
4294
4323
  try {
4295
4324
  const messages = socket.messages[Symbol.asyncIterator]();
4296
4325
  for (; ; ) {
4297
- const next2 = await Promise.race([messages.next(), unanswered]);
4298
- if (next2 === "late" || next2.done) break;
4299
- const data = next2.value;
4326
+ const next3 = await Promise.race([messages.next(), unanswered]);
4327
+ if (next3 === "late" || next3.done) break;
4328
+ const data = next3.value;
4300
4329
  if (flow.closing) continue;
4301
4330
  if (payloadSize(data) > MESSAGE_LIMIT) {
4302
4331
  oversize = true;
@@ -4337,12 +4366,12 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
4337
4366
  const body = open.get(nid)?.body;
4338
4367
  if (!body) return;
4339
4368
  try {
4340
- const next2 = await body.next();
4341
- if (next2.done) {
4369
+ const next3 = await body.next();
4370
+ if (next3.done) {
4342
4371
  open.delete(nid);
4343
4372
  peer.postMessage({ net: "end", nid });
4344
4373
  } else {
4345
- const bytes = fresh(next2.value);
4374
+ const bytes = fresh(next3.value);
4346
4375
  peer.postMessage({ net: "chunk", nid, bytes }, [bytes.buffer]);
4347
4376
  }
4348
4377
  } catch (e) {
@@ -4360,7 +4389,9 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
4360
4389
  return {
4361
4390
  answer(call) {
4362
4391
  if (call.net === "fetch") void start2(call);
4363
- else if (call.net === "read") void read(call.nid);
4392
+ else if (call.net === "body-chunk" || call.net === "body-end" || call.net === "body-error") {
4393
+ uploads.answer(call);
4394
+ } else if (call.net === "read") void read(call.nid);
4364
4395
  else if (call.net === "ws-open") void openSocket(call);
4365
4396
  else if (call.net === "ws-send") void sendOn(call);
4366
4397
  else if (call.net === "ws-close") {
@@ -4570,9 +4601,9 @@ var UserDb = class {
4570
4601
  }
4571
4602
  }
4572
4603
  serial(work) {
4573
- const next2 = this.writing.then(work);
4574
- this.writing = next2.catch(() => void 0);
4575
- return next2;
4604
+ const next3 = this.writing.then(work);
4605
+ this.writing = next3.catch(() => void 0);
4606
+ return next3;
4576
4607
  }
4577
4608
  async ensure() {
4578
4609
  await this.fs.mkdir("/etc", { recursive: true });
@@ -4744,14 +4775,14 @@ async function realpath(fs, path) {
4744
4775
  let resolved2 = "";
4745
4776
  for (let hops = 0; parts2.length > 0; ) {
4746
4777
  const [name2, ...rest2] = parts2;
4747
- const next2 = `${resolved2}/${name2}`;
4748
- if (!(await fs.lstat(next2)).isSymbolicLink) {
4749
- resolved2 = next2;
4778
+ const next3 = `${resolved2}/${name2}`;
4779
+ if (!(await fs.lstat(next3)).isSymbolicLink) {
4780
+ resolved2 = next3;
4750
4781
  parts2 = rest2;
4751
4782
  continue;
4752
4783
  }
4753
4784
  if (++hops > MAX_LINKS5) throw fsError("ELOOP", path);
4754
- const target = await fs.readlink(next2);
4785
+ const target = await fs.readlink(next3);
4755
4786
  const base = target.startsWith("/") ? "/" : resolved2 || "/";
4756
4787
  parts2 = [...fs.resolvePath(base, target).split("/").filter(Boolean), ...rest2];
4757
4788
  resolved2 = "";
@@ -5344,9 +5375,9 @@ var ClipboardBroker = class {
5344
5375
  // src/clipboard-relay.ts
5345
5376
  function clipboardAsker(port) {
5346
5377
  const asks = /* @__PURE__ */ new Map();
5347
- let next2 = 0;
5378
+ let next3 = 0;
5348
5379
  const ask = (op, stamp, text) => {
5349
- const id2 = ++next2;
5380
+ const id2 = ++next3;
5350
5381
  const waiting = Promise.withResolvers();
5351
5382
  asks.set(id2, waiting);
5352
5383
  const timer = setTimeout(() => {
@@ -5374,9 +5405,9 @@ function clipboardAsker(port) {
5374
5405
  // src/integrity-relay.ts
5375
5406
  function integrityAsker(port) {
5376
5407
  const asks = /* @__PURE__ */ new Map();
5377
- let next2 = 0;
5408
+ let next3 = 0;
5378
5409
  const lookup2 = (path) => {
5379
- const id2 = ++next2;
5410
+ const id2 = ++next3;
5380
5411
  const waiting = Promise.withResolvers();
5381
5412
  asks.set(id2, waiting);
5382
5413
  port.postMessage({ integrityAsk: { id: id2, path } });
@@ -5440,10 +5471,10 @@ var Incoming = class {
5440
5471
  this.eof = true;
5441
5472
  return false;
5442
5473
  }
5443
- const next2 = new Uint8Array(this.buf.length + chunk2.length);
5444
- next2.set(this.buf);
5445
- next2.set(chunk2, this.buf.length);
5446
- this.buf = next2;
5474
+ const next3 = new Uint8Array(this.buf.length + chunk2.length);
5475
+ next3.set(this.buf);
5476
+ next3.set(chunk2, this.buf.length);
5477
+ this.buf = next3;
5447
5478
  return true;
5448
5479
  }
5449
5480
  take(n) {
@@ -5583,23 +5614,50 @@ async function readBody(incoming, framing, cap, signal) {
5583
5614
  const chunks = [];
5584
5615
  let total = 0;
5585
5616
  for (; ; ) {
5586
- const sizeLine = await incoming.line(MAX_CHUNK_LINE, signal);
5587
- const size = /^([0-9A-Fa-f]{1,12})[ \t]*(;.*)?$/.exec(sizeLine);
5588
- if (!size) throw new HttpError(400, "malformed chunk size");
5589
- const n = Number.parseInt(size[1], 16);
5617
+ const n = await chunkSize(incoming, signal);
5590
5618
  if (n === 0) break;
5591
5619
  total += n;
5592
5620
  if (total > cap) throw new HttpError(413, `request body over ${cap} bytes`);
5593
5621
  chunks.push(await incoming.exactly(n, signal));
5594
5622
  if (await incoming.line(2, signal) !== "") throw new HttpError(400, "malformed chunk");
5595
5623
  }
5624
+ await skipTrailers(incoming, signal);
5625
+ return concat(chunks, total);
5626
+ }
5627
+ async function chunkSize(incoming, signal) {
5628
+ const sizeLine = await incoming.line(MAX_CHUNK_LINE, signal);
5629
+ const size = /^([0-9A-Fa-f]{1,12})[ \t]*(;.*)?$/.exec(sizeLine);
5630
+ if (!size) throw new HttpError(400, "malformed chunk size");
5631
+ return Number.parseInt(size[1], 16);
5632
+ }
5633
+ async function skipTrailers(incoming, signal) {
5596
5634
  let trailers = 0;
5597
5635
  for (let line = await incoming.line(MAX_TRAILERS, signal); line !== ""; ) {
5598
5636
  trailers += line.length;
5599
5637
  if (trailers > MAX_TRAILERS) throw new HttpError(431, "trailers too large");
5600
5638
  line = await incoming.line(MAX_TRAILERS, signal);
5601
5639
  }
5602
- return concat(chunks, total);
5640
+ }
5641
+ var STREAM_PIECE = 64 * 1024;
5642
+ async function* pieces(incoming, n, signal) {
5643
+ for (let left = n; left > 0; ) {
5644
+ const piece = await incoming.some(Math.min(left, STREAM_PIECE), signal);
5645
+ if (piece.length === 0) throw new HttpError(400, "connection closed inside a body");
5646
+ left -= piece.length;
5647
+ yield piece;
5648
+ }
5649
+ }
5650
+ async function* streamBody(incoming, framing, signal) {
5651
+ if (framing.kind === "none") return;
5652
+ if (framing.kind === "length") {
5653
+ yield* pieces(incoming, framing.length, signal);
5654
+ return;
5655
+ }
5656
+ for (let n = await chunkSize(incoming, signal); n !== 0; n = await chunkSize(incoming, signal)) {
5657
+ yield* pieces(incoming, n, signal);
5658
+ if (await incoming.line(2, signal) !== "") throw new HttpError(400, "malformed chunk");
5659
+ }
5660
+ await skipTrailers(incoming, signal);
5603
5661
  }
5604
5662
  function concat(chunks, total = chunks.reduce((n, c) => n + c.length, 0)) {
5605
5663
  const out = new Uint8Array(total);
@@ -5708,11 +5766,11 @@ function frame(opcode, payload) {
5708
5766
  return out;
5709
5767
  }
5710
5768
  function clientFrame(opcode, payload) {
5711
- const plain = frame(opcode, payload);
5712
- const head = plain.length - payload.length;
5769
+ const plain2 = frame(opcode, payload);
5770
+ const head = plain2.length - payload.length;
5713
5771
  const mask = crypto.getRandomValues(new Uint8Array(4));
5714
- const out = new Uint8Array(plain.length + 4);
5715
- out.set(plain.subarray(0, head));
5772
+ const out = new Uint8Array(plain2.length + 4);
5773
+ out.set(plain2.subarray(0, head));
5716
5774
  out[1] = out[1] | 128;
5717
5775
  out.set(mask, head);
5718
5776
  for (let i = 0; i < payload.length; i++) {
@@ -6045,6 +6103,11 @@ async function watchHangup(conn, abort, until) {
6045
6103
  }
6046
6104
  }
6047
6105
  }
6106
+ var BODILESS_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD"]);
6107
+ async function* drain(body, done4) {
6108
+ yield* body;
6109
+ done4();
6110
+ }
6048
6111
  function untilAborted(response, signal) {
6049
6112
  return new Promise((resolve2, reject) => {
6050
6113
  const onAbort = () => {
@@ -6187,7 +6250,8 @@ var RealmProxy = class {
6187
6250
  const url = ctx.origin ? tunnelRequestUrl(req, ctx.origin) : requestUrl(req);
6188
6251
  const keep2 = keepsAlive(req);
6189
6252
  const framing = requestFraming(req.headers);
6190
- const reserve = framing.kind === "length" ? framing.length : framing.kind === "chunked" ? this.cap() : 0;
6253
+ const streaming = this.options.transport.traits.streamingBodies === true && framing.kind !== "none" && !BODILESS_METHODS.has(req.method);
6254
+ const reserve = streaming ? 0 : framing.kind === "length" ? framing.length : framing.kind === "chunked" ? this.cap() : 0;
6191
6255
  if (reserve > this.cap()) throw new HttpError(413, `request body over ${this.cap()} bytes`);
6192
6256
  await this.expectContinue(conn, req, framing.kind !== "none");
6193
6257
  await this.bodies.acquire(reserve, this.stop.signal);
@@ -6195,9 +6259,12 @@ var RealmProxy = class {
6195
6259
  const abort = new AbortController();
6196
6260
  const stopExchange = () => abort.abort();
6197
6261
  this.stop.signal.addEventListener("abort", stopExchange, { once: true });
6262
+ let drained = !streaming;
6198
6263
  try {
6199
6264
  try {
6200
- const body = await readBody(incoming, framing, this.cap());
6265
+ const body = streaming ? drain(streamBody(incoming, framing, abort.signal), () => {
6266
+ drained = true;
6267
+ }) : await readBody(incoming, framing, this.cap());
6201
6268
  const waiting = new AbortController();
6202
6269
  void watchHangup(ctx.socket, abort, waiting.signal);
6203
6270
  try {
@@ -6208,7 +6275,8 @@ var RealmProxy = class {
6208
6275
  } finally {
6209
6276
  this.bodies.release(reserve);
6210
6277
  }
6211
- return await this.relay(conn, req.method, req.minor, response, keep2);
6278
+ const kept = await this.relay(conn, req.method, req.minor, response, keep2 && drained);
6279
+ return kept && drained;
6212
6280
  } finally {
6213
6281
  this.stop.signal.removeEventListener("abort", stopExchange);
6214
6282
  abort.abort();
@@ -6228,13 +6296,14 @@ var RealmProxy = class {
6228
6296
  }
6229
6297
  }
6230
6298
  async upstream(req, url, body, signal, owner) {
6299
+ const sent = BODILESS_METHODS.has(req.method) ? void 0 : body;
6231
6300
  try {
6232
6301
  const fetching = this.options.transport.fetch(
6233
6302
  {
6234
6303
  url: toHostLoopback(url),
6235
6304
  method: req.method.toUpperCase(),
6236
6305
  headers: forwardRequestHeaders(req.headers),
6237
- body: req.method === "GET" || req.method === "HEAD" ? void 0 : body,
6306
+ ...sent instanceof Uint8Array ? { body: sent } : sent ? { bodyStream: sent } : {},
6238
6307
  signal
6239
6308
  },
6240
6309
  owner
@@ -7152,12 +7221,17 @@ var RemoteTransport = class {
7152
7221
  waiting = /* @__PURE__ */ new Map();
7153
7222
  nextId = 0;
7154
7223
  sockets = /* @__PURE__ */ new Map();
7224
+ uploads = /* @__PURE__ */ new Map();
7155
7225
  failure;
7156
7226
  constructor(port, traits) {
7157
7227
  this.port = port;
7158
7228
  this.traits = traits;
7159
7229
  }
7160
7230
  receive(reply) {
7231
+ if (reply.net === "body-pull") {
7232
+ void this.pull(reply.nid);
7233
+ return;
7234
+ }
7161
7235
  const socket = this.sockets.get(reply.nid);
7162
7236
  if (socket && this.toSocket(reply, socket)) return;
7163
7237
  const waiter = this.waiting.get(reply.nid);
@@ -7176,8 +7250,35 @@ var RemoteTransport = class {
7176
7250
  } else return false;
7177
7251
  return true;
7178
7252
  }
7253
+ async pull(nid) {
7254
+ const upload = this.uploads.get(nid);
7255
+ if (!upload) {
7256
+ this.port.postMessage({ net: "body-error", nid, message: "the request body is gone" });
7257
+ return;
7258
+ }
7259
+ try {
7260
+ const next3 = await upload.next();
7261
+ if (next3.done) {
7262
+ this.uploads.delete(nid);
7263
+ this.port.postMessage({ net: "body-end", nid });
7264
+ } else this.port.postMessage({ net: "body-chunk", nid, bytes: next3.value });
7265
+ } catch (e) {
7266
+ this.uploads.delete(nid);
7267
+ this.port.postMessage({
7268
+ net: "body-error",
7269
+ nid,
7270
+ message: e instanceof Error ? e.message : String(e)
7271
+ });
7272
+ }
7273
+ }
7274
+ dropUpload(nid) {
7275
+ const upload = this.uploads.get(nid);
7276
+ this.uploads.delete(nid);
7277
+ void upload?.return?.().catch(() => void 0);
7278
+ }
7179
7279
  fail(error) {
7180
7280
  this.failure = error;
7281
+ for (const nid of [...this.uploads.keys()]) this.dropUpload(nid);
7181
7282
  for (const waiter of this.waiting.values()) waiter.reject(error);
7182
7283
  this.waiting.clear();
7183
7284
  for (const socket of this.sockets.values())
@@ -7204,14 +7305,17 @@ var RemoteTransport = class {
7204
7305
  let finished = false;
7205
7306
  const abort = () => {
7206
7307
  finished = true;
7308
+ this.dropUpload(nid);
7207
7309
  this.cancel(nid);
7208
7310
  };
7209
7311
  signal.addEventListener("abort", abort, { once: true });
7210
7312
  const finish = () => {
7313
+ this.dropUpload(nid);
7211
7314
  if (finished) return;
7212
7315
  finished = true;
7213
7316
  signal.removeEventListener("abort", abort);
7214
7317
  };
7318
+ if (request2.bodyStream) this.uploads.set(nid, request2.bodyStream[Symbol.asyncIterator]());
7215
7319
  let head;
7216
7320
  try {
7217
7321
  head = await this.ask({
@@ -7220,7 +7324,7 @@ var RemoteTransport = class {
7220
7324
  url: request2.url,
7221
7325
  method: request2.method,
7222
7326
  headers: request2.headers,
7223
- ...request2.body ? { body: request2.body } : {}
7327
+ ...request2.bodyStream ? { streamed: true } : request2.body ? { body: request2.body } : {}
7224
7328
  });
7225
7329
  } catch (e) {
7226
7330
  finish();
@@ -7730,6 +7834,164 @@ function closeAll(entry) {
7730
7834
  }
7731
7835
  }
7732
7836
 
7837
+ // src/fs/engines.ts
7838
+ var NUMBER = /^(0|[1-9]\d*)$/;
7839
+ var PART = /^(?:0|[1-9]\d*|[xX*])$/;
7840
+ var IDENTIFIER = /^(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)$/;
7841
+ function identifiers(pre) {
7842
+ if (pre === void 0) return [];
7843
+ const ids = pre.split(".");
7844
+ return ids.every((id2) => IDENTIFIER.test(id2)) ? ids : void 0;
7845
+ }
7846
+ function parseVersion(text) {
7847
+ const m = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(text.trim());
7848
+ const pre = identifiers(m?.[4]);
7849
+ if (!m || !pre || ![m[1], m[2], m[3]].every((n) => NUMBER.test(n))) return void 0;
7850
+ return [Number(m[1]), Number(m[2]), Number(m[3]), pre];
7851
+ }
7852
+ function comparePre(a, b) {
7853
+ if (a.length === 0 || b.length === 0) return b.length - a.length;
7854
+ for (let i = 0; i < Math.max(a.length, b.length); i++) {
7855
+ const [x, y] = [a[i], b[i]];
7856
+ if (x === void 0 || y === void 0) return x === void 0 ? -1 : 1;
7857
+ if (x === y) continue;
7858
+ const [nx, ny] = [NUMBER.test(x), NUMBER.test(y)];
7859
+ if (nx && ny) return Number(x) - Number(y);
7860
+ if (nx !== ny) return nx ? -1 : 1;
7861
+ return x < y ? -1 : 1;
7862
+ }
7863
+ return 0;
7864
+ }
7865
+ function compare(a, b) {
7866
+ return a[0] - b[0] || a[1] - b[1] || a[2] - b[2] || comparePre(a[3], b[3]);
7867
+ }
7868
+ function partial(text) {
7869
+ const m = /^v?([^.+-]+)(?:\.([^.+-]+))?(?:\.([^.+-]+))?(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(
7870
+ text
7871
+ );
7872
+ if (!m) return void 0;
7873
+ const raw = [m[1], m[2], m[3]];
7874
+ if (!raw.every((p) => p === void 0 || PART.test(p))) return void 0;
7875
+ const parts2 = [];
7876
+ for (const p of raw) {
7877
+ if (p === void 0 || /^[xX*]$/.test(p) || parts2.includes(void 0)) parts2.push(void 0);
7878
+ else parts2.push(Number(p));
7879
+ }
7880
+ const pre = identifiers(m[4]);
7881
+ return pre && { parts: parts2, pre };
7882
+ }
7883
+ function floor(p) {
7884
+ const [a = 0, b = 0, c = 0] = p.parts;
7885
+ return [a, b, c, p.parts.includes(void 0) ? [] : p.pre];
7886
+ }
7887
+ function next(p) {
7888
+ const [a, b] = p.parts;
7889
+ if (a === void 0) return void 0;
7890
+ if (b === void 0) return [a + 1, 0, 0, ["0"]];
7891
+ return [a, b + 1, 0, ["0"]];
7892
+ }
7893
+ var ANY = [[">=", [0, 0, 0, []]]];
7894
+ var NOTHING = [["<", [0, 0, 0, ["0"]]]];
7895
+ function plain(op, p) {
7896
+ const exact = !p.parts.includes(void 0);
7897
+ const low = floor(p);
7898
+ const high = next(p);
7899
+ if (op === ">=") return [[">=", low]];
7900
+ if (op === "<") return [["<", exact ? low : [low[0], low[1], low[2], ["0"]]]];
7901
+ if (exact) return [[op === "" ? "=" : op, low]];
7902
+ if (op === ">") return high ? [[">=", high]] : NOTHING;
7903
+ if (op === "<=") return high ? [["<", high]] : ANY;
7904
+ return high ? [
7905
+ [">=", low],
7906
+ ["<", high]
7907
+ ] : ANY;
7908
+ }
7909
+ function caret(p) {
7910
+ const [a = 0, b, c] = p.parts;
7911
+ const low = floor(p);
7912
+ if (b === void 0)
7913
+ return [
7914
+ [">=", low],
7915
+ ["<", [a + 1, 0, 0, ["0"]]]
7916
+ ];
7917
+ if (a > 0)
7918
+ return [
7919
+ [">=", low],
7920
+ ["<", [a + 1, 0, 0, ["0"]]]
7921
+ ];
7922
+ if (c === void 0 || b > 0)
7923
+ return [
7924
+ [">=", low],
7925
+ ["<", [0, b + 1, 0, ["0"]]]
7926
+ ];
7927
+ return [
7928
+ [">=", low],
7929
+ ["<", [0, b, c + 1, ["0"]]]
7930
+ ];
7931
+ }
7932
+ function tilde(p) {
7933
+ const [a = 0, b] = p.parts;
7934
+ const low = floor(p);
7935
+ return [
7936
+ [">=", low],
7937
+ ["<", b === void 0 ? [a + 1, 0, 0, ["0"]] : [a, b + 1, 0, ["0"]]]
7938
+ ];
7939
+ }
7940
+ function hyphen(from, to) {
7941
+ const high = to.parts.includes(void 0) ? next(to) : void 0;
7942
+ const upper = to.parts.includes(void 0) ? high ? [["<", high]] : [] : [["<=", floor(to)]];
7943
+ return [[">=", floor(from)], ...upper];
7944
+ }
7945
+ function comparator(token) {
7946
+ const m = /^(<=|>=|<|>|=|\^|~>?)?\s*(.*)$/.exec(token);
7947
+ const op = m?.[1] ?? "";
7948
+ const p = partial(m?.[2] ?? "");
7949
+ if (!p) return void 0;
7950
+ const wild = p.parts[0] === void 0;
7951
+ if (op === "^") return wild ? ANY : caret(p);
7952
+ if (op.startsWith("~")) return wild ? ANY : tilde(p);
7953
+ return plain(op, p);
7954
+ }
7955
+ function set2(text) {
7956
+ const trimmed = text.trim().replace(/(<=|>=|<|>|=|\^|~>?)\s+/g, "$1");
7957
+ if (trimmed === "") return ANY;
7958
+ const range = /^(\S+)\s+-\s+(\S+)$/.exec(trimmed);
7959
+ if (range) {
7960
+ const [from, to] = [partial(range[1]), partial(range[2])];
7961
+ return from && to ? hyphen(from, to) : void 0;
7962
+ }
7963
+ const out = [];
7964
+ for (const token of trimmed.split(/\s+/)) {
7965
+ const found = comparator(token);
7966
+ if (!found) return void 0;
7967
+ out.push(...found);
7968
+ }
7969
+ return out;
7970
+ }
7971
+ function holds(v, [op, w]) {
7972
+ const c = compare(v, w);
7973
+ if (op === "<") return c < 0;
7974
+ if (op === "<=") return c <= 0;
7975
+ if (op === ">") return c > 0;
7976
+ if (op === ">=") return c >= 0;
7977
+ return c === 0;
7978
+ }
7979
+ function satisfies(version, range) {
7980
+ const v = parseVersion(version);
7981
+ if (!v) return void 0;
7982
+ const sets = range.split("||").map(set2);
7983
+ if (sets.some((s) => s === void 0)) return void 0;
7984
+ return sets.some((s) => s.every((c) => holds(v, c)));
7985
+ }
7986
+
7987
+ // src/version.ts
7988
+ var KERNEL_VERSION = true ? "1.106.2" : "0.0.0-dev";
7989
+ var OS_RELEASE = "6.0.0-slicc";
7990
+ function procVersion() {
7991
+ return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
7992
+ `;
7993
+ }
7994
+
7733
7995
  // src/commands.ts
7734
7996
  var NAME2 = /^[A-Za-z0-9._+-]+$/;
7735
7997
  var COMMAND_NAME = /^[^/\s\p{Cc}]+$/u;
@@ -7941,13 +8203,25 @@ async function binCommandsOf(physical, pkg, manifest, taken) {
7941
8203
  }
7942
8204
  return out;
7943
8205
  }
8206
+ function engineRefusal(manifest, kernel) {
8207
+ const engines = manifest.engines;
8208
+ const range = engines && typeof engines === "object" ? engines["slicc-kernel"] : void 0;
8209
+ const running = parseVersion(kernel);
8210
+ if (typeof range !== "string" || !running || running.slice(0, 3).every((n) => n === 0)) {
8211
+ return void 0;
8212
+ }
8213
+ const fits = satisfies(kernel, range);
8214
+ if (fits) return void 0;
8215
+ const who = `${String(manifest.name ?? "?")}@${String(manifest.version ?? "?")}`;
8216
+ return fits === false ? `${who} needs slicc-kernel ${range}; this kernel is ${kernel}` : `${who} has an engines.slicc-kernel range this kernel can't read: ${JSON.stringify(range)}`;
8217
+ }
7944
8218
  var PACKAGE_NAME = /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
7945
8219
  function replacesOf(manifest) {
7946
8220
  const replaces = manifest.slicc?.replaces;
7947
8221
  if (!Array.isArray(replaces)) return [];
7948
8222
  return replaces.every((name2) => typeof name2 === "string" && PACKAGE_NAME.test(name2)) ? replaces : [];
7949
8223
  }
7950
- async function tierPackages(fs, physical, roots, bins) {
8224
+ async function tierPackages(fs, physical, roots, bins, kernel) {
7951
8225
  const out = [];
7952
8226
  for (const root of roots) {
7953
8227
  for (const pkg of await packages(fs, root)) {
@@ -7960,11 +8234,13 @@ async function tierPackages(fs, physical, roots, bins) {
7960
8234
  const own = commandsOf(pkg, manifest);
7961
8235
  const named = new Set(own.map((command) => command.name));
7962
8236
  const extra = bins ? await binCommandsOf(physical, pkg, manifest, named) : [];
8237
+ const refused2 = engineRefusal(manifest, kernel);
8238
+ const commands = [...own, ...extra];
7963
8239
  out.push({
7964
8240
  name: pkg.slice(root.length + 1),
7965
8241
  pkg,
7966
8242
  replaces: replacesOf(manifest),
7967
- commands: [...own, ...extra]
8243
+ commands: refused2 ? commands.map((command) => ({ ...command, refused: refused2 })) : commands
7968
8244
  });
7969
8245
  }
7970
8246
  }
@@ -7990,14 +8266,14 @@ function winners(tier) {
7990
8266
  }
7991
8267
  return out;
7992
8268
  }
7993
- async function scanCommands(fs, modules, binsFrom = Number.POSITIVE_INFINITY) {
8269
+ async function scanCommands(fs, modules, binsFrom = Number.POSITIVE_INFINITY, kernel = KERNEL_VERSION) {
7994
8270
  const found = /* @__PURE__ */ new Map();
7995
8271
  const physical = physicalResolver(fs);
7996
8272
  const tiers = (typeof modules === "string" ? [modules] : modules).map(
7997
8273
  (tier) => typeof tier === "string" ? [tier] : tier
7998
8274
  );
7999
8275
  for (const [index, roots] of tiers.entries()) {
8000
- const tier = await tierPackages(fs, physical, roots, index >= binsFrom);
8276
+ const tier = await tierPackages(fs, physical, roots, index >= binsFrom, kernel);
8001
8277
  for (const [name2, { pkg, command }] of winners(tier)) {
8002
8278
  if (!found.has(name2)) found.set(name2, await withPackagePaths(fs, pkg, command));
8003
8279
  }
@@ -8374,6 +8650,7 @@ var S_IFDIR2 = 16384;
8374
8650
  var S_IFREG2 = 32768;
8375
8651
  var COMMAND = /^\/(?:usr\/)?bin\/([^/]+)$/;
8376
8652
  var MAX_LINKS6 = 40;
8653
+ var BIN_ENTRY = /\/bin\/[^/]+$/;
8377
8654
  function missing3(err) {
8378
8655
  return err?.code === "ENOENT";
8379
8656
  }
@@ -8401,7 +8678,20 @@ async function followLinks(fs, path) {
8401
8678
  }
8402
8679
  return current;
8403
8680
  }
8404
- function withCommandDirs(fs, names, userDir = async () => void 0, owners = false) {
8681
+ var NOBODY = { ruid: -1, euid: -1, suid: -1, rgid: -1, egid: -1, sgid: -1, groups: [] };
8682
+ var EMPTY = new Uint8Array(0);
8683
+ function quiet(promise) {
8684
+ return promise.catch(() => void 0);
8685
+ }
8686
+ async function readsAs2(fs, cred, target) {
8687
+ const walked = await quiet(access(fs, cred, target, R_OK, { directory: false }));
8688
+ return walked !== void 0 && walked.denied === void 0 && !walked.missing;
8689
+ }
8690
+ async function shownTarget(fs, target) {
8691
+ const st = await quiet(fs.stat(target));
8692
+ return st?.isFile && await readsAs2(fs, NOBODY, target) ? st : void 0;
8693
+ }
8694
+ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false, targetOf2 = async () => void 0) {
8405
8695
  async function nearest(path) {
8406
8696
  const st = await fs.stat(path).catch(() => void 0);
8407
8697
  if (st || path === "/") return st ?? {};
@@ -8433,9 +8723,54 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
8433
8723
  const own = await userDir(dir);
8434
8724
  return own?.has(path.slice(slash + 1)) ? owned4(synthetic(false, path), dir) : void 0;
8435
8725
  }
8726
+ let generation;
8727
+ const targets = /* @__PURE__ */ new Map();
8728
+ async function targetStat(target) {
8729
+ const now = await names();
8730
+ if (now !== generation) {
8731
+ generation = now;
8732
+ targets.clear();
8733
+ }
8734
+ let found = targets.get(target);
8735
+ if (!found) {
8736
+ found = shownTarget(fs, target);
8737
+ targets.set(target, found);
8738
+ }
8739
+ return found;
8740
+ }
8741
+ async function shown(path) {
8742
+ const st = await virtual(path);
8743
+ if (!st?.isFile) return st;
8744
+ const target = await targetOf2(path);
8745
+ const real = target === void 0 ? void 0 : await targetStat(target.path);
8746
+ return real ? { ...st, size: real.size, mtime: real.mtime, ctime: real.mtime, atime: real.mtime } : st;
8747
+ }
8436
8748
  async function linked2(path) {
8437
- return await virtual(path) ?? virtual(await followLinks(fs, path));
8749
+ return await shown(path) ?? shown(await followLinks(fs, path));
8750
+ }
8751
+ async function entryTarget(path) {
8752
+ if (!BIN_ENTRY.test(path) || !(await virtual(path))?.isFile) return void 0;
8753
+ if (!await fs.lstat(path).then(() => false, missing3)) return void 0;
8754
+ const target = await targetOf2(path);
8755
+ return target?.path === path ? void 0 : target;
8756
+ }
8757
+ async function redirected2(path) {
8758
+ const target = await entryTarget(path) ?? await entryTarget(await followLinks(fs, path));
8759
+ if (owners && target?.owner && !await readsAs2(fs, target.owner, target.path)) {
8760
+ throw fsError("EACCES", path);
8761
+ }
8762
+ return target?.path;
8438
8763
  }
8764
+ const absent2 = (err) => {
8765
+ if (missing3(err)) return EMPTY;
8766
+ throw err;
8767
+ };
8768
+ async function content(path, err) {
8769
+ if (!missing3(err) || !(await linked2(path))?.isFile) throw err;
8770
+ const target = await redirected2(path);
8771
+ return target === void 0 ? EMPTY : fs.readFileBuffer(target).catch(absent2);
8772
+ }
8773
+ const ranged2 = rangedOps(fs);
8439
8774
  async function stat(path, real) {
8440
8775
  try {
8441
8776
  return await real(path);
@@ -8447,16 +8782,13 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
8447
8782
  }
8448
8783
  return {
8449
8784
  resolvePath: (base, path) => fs.resolvePath(base, path),
8450
- readFile: (path) => fs.readFile(path),
8451
- async readFileBuffer(path) {
8452
- try {
8453
- return await fs.readFileBuffer(path);
8454
- } catch (err) {
8455
- if (missing3(err) && (await linked2(path))?.isFile) return new Uint8Array(0);
8456
- throw err;
8457
- }
8458
- },
8459
- writeFile: (path, content, owner) => fs.writeFile(path, content, owner),
8785
+ readFile: (path) => fs.readFile(path).catch(async (err) => {
8786
+ if (!BIN_ENTRY.test(await followLinks(fs, path))) throw err;
8787
+ return new TextDecoder().decode(await content(path, err));
8788
+ }),
8789
+ readFileBuffer: (path) => fs.readFileBuffer(path).catch((err) => content(path, err)),
8790
+ commandTarget: async (path) => (await entryTarget(path))?.path,
8791
+ writeFile: (path, content2, owner) => fs.writeFile(path, content2, owner),
8460
8792
  link: (from, to, follow2) => fs.link(from, to, follow2),
8461
8793
  links: (path) => mayLink(fs, path),
8462
8794
  async exists(path) {
@@ -8487,7 +8819,7 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
8487
8819
  added.map(
8488
8820
  async (name2) => [
8489
8821
  name2,
8490
- await virtual(`${base}/${name2}`)
8822
+ await shown(`${base}/${name2}`)
8491
8823
  ]
8492
8824
  )
8493
8825
  );
@@ -8505,7 +8837,14 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
8505
8837
  chmod: (path, mode) => fs.chmod(path, mode),
8506
8838
  utimes: (path, atime, mtime) => fs.utimes(path, atime, mtime),
8507
8839
  lutimes: (path, atime, mtime) => lutimesOf(fs, path, atime, mtime),
8508
- ...rangedOps(fs) ?? {}
8840
+ ...ranged2 ? {
8841
+ ...ranged2,
8842
+ pread: (path, offset, length2, version) => ranged2.pread(path, offset, length2, version).catch(async (err) => {
8843
+ const target = missing3(err) ? await redirected2(path) : void 0;
8844
+ if (target === void 0) throw err;
8845
+ return ranged2.pread(target, offset, length2).catch(absent2);
8846
+ })
8847
+ } : {}
8509
8848
  };
8510
8849
  }
8511
8850
 
@@ -8650,7 +8989,7 @@ var MAX_HOPS2 = 40;
8650
8989
  function start(path) {
8651
8990
  return { done: [], todo: path.split("/").filter((p) => p !== ""), hops: 0 };
8652
8991
  }
8653
- function next(walk2) {
8992
+ function next2(walk2) {
8654
8993
  while (walk2.todo.length > 0) {
8655
8994
  const part = walk2.todo.shift();
8656
8995
  if (part === ".") continue;
@@ -8664,7 +9003,7 @@ function next(walk2) {
8664
9003
  }
8665
9004
  function lexical(path) {
8666
9005
  const walk2 = start(path);
8667
- for (let part = next(walk2); part !== void 0; part = next(walk2)) walk2.done.push(part);
9006
+ for (let part = next2(walk2); part !== void 0; part = next2(walk2)) walk2.done.push(part);
8668
9007
  return `/${walk2.done.join("/")}`;
8669
9008
  }
8670
9009
  function rest(walk2, part) {
@@ -8678,7 +9017,7 @@ function follow(walk2, target) {
8678
9017
  }
8679
9018
  async function physicalPathAsync(fs, path) {
8680
9019
  const walk2 = start(path);
8681
- for (let part = next(walk2); part !== void 0; part = next(walk2)) {
9020
+ for (let part = next2(walk2); part !== void 0; part = next2(walk2)) {
8682
9021
  const at = `/${[...walk2.done, part].join("/")}`;
8683
9022
  let target;
8684
9023
  try {
@@ -8751,9 +9090,9 @@ var UnlinkedKeeper = class {
8751
9090
  void locks?.request(lockName(this.instance), () => new Promise(() => void 0));
8752
9091
  }
8753
9092
  serial(op) {
8754
- const next2 = this.queue.then(op);
8755
- this.queue = next2.catch(() => void 0);
8756
- return next2;
9093
+ const next3 = this.queue.then(op);
9094
+ this.queue = next3.catch(() => void 0);
9095
+ return next3;
8757
9096
  }
8758
9097
  wrap(fs = this.fs) {
8759
9098
  const keeper = this;
@@ -9198,7 +9537,8 @@ var ChildTable = class {
9198
9537
  const child = this.children.get(pid);
9199
9538
  if (!child) return;
9200
9539
  this.watchers.set(pid, listener);
9201
- if (child.stoppedBy !== void 0) listener("stopped", child.stoppedBy);
9540
+ if (child.stoppedBy !== void 0 && child.code === void 0)
9541
+ listener("stopped", child.stoppedBy);
9202
9542
  }
9203
9543
  openSlot(slot2, n, captured) {
9204
9544
  if ("fd" in slot2) return this.parentFds.get(slot2.fd).retain();
@@ -9496,14 +9836,14 @@ var HeldSet = class extends Set {
9496
9836
  super();
9497
9837
  this.idle = idle;
9498
9838
  }
9499
- add(holds) {
9500
- holds.onIdle = this.idle;
9501
- return super.add(holds);
9839
+ add(holds2) {
9840
+ holds2.onIdle = this.idle;
9841
+ return super.add(holds2);
9502
9842
  }
9503
- delete(holds) {
9504
- const removed = super.delete(holds);
9505
- for (const path of /* @__PURE__ */ new Set([...holds.keys(), ...holds.opens.keys()])) this.idle(path);
9506
- holds.releaseAll();
9843
+ delete(holds2) {
9844
+ const removed = super.delete(holds2);
9845
+ for (const path of /* @__PURE__ */ new Set([...holds2.keys(), ...holds2.opens.keys()])) this.idle(path);
9846
+ holds2.releaseAll();
9507
9847
  return removed;
9508
9848
  }
9509
9849
  };
@@ -10690,9 +11030,9 @@ var MountFs = class _MountFs {
10690
11030
  this.writable(mount, real);
10691
11031
  const { uid, gid, mode } = change;
10692
11032
  const fresh2 = change.kind === "new" && { uid, gid, mode };
10693
- const set2 = fresh2 ? await this.driverOwner(found, real, fresh2, false) : change;
11033
+ const set3 = fresh2 ? await this.driverOwner(found, real, fresh2, false) : change;
10694
11034
  this.table.forget(mount, real);
10695
- const attrs = Object.entries({ uid: set2.uid, gid: set2.gid, mode: set2.mode });
11035
+ const attrs = Object.entries({ uid: set3.uid, gid: set3.gid, mode: set3.mode });
10696
11036
  await mount.conn.call({
10697
11037
  op: "setattr",
10698
11038
  path: rel2,
@@ -10719,11 +11059,11 @@ var MountFs = class _MountFs {
10719
11059
  const real = await this.follow(path);
10720
11060
  const found = this.at(real);
10721
11061
  if (!found) {
10722
- const set2 = () => this.base.utimes(real, atime, mtime);
11062
+ const set3 = () => this.base.utimes(real, atime, mtime);
10723
11063
  return this.based(
10724
11064
  [real],
10725
11065
  redirects,
10726
- set2,
11066
+ set3,
10727
11067
  ([to], n) => this.utimes(to, atime, mtime, n)
10728
11068
  );
10729
11069
  }
@@ -10732,11 +11072,11 @@ var MountFs = class _MountFs {
10732
11072
  async lutimes(path, atime, mtime, redirects = 0) {
10733
11073
  const found = this.at(path);
10734
11074
  if (!found) {
10735
- const set2 = () => lutimesOf(this.base, path, atime, mtime);
11075
+ const set3 = () => lutimesOf(this.base, path, atime, mtime);
10736
11076
  return this.based(
10737
11077
  [path],
10738
11078
  redirects,
10739
- set2,
11079
+ set3,
10740
11080
  ([to], n) => this.lutimes(to, atime, mtime, n)
10741
11081
  );
10742
11082
  }
@@ -10758,6 +11098,20 @@ var MountFs = class _MountFs {
10758
11098
  }
10759
11099
  };
10760
11100
 
11101
+ // src/fs/command-stat.ts
11102
+ async function callerStat(g, path, st) {
11103
+ if (!g?.fs.commandTarget || !st.isFile || st.size > 0) return st;
11104
+ const target = await g.fs.commandTarget(path);
11105
+ if (target === void 0) return st;
11106
+ const options = { directory: false, ...g.links === void 0 ? {} : { links: g.links } };
11107
+ const walked = await access(g.fs, g.cred, target, R_OK, options).catch(() => void 0);
11108
+ if (!walked || walked.denied !== void 0 || walked.missing) return st;
11109
+ const real = await g.fs.stat(target).catch(() => void 0);
11110
+ if (!real?.isFile) return st;
11111
+ const { size, mtime } = real;
11112
+ return { ...st, size, mtime, atime: mtime, ctime: mtime };
11113
+ }
11114
+
10761
11115
  // src/realm/sync-fs-token-registry.ts
10762
11116
  var registry = /* @__PURE__ */ new Map();
10763
11117
  function mintSyncFsToken(entry) {
@@ -10844,8 +11198,10 @@ function statJson(s) {
10844
11198
  ...s.gid !== void 0 ? { gid: s.gid } : {}
10845
11199
  };
10846
11200
  }
10847
- async function readdirStat(fs, dir) {
10848
- return (await fs.readdirStat(dir)).map(([name2, stat]) => [name2, stat && statJson(stat)]);
11201
+ async function readdirStat(fs, dir, g) {
11202
+ const listed = await fs.readdirStat(dir);
11203
+ const shown = async (name2, stat) => stat && statJson(await callerStat(g, joinName(dir, name2), stat));
11204
+ return Promise.all(listed.map(async ([name2, stat]) => [name2, await shown(name2, stat)]));
10849
11205
  }
10850
11206
  async function usageAt(entry, path) {
10851
11207
  if (!entry.statfs) return void 0;
@@ -10914,18 +11270,22 @@ async function chown(entry, path, req) {
10914
11270
  }
10915
11271
  async function runMkdir(entry, path, req) {
10916
11272
  const { fs } = entry;
10917
- const plain = plainDirMode(entry, req.mode);
11273
+ const plain2 = plainDirMode(entry, req.mode);
10918
11274
  const parentsMode = !req.exclusive ? plainParentsMode(entry) : void 0;
10919
11275
  const fixParents = parentsMode !== void 0 ? await missingParents(fs, path, parentsMode) : void 0;
10920
- const fresh2 = plain !== void 0 && !await fs.exists(path);
11276
+ const fresh2 = plain2 !== void 0 && !await fs.exists(path);
10921
11277
  await (req.exclusive ? mkdirExclusive(entry, path, req.mode) : fs.mkdir(path, mkdirOptions(entry, req.mode)));
10922
11278
  await fixParents?.();
10923
- if (fresh2) await fs.chmod(path, plain);
11279
+ if (fresh2) await fs.chmod(path, plain2);
10924
11280
  return done;
10925
11281
  }
11282
+ async function shownAs(entry, path, st) {
11283
+ const real = st.isFile && st.size === 0 ? entry.fs.realpath?.(path, true) : void 0;
11284
+ return callerStat(guardOf(entry), await real?.catch(() => path) ?? path, st);
11285
+ }
10926
11286
  function readSource(entry, path) {
10927
- const plain = () => entry.fs.readFileBuffer(path);
10928
- return entry.source ? entry.source.read(path, plain) : plain();
11287
+ const plain2 = () => entry.fs.readFileBuffer(path);
11288
+ return entry.source ? entry.source.read(path, plain2) : plain2();
10929
11289
  }
10930
11290
  async function run(entry, path, req) {
10931
11291
  const { fs, cwd } = entry;
@@ -10938,13 +11298,13 @@ async function run(entry, path, req) {
10938
11298
  case "exists":
10939
11299
  return json2(await fs.exists(path));
10940
11300
  case "stat":
10941
- return json2(statJson(await fs.stat(path)));
11301
+ return json2(statJson(await shownAs(entry, path, await fs.stat(path))));
10942
11302
  case "lstat":
10943
- return json2(statJson(await fs.lstat(path)));
11303
+ return json2(statJson(await shownAs(entry, path, await fs.lstat(path))));
10944
11304
  case "readdir":
10945
11305
  return json2(await fs.readdir(path));
10946
11306
  case "readdir-stat":
10947
- return json2(await readdirStat(fs, path));
11307
+ return json2(await readdirStat(fs, path, guardOf(entry)));
10948
11308
  case "mkdir":
10949
11309
  return runMkdir(entry, path, req);
10950
11310
  case "rm":
@@ -11866,18 +12226,18 @@ var KernelDgram = class {
11866
12226
  const pending = this.takeError();
11867
12227
  if (pending) throw new KernelError(pending);
11868
12228
  if (this.closed) throw new KernelError("EBADF");
11869
- const next2 = this.queue[0];
11870
- if (next2) {
12229
+ const next3 = this.queue[0];
12230
+ if (next3) {
11871
12231
  if (!peek) {
11872
12232
  this.queue.shift();
11873
- this.queued -= next2.bytes.length;
12233
+ this.queued -= next3.bytes.length;
11874
12234
  this.changes.wake();
11875
12235
  }
11876
12236
  const size = Math.max(0, max);
11877
12237
  return {
11878
- bytes: next2.bytes.slice(0, size),
11879
- from: next2.from,
11880
- truncated: next2.bytes.length > size
12238
+ bytes: next3.bytes.slice(0, size),
12239
+ from: next3.from,
12240
+ truncated: next3.bytes.length > size
11881
12241
  };
11882
12242
  }
11883
12243
  await this.changes.wait(signal);
@@ -12196,14 +12556,6 @@ var PATH_OPS = new Set(SYNC_FS_OPS);
12196
12556
  // src/realm/live-vfs-fs.ts
12197
12557
  var EARLY_WHOLE_FILE = 64 * 1024;
12198
12558
 
12199
- // src/version.ts
12200
- var KERNEL_VERSION = true ? "1.103.4" : "0.0.0-dev";
12201
- var OS_RELEASE = "6.0.0-slicc";
12202
- function procVersion() {
12203
- return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
12204
- `;
12205
- }
12206
-
12207
12559
  // src/process/process-fds.ts
12208
12560
  var field2 = (text) => (text || "none").replace(/[\s\\]/g, (c) => `\\${c.charCodeAt(0).toString(8).padStart(3, "0")}`);
12209
12561
  var pinnedRoot;
@@ -12595,7 +12947,7 @@ function* procWalk(path, follow2) {
12595
12947
  const whole = normalize(`/${[...done4, part, ...todo].join("/")}`);
12596
12948
  if (inProc(at) && inProc(whole)) return broken ?? whole;
12597
12949
  if (!follow2 && todo.length === 0) return void 0;
12598
- const target = yield { at, check: todo.find((next2) => next2 !== ".") === ".." };
12950
+ const target = yield { at, check: todo.find((next3) => next3 !== ".") === ".." };
12599
12951
  if (typeof target === "string") {
12600
12952
  if (++hops > MAX_HOPS3) return void 0;
12601
12953
  [done4, todo] = throughLink(target, done4, todo);
@@ -12731,18 +13083,18 @@ function timerAfter(ms, fire) {
12731
13083
  return timerAt(performance.now() + ms, fire);
12732
13084
  }
12733
13085
  function repeating(first, every, fire) {
12734
- let next2 = performance.now() + first;
13086
+ let next3 = performance.now() + first;
12735
13087
  let cancel = () => {
12736
13088
  };
12737
13089
  const tick = () => {
12738
13090
  fire();
12739
13091
  if (every <= 0) return;
12740
- next2 += every;
13092
+ next3 += every;
12741
13093
  const now = performance.now();
12742
- if (next2 <= now) next2 += (Math.floor((now - next2) / every) + 1) * every;
12743
- cancel = timerAt(next2, tick);
13094
+ if (next3 <= now) next3 += (Math.floor((now - next3) / every) + 1) * every;
13095
+ cancel = timerAt(next3, tick);
12744
13096
  };
12745
- cancel = timerAt(next2, tick);
13097
+ cancel = timerAt(next3, tick);
12746
13098
  return () => cancel();
12747
13099
  }
12748
13100
 
@@ -13033,11 +13385,11 @@ var LockTable = class {
13033
13385
  }
13034
13386
  changed(key, signal) {
13035
13387
  return new Promise((resolve2, reject) => {
13036
- const set2 = this.wakers.get(key) ?? /* @__PURE__ */ new Set();
13037
- this.wakers.set(key, set2);
13388
+ const set3 = this.wakers.get(key) ?? /* @__PURE__ */ new Set();
13389
+ this.wakers.set(key, set3);
13038
13390
  const done4 = () => {
13039
- set2.delete(wake);
13040
- if (set2.size === 0 && this.wakers.get(key) === set2) this.wakers.delete(key);
13391
+ set3.delete(wake);
13392
+ if (set3.size === 0 && this.wakers.get(key) === set3) this.wakers.delete(key);
13041
13393
  signal.removeEventListener("abort", abort);
13042
13394
  };
13043
13395
  const wake = () => {
@@ -13048,7 +13400,7 @@ var LockTable = class {
13048
13400
  done4();
13049
13401
  reject(new KernelError("EINTR"));
13050
13402
  };
13051
- set2.add(wake);
13403
+ set3.add(wake);
13052
13404
  signal.addEventListener("abort", abort, { once: true });
13053
13405
  });
13054
13406
  }
@@ -13577,10 +13929,10 @@ var LoopbackWebSocket = class {
13577
13929
  this.messages = {
13578
13930
  [Symbol.asyncIterator]: async function* () {
13579
13931
  for (; ; ) {
13580
- const next2 = queue.shift();
13581
- if (next2 !== void 0) {
13582
- taken(next2);
13583
- yield next2;
13932
+ const next3 = queue.shift();
13933
+ if (next3 !== void 0) {
13934
+ taken(next3);
13935
+ yield next3;
13584
13936
  continue;
13585
13937
  }
13586
13938
  if (ended) return;
@@ -13690,9 +14042,9 @@ var WsHandles = class {
13690
14042
  open = /* @__PURE__ */ new Map();
13691
14043
  next;
13692
14044
  local;
13693
- constructor(transport, next2, local) {
14045
+ constructor(transport, next3, local) {
13694
14046
  this.transport = transport;
13695
- this.next = next2;
14047
+ this.next = next3;
13696
14048
  this.local = local;
13697
14049
  }
13698
14050
  owns(handle) {
@@ -13754,9 +14106,9 @@ var WsHandles = class {
13754
14106
  }
13755
14107
  async recv(socket, entry) {
13756
14108
  try {
13757
- const next2 = await entry.messages.next();
13758
- if (!next2.done) {
13759
- return typeof next2.value === "string" ? { ok: true, kind: "json", json: { text: next2.value } } : { ok: true, kind: "bytes", bytes: next2.value };
14109
+ const next3 = await entry.messages.next();
14110
+ if (!next3.done) {
14111
+ return typeof next3.value === "string" ? { ok: true, kind: "json", json: { text: next3.value } } : { ok: true, kind: "bytes", bytes: next3.value };
13760
14112
  }
13761
14113
  const closed = await socket.closed;
13762
14114
  return {
@@ -13804,11 +14156,49 @@ var HTTP_OPS = [
13804
14156
  "net-open",
13805
14157
  "net-head",
13806
14158
  "net-read",
14159
+ "net-write",
14160
+ "net-end",
13807
14161
  "net-close",
13808
14162
  "net-traits",
13809
14163
  ...WS_OPS
13810
14164
  ];
13811
- var EMPTY = new Uint8Array(0);
14165
+ var Upload = class {
14166
+ queue = [];
14167
+ ended = false;
14168
+ wake;
14169
+ write(bytes) {
14170
+ if (this.ended) return Promise.resolve(false);
14171
+ return new Promise((taken) => {
14172
+ this.queue.push({ bytes, taken });
14173
+ this.wake?.();
14174
+ });
14175
+ }
14176
+ end() {
14177
+ this.ended = true;
14178
+ this.wake?.();
14179
+ }
14180
+ cancel() {
14181
+ this.ended = true;
14182
+ for (const { taken } of this.queue.splice(0)) taken(false);
14183
+ this.wake?.();
14184
+ }
14185
+ async *[Symbol.asyncIterator]() {
14186
+ for (; ; ) {
14187
+ const next3 = this.queue.shift();
14188
+ if (next3) {
14189
+ next3.taken(true);
14190
+ yield next3.bytes;
14191
+ continue;
14192
+ }
14193
+ if (this.ended) return;
14194
+ await new Promise((resolve2) => {
14195
+ this.wake = resolve2;
14196
+ });
14197
+ this.wake = void 0;
14198
+ }
14199
+ }
14200
+ };
14201
+ var EMPTY2 = new Uint8Array(0);
13812
14202
  function message2(e) {
13813
14203
  return e instanceof Error ? e.message : String(e);
13814
14204
  }
@@ -13838,6 +14228,8 @@ var HttpHandles = class {
13838
14228
  if (req.op === "net-open") return Promise.resolve(this.opened(req));
13839
14229
  if (req.op === "net-head") return this.head(req.handle);
13840
14230
  if (req.op === "net-read") return this.read(req.handle, req.max);
14231
+ if (req.op === "net-write") return this.write(req.handle, req.bytes);
14232
+ if (req.op === "net-end") return Promise.resolve(this.end(req.handle));
13841
14233
  if (req.op === "net-traits") return Promise.resolve(this.traits(req.url));
13842
14234
  return this.close(req.handle);
13843
14235
  }
@@ -13856,19 +14248,37 @@ var HttpHandles = class {
13856
14248
  return result;
13857
14249
  }
13858
14250
  opened(req) {
14251
+ if (req.stream && (this.local?.port(req.url) !== void 0 || !this.transport.traits.streamingBodies)) {
14252
+ return { ok: false, errno: "ENOTSUP", message: "this transport takes whole request bodies" };
14253
+ }
13859
14254
  return { ok: true, kind: "json", json: this.start(req).handle };
13860
14255
  }
14256
+ async write(handle, bytes) {
14257
+ const upload = this.open.get(handle)?.upload;
14258
+ if (!upload) return { ok: false, errno: "EBADF", message: `no upload ${handle}` };
14259
+ if (!await upload.write(bytes)) {
14260
+ return { ok: false, errno: "EPIPE", message: "the request no longer takes a body" };
14261
+ }
14262
+ return { ok: true, kind: "void" };
14263
+ }
14264
+ end(handle) {
14265
+ const upload = this.open.get(handle)?.upload;
14266
+ if (!upload) return { ok: false, errno: "EBADF", message: `no upload ${handle}` };
14267
+ upload.end();
14268
+ return { ok: true, kind: "void" };
14269
+ }
13861
14270
  start(req) {
13862
14271
  const handle = ++this.nextHandle;
13863
14272
  const abort = new AbortController();
13864
14273
  const entry = {
13865
14274
  abort,
13866
14275
  head: Promise.resolve({ ok: true, kind: "void" }),
13867
- body: once(EMPTY),
13868
- left: EMPTY,
14276
+ body: once(EMPTY2),
14277
+ left: EMPTY2,
13869
14278
  done: false,
13870
14279
  reading: Promise.resolve(),
13871
- cancel: async () => void 0
14280
+ cancel: async () => void 0,
14281
+ ...req.stream ? { upload: new Upload() } : {}
13872
14282
  };
13873
14283
  this.open.set(handle, entry);
13874
14284
  entry.head = this.respond(handle, req, entry);
@@ -13882,7 +14292,7 @@ var HttpHandles = class {
13882
14292
  url: target,
13883
14293
  method: req.method,
13884
14294
  headers: req.headers,
13885
- ...req.body ? { body: req.body } : {},
14295
+ ...entry.upload ? { bodyStream: entry.upload } : req.body ? { body: req.body } : {},
13886
14296
  signal: entry.abort.signal
13887
14297
  };
13888
14298
  const response = port === void 0 ? await this.transport.fetch(request2, this.owner) : await this.local.fetch(request2, port);
@@ -13893,6 +14303,7 @@ var HttpHandles = class {
13893
14303
  const head = this.headOf(handle, shown, response);
13894
14304
  return response.redirected ? { ...head, json: { ...head.json, redirected: true } } : head;
13895
14305
  } catch (e) {
14306
+ entry.upload?.cancel();
13896
14307
  const text = message2(e);
13897
14308
  if (port !== void 0) return { ok: false, errno: codeOf2(e), message: text };
13898
14309
  const status = e.status;
@@ -13929,9 +14340,9 @@ var HttpHandles = class {
13929
14340
  async take(entry, max) {
13930
14341
  if (entry.left.length === 0 && !entry.done) {
13931
14342
  try {
13932
- const next2 = await entry.body.next();
13933
- if (next2.done) entry.done = true;
13934
- else entry.left = next2.value;
14343
+ const next3 = await entry.body.next();
14344
+ if (next3.done) entry.done = true;
14345
+ else entry.left = next3.value;
13935
14346
  } catch (e) {
13936
14347
  entry.done = true;
13937
14348
  const timedOut = e?.code === "ETIMEDOUT";
@@ -13950,6 +14361,7 @@ var HttpHandles = class {
13950
14361
  const entry = this.open.get(handle);
13951
14362
  if (!entry) return { ok: false, errno: "EBADF", message: `no request ${handle}` };
13952
14363
  this.open.delete(handle);
14364
+ entry.upload?.cancel();
13953
14365
  entry.abort.abort();
13954
14366
  await entry.cancel().catch(() => void 0);
13955
14367
  return { ok: true, kind: "void" };
@@ -14304,12 +14716,12 @@ var KernelTty = class {
14304
14716
  const chunks = [];
14305
14717
  let n = 0;
14306
14718
  while (this.readable.length > 0 && n < max) {
14307
- const next2 = this.readable[0];
14308
- if (next2 === null || next2 === void 0) break;
14309
- const take = next2.subarray(0, max - n);
14719
+ const next3 = this.readable[0];
14720
+ if (next3 === null || next3 === void 0) break;
14721
+ const take = next3.subarray(0, max - n);
14310
14722
  chunks.push(take);
14311
14723
  n += take.length;
14312
- if (take.length < next2.length) this.readable[0] = next2.subarray(take.length);
14724
+ if (take.length < next3.length) this.readable[0] = next3.subarray(take.length);
14313
14725
  else this.readable.shift();
14314
14726
  if (this.canonical) break;
14315
14727
  }
@@ -14406,11 +14818,11 @@ var PtyPair = class {
14406
14818
  const chunks = [];
14407
14819
  let n = 0;
14408
14820
  while (this.queue.length > 0 && n < max - status) {
14409
- const next2 = this.queue[0];
14410
- const take = next2.subarray(0, max - status - n);
14821
+ const next3 = this.queue[0];
14822
+ const take = next3.subarray(0, max - status - n);
14411
14823
  chunks.push(take);
14412
14824
  n += take.length;
14413
- if (take.length < next2.length) this.queue[0] = next2.subarray(take.length);
14825
+ if (take.length < next3.length) this.queue[0] = next3.subarray(take.length);
14414
14826
  else this.queue.shift();
14415
14827
  }
14416
14828
  const out = new Uint8Array(status + n);
@@ -14730,8 +15142,8 @@ var Semaphore = class {
14730
15142
  this.mode = mode;
14731
15143
  }
14732
15144
  post() {
14733
- const next2 = this.waiters.shift();
14734
- if (next2) next2.grant();
15145
+ const next3 = this.waiters.shift();
15146
+ if (next3) next3.grant();
14735
15147
  else if (this.value >= SEM_VALUE_MAX) throw new KernelError("EOVERFLOW");
14736
15148
  else this.value++;
14737
15149
  }
@@ -15159,9 +15571,9 @@ var VfsNode = class {
15159
15571
  if (dirty && contents2 !== void 0) this.markDirty();
15160
15572
  }
15161
15573
  serial(op) {
15162
- const next2 = this.queue.then(op);
15163
- this.queue = next2.catch(() => void 0);
15164
- return next2;
15574
+ const next3 = this.queue.then(op);
15575
+ this.queue = next3.catch(() => void 0);
15576
+ return next3;
15165
15577
  }
15166
15578
  revoke() {
15167
15579
  this.revoked = true;
@@ -15583,9 +15995,9 @@ var VfsNodes = class {
15583
15995
  }
15584
15996
  for (const [p] of moved2) this.byPath.delete(p);
15585
15997
  for (const [p, node] of moved2) {
15586
- const next2 = to + p.slice(from.length);
15587
- if (node.path === p) node.renamedTo(next2);
15588
- this.byPath.set(next2, node);
15998
+ const next3 = to + p.slice(from.length);
15999
+ if (node.path === p) node.renamedTo(next3);
16000
+ this.byPath.set(next3, node);
15589
16001
  }
15590
16002
  }
15591
16003
  };
@@ -15960,6 +16372,7 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
15960
16372
  "tty-pgrp-get",
15961
16373
  "tty-pgrp-set",
15962
16374
  "sig-mask",
16375
+ "sig-waiting",
15963
16376
  "sig-pause",
15964
16377
  ...SOCKET_OPS,
15965
16378
  ...EPOLL_OPS,
@@ -16073,16 +16486,8 @@ var WasmProcess = class {
16073
16486
  if (sig === SIG.STOP) return this.stop(sig);
16074
16487
  const bit = sigbit(sig);
16075
16488
  if (this.ignores(bit)) return "ignore";
16076
- if (((this.options.blockedBits?.() ?? 0) & bit) !== 0) {
16077
- const tid = this.options.unblockedThread?.(sig);
16078
- if (tid === void 0) {
16079
- this.options.onPending?.(sig);
16080
- return "deliver";
16081
- }
16082
- if (this.caught & bit) {
16083
- this.options.threadKill?.(tid, sig);
16084
- return "deliver";
16085
- }
16489
+ if (((this.options.blockedBits?.() ?? 0) & bit) !== 0 && this.routeBlocked(sig, bit)) {
16490
+ return "deliver";
16086
16491
  }
16087
16492
  if (!(this.caught & bit)) {
16088
16493
  const action = defaultAction(sig);
@@ -16099,6 +16504,17 @@ var WasmProcess = class {
16099
16504
  blocked2.abort();
16100
16505
  return "deliver";
16101
16506
  }
16507
+ routeBlocked(sig, bit) {
16508
+ const waiter = this.waiterFor(bit);
16509
+ const tid = waiter ?? this.options.unblockedThread?.(sig);
16510
+ if (tid === void 0 || tid === 1) {
16511
+ this.options.onPending?.(sig);
16512
+ return true;
16513
+ }
16514
+ if (waiter === void 0 && !(this.caught & bit)) return false;
16515
+ this.options.threadKill?.(tid, sig);
16516
+ return true;
16517
+ }
16102
16518
  async spawn(req) {
16103
16519
  const { file, argv, argv0, env, cwd, chdir, stdio, inherit: inherit2, exec, pgid, session, deathSignal } = req;
16104
16520
  if (deathSignal !== void 0 && (exec || !isSignal(deathSignal))) {
@@ -16179,6 +16595,16 @@ var WasmProcess = class {
16179
16595
  }
16180
16596
  blocks = /* @__PURE__ */ new WeakMap();
16181
16597
  sizesRead = /* @__PURE__ */ new WeakMap();
16598
+ sigwaiters = /* @__PURE__ */ new Map();
16599
+ setSigwaiter(tid, set3) {
16600
+ if (set3 === 0) this.sigwaiters.delete(tid);
16601
+ else this.sigwaiters.set(tid, set3);
16602
+ }
16603
+ waiterFor(bit) {
16604
+ if (((this.sigwaiters.get(1) ?? 0) & bit) !== 0) return 1;
16605
+ for (const [tid, set3] of this.sigwaiters) if (set3 & bit) return tid;
16606
+ return void 0;
16607
+ }
16182
16608
  async syscall(req, cancelled, block) {
16183
16609
  this.options.onSyscall?.(req);
16184
16610
  const started = performance.now();
@@ -16435,18 +16861,18 @@ var WasmProcess = class {
16435
16861
  }
16436
16862
  if (this.writesAt(req) && this.options.readonlyAt?.(path)) throw new KernelError("EROFS");
16437
16863
  const open = { ...req, path };
16438
- const plain = this.plainMode(req.mode);
16864
+ const plain2 = this.plainMode(req.mode);
16439
16865
  if (req.exclusive) {
16440
16866
  await nodes.createExclusive(path, this.newOwner(req.mode));
16441
- if (plain !== void 0) await fs.chmod?.(path, plain);
16867
+ if (plain2 !== void 0) await fs.chmod?.(path, plain2);
16442
16868
  this.fds.installAt(fd, this.openVfsFile(open, void 0, fs));
16443
16869
  } else if (req.create) {
16444
16870
  await nodes.onEntry(path, async () => {
16445
- const marks = this.options.owners || plain !== void 0;
16871
+ const marks = this.options.owners || plain2 !== void 0;
16446
16872
  const fresh2 = marks && !await present2(fs, path, true);
16447
16873
  this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path), fs));
16448
16874
  await this.fds.get(fd).file.stat?.();
16449
- if (fresh2 && plain !== void 0) await fs.chmod?.(path, plain);
16875
+ if (fresh2 && plain2 !== void 0) await fs.chmod?.(path, plain2);
16450
16876
  else if (fresh2) {
16451
16877
  await fs.chown?.(path, { ...this.newOwner(req.mode), kind: "new" });
16452
16878
  }
@@ -17087,6 +17513,9 @@ var WasmProcess = class {
17087
17513
  case "dl-unlock":
17088
17514
  this.unlockDl(req.owner);
17089
17515
  return { ok: true, kind: "void" };
17516
+ case "sig-waiting":
17517
+ this.setSigwaiter(req.tid, req.set);
17518
+ return { ok: true, kind: "void" };
17090
17519
  case "sig-mask":
17091
17520
  this.caught = req.caught;
17092
17521
  this.ignored = req.ignored;
@@ -17154,8 +17583,8 @@ var WasmProcess = class {
17154
17583
  };
17155
17584
  withDl(state) {
17156
17585
  if (this.dlLog.length === 0) return state;
17157
- const { next: next2, handle, free, live } = this.closures;
17158
- const dl = { log: [...this.dlLog], next: next2, handle, free: [...free], live: [...live] };
17586
+ const { next: next3, handle, free, live } = this.closures;
17587
+ const dl = { log: [...this.dlLog], next: next3, handle, free: [...free], live: [...live] };
17159
17588
  return { ...state, dl };
17160
17589
  }
17161
17590
  seedDl(dl) {
@@ -17166,8 +17595,8 @@ var WasmProcess = class {
17166
17595
  this.closures.free.push(...dl.free);
17167
17596
  for (const index of dl.live) this.closures.live.add(index);
17168
17597
  }
17169
- allocateClosure(floor) {
17170
- const index = this.closures.free.pop() ?? Math.max(floor, this.closures.next);
17598
+ allocateClosure(floor2) {
17599
+ const index = this.closures.free.pop() ?? Math.max(floor2, this.closures.next);
17171
17600
  this.closures.next = Math.max(this.closures.next, index + 1);
17172
17601
  this.closures.live.add(index);
17173
17602
  this.dlLog.push({ kind: "closure", index });
@@ -17219,8 +17648,8 @@ var WasmProcess = class {
17219
17648
  wakeDl() {
17220
17649
  for (const waiter of [...this.dlLock.waiters]) waiter.wake(false);
17221
17650
  }
17222
- reserveSlots(floor, size) {
17223
- const base = Math.max(floor, this.closures.next);
17651
+ reserveSlots(floor2, size) {
17652
+ const base = Math.max(floor2, this.closures.next);
17224
17653
  this.closures.next = base + size;
17225
17654
  return base;
17226
17655
  }
@@ -17277,6 +17706,7 @@ var WASM_MAX_THREADS = 256;
17277
17706
  // src/kernel/host.ts
17278
17707
  var CRASHED = 70;
17279
17708
  var KILL_GRACE_MS = 2e3;
17709
+ var KILL_HARD_MS = 5e3;
17280
17710
  var descIds = /* @__PURE__ */ new WeakMap();
17281
17711
  var nextDescId = 1;
17282
17712
  function descId(file) {
@@ -17303,16 +17733,21 @@ function inheritedFds(fds) {
17303
17733
  var Dying = class {
17304
17734
  ending;
17305
17735
  timer;
17736
+ hard;
17306
17737
  busy = 0;
17307
17738
  finish;
17308
17739
  start(code, sig, header3, process, finish) {
17309
17740
  if (this.ending) return;
17310
17741
  this.ending = { code, sig };
17311
17742
  this.finish = finish;
17743
+ this.hard = setTimeout(finish, KILL_HARD_MS);
17312
17744
  this.arm();
17313
17745
  Atomics.store(header3, SAB_I_KILLED, code);
17314
17746
  process.die();
17315
17747
  }
17748
+ serve(req, work) {
17749
+ return this.ending && isWasmSyscall(req) ? work() : this.serving(work);
17750
+ }
17316
17751
  async serving(work) {
17317
17752
  this.busy++;
17318
17753
  clearTimeout(this.timer);
@@ -17329,6 +17764,7 @@ var Dying = class {
17329
17764
  }
17330
17765
  clear() {
17331
17766
  clearTimeout(this.timer);
17767
+ clearTimeout(this.hard);
17332
17768
  }
17333
17769
  status(reported2) {
17334
17770
  if (this.ending) return [this.ending.code, this.ending.sig];
@@ -17340,8 +17776,8 @@ function killThread(thread, caught, sig) {
17340
17776
  const held = (Atomics.load(thread.header, SAB_I_BLOCKED) & sigbit(sig)) !== 0;
17341
17777
  if (!held && (caught & sigbit(sig)) === 0) return false;
17342
17778
  Atomics.or(thread.header, SAB_I_SIGNALS, sigbit(sig));
17343
- if (held) return true;
17344
17779
  Atomics.notify(thread.header, SAB_I_SIGNALS);
17780
+ if (held) return true;
17345
17781
  const blocked2 = thread.interrupt;
17346
17782
  thread.interrupt = new AbortController();
17347
17783
  blocked2.abort();
@@ -17437,7 +17873,7 @@ function inherited(opts) {
17437
17873
  ...opts.cred ? { cred: opts.cred } : {}
17438
17874
  };
17439
17875
  }
17440
- function holdingToken(opts, holds, caller) {
17876
+ function holdingToken(opts, holds2, caller) {
17441
17877
  return mintSyncFsToken({
17442
17878
  ...opts.owners ? caller : { umask: caller.umask },
17443
17879
  ...opts.dac ? { dac: opts.dac, pid: opts.pid, created: caller.created } : {},
@@ -17448,13 +17884,13 @@ function holdingToken(opts, holds, caller) {
17448
17884
  noLinks: opts.noLinks,
17449
17885
  cwd: opts.cwd,
17450
17886
  ...opts.statfs ? { statfs: opts.statfs } : {},
17451
- hold: (path, on, open, real, handle) => holds.hold(path, on, open, real, handle),
17452
- keptFor: (handle) => holds.keptFor(handle),
17453
- unlinked: (path) => holds.keptAt(path),
17454
- gate: (path, op) => opts.unlinkGate && holds.holding(path) ? opts.unlinkGate.run(holds.keyOf(path), op) : op(),
17455
- own: (path, op) => holds.own(path, op),
17456
- revoked: (path) => holds.isRevoked(path),
17457
- renamed: (from, to) => holds.renamed(from, to)
17887
+ hold: (path, on, open, real, handle) => holds2.hold(path, on, open, real, handle),
17888
+ keptFor: (handle) => holds2.keptFor(handle),
17889
+ unlinked: (path) => holds2.keptAt(path),
17890
+ gate: (path, op) => opts.unlinkGate && holds2.holding(path) ? opts.unlinkGate.run(holds2.keyOf(path), op) : op(),
17891
+ own: (path, op) => holds2.own(path, op),
17892
+ revoked: (path) => holds2.isRevoked(path),
17893
+ renamed: (from, to) => holds2.renamed(from, to)
17458
17894
  });
17459
17895
  }
17460
17896
  function linksOf(opts) {
@@ -17528,6 +17964,9 @@ function threadInit(opts, program, sab, cred, thread, main) {
17528
17964
  signals: main
17529
17965
  };
17530
17966
  }
17967
+ function settleExit(exit, settle3, code) {
17968
+ void exit.catch(() => void 0).then(() => settle3(code & 255));
17969
+ }
17531
17970
  function spawnWasmProcess(opts) {
17532
17971
  const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
17533
17972
  const header3 = new Int32Array(sab, 0, SAB_HEADER_I32);
@@ -17554,19 +17993,20 @@ function spawnWasmProcess(opts) {
17554
17993
  )
17555
17994
  );
17556
17995
  publishStops(process, header3);
17557
- const holds = new HeldPaths();
17558
- opts.held?.add(holds);
17559
- const token = holdingToken(opts, holds, {
17996
+ const holds2 = new HeldPaths();
17997
+ opts.held?.add(holds2);
17998
+ const token = holdingToken(opts, holds2, {
17560
17999
  cred: () => process.cred,
17561
18000
  umask: () => process.umask,
17562
18001
  created: created3,
17563
- holdsKept: async (path) => path.includes(`/${UNLINKED_DIR}/`) && (holds.keeps(path) || await process.holds(path))
18002
+ holdsKept: async (path) => path.includes(`/${UNLINKED_DIR}/`) && (holds2.keeps(path) || await process.holds(path))
17564
18003
  });
17565
- process.keptPath = (path) => holds.keptAt(path)?.hidden;
17566
- process.keptHandle = (handle2) => holds.keptFor(handle2)?.hidden;
18004
+ process.keptPath = (path) => holds2.keptAt(path)?.hidden;
18005
+ process.keptHandle = (handle2) => holds2.keptFor(handle2)?.hidden;
17567
18006
  const worker = opts.createWorker();
17568
18007
  const dying = new Dying();
17569
- const dispatch2 = (req, block) => dying.serving(
18008
+ const dispatch2 = (req, block) => dying.serve(
18009
+ req,
17570
18010
  () => isWasmSyscall(req) ? process.syscall(req, void 0, block) : dispatchSyncFs(req)
17571
18011
  );
17572
18012
  const clocks = new ProcessClocks(sab, opts.program.abi !== "js", process);
@@ -17589,12 +18029,9 @@ function spawnWasmProcess(opts) {
17589
18029
  responder.dispose();
17590
18030
  lane?.dispose();
17591
18031
  revokeSyncFsToken(token);
17592
- opts.held?.delete(holds);
18032
+ opts.held?.delete(holds2);
17593
18033
  worker.terminate();
17594
- void process.exit().then(
17595
- () => settle3(code),
17596
- () => settle3(code)
17597
- );
18034
+ settleExit(process.exit(), settle3, code);
17598
18035
  };
17599
18036
  const exitNow = (code) => {
17600
18037
  queueMicrotask(() => finish(...dying.status(code)));
@@ -17638,7 +18075,7 @@ function spawnWasmProcess(opts) {
17638
18075
  const tsignals = threadSignalsOf(tsab);
17639
18076
  if (thread.blocked) storeBlocked(tsignals.header, thread.blocked);
17640
18077
  threadSignals.set(thread.tid, tsignals);
17641
- const tdispatch = (req, block) => dying.serving(() => threadCall(process, req, threadCancel(tsignals), block));
18078
+ const tdispatch = (req, block) => dying.serve(req, () => threadCall(process, req, threadCancel(tsignals), block));
17642
18079
  const tresponder = attachSyncSabResponder(tw, tsab, token, {
17643
18080
  dispatch: clocks.timed(clocks.thread(thread.tid, tsab), tdispatch)
17644
18081
  });
@@ -18108,10 +18545,10 @@ var Automaton = class {
18108
18545
  let node = 0;
18109
18546
  for (const byte of bytes) {
18110
18547
  const key = node * 256 + byte;
18111
- let next2 = this.edges.get(key);
18112
- if (next2 === void 0) {
18113
- next2 = this.depth.length;
18114
- this.edges.set(key, next2);
18548
+ let next3 = this.edges.get(key);
18549
+ if (next3 === void 0) {
18550
+ next3 = this.depth.length;
18551
+ this.edges.set(key, next3);
18115
18552
  this.fail.push(0);
18116
18553
  this.depth.push(this.depth[node] + 1);
18117
18554
  this.children.push(0);
@@ -18119,7 +18556,7 @@ var Automaton = class {
18119
18556
  this.out.push([]);
18120
18557
  this.dict.push(-1);
18121
18558
  }
18122
- node = next2;
18559
+ node = next3;
18123
18560
  }
18124
18561
  const id2 = this.lengths.length;
18125
18562
  this.lengths.push(bytes.length);
@@ -18155,8 +18592,8 @@ var Automaton = class {
18155
18592
  step(state, byte) {
18156
18593
  let current = state;
18157
18594
  for (; ; ) {
18158
- const next2 = this.edges.get(current * 256 + byte);
18159
- if (next2 !== void 0) return next2;
18595
+ const next3 = this.edges.get(current * 256 + byte);
18596
+ if (next3 !== void 0) return next3;
18160
18597
  if (current === 0) return 0;
18161
18598
  current = this.fail[current];
18162
18599
  }
@@ -18184,7 +18621,7 @@ var RANK = {
18184
18621
  };
18185
18622
  var CR2 = 13;
18186
18623
  var LF2 = 10;
18187
- var EMPTY2 = new Uint8Array(0);
18624
+ var EMPTY3 = new Uint8Array(0);
18188
18625
  function register(automaton, sources, ids, bytes, source) {
18189
18626
  const key = Array.from(bytes).join(",");
18190
18627
  let id2 = ids.get(key);
@@ -18310,7 +18747,7 @@ function pass(c, input, final) {
18310
18747
  const partials = [];
18311
18748
  scanMain(c, input, best, partials);
18312
18749
  scanWrapped(c, input, best, partials);
18313
- const holds = final ? [] : [...new Set(partials)].sort((a, b) => a - b);
18750
+ const holds2 = final ? [] : [...new Set(partials)].sort((a, b) => a - b);
18314
18751
  const starts = [...best.keys()].sort((a, b) => a - b);
18315
18752
  const parts2 = [];
18316
18753
  const hits = /* @__PURE__ */ new Map();
@@ -18320,16 +18757,16 @@ function pass(c, input, final) {
18320
18757
  let h = 0;
18321
18758
  for (; ; ) {
18322
18759
  while (s < starts.length && starts[s] < pos) s++;
18323
- while (h < holds.length && holds[h] < pos) h++;
18324
- const hold = h < holds.length ? holds[h] : Infinity;
18325
- const next2 = s < starts.length ? starts[s] : Infinity;
18326
- if (hold <= next2) {
18760
+ while (h < holds2.length && holds2[h] < pos) h++;
18761
+ const hold = h < holds2.length ? holds2[h] : Infinity;
18762
+ const next3 = s < starts.length ? starts[s] : Infinity;
18763
+ if (hold <= next3) {
18327
18764
  cut = Math.min(hold, input.length);
18328
18765
  break;
18329
18766
  }
18330
- const candidate = best.get(next2);
18767
+ const candidate = best.get(next3);
18331
18768
  parts2.push(
18332
- input.subarray(pos, next2),
18769
+ input.subarray(pos, next3),
18333
18770
  c.replacements[candidate.secret][candidate.form === "raw" ? 0 : 1]
18334
18771
  );
18335
18772
  count(c, hits, candidate);
@@ -18364,12 +18801,12 @@ function compile(secrets, options = {}) {
18364
18801
  return { text: decoder4.decode(bytes), hits };
18365
18802
  },
18366
18803
  stream() {
18367
- let pending = EMPTY2;
18804
+ let pending = EMPTY3;
18368
18805
  const hits = /* @__PURE__ */ new Map();
18369
18806
  const run2 = (final) => {
18370
18807
  if (c.secrets.length === 0 || pending.length === 0) {
18371
18808
  const out = pending;
18372
- pending = EMPTY2;
18809
+ pending = EMPTY3;
18373
18810
  return out;
18374
18811
  }
18375
18812
  const result = pass(c, pending, final);
@@ -18383,7 +18820,7 @@ function compile(secrets, options = {}) {
18383
18820
  return run2(false);
18384
18821
  },
18385
18822
  idle() {
18386
- return pending.length < FRAGMENT ? run2(true) : EMPTY2;
18823
+ return pending.length < FRAGMENT ? run2(true) : EMPTY3;
18387
18824
  },
18388
18825
  end() {
18389
18826
  return run2(true);
@@ -18425,7 +18862,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
18425
18862
  let timer;
18426
18863
  let failed3;
18427
18864
  const waiters = /* @__PURE__ */ new Set();
18428
- const quiet = () => !busy && pending === 0;
18865
+ const quiet2 = () => !busy && pending === 0;
18429
18866
  const wake = () => {
18430
18867
  for (const waiter of [...waiters]) waiter();
18431
18868
  };
@@ -18442,7 +18879,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
18442
18879
  if (failed3 === void 0) failed3 = err;
18443
18880
  }).finally(() => {
18444
18881
  pending -= 1;
18445
- if (quiet()) wake();
18882
+ if (quiet2()) wake();
18446
18883
  });
18447
18884
  };
18448
18885
  const settling = (signal) => new Promise((resolve2, reject) => {
@@ -18460,7 +18897,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
18460
18897
  signal?.addEventListener("abort", stop, { once: true });
18461
18898
  });
18462
18899
  const settled = async () => {
18463
- while (!quiet()) await settling();
18900
+ while (!quiet2()) await settling();
18464
18901
  };
18465
18902
  const changed = async (signal) => {
18466
18903
  const round = new AbortController();
@@ -18476,7 +18913,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
18476
18913
  };
18477
18914
  const turn = async (signal) => {
18478
18915
  for (let waited = false; ; waited = true) {
18479
- if (quiet() && (pollFile(file).writable || !file.changed)) {
18916
+ if (quiet2() && (pollFile(file).writable || !file.changed)) {
18480
18917
  busy = true;
18481
18918
  return;
18482
18919
  }
@@ -18505,7 +18942,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
18505
18942
  ...bound(file, "stat"),
18506
18943
  poll: () => {
18507
18944
  const state = pollFile(file);
18508
- return quiet() ? state : { ...state, writable: false };
18945
+ return quiet2() ? state : { ...state, writable: false };
18509
18946
  },
18510
18947
  changed,
18511
18948
  ...file.tty ? { tty: file.tty } : {},
@@ -18521,7 +18958,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
18521
18958
  await send(stream.push(taken), signal);
18522
18959
  } finally {
18523
18960
  busy = false;
18524
- if (quiet()) wake();
18961
+ if (quiet2()) wake();
18525
18962
  }
18526
18963
  timer = setTimeout(release, idleMs);
18527
18964
  return taken.length;
@@ -18622,15 +19059,15 @@ function isRecord(value) {
18622
19059
  return typeof value === "object" && value !== null && !Array.isArray(value);
18623
19060
  }
18624
19061
  function serialized(reply) {
18625
- let plain;
19062
+ let plain2;
18626
19063
  try {
18627
19064
  const text = JSON.stringify(reply);
18628
- plain = text === void 0 ? void 0 : JSON.parse(text);
19065
+ plain2 = text === void 0 ? void 0 : JSON.parse(text);
18629
19066
  } catch (err) {
18630
19067
  return JSON.stringify(errorReply("EIO", lineOf2(err)));
18631
19068
  }
18632
- if (!isRecord(plain)) return JSON.stringify(errorReply("EIO", "the vault gave no reply"));
18633
- const { v: _, ...rest2 } = plain;
19069
+ if (!isRecord(plain2)) return JSON.stringify(errorReply("EIO", "the vault gave no reply"));
19070
+ const { v: _, ...rest2 } = plain2;
18634
19071
  return JSON.stringify({ v: 1, ...rest2 });
18635
19072
  }
18636
19073
  function lineOf2(err) {
@@ -19649,22 +20086,22 @@ function keepWatching(loop, signal) {
19649
20086
  const stop = () => session2.abort();
19650
20087
  signal.addEventListener("abort", stop);
19651
20088
  let timer;
19652
- const quiet = () => {
20089
+ const quiet2 = () => {
19653
20090
  clearTimeout(timer);
19654
20091
  timer = setTimeout(stop, silenceMs);
19655
20092
  };
19656
20093
  let opened2 = false;
19657
20094
  try {
19658
20095
  const client = await loop.connect();
19659
- quiet();
20096
+ quiet2();
19660
20097
  await client.watch(
19661
20098
  session2.signal,
19662
20099
  (event) => {
19663
- quiet();
20100
+ quiet2();
19664
20101
  if (!event.ping) loop.event(event);
19665
20102
  },
19666
20103
  () => {
19667
- quiet();
20104
+ quiet2();
19668
20105
  opened2 = true;
19669
20106
  up = true;
19670
20107
  loop.up(client);
@@ -20106,9 +20543,9 @@ function tmpfs() {
20106
20543
  let node = root;
20107
20544
  for (const name2 of parts2(path)) {
20108
20545
  if (node.kind !== "directory") throw fsError2("ENOTDIR", path);
20109
- const next2 = node.children.get(name2);
20110
- if (!next2) throw fsError2("ENOENT", path);
20111
- node = next2;
20546
+ const next3 = node.children.get(name2);
20547
+ if (!next3) throw fsError2("ENOENT", path);
20548
+ node = next3;
20112
20549
  }
20113
20550
  return node;
20114
20551
  }
@@ -20414,6 +20851,28 @@ function foreignImports(bytes) {
20414
20851
  return {};
20415
20852
  }
20416
20853
 
20854
+ // src/refused-command.ts
20855
+ var REFUSED_PATH = "/usr/lib/slicc/refused";
20856
+ var REFUSED_SOURCE = [
20857
+ "export default async function refused(ctx) {",
20858
+ " await ctx.write(2, ctx.argv[1] + '\\n').catch(() => undefined);",
20859
+ " return 126;",
20860
+ "}"
20861
+ ].join("\n");
20862
+ function refusing(command) {
20863
+ if (!command.refused) return command;
20864
+ return {
20865
+ name: command.name,
20866
+ abi: "js",
20867
+ glue: REFUSED_PATH,
20868
+ wasm: REFUSED_PATH,
20869
+ argv0: command.name,
20870
+ args: [`${command.name}: ${command.refused}`],
20871
+ refused: command.refused,
20872
+ shows: command.script ?? command.wasm
20873
+ };
20874
+ }
20875
+
20417
20876
  // src/sudo.ts
20418
20877
  var SUDOERS = "/etc/sudoers";
20419
20878
  var SUDOERS_D = "/etc/sudoers.d";
@@ -20441,13 +20900,13 @@ function splitWords(text) {
20441
20900
  while (piece.lastIndex < text.length) {
20442
20901
  const m = piece.exec(text);
20443
20902
  if (!m) return void 0;
20444
- const [, single, double, escaped, plain, space] = m;
20903
+ const [, single, double, escaped, plain2, space] = m;
20445
20904
  if (space !== void 0) {
20446
20905
  if (word !== void 0) words2.push(word);
20447
20906
  word = void 0;
20448
20907
  continue;
20449
20908
  }
20450
- word = (word ?? "") + (single ?? (double !== void 0 ? unescaped(double) : escaped ?? plain));
20909
+ word = (word ?? "") + (single ?? (double !== void 0 ? unescaped(double) : escaped ?? plain2));
20451
20910
  }
20452
20911
  if (word !== void 0) words2.push(word);
20453
20912
  return words2;
@@ -20479,7 +20938,7 @@ function parseRule(line) {
20479
20938
  if (!cmnd?.[0]?.startsWith("/")) return void 0;
20480
20939
  return { ...rule, cmnd };
20481
20940
  }
20482
- function flags(parsed2, arg, next2) {
20941
+ function flags(parsed2, arg, next3) {
20483
20942
  for (let j = 1; j < arg.length; j++) {
20484
20943
  const flag = arg[j];
20485
20944
  if (flag === "n") parsed2.nonInteractive = true;
@@ -20487,7 +20946,7 @@ function flags(parsed2, arg, next2) {
20487
20946
  else if (flag !== "u") return `sudo: invalid option -- '${flag}'
20488
20947
  ${USAGE}`;
20489
20948
  else {
20490
- const user = arg.slice(j + 1) || next2;
20949
+ const user = arg.slice(j + 1) || next3;
20491
20950
  if (!user) return `sudo: option requires an argument -- 'u'
20492
20951
  ${USAGE}`;
20493
20952
  parsed2.user = user;
@@ -20937,9 +21396,9 @@ function appendedRoots(given) {
20937
21396
  return [...roots];
20938
21397
  }
20939
21398
  function plainEnv(words2) {
20940
- const next2 = words2[1];
20941
- if (baseName2(words2[0] ?? "") !== "env" || next2 === void 0) return false;
20942
- return next2 === "-S" || !next2.startsWith("-") && !next2.includes("=");
21399
+ const next3 = words2[1];
21400
+ if (baseName2(words2[0] ?? "") !== "env" || next3 === void 0) return false;
21401
+ return next3 === "-S" || !next3.startsWith("-") && !next3.includes("=");
20943
21402
  }
20944
21403
  function shebangCommand(words2) {
20945
21404
  if (!plainEnv(words2)) {
@@ -20963,9 +21422,9 @@ function expandDefaults(defaults, env) {
20963
21422
  for (const [key, value] of Object.entries(defaults ?? {})) {
20964
21423
  let missing4 = false;
20965
21424
  const expanded = value.replace(ENV_REFERENCE, (_, name2) => {
20966
- const set2 = Object.hasOwn(env, name2) ? env[name2] : void 0;
20967
- if (set2 === void 0) missing4 = true;
20968
- return set2 ?? "";
21425
+ const set3 = Object.hasOwn(env, name2) ? env[name2] : void 0;
21426
+ if (set3 === void 0) missing4 = true;
21427
+ return set3 ?? "";
20969
21428
  });
20970
21429
  if (!missing4) out[key] = expanded;
20971
21430
  }
@@ -21190,7 +21649,7 @@ var Launcher = class {
21190
21649
  const holders = [this.held, this.openFiles];
21191
21650
  this.unlinked = new UnlinkedKeeper(
21192
21651
  fs,
21193
- { [Symbol.iterator]: () => holders.flatMap((set2) => [...set2]).values() },
21652
+ { [Symbol.iterator]: () => holders.flatMap((set3) => [...set3]).values() },
21194
21653
  (path) => this.mountRoot(path),
21195
21654
  {
21196
21655
  locks: webLocks(),
@@ -21275,11 +21734,21 @@ var Launcher = class {
21275
21734
  found = this.trustedAppended().then(async (appended2) => {
21276
21735
  const tiers = await this.tiers(scope, appended2);
21277
21736
  return scanCommands(this.scanned, tiers, tiers.length - appended2.length);
21278
- }).then((commands) => commands.set(SUDO_COMMAND.name, SUDO_COMMAND));
21737
+ }).then((commands) => this.refusals(commands).set(SUDO_COMMAND.name, SUDO_COMMAND));
21279
21738
  this.catalogs.set(scope, found);
21280
21739
  }
21281
21740
  return found;
21282
21741
  }
21742
+ refusals(commands) {
21743
+ for (const [name2, command] of commands) {
21744
+ if (!command.refused) continue;
21745
+ commands.set(name2, refusing(command));
21746
+ if (this.warned.has(command.refused)) continue;
21747
+ this.warned.add(command.refused);
21748
+ console.warn(`slicc-kernel: refusing the commands of ${command.refused}`);
21749
+ }
21750
+ return commands;
21751
+ }
21283
21752
  ownCommands(scope) {
21284
21753
  let found = this.ownCatalogs.get(scope);
21285
21754
  if (!found) {
@@ -21354,7 +21823,8 @@ var Launcher = class {
21354
21823
  );
21355
21824
  return {
21356
21825
  byUid: new Map(users.map((account, i) => [account.uid, homes[i]])),
21357
- byBin: new Map(homes.map((home) => [`${home}/bin`, home]))
21826
+ byBin: new Map(homes.map((home) => [`${home}/bin`, home])),
21827
+ owners: new Map(homes.map((home, i) => [home, credOf(users[i])]))
21358
21828
  };
21359
21829
  },
21360
21830
  () => ({ byUid: /* @__PURE__ */ new Map(), byBin: /* @__PURE__ */ new Map() })
@@ -21696,12 +22166,12 @@ var Launcher = class {
21696
22166
  return `${path}:${st.size}:${st.mtime.getTime()}`;
21697
22167
  }
21698
22168
  verified(path, caller, fs = this.base) {
21699
- const plain = () => fs.readFileBuffer(path);
21700
- return this.integrity ? this.integrity.read(path, caller, plain) : plain();
22169
+ const plain2 = () => fs.readFileBuffer(path);
22170
+ return this.integrity ? this.integrity.read(path, caller, plain2) : plain2();
21701
22171
  }
21702
22172
  keyed(path, caller, asked) {
21703
- const plain = async () => this.base.readFileBuffer(path);
21704
- return this.integrity ? this.integrity.readKeyed(path, caller, plain, asked) : plain().then((bytes) => ({ bytes }));
22173
+ const plain2 = async () => this.base.readFileBuffer(path);
22174
+ return this.integrity ? this.integrity.readKeyed(path, caller, plain2, asked) : plain2().then((bytes) => ({ bytes }));
21705
22175
  }
21706
22176
  cached(cache, key, load) {
21707
22177
  const hit = cache.get(key);
@@ -21772,6 +22242,9 @@ var Launcher = class {
21772
22242
  if (target.abi === "js" && target.wasm === SUDO_PATH) {
21773
22243
  return { abi: "js", glue: SUDO_SOURCE, path: SUDO_PATH };
21774
22244
  }
22245
+ if (target.abi === "js" && target.wasm === REFUSED_PATH) {
22246
+ return { abi: "js", glue: REFUSED_SOURCE, path: REFUSED_PATH };
22247
+ }
21775
22248
  const { asked } = target;
21776
22249
  if (target.abi === "js") {
21777
22250
  return { abi: "js", glue: await this.text(target.wasm, caller, asked), path: target.wasm };
@@ -21841,7 +22314,7 @@ var Launcher = class {
21841
22314
  });
21842
22315
  return {
21843
22316
  source: {
21844
- read: (path, plain) => integrity.readSource(path, caller(), plain),
22317
+ read: (path, plain2) => integrity.readSource(path, caller(), plain2),
21845
22318
  open: (path, real) => integrity.openSource(path, caller(), real)
21846
22319
  }
21847
22320
  };
@@ -22280,9 +22753,9 @@ var Launcher = class {
22280
22753
  const members = this.jobs.list();
22281
22754
  let image = pid;
22282
22755
  for (; ; ) {
22283
- const next2 = members.find((m) => m.execParent === image);
22284
- if (!next2) return image;
22285
- image = next2.pid;
22756
+ const next3 = members.find((m) => m.execParent === image);
22757
+ if (!next3) return image;
22758
+ image = next3.pid;
22286
22759
  }
22287
22760
  }
22288
22761
  credOf(pid) {
@@ -22569,7 +23042,7 @@ var Launcher = class {
22569
23042
  this.pnpmStale();
22570
23043
  if (!detach) return;
22571
23044
  for (const nodes of this.openFiles) nodes.revoke(at);
22572
- for (const holds of this.held) holds.revoke(at);
23045
+ for (const holds2 of this.held) holds2.revoke(at);
22573
23046
  }
22574
23047
  unmountAll() {
22575
23048
  this.booting.abort();
@@ -22715,7 +23188,7 @@ var Launcher = class {
22715
23188
  this.openFiles.add(nodes);
22716
23189
  const unlinked = new UnlinkedKeeper(
22717
23190
  fs,
22718
- { [Symbol.iterator]: () => [this.held, this.openFiles].flatMap((set2) => [...set2]).values() },
23191
+ { [Symbol.iterator]: () => [this.held, this.openFiles].flatMap((set3) => [...set3]).values() },
22719
23192
  (path) => this.mountRoot(path),
22720
23193
  {
22721
23194
  locks: webLocks(),
@@ -22743,11 +23216,32 @@ var Launcher = class {
22743
23216
  commandFs(root) {
22744
23217
  return withCommandDirs(
22745
23218
  root,
22746
- async () => new Set((await this.commands()).keys()),
23219
+ async () => this.commandNames(await this.commands()),
22747
23220
  (dir) => this.userCommandDir(dir),
22748
- this.owners
23221
+ this.owners,
23222
+ (path) => this.entryTarget(path)
22749
23223
  );
22750
23224
  }
23225
+ named = /* @__PURE__ */ new WeakMap();
23226
+ commandNames(commands) {
23227
+ let names = this.named.get(commands);
23228
+ if (!names) {
23229
+ names = new Set(commands.keys());
23230
+ this.named.set(commands, names);
23231
+ }
23232
+ return names;
23233
+ }
23234
+ async entryTarget(path) {
23235
+ const system = COMMAND2.exec(path)?.[1];
23236
+ const slash = path.lastIndexOf("/");
23237
+ const scopes = await this.userScopes();
23238
+ const scope = system === void 0 ? scopes.byBin.get(path.slice(0, slash)) : "";
23239
+ const name2 = system ?? path.slice(slash + 1);
23240
+ const command = scope === void 0 ? void 0 : (await this.commands(scope)).get(name2);
23241
+ const owner = scope ? scopes.owners?.get(scope) : void 0;
23242
+ const target = command && (command.shows ?? command.script ?? command.wasm);
23243
+ return target === void 0 ? void 0 : { path: target, ...owner ? { owner } : {} };
23244
+ }
22751
23245
  processChain(fs, nodes, unlinked) {
22752
23246
  nodes.changed = (path) => this.changes.publish(path);
22753
23247
  nodes.dirtied = (delta) => this.changes.dirtied(delta);
@@ -22862,9 +23356,9 @@ var Launcher = class {
22862
23356
  // src/sealed-relay.ts
22863
23357
  function sealedAsker(port) {
22864
23358
  const asks = /* @__PURE__ */ new Map();
22865
- let next2 = 0;
23359
+ let next3 = 0;
22866
23360
  const hook = (req, { signal }) => {
22867
- const id2 = ++next2;
23361
+ const id2 = ++next3;
22868
23362
  const waiting = Promise.withResolvers();
22869
23363
  asks.set(id2, waiting);
22870
23364
  const abort = () => {