@ai-ecoverse/slicc-kernel 1.103.4 → 1.106.3

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 (38) hide show
  1. package/README.md +57 -16
  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 +798 -295
  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 +489 -234
  12. package/dist/node-process-worker.js.map +4 -4
  13. package/dist/node.js +846 -315
  14. package/dist/node.js.map +4 -4
  15. package/dist/process-worker.js +426 -171
  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/js/js-kernel.d.ts +2 -1
  30. package/dist/types/process/process-signals.d.ts +3 -0
  31. package/dist/types/process/wasi/wasi-cred.d.ts +11 -0
  32. package/dist/types/process/wasi/wasi-files.d.ts +1 -0
  33. package/dist/types/process/wasi/wasi-host.d.ts +9 -0
  34. package/dist/types/process/wasi/wasi-sigmask.d.ts +7 -1
  35. package/dist/types/process/wasi/wasix-epoll.d.ts +3 -0
  36. package/dist/types/refused-command.d.ts +4 -0
  37. package/dist/types/transport.d.ts +1 -0
  38. 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.3" : "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;
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);
8438
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
  }
@@ -13349,9 +13701,10 @@ var MAX_UNREAD = 16 * 1024 * 1024;
13349
13701
  function errno(code, message3) {
13350
13702
  return Object.assign(new Error(`${code}: ${message3}`), { code });
13351
13703
  }
13352
- function requestHead(method, url, headers, extra) {
13704
+ function requestHead(method, url, headers, extra, first = []) {
13353
13705
  if (!TOKEN3.test(method)) throw errno("EINVAL", `method ${method}`);
13354
- const lines2 = [`${method} ${url.pathname}${url.search} HTTP/1.1`, `Host: ${url.host}`];
13706
+ const lines2 = [`${method} ${url.pathname}${url.search} HTTP/1.1`, `host: ${url.host}`];
13707
+ for (const [name2, value] of first) lines2.push(`${name2}: ${value}`);
13355
13708
  for (const [name2, value] of headers) {
13356
13709
  if (OWN_HEADERS.has(name2.toLowerCase())) continue;
13357
13710
  if (!TOKEN3.test(name2) || !SAFE_VALUE.test(value)) throw errno("EINVAL", `header ${name2}`);
@@ -13466,8 +13819,8 @@ var KernelLoopback = class {
13466
13819
  async fetch(req, port) {
13467
13820
  const url = new URL(req.url);
13468
13821
  const body = req.body ?? (/^(POST|PUT|PATCH)$/i.test(req.method) ? new Uint8Array(0) : void 0);
13469
- const length2 = body ? [["Content-Length", String(body.length)]] : [];
13470
- const head = requestHead(req.method, url, req.headers, [...length2, ["Connection", "close"]]);
13822
+ const length2 = body ? [["content-length", String(body.length)]] : [];
13823
+ const head = requestHead(req.method, url, req.headers, length2, [["connection", "close"]]);
13471
13824
  const socket = await this.connect(url, port, req.signal);
13472
13825
  try {
13473
13826
  await writeAll2(socket, body ? concat([head, body]) : head, req.signal);
@@ -13560,7 +13913,7 @@ var LoopbackWebSocket = class {
13560
13913
  const queue = [];
13561
13914
  let wake;
13562
13915
  let ended = false;
13563
- void this.pump(new WsReader(incoming, void 0, true, true), (message3) => {
13916
+ void this.pump(incoming, (message3) => {
13564
13917
  queue.push(message3);
13565
13918
  this.unread += message3.length;
13566
13919
  wake?.();
@@ -13577,10 +13930,10 @@ var LoopbackWebSocket = class {
13577
13930
  this.messages = {
13578
13931
  [Symbol.asyncIterator]: async function* () {
13579
13932
  for (; ; ) {
13580
- const next2 = queue.shift();
13581
- if (next2 !== void 0) {
13582
- taken(next2);
13583
- yield next2;
13933
+ const next3 = queue.shift();
13934
+ if (next3 !== void 0) {
13935
+ taken(next3);
13936
+ yield next3;
13584
13937
  continue;
13585
13938
  }
13586
13939
  if (ended) return;
@@ -13615,7 +13968,8 @@ var LoopbackWebSocket = class {
13615
13968
  this.pending -= bytes.length;
13616
13969
  })();
13617
13970
  }
13618
- async pump(reader, deliver) {
13971
+ async pump(incoming, deliver) {
13972
+ const reader = new WsReader(incoming, void 0, true, true);
13619
13973
  let close = { code: 1006, reason: "", wasClean: false };
13620
13974
  try {
13621
13975
  for (; ; ) {
@@ -13637,11 +13991,18 @@ var LoopbackWebSocket = class {
13637
13991
  } finally {
13638
13992
  const late = setTimeout(() => this.stop.abort(), CLOSE_WAIT_MS3);
13639
13993
  await this.writes;
13994
+ if (close.wasClean) await this.serverEnd(incoming);
13640
13995
  clearTimeout(late);
13641
13996
  this.socket.close();
13642
13997
  this.settle(close);
13643
13998
  }
13644
13999
  }
14000
+ async serverEnd(incoming) {
14001
+ try {
14002
+ while ((await incoming.some(64 * 1024, this.stop.signal)).length > 0) ;
14003
+ } catch {
14004
+ }
14005
+ }
13645
14006
  sendClose(code, reason) {
13646
14007
  if (this.sentClose) return;
13647
14008
  this.sentClose = true;
@@ -13690,9 +14051,9 @@ var WsHandles = class {
13690
14051
  open = /* @__PURE__ */ new Map();
13691
14052
  next;
13692
14053
  local;
13693
- constructor(transport, next2, local) {
14054
+ constructor(transport, next3, local) {
13694
14055
  this.transport = transport;
13695
- this.next = next2;
14056
+ this.next = next3;
13696
14057
  this.local = local;
13697
14058
  }
13698
14059
  owns(handle) {
@@ -13754,9 +14115,9 @@ var WsHandles = class {
13754
14115
  }
13755
14116
  async recv(socket, entry) {
13756
14117
  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 };
14118
+ const next3 = await entry.messages.next();
14119
+ if (!next3.done) {
14120
+ return typeof next3.value === "string" ? { ok: true, kind: "json", json: { text: next3.value } } : { ok: true, kind: "bytes", bytes: next3.value };
13760
14121
  }
13761
14122
  const closed = await socket.closed;
13762
14123
  return {
@@ -13804,11 +14165,49 @@ var HTTP_OPS = [
13804
14165
  "net-open",
13805
14166
  "net-head",
13806
14167
  "net-read",
14168
+ "net-write",
14169
+ "net-end",
13807
14170
  "net-close",
13808
14171
  "net-traits",
13809
14172
  ...WS_OPS
13810
14173
  ];
13811
- var EMPTY = new Uint8Array(0);
14174
+ var Upload = class {
14175
+ queue = [];
14176
+ ended = false;
14177
+ wake;
14178
+ write(bytes) {
14179
+ if (this.ended) return Promise.resolve(false);
14180
+ return new Promise((taken) => {
14181
+ this.queue.push({ bytes, taken });
14182
+ this.wake?.();
14183
+ });
14184
+ }
14185
+ end() {
14186
+ this.ended = true;
14187
+ this.wake?.();
14188
+ }
14189
+ cancel() {
14190
+ this.ended = true;
14191
+ for (const { taken } of this.queue.splice(0)) taken(false);
14192
+ this.wake?.();
14193
+ }
14194
+ async *[Symbol.asyncIterator]() {
14195
+ for (; ; ) {
14196
+ const next3 = this.queue.shift();
14197
+ if (next3) {
14198
+ next3.taken(true);
14199
+ yield next3.bytes;
14200
+ continue;
14201
+ }
14202
+ if (this.ended) return;
14203
+ await new Promise((resolve2) => {
14204
+ this.wake = resolve2;
14205
+ });
14206
+ this.wake = void 0;
14207
+ }
14208
+ }
14209
+ };
14210
+ var EMPTY2 = new Uint8Array(0);
13812
14211
  function message2(e) {
13813
14212
  return e instanceof Error ? e.message : String(e);
13814
14213
  }
@@ -13838,6 +14237,8 @@ var HttpHandles = class {
13838
14237
  if (req.op === "net-open") return Promise.resolve(this.opened(req));
13839
14238
  if (req.op === "net-head") return this.head(req.handle);
13840
14239
  if (req.op === "net-read") return this.read(req.handle, req.max);
14240
+ if (req.op === "net-write") return this.write(req.handle, req.bytes);
14241
+ if (req.op === "net-end") return Promise.resolve(this.end(req.handle));
13841
14242
  if (req.op === "net-traits") return Promise.resolve(this.traits(req.url));
13842
14243
  return this.close(req.handle);
13843
14244
  }
@@ -13856,19 +14257,37 @@ var HttpHandles = class {
13856
14257
  return result;
13857
14258
  }
13858
14259
  opened(req) {
14260
+ if (req.stream && (this.local?.port(req.url) !== void 0 || !this.transport.traits.streamingBodies)) {
14261
+ return { ok: false, errno: "ENOTSUP", message: "this transport takes whole request bodies" };
14262
+ }
13859
14263
  return { ok: true, kind: "json", json: this.start(req).handle };
13860
14264
  }
14265
+ async write(handle, bytes) {
14266
+ const upload = this.open.get(handle)?.upload;
14267
+ if (!upload) return { ok: false, errno: "EBADF", message: `no upload ${handle}` };
14268
+ if (!await upload.write(bytes)) {
14269
+ return { ok: false, errno: "EPIPE", message: "the request no longer takes a body" };
14270
+ }
14271
+ return { ok: true, kind: "void" };
14272
+ }
14273
+ end(handle) {
14274
+ const upload = this.open.get(handle)?.upload;
14275
+ if (!upload) return { ok: false, errno: "EBADF", message: `no upload ${handle}` };
14276
+ upload.end();
14277
+ return { ok: true, kind: "void" };
14278
+ }
13861
14279
  start(req) {
13862
14280
  const handle = ++this.nextHandle;
13863
14281
  const abort = new AbortController();
13864
14282
  const entry = {
13865
14283
  abort,
13866
14284
  head: Promise.resolve({ ok: true, kind: "void" }),
13867
- body: once(EMPTY),
13868
- left: EMPTY,
14285
+ body: once(EMPTY2),
14286
+ left: EMPTY2,
13869
14287
  done: false,
13870
14288
  reading: Promise.resolve(),
13871
- cancel: async () => void 0
14289
+ cancel: async () => void 0,
14290
+ ...req.stream ? { upload: new Upload() } : {}
13872
14291
  };
13873
14292
  this.open.set(handle, entry);
13874
14293
  entry.head = this.respond(handle, req, entry);
@@ -13882,7 +14301,7 @@ var HttpHandles = class {
13882
14301
  url: target,
13883
14302
  method: req.method,
13884
14303
  headers: req.headers,
13885
- ...req.body ? { body: req.body } : {},
14304
+ ...entry.upload ? { bodyStream: entry.upload } : req.body ? { body: req.body } : {},
13886
14305
  signal: entry.abort.signal
13887
14306
  };
13888
14307
  const response = port === void 0 ? await this.transport.fetch(request2, this.owner) : await this.local.fetch(request2, port);
@@ -13893,6 +14312,7 @@ var HttpHandles = class {
13893
14312
  const head = this.headOf(handle, shown, response);
13894
14313
  return response.redirected ? { ...head, json: { ...head.json, redirected: true } } : head;
13895
14314
  } catch (e) {
14315
+ entry.upload?.cancel();
13896
14316
  const text = message2(e);
13897
14317
  if (port !== void 0) return { ok: false, errno: codeOf2(e), message: text };
13898
14318
  const status = e.status;
@@ -13929,9 +14349,9 @@ var HttpHandles = class {
13929
14349
  async take(entry, max) {
13930
14350
  if (entry.left.length === 0 && !entry.done) {
13931
14351
  try {
13932
- const next2 = await entry.body.next();
13933
- if (next2.done) entry.done = true;
13934
- else entry.left = next2.value;
14352
+ const next3 = await entry.body.next();
14353
+ if (next3.done) entry.done = true;
14354
+ else entry.left = next3.value;
13935
14355
  } catch (e) {
13936
14356
  entry.done = true;
13937
14357
  const timedOut = e?.code === "ETIMEDOUT";
@@ -13950,6 +14370,7 @@ var HttpHandles = class {
13950
14370
  const entry = this.open.get(handle);
13951
14371
  if (!entry) return { ok: false, errno: "EBADF", message: `no request ${handle}` };
13952
14372
  this.open.delete(handle);
14373
+ entry.upload?.cancel();
13953
14374
  entry.abort.abort();
13954
14375
  await entry.cancel().catch(() => void 0);
13955
14376
  return { ok: true, kind: "void" };
@@ -14304,12 +14725,12 @@ var KernelTty = class {
14304
14725
  const chunks = [];
14305
14726
  let n = 0;
14306
14727
  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);
14728
+ const next3 = this.readable[0];
14729
+ if (next3 === null || next3 === void 0) break;
14730
+ const take = next3.subarray(0, max - n);
14310
14731
  chunks.push(take);
14311
14732
  n += take.length;
14312
- if (take.length < next2.length) this.readable[0] = next2.subarray(take.length);
14733
+ if (take.length < next3.length) this.readable[0] = next3.subarray(take.length);
14313
14734
  else this.readable.shift();
14314
14735
  if (this.canonical) break;
14315
14736
  }
@@ -14406,11 +14827,11 @@ var PtyPair = class {
14406
14827
  const chunks = [];
14407
14828
  let n = 0;
14408
14829
  while (this.queue.length > 0 && n < max - status) {
14409
- const next2 = this.queue[0];
14410
- const take = next2.subarray(0, max - status - n);
14830
+ const next3 = this.queue[0];
14831
+ const take = next3.subarray(0, max - status - n);
14411
14832
  chunks.push(take);
14412
14833
  n += take.length;
14413
- if (take.length < next2.length) this.queue[0] = next2.subarray(take.length);
14834
+ if (take.length < next3.length) this.queue[0] = next3.subarray(take.length);
14414
14835
  else this.queue.shift();
14415
14836
  }
14416
14837
  const out = new Uint8Array(status + n);
@@ -14730,8 +15151,8 @@ var Semaphore = class {
14730
15151
  this.mode = mode;
14731
15152
  }
14732
15153
  post() {
14733
- const next2 = this.waiters.shift();
14734
- if (next2) next2.grant();
15154
+ const next3 = this.waiters.shift();
15155
+ if (next3) next3.grant();
14735
15156
  else if (this.value >= SEM_VALUE_MAX) throw new KernelError("EOVERFLOW");
14736
15157
  else this.value++;
14737
15158
  }
@@ -15159,9 +15580,9 @@ var VfsNode = class {
15159
15580
  if (dirty && contents2 !== void 0) this.markDirty();
15160
15581
  }
15161
15582
  serial(op) {
15162
- const next2 = this.queue.then(op);
15163
- this.queue = next2.catch(() => void 0);
15164
- return next2;
15583
+ const next3 = this.queue.then(op);
15584
+ this.queue = next3.catch(() => void 0);
15585
+ return next3;
15165
15586
  }
15166
15587
  revoke() {
15167
15588
  this.revoked = true;
@@ -15583,9 +16004,9 @@ var VfsNodes = class {
15583
16004
  }
15584
16005
  for (const [p] of moved2) this.byPath.delete(p);
15585
16006
  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);
16007
+ const next3 = to + p.slice(from.length);
16008
+ if (node.path === p) node.renamedTo(next3);
16009
+ this.byPath.set(next3, node);
15589
16010
  }
15590
16011
  }
15591
16012
  };
@@ -15960,6 +16381,7 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
15960
16381
  "tty-pgrp-get",
15961
16382
  "tty-pgrp-set",
15962
16383
  "sig-mask",
16384
+ "sig-waiting",
15963
16385
  "sig-pause",
15964
16386
  ...SOCKET_OPS,
15965
16387
  ...EPOLL_OPS,
@@ -16073,16 +16495,8 @@ var WasmProcess = class {
16073
16495
  if (sig === SIG.STOP) return this.stop(sig);
16074
16496
  const bit = sigbit(sig);
16075
16497
  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
- }
16498
+ if (((this.options.blockedBits?.() ?? 0) & bit) !== 0 && this.routeBlocked(sig, bit)) {
16499
+ return "deliver";
16086
16500
  }
16087
16501
  if (!(this.caught & bit)) {
16088
16502
  const action = defaultAction(sig);
@@ -16099,6 +16513,17 @@ var WasmProcess = class {
16099
16513
  blocked2.abort();
16100
16514
  return "deliver";
16101
16515
  }
16516
+ routeBlocked(sig, bit) {
16517
+ const waiter = this.waiterFor(bit);
16518
+ const tid = waiter ?? this.options.unblockedThread?.(sig);
16519
+ if (tid === void 0 || tid === 1) {
16520
+ this.options.onPending?.(sig);
16521
+ return true;
16522
+ }
16523
+ if (waiter === void 0 && !(this.caught & bit)) return false;
16524
+ this.options.threadKill?.(tid, sig);
16525
+ return true;
16526
+ }
16102
16527
  async spawn(req) {
16103
16528
  const { file, argv, argv0, env, cwd, chdir, stdio, inherit: inherit2, exec, pgid, session, deathSignal } = req;
16104
16529
  if (deathSignal !== void 0 && (exec || !isSignal(deathSignal))) {
@@ -16179,6 +16604,16 @@ var WasmProcess = class {
16179
16604
  }
16180
16605
  blocks = /* @__PURE__ */ new WeakMap();
16181
16606
  sizesRead = /* @__PURE__ */ new WeakMap();
16607
+ sigwaiters = /* @__PURE__ */ new Map();
16608
+ setSigwaiter(tid, set3) {
16609
+ if (set3 === 0) this.sigwaiters.delete(tid);
16610
+ else this.sigwaiters.set(tid, set3);
16611
+ }
16612
+ waiterFor(bit) {
16613
+ if (((this.sigwaiters.get(1) ?? 0) & bit) !== 0) return 1;
16614
+ for (const [tid, set3] of this.sigwaiters) if (set3 & bit) return tid;
16615
+ return void 0;
16616
+ }
16182
16617
  async syscall(req, cancelled, block) {
16183
16618
  this.options.onSyscall?.(req);
16184
16619
  const started = performance.now();
@@ -16435,18 +16870,18 @@ var WasmProcess = class {
16435
16870
  }
16436
16871
  if (this.writesAt(req) && this.options.readonlyAt?.(path)) throw new KernelError("EROFS");
16437
16872
  const open = { ...req, path };
16438
- const plain = this.plainMode(req.mode);
16873
+ const plain2 = this.plainMode(req.mode);
16439
16874
  if (req.exclusive) {
16440
16875
  await nodes.createExclusive(path, this.newOwner(req.mode));
16441
- if (plain !== void 0) await fs.chmod?.(path, plain);
16876
+ if (plain2 !== void 0) await fs.chmod?.(path, plain2);
16442
16877
  this.fds.installAt(fd, this.openVfsFile(open, void 0, fs));
16443
16878
  } else if (req.create) {
16444
16879
  await nodes.onEntry(path, async () => {
16445
- const marks = this.options.owners || plain !== void 0;
16880
+ const marks = this.options.owners || plain2 !== void 0;
16446
16881
  const fresh2 = marks && !await present2(fs, path, true);
16447
16882
  this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path), fs));
16448
16883
  await this.fds.get(fd).file.stat?.();
16449
- if (fresh2 && plain !== void 0) await fs.chmod?.(path, plain);
16884
+ if (fresh2 && plain2 !== void 0) await fs.chmod?.(path, plain2);
16450
16885
  else if (fresh2) {
16451
16886
  await fs.chown?.(path, { ...this.newOwner(req.mode), kind: "new" });
16452
16887
  }
@@ -17087,6 +17522,9 @@ var WasmProcess = class {
17087
17522
  case "dl-unlock":
17088
17523
  this.unlockDl(req.owner);
17089
17524
  return { ok: true, kind: "void" };
17525
+ case "sig-waiting":
17526
+ this.setSigwaiter(req.tid, req.set);
17527
+ return { ok: true, kind: "void" };
17090
17528
  case "sig-mask":
17091
17529
  this.caught = req.caught;
17092
17530
  this.ignored = req.ignored;
@@ -17154,8 +17592,8 @@ var WasmProcess = class {
17154
17592
  };
17155
17593
  withDl(state) {
17156
17594
  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] };
17595
+ const { next: next3, handle, free, live } = this.closures;
17596
+ const dl = { log: [...this.dlLog], next: next3, handle, free: [...free], live: [...live] };
17159
17597
  return { ...state, dl };
17160
17598
  }
17161
17599
  seedDl(dl) {
@@ -17166,8 +17604,8 @@ var WasmProcess = class {
17166
17604
  this.closures.free.push(...dl.free);
17167
17605
  for (const index of dl.live) this.closures.live.add(index);
17168
17606
  }
17169
- allocateClosure(floor) {
17170
- const index = this.closures.free.pop() ?? Math.max(floor, this.closures.next);
17607
+ allocateClosure(floor2) {
17608
+ const index = this.closures.free.pop() ?? Math.max(floor2, this.closures.next);
17171
17609
  this.closures.next = Math.max(this.closures.next, index + 1);
17172
17610
  this.closures.live.add(index);
17173
17611
  this.dlLog.push({ kind: "closure", index });
@@ -17219,8 +17657,8 @@ var WasmProcess = class {
17219
17657
  wakeDl() {
17220
17658
  for (const waiter of [...this.dlLock.waiters]) waiter.wake(false);
17221
17659
  }
17222
- reserveSlots(floor, size) {
17223
- const base = Math.max(floor, this.closures.next);
17660
+ reserveSlots(floor2, size) {
17661
+ const base = Math.max(floor2, this.closures.next);
17224
17662
  this.closures.next = base + size;
17225
17663
  return base;
17226
17664
  }
@@ -17277,6 +17715,7 @@ var WASM_MAX_THREADS = 256;
17277
17715
  // src/kernel/host.ts
17278
17716
  var CRASHED = 70;
17279
17717
  var KILL_GRACE_MS = 2e3;
17718
+ var KILL_HARD_MS = 5e3;
17280
17719
  var descIds = /* @__PURE__ */ new WeakMap();
17281
17720
  var nextDescId = 1;
17282
17721
  function descId(file) {
@@ -17303,16 +17742,21 @@ function inheritedFds(fds) {
17303
17742
  var Dying = class {
17304
17743
  ending;
17305
17744
  timer;
17745
+ hard;
17306
17746
  busy = 0;
17307
17747
  finish;
17308
17748
  start(code, sig, header3, process, finish) {
17309
17749
  if (this.ending) return;
17310
17750
  this.ending = { code, sig };
17311
17751
  this.finish = finish;
17752
+ this.hard = setTimeout(finish, KILL_HARD_MS);
17312
17753
  this.arm();
17313
17754
  Atomics.store(header3, SAB_I_KILLED, code);
17314
17755
  process.die();
17315
17756
  }
17757
+ serve(req, work) {
17758
+ return this.ending && isWasmSyscall(req) ? work() : this.serving(work);
17759
+ }
17316
17760
  async serving(work) {
17317
17761
  this.busy++;
17318
17762
  clearTimeout(this.timer);
@@ -17329,6 +17773,7 @@ var Dying = class {
17329
17773
  }
17330
17774
  clear() {
17331
17775
  clearTimeout(this.timer);
17776
+ clearTimeout(this.hard);
17332
17777
  }
17333
17778
  status(reported2) {
17334
17779
  if (this.ending) return [this.ending.code, this.ending.sig];
@@ -17340,8 +17785,8 @@ function killThread(thread, caught, sig) {
17340
17785
  const held = (Atomics.load(thread.header, SAB_I_BLOCKED) & sigbit(sig)) !== 0;
17341
17786
  if (!held && (caught & sigbit(sig)) === 0) return false;
17342
17787
  Atomics.or(thread.header, SAB_I_SIGNALS, sigbit(sig));
17343
- if (held) return true;
17344
17788
  Atomics.notify(thread.header, SAB_I_SIGNALS);
17789
+ if (held) return true;
17345
17790
  const blocked2 = thread.interrupt;
17346
17791
  thread.interrupt = new AbortController();
17347
17792
  blocked2.abort();
@@ -17437,7 +17882,7 @@ function inherited(opts) {
17437
17882
  ...opts.cred ? { cred: opts.cred } : {}
17438
17883
  };
17439
17884
  }
17440
- function holdingToken(opts, holds, caller) {
17885
+ function holdingToken(opts, holds2, caller) {
17441
17886
  return mintSyncFsToken({
17442
17887
  ...opts.owners ? caller : { umask: caller.umask },
17443
17888
  ...opts.dac ? { dac: opts.dac, pid: opts.pid, created: caller.created } : {},
@@ -17448,13 +17893,13 @@ function holdingToken(opts, holds, caller) {
17448
17893
  noLinks: opts.noLinks,
17449
17894
  cwd: opts.cwd,
17450
17895
  ...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)
17896
+ hold: (path, on, open, real, handle) => holds2.hold(path, on, open, real, handle),
17897
+ keptFor: (handle) => holds2.keptFor(handle),
17898
+ unlinked: (path) => holds2.keptAt(path),
17899
+ gate: (path, op) => opts.unlinkGate && holds2.holding(path) ? opts.unlinkGate.run(holds2.keyOf(path), op) : op(),
17900
+ own: (path, op) => holds2.own(path, op),
17901
+ revoked: (path) => holds2.isRevoked(path),
17902
+ renamed: (from, to) => holds2.renamed(from, to)
17458
17903
  });
17459
17904
  }
17460
17905
  function linksOf(opts) {
@@ -17528,6 +17973,9 @@ function threadInit(opts, program, sab, cred, thread, main) {
17528
17973
  signals: main
17529
17974
  };
17530
17975
  }
17976
+ function settleExit(exit, settle3, code) {
17977
+ void exit.catch(() => void 0).then(() => settle3(code & 255));
17978
+ }
17531
17979
  function spawnWasmProcess(opts) {
17532
17980
  const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
17533
17981
  const header3 = new Int32Array(sab, 0, SAB_HEADER_I32);
@@ -17554,19 +18002,20 @@ function spawnWasmProcess(opts) {
17554
18002
  )
17555
18003
  );
17556
18004
  publishStops(process, header3);
17557
- const holds = new HeldPaths();
17558
- opts.held?.add(holds);
17559
- const token = holdingToken(opts, holds, {
18005
+ const holds2 = new HeldPaths();
18006
+ opts.held?.add(holds2);
18007
+ const token = holdingToken(opts, holds2, {
17560
18008
  cred: () => process.cred,
17561
18009
  umask: () => process.umask,
17562
18010
  created: created3,
17563
- holdsKept: async (path) => path.includes(`/${UNLINKED_DIR}/`) && (holds.keeps(path) || await process.holds(path))
18011
+ holdsKept: async (path) => path.includes(`/${UNLINKED_DIR}/`) && (holds2.keeps(path) || await process.holds(path))
17564
18012
  });
17565
- process.keptPath = (path) => holds.keptAt(path)?.hidden;
17566
- process.keptHandle = (handle2) => holds.keptFor(handle2)?.hidden;
18013
+ process.keptPath = (path) => holds2.keptAt(path)?.hidden;
18014
+ process.keptHandle = (handle2) => holds2.keptFor(handle2)?.hidden;
17567
18015
  const worker = opts.createWorker();
17568
18016
  const dying = new Dying();
17569
- const dispatch2 = (req, block) => dying.serving(
18017
+ const dispatch2 = (req, block) => dying.serve(
18018
+ req,
17570
18019
  () => isWasmSyscall(req) ? process.syscall(req, void 0, block) : dispatchSyncFs(req)
17571
18020
  );
17572
18021
  const clocks = new ProcessClocks(sab, opts.program.abi !== "js", process);
@@ -17589,12 +18038,9 @@ function spawnWasmProcess(opts) {
17589
18038
  responder.dispose();
17590
18039
  lane?.dispose();
17591
18040
  revokeSyncFsToken(token);
17592
- opts.held?.delete(holds);
18041
+ opts.held?.delete(holds2);
17593
18042
  worker.terminate();
17594
- void process.exit().then(
17595
- () => settle3(code),
17596
- () => settle3(code)
17597
- );
18043
+ settleExit(process.exit(), settle3, code);
17598
18044
  };
17599
18045
  const exitNow = (code) => {
17600
18046
  queueMicrotask(() => finish(...dying.status(code)));
@@ -17638,7 +18084,7 @@ function spawnWasmProcess(opts) {
17638
18084
  const tsignals = threadSignalsOf(tsab);
17639
18085
  if (thread.blocked) storeBlocked(tsignals.header, thread.blocked);
17640
18086
  threadSignals.set(thread.tid, tsignals);
17641
- const tdispatch = (req, block) => dying.serving(() => threadCall(process, req, threadCancel(tsignals), block));
18087
+ const tdispatch = (req, block) => dying.serve(req, () => threadCall(process, req, threadCancel(tsignals), block));
17642
18088
  const tresponder = attachSyncSabResponder(tw, tsab, token, {
17643
18089
  dispatch: clocks.timed(clocks.thread(thread.tid, tsab), tdispatch)
17644
18090
  });
@@ -18108,10 +18554,10 @@ var Automaton = class {
18108
18554
  let node = 0;
18109
18555
  for (const byte of bytes) {
18110
18556
  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);
18557
+ let next3 = this.edges.get(key);
18558
+ if (next3 === void 0) {
18559
+ next3 = this.depth.length;
18560
+ this.edges.set(key, next3);
18115
18561
  this.fail.push(0);
18116
18562
  this.depth.push(this.depth[node] + 1);
18117
18563
  this.children.push(0);
@@ -18119,7 +18565,7 @@ var Automaton = class {
18119
18565
  this.out.push([]);
18120
18566
  this.dict.push(-1);
18121
18567
  }
18122
- node = next2;
18568
+ node = next3;
18123
18569
  }
18124
18570
  const id2 = this.lengths.length;
18125
18571
  this.lengths.push(bytes.length);
@@ -18155,8 +18601,8 @@ var Automaton = class {
18155
18601
  step(state, byte) {
18156
18602
  let current = state;
18157
18603
  for (; ; ) {
18158
- const next2 = this.edges.get(current * 256 + byte);
18159
- if (next2 !== void 0) return next2;
18604
+ const next3 = this.edges.get(current * 256 + byte);
18605
+ if (next3 !== void 0) return next3;
18160
18606
  if (current === 0) return 0;
18161
18607
  current = this.fail[current];
18162
18608
  }
@@ -18184,7 +18630,7 @@ var RANK = {
18184
18630
  };
18185
18631
  var CR2 = 13;
18186
18632
  var LF2 = 10;
18187
- var EMPTY2 = new Uint8Array(0);
18633
+ var EMPTY3 = new Uint8Array(0);
18188
18634
  function register(automaton, sources, ids, bytes, source) {
18189
18635
  const key = Array.from(bytes).join(",");
18190
18636
  let id2 = ids.get(key);
@@ -18310,7 +18756,7 @@ function pass(c, input, final) {
18310
18756
  const partials = [];
18311
18757
  scanMain(c, input, best, partials);
18312
18758
  scanWrapped(c, input, best, partials);
18313
- const holds = final ? [] : [...new Set(partials)].sort((a, b) => a - b);
18759
+ const holds2 = final ? [] : [...new Set(partials)].sort((a, b) => a - b);
18314
18760
  const starts = [...best.keys()].sort((a, b) => a - b);
18315
18761
  const parts2 = [];
18316
18762
  const hits = /* @__PURE__ */ new Map();
@@ -18320,16 +18766,16 @@ function pass(c, input, final) {
18320
18766
  let h = 0;
18321
18767
  for (; ; ) {
18322
18768
  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) {
18769
+ while (h < holds2.length && holds2[h] < pos) h++;
18770
+ const hold = h < holds2.length ? holds2[h] : Infinity;
18771
+ const next3 = s < starts.length ? starts[s] : Infinity;
18772
+ if (hold <= next3) {
18327
18773
  cut = Math.min(hold, input.length);
18328
18774
  break;
18329
18775
  }
18330
- const candidate = best.get(next2);
18776
+ const candidate = best.get(next3);
18331
18777
  parts2.push(
18332
- input.subarray(pos, next2),
18778
+ input.subarray(pos, next3),
18333
18779
  c.replacements[candidate.secret][candidate.form === "raw" ? 0 : 1]
18334
18780
  );
18335
18781
  count(c, hits, candidate);
@@ -18364,12 +18810,12 @@ function compile(secrets, options = {}) {
18364
18810
  return { text: decoder4.decode(bytes), hits };
18365
18811
  },
18366
18812
  stream() {
18367
- let pending = EMPTY2;
18813
+ let pending = EMPTY3;
18368
18814
  const hits = /* @__PURE__ */ new Map();
18369
18815
  const run2 = (final) => {
18370
18816
  if (c.secrets.length === 0 || pending.length === 0) {
18371
18817
  const out = pending;
18372
- pending = EMPTY2;
18818
+ pending = EMPTY3;
18373
18819
  return out;
18374
18820
  }
18375
18821
  const result = pass(c, pending, final);
@@ -18383,7 +18829,7 @@ function compile(secrets, options = {}) {
18383
18829
  return run2(false);
18384
18830
  },
18385
18831
  idle() {
18386
- return pending.length < FRAGMENT ? run2(true) : EMPTY2;
18832
+ return pending.length < FRAGMENT ? run2(true) : EMPTY3;
18387
18833
  },
18388
18834
  end() {
18389
18835
  return run2(true);
@@ -18425,7 +18871,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
18425
18871
  let timer;
18426
18872
  let failed3;
18427
18873
  const waiters = /* @__PURE__ */ new Set();
18428
- const quiet = () => !busy && pending === 0;
18874
+ const quiet2 = () => !busy && pending === 0;
18429
18875
  const wake = () => {
18430
18876
  for (const waiter of [...waiters]) waiter();
18431
18877
  };
@@ -18442,7 +18888,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
18442
18888
  if (failed3 === void 0) failed3 = err;
18443
18889
  }).finally(() => {
18444
18890
  pending -= 1;
18445
- if (quiet()) wake();
18891
+ if (quiet2()) wake();
18446
18892
  });
18447
18893
  };
18448
18894
  const settling = (signal) => new Promise((resolve2, reject) => {
@@ -18460,7 +18906,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
18460
18906
  signal?.addEventListener("abort", stop, { once: true });
18461
18907
  });
18462
18908
  const settled = async () => {
18463
- while (!quiet()) await settling();
18909
+ while (!quiet2()) await settling();
18464
18910
  };
18465
18911
  const changed = async (signal) => {
18466
18912
  const round = new AbortController();
@@ -18476,7 +18922,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
18476
18922
  };
18477
18923
  const turn = async (signal) => {
18478
18924
  for (let waited = false; ; waited = true) {
18479
- if (quiet() && (pollFile(file).writable || !file.changed)) {
18925
+ if (quiet2() && (pollFile(file).writable || !file.changed)) {
18480
18926
  busy = true;
18481
18927
  return;
18482
18928
  }
@@ -18505,7 +18951,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
18505
18951
  ...bound(file, "stat"),
18506
18952
  poll: () => {
18507
18953
  const state = pollFile(file);
18508
- return quiet() ? state : { ...state, writable: false };
18954
+ return quiet2() ? state : { ...state, writable: false };
18509
18955
  },
18510
18956
  changed,
18511
18957
  ...file.tty ? { tty: file.tty } : {},
@@ -18521,7 +18967,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
18521
18967
  await send(stream.push(taken), signal);
18522
18968
  } finally {
18523
18969
  busy = false;
18524
- if (quiet()) wake();
18970
+ if (quiet2()) wake();
18525
18971
  }
18526
18972
  timer = setTimeout(release, idleMs);
18527
18973
  return taken.length;
@@ -18622,15 +19068,15 @@ function isRecord(value) {
18622
19068
  return typeof value === "object" && value !== null && !Array.isArray(value);
18623
19069
  }
18624
19070
  function serialized(reply) {
18625
- let plain;
19071
+ let plain2;
18626
19072
  try {
18627
19073
  const text = JSON.stringify(reply);
18628
- plain = text === void 0 ? void 0 : JSON.parse(text);
19074
+ plain2 = text === void 0 ? void 0 : JSON.parse(text);
18629
19075
  } catch (err) {
18630
19076
  return JSON.stringify(errorReply("EIO", lineOf2(err)));
18631
19077
  }
18632
- if (!isRecord(plain)) return JSON.stringify(errorReply("EIO", "the vault gave no reply"));
18633
- const { v: _, ...rest2 } = plain;
19078
+ if (!isRecord(plain2)) return JSON.stringify(errorReply("EIO", "the vault gave no reply"));
19079
+ const { v: _, ...rest2 } = plain2;
18634
19080
  return JSON.stringify({ v: 1, ...rest2 });
18635
19081
  }
18636
19082
  function lineOf2(err) {
@@ -19649,22 +20095,22 @@ function keepWatching(loop, signal) {
19649
20095
  const stop = () => session2.abort();
19650
20096
  signal.addEventListener("abort", stop);
19651
20097
  let timer;
19652
- const quiet = () => {
20098
+ const quiet2 = () => {
19653
20099
  clearTimeout(timer);
19654
20100
  timer = setTimeout(stop, silenceMs);
19655
20101
  };
19656
20102
  let opened2 = false;
19657
20103
  try {
19658
20104
  const client = await loop.connect();
19659
- quiet();
20105
+ quiet2();
19660
20106
  await client.watch(
19661
20107
  session2.signal,
19662
20108
  (event) => {
19663
- quiet();
20109
+ quiet2();
19664
20110
  if (!event.ping) loop.event(event);
19665
20111
  },
19666
20112
  () => {
19667
- quiet();
20113
+ quiet2();
19668
20114
  opened2 = true;
19669
20115
  up = true;
19670
20116
  loop.up(client);
@@ -20106,9 +20552,9 @@ function tmpfs() {
20106
20552
  let node = root;
20107
20553
  for (const name2 of parts2(path)) {
20108
20554
  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;
20555
+ const next3 = node.children.get(name2);
20556
+ if (!next3) throw fsError2("ENOENT", path);
20557
+ node = next3;
20112
20558
  }
20113
20559
  return node;
20114
20560
  }
@@ -20414,6 +20860,28 @@ function foreignImports(bytes) {
20414
20860
  return {};
20415
20861
  }
20416
20862
 
20863
+ // src/refused-command.ts
20864
+ var REFUSED_PATH = "/usr/lib/slicc/refused";
20865
+ var REFUSED_SOURCE = [
20866
+ "export default async function refused(ctx) {",
20867
+ " await ctx.write(2, ctx.argv[1] + '\\n').catch(() => undefined);",
20868
+ " return 126;",
20869
+ "}"
20870
+ ].join("\n");
20871
+ function refusing(command) {
20872
+ if (!command.refused) return command;
20873
+ return {
20874
+ name: command.name,
20875
+ abi: "js",
20876
+ glue: REFUSED_PATH,
20877
+ wasm: REFUSED_PATH,
20878
+ argv0: command.name,
20879
+ args: [`${command.name}: ${command.refused}`],
20880
+ refused: command.refused,
20881
+ shows: command.script ?? command.wasm
20882
+ };
20883
+ }
20884
+
20417
20885
  // src/sudo.ts
20418
20886
  var SUDOERS = "/etc/sudoers";
20419
20887
  var SUDOERS_D = "/etc/sudoers.d";
@@ -20441,13 +20909,13 @@ function splitWords(text) {
20441
20909
  while (piece.lastIndex < text.length) {
20442
20910
  const m = piece.exec(text);
20443
20911
  if (!m) return void 0;
20444
- const [, single, double, escaped, plain, space] = m;
20912
+ const [, single, double, escaped, plain2, space] = m;
20445
20913
  if (space !== void 0) {
20446
20914
  if (word !== void 0) words2.push(word);
20447
20915
  word = void 0;
20448
20916
  continue;
20449
20917
  }
20450
- word = (word ?? "") + (single ?? (double !== void 0 ? unescaped(double) : escaped ?? plain));
20918
+ word = (word ?? "") + (single ?? (double !== void 0 ? unescaped(double) : escaped ?? plain2));
20451
20919
  }
20452
20920
  if (word !== void 0) words2.push(word);
20453
20921
  return words2;
@@ -20479,7 +20947,7 @@ function parseRule(line) {
20479
20947
  if (!cmnd?.[0]?.startsWith("/")) return void 0;
20480
20948
  return { ...rule, cmnd };
20481
20949
  }
20482
- function flags(parsed2, arg, next2) {
20950
+ function flags(parsed2, arg, next3) {
20483
20951
  for (let j = 1; j < arg.length; j++) {
20484
20952
  const flag = arg[j];
20485
20953
  if (flag === "n") parsed2.nonInteractive = true;
@@ -20487,7 +20955,7 @@ function flags(parsed2, arg, next2) {
20487
20955
  else if (flag !== "u") return `sudo: invalid option -- '${flag}'
20488
20956
  ${USAGE}`;
20489
20957
  else {
20490
- const user = arg.slice(j + 1) || next2;
20958
+ const user = arg.slice(j + 1) || next3;
20491
20959
  if (!user) return `sudo: option requires an argument -- 'u'
20492
20960
  ${USAGE}`;
20493
20961
  parsed2.user = user;
@@ -20937,9 +21405,9 @@ function appendedRoots(given) {
20937
21405
  return [...roots];
20938
21406
  }
20939
21407
  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("=");
21408
+ const next3 = words2[1];
21409
+ if (baseName2(words2[0] ?? "") !== "env" || next3 === void 0) return false;
21410
+ return next3 === "-S" || !next3.startsWith("-") && !next3.includes("=");
20943
21411
  }
20944
21412
  function shebangCommand(words2) {
20945
21413
  if (!plainEnv(words2)) {
@@ -20963,9 +21431,9 @@ function expandDefaults(defaults, env) {
20963
21431
  for (const [key, value] of Object.entries(defaults ?? {})) {
20964
21432
  let missing4 = false;
20965
21433
  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 ?? "";
21434
+ const set3 = Object.hasOwn(env, name2) ? env[name2] : void 0;
21435
+ if (set3 === void 0) missing4 = true;
21436
+ return set3 ?? "";
20969
21437
  });
20970
21438
  if (!missing4) out[key] = expanded;
20971
21439
  }
@@ -21190,7 +21658,7 @@ var Launcher = class {
21190
21658
  const holders = [this.held, this.openFiles];
21191
21659
  this.unlinked = new UnlinkedKeeper(
21192
21660
  fs,
21193
- { [Symbol.iterator]: () => holders.flatMap((set2) => [...set2]).values() },
21661
+ { [Symbol.iterator]: () => holders.flatMap((set3) => [...set3]).values() },
21194
21662
  (path) => this.mountRoot(path),
21195
21663
  {
21196
21664
  locks: webLocks(),
@@ -21275,11 +21743,21 @@ var Launcher = class {
21275
21743
  found = this.trustedAppended().then(async (appended2) => {
21276
21744
  const tiers = await this.tiers(scope, appended2);
21277
21745
  return scanCommands(this.scanned, tiers, tiers.length - appended2.length);
21278
- }).then((commands) => commands.set(SUDO_COMMAND.name, SUDO_COMMAND));
21746
+ }).then((commands) => this.refusals(commands).set(SUDO_COMMAND.name, SUDO_COMMAND));
21279
21747
  this.catalogs.set(scope, found);
21280
21748
  }
21281
21749
  return found;
21282
21750
  }
21751
+ refusals(commands) {
21752
+ for (const [name2, command] of commands) {
21753
+ if (!command.refused) continue;
21754
+ commands.set(name2, refusing(command));
21755
+ if (this.warned.has(command.refused)) continue;
21756
+ this.warned.add(command.refused);
21757
+ console.warn(`slicc-kernel: refusing the commands of ${command.refused}`);
21758
+ }
21759
+ return commands;
21760
+ }
21283
21761
  ownCommands(scope) {
21284
21762
  let found = this.ownCatalogs.get(scope);
21285
21763
  if (!found) {
@@ -21354,7 +21832,8 @@ var Launcher = class {
21354
21832
  );
21355
21833
  return {
21356
21834
  byUid: new Map(users.map((account, i) => [account.uid, homes[i]])),
21357
- byBin: new Map(homes.map((home) => [`${home}/bin`, home]))
21835
+ byBin: new Map(homes.map((home) => [`${home}/bin`, home])),
21836
+ owners: new Map(homes.map((home, i) => [home, credOf(users[i])]))
21358
21837
  };
21359
21838
  },
21360
21839
  () => ({ byUid: /* @__PURE__ */ new Map(), byBin: /* @__PURE__ */ new Map() })
@@ -21696,12 +22175,12 @@ var Launcher = class {
21696
22175
  return `${path}:${st.size}:${st.mtime.getTime()}`;
21697
22176
  }
21698
22177
  verified(path, caller, fs = this.base) {
21699
- const plain = () => fs.readFileBuffer(path);
21700
- return this.integrity ? this.integrity.read(path, caller, plain) : plain();
22178
+ const plain2 = () => fs.readFileBuffer(path);
22179
+ return this.integrity ? this.integrity.read(path, caller, plain2) : plain2();
21701
22180
  }
21702
22181
  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 }));
22182
+ const plain2 = async () => this.base.readFileBuffer(path);
22183
+ return this.integrity ? this.integrity.readKeyed(path, caller, plain2, asked) : plain2().then((bytes) => ({ bytes }));
21705
22184
  }
21706
22185
  cached(cache, key, load) {
21707
22186
  const hit = cache.get(key);
@@ -21772,6 +22251,9 @@ var Launcher = class {
21772
22251
  if (target.abi === "js" && target.wasm === SUDO_PATH) {
21773
22252
  return { abi: "js", glue: SUDO_SOURCE, path: SUDO_PATH };
21774
22253
  }
22254
+ if (target.abi === "js" && target.wasm === REFUSED_PATH) {
22255
+ return { abi: "js", glue: REFUSED_SOURCE, path: REFUSED_PATH };
22256
+ }
21775
22257
  const { asked } = target;
21776
22258
  if (target.abi === "js") {
21777
22259
  return { abi: "js", glue: await this.text(target.wasm, caller, asked), path: target.wasm };
@@ -21841,7 +22323,7 @@ var Launcher = class {
21841
22323
  });
21842
22324
  return {
21843
22325
  source: {
21844
- read: (path, plain) => integrity.readSource(path, caller(), plain),
22326
+ read: (path, plain2) => integrity.readSource(path, caller(), plain2),
21845
22327
  open: (path, real) => integrity.openSource(path, caller(), real)
21846
22328
  }
21847
22329
  };
@@ -22280,9 +22762,9 @@ var Launcher = class {
22280
22762
  const members = this.jobs.list();
22281
22763
  let image = pid;
22282
22764
  for (; ; ) {
22283
- const next2 = members.find((m) => m.execParent === image);
22284
- if (!next2) return image;
22285
- image = next2.pid;
22765
+ const next3 = members.find((m) => m.execParent === image);
22766
+ if (!next3) return image;
22767
+ image = next3.pid;
22286
22768
  }
22287
22769
  }
22288
22770
  credOf(pid) {
@@ -22569,7 +23051,7 @@ var Launcher = class {
22569
23051
  this.pnpmStale();
22570
23052
  if (!detach) return;
22571
23053
  for (const nodes of this.openFiles) nodes.revoke(at);
22572
- for (const holds of this.held) holds.revoke(at);
23054
+ for (const holds2 of this.held) holds2.revoke(at);
22573
23055
  }
22574
23056
  unmountAll() {
22575
23057
  this.booting.abort();
@@ -22715,7 +23197,7 @@ var Launcher = class {
22715
23197
  this.openFiles.add(nodes);
22716
23198
  const unlinked = new UnlinkedKeeper(
22717
23199
  fs,
22718
- { [Symbol.iterator]: () => [this.held, this.openFiles].flatMap((set2) => [...set2]).values() },
23200
+ { [Symbol.iterator]: () => [this.held, this.openFiles].flatMap((set3) => [...set3]).values() },
22719
23201
  (path) => this.mountRoot(path),
22720
23202
  {
22721
23203
  locks: webLocks(),
@@ -22743,11 +23225,32 @@ var Launcher = class {
22743
23225
  commandFs(root) {
22744
23226
  return withCommandDirs(
22745
23227
  root,
22746
- async () => new Set((await this.commands()).keys()),
23228
+ async () => this.commandNames(await this.commands()),
22747
23229
  (dir) => this.userCommandDir(dir),
22748
- this.owners
23230
+ this.owners,
23231
+ (path) => this.entryTarget(path)
22749
23232
  );
22750
23233
  }
23234
+ named = /* @__PURE__ */ new WeakMap();
23235
+ commandNames(commands) {
23236
+ let names = this.named.get(commands);
23237
+ if (!names) {
23238
+ names = new Set(commands.keys());
23239
+ this.named.set(commands, names);
23240
+ }
23241
+ return names;
23242
+ }
23243
+ async entryTarget(path) {
23244
+ const system = COMMAND2.exec(path)?.[1];
23245
+ const slash = path.lastIndexOf("/");
23246
+ const scopes = await this.userScopes();
23247
+ const scope = system === void 0 ? scopes.byBin.get(path.slice(0, slash)) : "";
23248
+ const name2 = system ?? path.slice(slash + 1);
23249
+ const command = scope === void 0 ? void 0 : (await this.commands(scope)).get(name2);
23250
+ const owner = scope ? scopes.owners?.get(scope) : void 0;
23251
+ const target = command && (command.shows ?? command.script ?? command.wasm);
23252
+ return target === void 0 ? void 0 : { path: target, ...owner ? { owner } : {} };
23253
+ }
22751
23254
  processChain(fs, nodes, unlinked) {
22752
23255
  nodes.changed = (path) => this.changes.publish(path);
22753
23256
  nodes.dirtied = (delta) => this.changes.dirtied(delta);
@@ -22862,9 +23365,9 @@ var Launcher = class {
22862
23365
  // src/sealed-relay.ts
22863
23366
  function sealedAsker(port) {
22864
23367
  const asks = /* @__PURE__ */ new Map();
22865
- let next2 = 0;
23368
+ let next3 = 0;
22866
23369
  const hook = (req, { signal }) => {
22867
- const id2 = ++next2;
23370
+ const id2 = ++next3;
22868
23371
  const waiting = Promise.withResolvers();
22869
23372
  asks.set(id2, waiting);
22870
23373
  const abort = () => {