@ai-ecoverse/slicc-kernel 1.182.0 → 1.183.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +54 -5
  2. package/dist/driver-worker.js +290 -90
  3. package/dist/driver-worker.js.map +4 -4
  4. package/dist/index.js +328 -102
  5. package/dist/index.js.map +4 -4
  6. package/dist/kernel-worker.js +1705 -695
  7. package/dist/kernel-worker.js.map +4 -4
  8. package/dist/node-driver-worker.js +298 -98
  9. package/dist/node-driver-worker.js.map +4 -4
  10. package/dist/node-process-worker.cjs +849 -193
  11. package/dist/node-process-worker.cjs.map +4 -4
  12. package/dist/node-process-worker.js +849 -193
  13. package/dist/node-process-worker.js.map +4 -4
  14. package/dist/node.js +1875 -743
  15. package/dist/node.js.map +4 -4
  16. package/dist/process-worker.js +805 -149
  17. package/dist/process-worker.js.map +4 -4
  18. package/dist/types/client/attach.d.ts +2 -0
  19. package/dist/types/client/protocol.d.ts +1 -0
  20. package/dist/types/client/serve-client.d.ts +1 -0
  21. package/dist/types/devices-relay.d.ts +74 -0
  22. package/dist/types/index.d.ts +14 -0
  23. package/dist/types/kernel/devices.d.ts +155 -0
  24. package/dist/types/kernel/embedder.d.ts +2 -0
  25. package/dist/types/kernel/fd-table.d.ts +3 -1
  26. package/dist/types/kernel/host.d.ts +2 -0
  27. package/dist/types/kernel/process.d.ts +20 -0
  28. package/dist/types/kernel/serial-ioctl.d.ts +73 -0
  29. package/dist/types/launcher.d.ts +4 -0
  30. package/dist/types/node.d.ts +6 -2
  31. package/dist/types/process/js/js-dev.d.ts +24 -0
  32. package/dist/types/process/kernel-streams.d.ts +11 -1
  33. package/dist/types/process/process-pty.d.ts +1 -0
  34. package/dist/types/process/wasi/wasi-fds.d.ts +1 -0
  35. package/dist/types/process/wasi/wasi-termios.d.ts +1 -0
  36. package/dist/types/serve.d.ts +14 -1
  37. package/package.json +2 -2
@@ -652,8 +652,8 @@ function dirsChannel(metadata) {
652
652
  function noticeOf(data) {
653
653
  if (Array.isArray(data)) return { paths: data.filter((p) => typeof p === "string"), trees: [] };
654
654
  const notice = data ?? {};
655
- const strings2 = (list) => Array.isArray(list) ? list.filter((p) => typeof p === "string") : [];
656
- return { paths: strings2(notice.paths), trees: strings2(notice.trees), outside: true };
655
+ const strings3 = (list) => Array.isArray(list) ? list.filter((p) => typeof p === "string") : [];
656
+ return { paths: strings3(notice.paths), trees: strings3(notice.trees), outside: true };
657
657
  }
658
658
 
659
659
  // src/fs/types.ts
@@ -1427,15 +1427,15 @@ var RemoteWatch = class {
1427
1427
  this.channel.postMessage({ ack: seq2, from: this.tab, to: from });
1428
1428
  }
1429
1429
  hold(add, drop, wait2) {
1430
- const fresh3 = add.filter(
1430
+ const fresh4 = add.filter(
1431
1431
  ([path, fid]) => !this.myHolds.has(path) || this.myHolds.get(path) !== fid
1432
1432
  );
1433
1433
  const gone = drop.filter((path) => this.myHolds.has(path));
1434
1434
  for (const path of gone) this.myHolds.delete(path);
1435
- for (const [path, fid] of fresh3) this.myHolds.set(path, fid);
1436
- if (fresh3.length === 0 && gone.length === 0 || this.peers.size === 0)
1435
+ for (const [path, fid] of fresh4) this.myHolds.set(path, fid);
1436
+ if (fresh4.length === 0 && gone.length === 0 || this.peers.size === 0)
1437
1437
  return Promise.resolve();
1438
- const seq2 = this.sendHolds(fresh3, gone);
1438
+ const seq2 = this.sendHolds(fresh4, gone);
1439
1439
  if (!wait2) return Promise.resolve();
1440
1440
  const heard = new Set(
1441
1441
  [...this.peers].filter((peer) => this.holders.has(peer) && !this.silent.has(peer))
@@ -2121,7 +2121,7 @@ var OpfsFs = class {
2121
2121
  const parent = entries.at(-1);
2122
2122
  const setgid = ((parent?.mode ?? 0) & S_ISGID2) !== 0;
2123
2123
  const parentGid = (ownOf(parent) ?? inherit(entries.slice(0, -1))).gid;
2124
- const want = { uid: owner.uid, gid: setgid ? parentGid : owner.gid };
2124
+ const want2 = { uid: owner.uid, gid: setgid ? parentGid : owner.gid };
2125
2125
  let mode = owner.mode === void 0 ? void 0 : owner.mode & PERM_MASK;
2126
2126
  if (directory && setgid) mode = (mode ?? 493) | S_ISGID2;
2127
2127
  const fields = {};
@@ -2129,7 +2129,7 @@ var OpfsFs = class {
2129
2129
  fields.mode = mode;
2130
2130
  }
2131
2131
  if (self) fields.heir = void 0;
2132
- if (ownOf(self) || !sameOwner(want, inherit(entries))) Object.assign(fields, want);
2132
+ if (ownOf(self) || !sameOwner(want2, inherit(entries))) Object.assign(fields, want2);
2133
2133
  return Object.keys(fields).length > 0 ? fields : void 0;
2134
2134
  }
2135
2135
  async stampNew(path, owner, directory) {
@@ -2366,9 +2366,9 @@ var OpfsFs = class {
2366
2366
  this.lookups++;
2367
2367
  if (split(path).length === 0) return this.root;
2368
2368
  const cached = this.files.get(path);
2369
- const fresh3 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
2370
- if (cached && !fresh3) this.files.delete(path);
2371
- if (cached && fresh3) {
2369
+ const fresh4 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
2370
+ if (cached && !fresh4) this.files.delete(path);
2371
+ if (cached && fresh4) {
2372
2372
  if (this.glanced(cached)) return this.remember(path, cached);
2373
2373
  try {
2374
2374
  await this.glance(cached);
@@ -2586,8 +2586,8 @@ var OpfsFs = class {
2586
2586
  glanced(handle) {
2587
2587
  if (this.exact > 0) return void 0;
2588
2588
  const seen = this.glances.get(handle);
2589
- const fresh3 = seen && seen.changes === this.changes && this.recent(seen, GLANCE_LOOKUPS);
2590
- return fresh3 ? seen.file : void 0;
2589
+ const fresh4 = seen && seen.changes === this.changes && this.recent(seen, GLANCE_LOOKUPS);
2590
+ return fresh4 ? seen.file : void 0;
2591
2591
  }
2592
2592
  async glance(handle) {
2593
2593
  const at = performance.now();
@@ -2789,13 +2789,13 @@ var OpfsFs = class {
2789
2789
  async keptFor(path) {
2790
2790
  const at = normalizePath(path);
2791
2791
  const cached = this.files.get(at);
2792
- const fresh3 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
2793
- const [target, existed] = fresh3 ? [at, true] : await this.writeTarget(path);
2792
+ const fresh4 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
2793
+ const [target, existed] = fresh4 ? [at, true] : await this.writeTarget(path);
2794
2794
  if (!existed) return [void 0, target, false];
2795
2795
  this.refuse(target);
2796
2796
  const [entry] = await this.meta.get([target]);
2797
2797
  if (entry?.fifo || entry?.socket) throw fsError("ENXIO", path);
2798
- const kept = fresh3 ? cached : this.files.get(target);
2798
+ const kept = fresh4 ? cached : this.files.get(target);
2799
2799
  const usable3 = kept && performance.now() - (this.found.get(kept) ?? -CHECK_MS) < CHECK_MS;
2800
2800
  return [usable3 ? kept : void 0, target, true];
2801
2801
  }
@@ -4342,21 +4342,21 @@ function latin1Bytes(text) {
4342
4342
  return out;
4343
4343
  }
4344
4344
  function parseRequestHead(bytes) {
4345
- const lines2 = latin1(bytes).split(/\r?\n/);
4346
- while (lines2.length > 0 && lines2[0] === "") lines2.shift();
4347
- const match = /^([!#$%&'*+\-.^_`|~0-9A-Za-z]+) (\S+) HTTP\/(\d)\.(\d)$/.exec(lines2.shift() ?? "");
4345
+ const lines3 = latin1(bytes).split(/\r?\n/);
4346
+ while (lines3.length > 0 && lines3[0] === "") lines3.shift();
4347
+ const match = /^([!#$%&'*+\-.^_`|~0-9A-Za-z]+) (\S+) HTTP\/(\d)\.(\d)$/.exec(lines3.shift() ?? "");
4348
4348
  if (!match) throw new HttpError(400, "malformed request line");
4349
4349
  if (match[3] !== "1") throw new HttpError(505, `HTTP/${match[3]}.${match[4]} is not supported`);
4350
4350
  return {
4351
4351
  method: match[1],
4352
4352
  target: match[2],
4353
4353
  minor: Number(match[4]),
4354
- headers: parseFields(lines2)
4354
+ headers: parseFields(lines3)
4355
4355
  };
4356
4356
  }
4357
- function parseFields(lines2) {
4357
+ function parseFields(lines3) {
4358
4358
  const headers = [];
4359
- for (const line of lines2) {
4359
+ for (const line of lines3) {
4360
4360
  if (line === "") continue;
4361
4361
  if (line.startsWith(" ") || line.startsWith(" ")) {
4362
4362
  throw new HttpError(400, "obsolete line folding");
@@ -4841,10 +4841,10 @@ var CdpFacade = class {
4841
4841
  reject(new HttpError(502, `the CDP host closed: ${reason ?? "no reason"}`));
4842
4842
  };
4843
4843
  conn.onmessage = (message3) => {
4844
- const parsed2 = JSON.parse(message3);
4845
- if (parsed2.id !== 1) return;
4844
+ const parsed3 = JSON.parse(message3);
4845
+ if (parsed3.id !== 1) return;
4846
4846
  clearTimeout(timer);
4847
- resolve2(parsed2);
4847
+ resolve2(parsed3);
4848
4848
  };
4849
4849
  conn.send(JSON.stringify({ id: 1, method: "Target.getTargets" }));
4850
4850
  }
@@ -5075,10 +5075,129 @@ function portCdpHook(ask, gone) {
5075
5075
  });
5076
5076
  }
5077
5077
 
5078
- // src/embedder-relay.ts
5078
+ // src/devices-relay.ts
5079
+ function quiet() {
5080
+ return void 0;
5081
+ }
5082
+ function raised(failure2) {
5083
+ return Object.assign(new Error(failure2.message), { name: failure2.name });
5084
+ }
5079
5085
  function fresh2(bytes) {
5080
5086
  return bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength ? bytes : bytes.slice();
5081
5087
  }
5088
+ function devicesAsker(port) {
5089
+ const remotes2 = /* @__PURE__ */ new Map();
5090
+ let next3 = 0;
5091
+ const post = (message3, transfer = []) => port.postMessage({ devices: message3 }, transfer);
5092
+ const push = (remote, item) => {
5093
+ remote.chunks.push(item);
5094
+ remote.wake?.();
5095
+ };
5096
+ const streams = (id2, remote) => ({
5097
+ readable: new ReadableStream(
5098
+ {
5099
+ pull: async (controller) => {
5100
+ if (remote.chunks.length === 0) {
5101
+ post({ id: id2, pull: true });
5102
+ await new Promise((resolve2) => {
5103
+ remote.wake = resolve2;
5104
+ });
5105
+ }
5106
+ const item = remote.chunks.shift();
5107
+ if (item instanceof Uint8Array) controller.enqueue(item);
5108
+ else if (item === null || item === void 0) controller.close();
5109
+ else controller.error(raised(item));
5110
+ },
5111
+ cancel: (reason) => {
5112
+ post({ id: id2, cancel: String(reason ?? "closed") });
5113
+ }
5114
+ },
5115
+ { highWaterMark: 0 }
5116
+ ),
5117
+ writable: new WritableStream({
5118
+ write: (chunk2) => {
5119
+ const seq2 = ++remote.seq;
5120
+ const done5 = Promise.withResolvers();
5121
+ remote.writes.set(seq2, done5);
5122
+ const bytes = fresh2(chunk2);
5123
+ post({ id: id2, write: bytes, seq: seq2 }, [bytes.buffer]);
5124
+ return done5.promise;
5125
+ },
5126
+ abort: (reason) => {
5127
+ post({ id: id2, cancel: String(reason ?? "closed") });
5128
+ }
5129
+ }),
5130
+ control: (op, args) => {
5131
+ const seq2 = ++remote.seq;
5132
+ const done5 = Promise.withResolvers();
5133
+ remote.controls.set(seq2, done5);
5134
+ post({ id: id2, control: { seq: seq2, op, ...args ? { args } : {} } });
5135
+ if (op === "close") void done5.promise.finally(() => remotes2.delete(id2)).catch(quiet);
5136
+ return done5.promise;
5137
+ }
5138
+ });
5139
+ const hookFor = (client) => ({
5140
+ open: async (request3) => {
5141
+ const id2 = ++next3;
5142
+ const remote = {
5143
+ opened: Promise.withResolvers(),
5144
+ chunks: [],
5145
+ wake: void 0,
5146
+ writes: /* @__PURE__ */ new Map(),
5147
+ controls: /* @__PURE__ */ new Map(),
5148
+ seq: 0
5149
+ };
5150
+ remotes2.set(id2, remote);
5151
+ const abort = () => {
5152
+ remotes2.delete(id2);
5153
+ post({ id: id2, cancel: "interrupted" });
5154
+ remote.opened.reject(request3.signal.reason);
5155
+ };
5156
+ request3.signal.addEventListener("abort", abort, { once: true });
5157
+ const { path, flags: flags2, stamp } = request3;
5158
+ post({ id: id2, open: { client, path, flags: flags2, stamp } });
5159
+ try {
5160
+ await remote.opened.promise;
5161
+ } finally {
5162
+ request3.signal.removeEventListener("abort", abort);
5163
+ }
5164
+ return streams(id2, remote);
5165
+ }
5166
+ });
5167
+ const answer2 = (message3) => {
5168
+ const remote = remotes2.get(message3.id);
5169
+ if (!remote) return;
5170
+ if ("opened" in message3) remote.opened.resolve();
5171
+ else if ("failed" in message3) {
5172
+ remotes2.delete(message3.id);
5173
+ remote.opened.reject(raised(message3.failed));
5174
+ } else if ("chunk" in message3) push(remote, message3.chunk);
5175
+ else if ("done" in message3) push(remote, null);
5176
+ else if ("readError" in message3) push(remote, message3.readError);
5177
+ else if ("wrote" in message3) {
5178
+ remote.writes.get(message3.wrote)?.resolve();
5179
+ remote.writes.delete(message3.wrote);
5180
+ } else if ("writeError" in message3) {
5181
+ remote.writes.get(message3.seq)?.reject(raised(message3.writeError));
5182
+ remote.writes.delete(message3.seq);
5183
+ } else if ("answered" in message3) {
5184
+ remote.controls.get(message3.answered)?.resolve(message3.value);
5185
+ remote.controls.delete(message3.answered);
5186
+ } else {
5187
+ remote.controls.get(message3.refused)?.reject(raised(message3.error));
5188
+ remote.controls.delete(message3.refused);
5189
+ }
5190
+ };
5191
+ return { hookFor, answer: answer2 };
5192
+ }
5193
+ function devicesReplyOf(data) {
5194
+ return data?.devicesReply;
5195
+ }
5196
+
5197
+ // src/embedder-relay.ts
5198
+ function fresh3(bytes) {
5199
+ return bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength ? bytes : bytes.slice();
5200
+ }
5082
5201
  function embedderAsker(port) {
5083
5202
  const asks = /* @__PURE__ */ new Map();
5084
5203
  let next3 = 0;
@@ -5117,7 +5236,7 @@ function embedderAsker(port) {
5117
5236
  const got = await asked.body.read().catch(() => ({ done: true, value: void 0 }));
5118
5237
  if (got.done) post({ id: id2, bodyEnd: true });
5119
5238
  else {
5120
- const bytes = fresh2(got.value);
5239
+ const bytes = fresh3(got.value);
5121
5240
  post({ id: id2, body: bytes }, [bytes.buffer]);
5122
5241
  }
5123
5242
  };
@@ -5288,11 +5407,11 @@ function permOf(st, link2) {
5288
5407
  ...link2 !== void 0 ? { link: link2 } : {}
5289
5408
  };
5290
5409
  }
5291
- function permits(cred, perm, want, directory = true) {
5410
+ function permits(cred, perm, want2, directory = true) {
5292
5411
  const mode = perm.mode ?? (directory ? 493 : 420);
5293
- if (cred.euid === 0) return (want & X_OK) === 0 || directory || (mode & 73) !== 0;
5412
+ if (cred.euid === 0) return (want2 & X_OK) === 0 || directory || (mode & 73) !== 0;
5294
5413
  const shift = cred.euid === perm.uid ? 6 : inGroup(cred, perm.gid) ? 3 : 0;
5295
- return (mode >> shift & want) === want;
5414
+ return (mode >> shift & want2) === want2;
5296
5415
  }
5297
5416
  function sticky(cred, dir, entry) {
5298
5417
  if (cred.euid === 0 || ((dir.mode ?? 0) & S_ISVTX) === 0) return true;
@@ -5367,27 +5486,27 @@ async function present(fs, path, directory) {
5367
5486
  const st = await fs.lstat(path).catch(() => void 0);
5368
5487
  return st !== void 0 && (!directory || st.isDirectory);
5369
5488
  }
5370
- async function finalOf(fs, walked, want, options) {
5489
+ async function finalOf(fs, walked, want2, options) {
5371
5490
  const perm = walked.perm;
5372
5491
  if (options.follow === false && perm.link !== void 0) {
5373
5492
  return [{ ...perm, mode: 511 }, false];
5374
5493
  }
5375
- if ((want & X_OK) === 0 || options.directory !== void 0) {
5494
+ if ((want2 & X_OK) === 0 || options.directory !== void 0) {
5376
5495
  return [perm, options.directory !== false];
5377
5496
  }
5378
5497
  const look = options.follow === false ? fs.lstat : fs.stat;
5379
5498
  const st = await look.call(fs, walked.path).catch(() => void 0);
5380
5499
  return st ? [permOf(st), st.isDirectory] : [perm, false];
5381
5500
  }
5382
- async function access(fs, cred, path, want, options = {}) {
5501
+ async function access(fs, cred, path, want2, options = {}) {
5383
5502
  const walked = await walk(fs, cred, path, options.follow !== false, options.links);
5384
5503
  if (walked.denied !== void 0) {
5385
5504
  if (walked.link || await present(fs, walked.denied, true)) return walked;
5386
5505
  return { path, perm: void 0, missing: true };
5387
5506
  }
5388
5507
  if (!walked.perm) return { ...walked, missing: true };
5389
- const [perm, directory] = await finalOf(fs, walked, want, options);
5390
- if (want === 0 || permits(cred, perm, want, directory)) return walked;
5508
+ const [perm, directory] = await finalOf(fs, walked, want2, options);
5509
+ if (want2 === 0 || permits(cred, perm, want2, directory)) return walked;
5391
5510
  if (await present(fs, walked.path, false)) return { ...walked, denied: walked.path };
5392
5511
  return { ...walked, missing: true };
5393
5512
  }
@@ -5493,11 +5612,11 @@ function ttyOpenable(g, tty) {
5493
5612
  if (!g || !tty.owner || permits(g.cred, tty.owner, R_OK | W_OK, false)) return;
5494
5613
  deny(g, "open", tty.name ?? "/dev/tty");
5495
5614
  }
5496
- async function need(g, op, path, want, options = {}, shown2 = path) {
5497
- if (want & W_OK && shown2 === path) await fence(g, op, path);
5498
- const walked = await access(g.fs, g.cred, path, want, linked(g, options));
5615
+ async function need(g, op, path, want2, options = {}, shown2 = path) {
5616
+ if (want2 & W_OK && shown2 === path) await fence(g, op, path);
5617
+ const walked = await access(g.fs, g.cred, path, want2, linked(g, options));
5499
5618
  if (walked.denied !== void 0) deny(g, op, walked.link ? walked.denied : shown2);
5500
- else if (want & R_OK && g.fs.commandTarget) await readsAs(g, op, walked.path, shown2);
5619
+ else if (want2 & R_OK && g.fs.commandTarget) await readsAs(g, op, walked.path, shown2);
5501
5620
  return walked;
5502
5621
  }
5503
5622
  async function readsAs(g, op, path, shown2) {
@@ -5646,10 +5765,10 @@ async function creatable(g, op, path) {
5646
5765
  const target = await need(g, op, path, 0);
5647
5766
  if (target.denied === void 0) await need(g, op, parentOf5(target.path), W_OK | X_OK, DIR, path);
5648
5767
  }
5649
- async function openable(g, op, path, want, create) {
5768
+ async function openable(g, op, path, want2, create) {
5650
5769
  const st = create ? await g.fs.stat(path).catch(() => void 0) : void 0;
5651
5770
  if (create && !st) return creatable(g, op, path);
5652
- await need(g, op, path, want, { directory: st?.isDirectory ?? false });
5771
+ await need(g, op, path, want2, { directory: st?.isDirectory ?? false });
5653
5772
  }
5654
5773
 
5655
5774
  // src/kernel/cred.ts
@@ -6090,8 +6209,8 @@ var FdTable = class _FdTable {
6090
6209
  }
6091
6210
  stdioTerminal() {
6092
6211
  for (const fd of [0, 1, 2]) {
6093
- const tty = this.fds.get(fd)?.file.tty;
6094
- if (tty) return tty;
6212
+ const file = this.fds.get(fd)?.file;
6213
+ if (file?.tty && !file.device) return file.tty;
6095
6214
  }
6096
6215
  return void 0;
6097
6216
  }
@@ -6392,13 +6511,13 @@ function addressBits(host) {
6392
6511
  function parsePrefix(text) {
6393
6512
  const parts2 = text.trim().split("/");
6394
6513
  if (parts2.length > 2) return void 0;
6395
- const [address = "", size] = parts2;
6396
- const parsed2 = addressBits(address);
6397
- if (!parsed2) return void 0;
6398
- const max = parsed2.v6 ? 128 : 32;
6514
+ const [address2 = "", size] = parts2;
6515
+ const parsed3 = addressBits(address2);
6516
+ if (!parsed3) return void 0;
6517
+ const max = parsed3.v6 ? 128 : 32;
6399
6518
  const length2 = size === void 0 ? max : /^\d{1,3}$/.test(size) ? Number(size) : -1;
6400
6519
  if (length2 < 0 || length2 > max) return void 0;
6401
- return { ...parsed2, length: length2 };
6520
+ return { ...parsed3, length: length2 };
6402
6521
  }
6403
6522
  function within4(prefix, addr) {
6404
6523
  if (prefix.v6 !== addr.v6) return false;
@@ -6424,10 +6543,10 @@ var Routes = class {
6424
6543
  this.ipv6 = ipv62;
6425
6544
  }
6426
6545
  set(table) {
6427
- const parsed2 = (table.prefixes ?? []).map(parsePrefix);
6428
- const bad = (table.prefixes ?? []).find((_, i) => !parsed2[i]);
6546
+ const parsed3 = (table.prefixes ?? []).map(parsePrefix);
6547
+ const bad = (table.prefixes ?? []).find((_, i) => !parsed3[i]);
6429
6548
  if (bad !== void 0) throw new Error(`not an address prefix: ${bad}`);
6430
- this.table = parsed2;
6549
+ this.table = parsed3;
6431
6550
  this.exit = table.exit === true;
6432
6551
  }
6433
6552
  classify(host) {
@@ -6963,10 +7082,10 @@ var KernelSocket = class _KernelSocket {
6963
7082
  }
6964
7083
  };
6965
7084
  let made2;
6966
- const abandon = () => {
7085
+ const abandon2 = () => {
6967
7086
  if (!keeps(signal.reason)) view2.close();
6968
7087
  };
6969
- signal.addEventListener("abort", abandon, { once: true });
7088
+ signal.addEventListener("abort", abandon2, { once: true });
6970
7089
  try {
6971
7090
  made2 = await make(view2);
6972
7091
  } catch (err) {
@@ -6979,7 +7098,7 @@ var KernelSocket = class _KernelSocket {
6979
7098
  throw signal.aborted ? signal.reason : err;
6980
7099
  } finally {
6981
7100
  handshaking = false;
6982
- signal.removeEventListener("abort", abandon);
7101
+ signal.removeEventListener("abort", abandon2);
6983
7102
  }
6984
7103
  const plain3 = uplinkLink(made2.conn, (err) => {
6985
7104
  this.reset = err instanceof KernelError && err.code === "EPROTO" ? err : new KernelError("ECONNRESET");
@@ -8128,7 +8247,7 @@ var UserDb = class {
8128
8247
  joined(name2, extra);
8129
8248
  group.push({ name: name2, gid: uid, members: [] });
8130
8249
  passwd.push(row);
8131
- const fresh3 = !await this.fs.exists(home);
8250
+ const fresh4 = !await this.fs.exists(home);
8132
8251
  await this.fs.mkdir(home, { recursive: true });
8133
8252
  try {
8134
8253
  await this.provision(row, this.umaskFor(options), shared2);
@@ -8136,7 +8255,7 @@ var UserDb = class {
8136
8255
  await this.fs.mkdir("/etc", { recursive: true });
8137
8256
  await this.commit(formatPasswd(passwd), formatGroup(group));
8138
8257
  } catch (err) {
8139
- if (fresh3) await this.fs.rm(home, { recursive: true, force: true });
8258
+ if (fresh4) await this.fs.rm(home, { recursive: true, force: true });
8140
8259
  throw err;
8141
8260
  }
8142
8261
  remember();
@@ -8220,7 +8339,7 @@ var UserDb = class {
8220
8339
  };
8221
8340
 
8222
8341
  // src/client/protocol.ts
8223
- var PROTOCOL = [1, 17];
8342
+ var PROTOCOL = [1, 18];
8224
8343
  var FS_METHODS = [
8225
8344
  "readFile",
8226
8345
  "writeFile",
@@ -8480,8 +8599,10 @@ function hostRegistries(send2, gone, port) {
8480
8599
  const cdps = cdpRegistry(send2, gone);
8481
8600
  const facades = facadeRegistry(send2, gone);
8482
8601
  const requests = requestBinding(send2, port);
8602
+ const devices = deviceBinding(send2, port);
8483
8603
  return {
8484
8604
  requests,
8605
+ devices,
8485
8606
  answer(l, req) {
8486
8607
  if (req.op === "serve-cdp") return cdps.serve(l, req);
8487
8608
  if (req.op === "unserve-cdp") return cdps.drop(req.registration);
@@ -8490,6 +8611,7 @@ function hostRegistries(send2, gone, port) {
8490
8611
  },
8491
8612
  close() {
8492
8613
  requests.close();
8614
+ devices.close();
8493
8615
  cdps.close();
8494
8616
  facades.close();
8495
8617
  }
@@ -8603,6 +8725,28 @@ function answered(pending, req, reply, detach) {
8603
8725
  }
8604
8726
  );
8605
8727
  }
8728
+ async function helloRefused(host, hosts, message3) {
8729
+ return versionError(message3.protocol) ?? await hosts.requests.accept(host, message3) ?? await hosts.devices.accept(host, message3);
8730
+ }
8731
+ function deviceBinding(send2, port) {
8732
+ const asker = devicesAsker({ postMessage: (m, t) => send2(m, t) });
8733
+ let unbind;
8734
+ port.addEventListener("message", (event) => {
8735
+ const reply = devicesReplyOf(event.data);
8736
+ if (reply) asker.answer(reply);
8737
+ });
8738
+ return {
8739
+ async accept(host, hello) {
8740
+ if (!hello.devices) return void 0;
8741
+ if (!host.attachDevices || host.session === void 0)
8742
+ return "EPERM: this port may not answer device opens";
8743
+ const launcher = await host.launcher();
8744
+ unbind = launcher.devices.register(host.session, asker.hookFor(host.session));
8745
+ return void 0;
8746
+ },
8747
+ close: () => unbind?.()
8748
+ };
8749
+ }
8606
8750
  function serveClient(port, host) {
8607
8751
  let state = "new";
8608
8752
  let self;
@@ -8636,9 +8780,9 @@ function serveClient(port, host) {
8636
8780
  ended.resolve();
8637
8781
  }
8638
8782
  async function hello(message3) {
8639
- const refused4 = versionError(message3.protocol) ?? await hosts.requests.accept(host, message3);
8640
- if (refused4) {
8641
- send2({ hello: { protocol: PROTOCOL, error: refused4 } });
8783
+ const refused5 = await helloRefused(host, hosts, message3);
8784
+ if (refused5) {
8785
+ send2({ hello: { protocol: PROTOCOL, error: refused5 } });
8642
8786
  detach();
8643
8787
  return;
8644
8788
  }
@@ -8922,7 +9066,7 @@ function satisfies(version, range2) {
8922
9066
  }
8923
9067
 
8924
9068
  // src/version.ts
8925
- var KERNEL_VERSION = true ? "1.182.0" : "0.0.0-dev";
9069
+ var KERNEL_VERSION = true ? "1.183.0" : "0.0.0-dev";
8926
9070
  var OS_RELEASE = "6.0.0-slicc";
8927
9071
  function procVersion() {
8928
9072
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -9214,10 +9358,10 @@ async function tierPackages(fs, physical, roots, bins, kernel, global) {
9214
9358
  const own = commandsOf(pkg, manifest);
9215
9359
  const named = new Set(own.map((command) => command.name));
9216
9360
  const extra = bins ? await binCommandsOf(physical, pkg, manifest, named) : [];
9217
- const refused4 = engineRefusal(manifest, kernel);
9361
+ const refused5 = engineRefusal(manifest, kernel);
9218
9362
  const name2 = pkg.slice(root.length + 1);
9219
9363
  const dependency = direct?.has(name2) === false;
9220
- const marks = { ...refused4 ? { refused: refused4 } : {}, ...dependency ? { dependency } : {} };
9364
+ const marks = { ...refused5 ? { refused: refused5 } : {}, ...dependency ? { dependency } : {} };
9221
9365
  out.push({
9222
9366
  name: name2,
9223
9367
  pkg,
@@ -9249,7 +9393,7 @@ function winners(tier) {
9249
9393
  }
9250
9394
  return out;
9251
9395
  }
9252
- async function scanCommands(fs, modules, binsFrom = Number.POSITIVE_INFINITY, kernel = KERNEL_VERSION, refused4, global = /* @__PURE__ */ new Set()) {
9396
+ async function scanCommands(fs, modules, binsFrom = Number.POSITIVE_INFINITY, kernel = KERNEL_VERSION, refused5, global = /* @__PURE__ */ new Set()) {
9253
9397
  const found = /* @__PURE__ */ new Map();
9254
9398
  const physical = physicalResolver(fs);
9255
9399
  const tiers = (typeof modules === "string" ? [modules] : modules).map(
@@ -9263,7 +9407,7 @@ async function scanCommands(fs, modules, binsFrom = Number.POSITIVE_INFINITY, ke
9263
9407
  const dependencies = [];
9264
9408
  for (const [index, roots] of tiers.entries()) {
9265
9409
  const tier = await tierPackages(fs, physical, roots, index >= binsFrom, kernel, global);
9266
- if (refused4) refused4.push(...tier.flatMap((entry) => entry.commands.filter((c) => c.refused)));
9410
+ if (refused5) refused5.push(...tier.flatMap((entry) => entry.commands.filter((c) => c.refused)));
9267
9411
  await take(tier.filter((entry) => !entry.dependency));
9268
9412
  dependencies.push(tier.filter((entry) => entry.dependency));
9269
9413
  }
@@ -9867,9 +10011,9 @@ var TAGS_VERSION = 1;
9867
10011
  async function readTags(fs) {
9868
10012
  const text = await fs.readFile(TAGS_PATH).catch(() => "");
9869
10013
  try {
9870
- const parsed2 = JSON.parse(text);
9871
- if (!parsed2 || typeof parsed2 !== "object") return {};
9872
- const { v: _, ...tables } = parsed2;
10014
+ const parsed3 = JSON.parse(text);
10015
+ if (!parsed3 || typeof parsed3 !== "object") return {};
10016
+ const { v: _, ...tables } = parsed3;
9873
10017
  return tables;
9874
10018
  } catch {
9875
10019
  return {};
@@ -9939,27 +10083,27 @@ var Crontabs = class {
9939
10083
  const json5 = JSON.stringify({ v: TAGS_VERSION, ...kept });
9940
10084
  if (json5 === await fs.readFile(TAGS_PATH).catch(() => "")) return;
9941
10085
  const owner = owners ? { uid: 0, gid: 0, mode: 384 } : void 0;
9942
- const fresh3 = `${TAGS_PATH}.${crypto.randomUUID()}.new`;
9943
- await fs.writeFile(fresh3, json5, owner);
9944
- await fs.rename(fresh3, TAGS_PATH);
10086
+ const fresh4 = `${TAGS_PATH}.${crypto.randomUUID()}.new`;
10087
+ await fs.writeFile(fresh4, json5, owner);
10088
+ await fs.rename(fresh4, TAGS_PATH);
9945
10089
  }
9946
10090
  async install(path, target, text, caller) {
9947
10091
  if (text.length > CRONTAB_MAX) return { error: "crontab: the table is too long" };
9948
10092
  const { errors } = parseCrontab(text, path);
9949
10093
  if (errors.length > 0) {
9950
- const lines2 = errors.map(({ line, message: message3 }) => `"-":${line}: ${message3}`);
9951
- return { error: ["crontab: errors in crontab file, cannot install", ...lines2].join("\n") };
10094
+ const lines3 = errors.map(({ line, message: message3 }) => `"-":${line}: ${message3}`);
10095
+ return { error: ["crontab: errors in crontab file, cannot install", ...lines3].join("\n") };
9952
10096
  }
9953
10097
  const { fs, owners } = this.o;
9954
10098
  if (!await fs.exists(SPOOL_DIR)) {
9955
10099
  await fs.mkdir(SPOOL_DIR, { recursive: true });
9956
10100
  if (owners) await fs.chown?.(SPOOL_DIR, { uid: 0, gid: 0, mode: 448, follow: false });
9957
10101
  }
9958
- const fresh3 = `${SPOOL_DIR}/.${target.name}.${crypto.randomUUID()}.new`;
10102
+ const fresh4 = `${SPOOL_DIR}/.${target.name}.${crypto.randomUUID()}.new`;
9959
10103
  const owner = owners ? { uid: target.uid, gid: target.gid, mode: 384 } : void 0;
9960
10104
  await this.retag(path, text, caller);
9961
- await fs.writeFile(fresh3, text, owner);
9962
- await fs.rename(fresh3, path);
10105
+ await fs.writeFile(fresh4, text, owner);
10106
+ await fs.rename(fresh4, path);
9963
10107
  return { text: "" };
9964
10108
  }
9965
10109
  };
@@ -10280,15 +10424,15 @@ var UnlinkedKeeper = class {
10280
10424
  const releases = [];
10281
10425
  const take = async () => {
10282
10426
  for (; ; ) {
10283
- const fresh3 = [...this.heldWithin(target).keys()].filter((path) => !paths.includes(path));
10284
- if (fresh3.length === 0) return;
10285
- fresh3.sort();
10286
- if (paths.some((path) => path > fresh3[0])) {
10427
+ const fresh4 = [...this.heldWithin(target).keys()].filter((path) => !paths.includes(path));
10428
+ if (fresh4.length === 0) return;
10429
+ fresh4.sort();
10430
+ if (paths.some((path) => path > fresh4[0])) {
10287
10431
  for (const release of releases.splice(0)) release();
10288
- fresh3.push(...paths.splice(0));
10289
- fresh3.sort();
10432
+ fresh4.push(...paths.splice(0));
10433
+ fresh4.sort();
10290
10434
  }
10291
- for (const path of fresh3) {
10435
+ for (const path of fresh4) {
10292
10436
  paths.push(path);
10293
10437
  releases.push(await this.gate.acquire(path));
10294
10438
  }
@@ -10684,10 +10828,10 @@ var Output = class {
10684
10828
  };
10685
10829
  function readStamp(text) {
10686
10830
  try {
10687
- const parsed2 = JSON.parse(text ?? "");
10688
- const jobs = parsed2.jobs && typeof parsed2.jobs === "object" ? parsed2.jobs : {};
10689
- const reboot = parsed2.reboot && typeof parsed2.reboot === "object" ? parsed2.reboot : {};
10690
- const when = parsed2.when && typeof parsed2.when === "object" ? parsed2.when : {};
10831
+ const parsed3 = JSON.parse(text ?? "");
10832
+ const jobs = parsed3.jobs && typeof parsed3.jobs === "object" ? parsed3.jobs : {};
10833
+ const reboot = parsed3.reboot && typeof parsed3.reboot === "object" ? parsed3.reboot : {};
10834
+ const when = parsed3.when && typeof parsed3.when === "object" ? parsed3.when : {};
10691
10835
  return { jobs: { ...jobs }, reboot: { ...reboot }, when: { ...when } };
10692
10836
  } catch {
10693
10837
  return { jobs: {}, reboot: {}, when: {} };
@@ -10807,10 +10951,10 @@ var CronService = class {
10807
10951
  const text = JSON.stringify(stamp);
10808
10952
  const old = await fs.readFile(STAMP_PATH).catch(() => void 0);
10809
10953
  if (old === text || old === void 0 && text === EMPTY_STAMP) return;
10810
- const fresh3 = `${STAMP_PATH}.new`;
10954
+ const fresh4 = `${STAMP_PATH}.new`;
10811
10955
  const owner = owners ? { uid: 0, gid: 0, mode: 384 } : void 0;
10812
- await fs.writeFile(fresh3, text, owner);
10813
- await fs.rename(fresh3, STAMP_PATH);
10956
+ await fs.writeFile(fresh4, text, owner);
10957
+ await fs.rename(fresh4, STAMP_PATH);
10814
10958
  }
10815
10959
  async load() {
10816
10960
  const { fs } = this.host;
@@ -10863,10 +11007,10 @@ var CronService = class {
10863
11007
  await this.once(path, text, errors);
10864
11008
  return table;
10865
11009
  }
10866
- async once(path, version, lines2) {
11010
+ async once(path, version, lines3) {
10867
11011
  if (this.reported.get(path) === version) return;
10868
11012
  this.reported.set(path, version);
10869
- for (const line of lines2) await this.error(line);
11013
+ for (const line of lines3) await this.error(line);
10870
11014
  }
10871
11015
  async error(line) {
10872
11016
  if (this.stopping.signal.aborted) return;
@@ -11100,8 +11244,8 @@ function canonicalHost2(host) {
11100
11244
  function localV4(a, b) {
11101
11245
  return a === 127 || a === 0 || a === 169 && b === 254;
11102
11246
  }
11103
- function hextets(address) {
11104
- const [head, tail2, extra] = address.split("::");
11247
+ function hextets(address2) {
11248
+ const [head, tail2, extra] = address2.split("::");
11105
11249
  if (extra !== void 0) return void 0;
11106
11250
  const part = (text) => text ? text.split(":").map((h) => Number.parseInt(h, 16)) : [];
11107
11251
  const front = part(head);
@@ -11109,8 +11253,8 @@ function hextets(address) {
11109
11253
  const groups = tail2 === void 0 ? front : [...front, ...new Array(8 - front.length - back.length).fill(0), ...back];
11110
11254
  return groups.length === 8 && groups.every((g) => g >= 0 && g <= 65535) ? groups : void 0;
11111
11255
  }
11112
- function localV6(address) {
11113
- const g = hextets(address);
11256
+ function localV6(address2) {
11257
+ const g = hextets(address2);
11114
11258
  if (!g) return true;
11115
11259
  if ((g[0] & 65472) === 65152) return true;
11116
11260
  const zeroPrefix = g.slice(0, 5).every((x) => x === 0);
@@ -11483,8 +11627,8 @@ var RealmProxy = class {
11483
11627
  }
11484
11628
  async connect(conn, incoming, req, idle2) {
11485
11629
  const target = tunnelTarget(req);
11486
- const refused4 = target.port === 443 ? await this.tunnelReady(target) : void 0;
11487
- if (refused4) throw refused4;
11630
+ const refused5 = target.port === 443 ? await this.tunnelReady(target) : void 0;
11631
+ if (refused5) throw refused5;
11488
11632
  await conn.write(latin1Bytes("HTTP/1.1 200 Connection Established\r\n\r\n"), this.stop.signal);
11489
11633
  try {
11490
11634
  const first = await incoming.peek(this.stop.signal);
@@ -11928,15 +12072,15 @@ async function followLinks(fs, path) {
11928
12072
  }
11929
12073
  var NOBODY = { ruid: -1, euid: -1, suid: -1, rgid: -1, egid: -1, sgid: -1, groups: [] };
11930
12074
  var EMPTY2 = new Uint8Array(0);
11931
- function quiet(promise) {
12075
+ function quiet2(promise) {
11932
12076
  return promise.catch(() => void 0);
11933
12077
  }
11934
12078
  async function readsAs2(fs, cred, target) {
11935
- const walked = await quiet(access(fs, cred, target, R_OK, { directory: false }));
12079
+ const walked = await quiet2(access(fs, cred, target, R_OK, { directory: false }));
11936
12080
  return walked !== void 0 && walked.denied === void 0 && !walked.missing;
11937
12081
  }
11938
12082
  async function shownTarget(fs, target) {
11939
- const st = await quiet(fs.stat(target));
12083
+ const st = await quiet2(fs.stat(target));
11940
12084
  return st?.isFile && await readsAs2(fs, NOBODY, target) ? st : void 0;
11941
12085
  }
11942
12086
  async function madeFor(owner, virtual) {
@@ -12259,10 +12403,10 @@ var FreshModules = class {
12259
12403
  this.fresh.set(key, { umask: mask, pid, added: 0, setup: mask !== DEFAULT_UMASK });
12260
12404
  }
12261
12405
  forget(pid) {
12262
- for (const [path, fresh3] of this.fresh) if (fresh3.pid === pid) this.fresh.delete(path);
12406
+ for (const [path, fresh4] of this.fresh) if (fresh4.pid === pid) this.fresh.delete(path);
12263
12407
  }
12264
- drop(fresh3) {
12265
- for (const [key, note] of [...this.fresh]) if (note === fresh3) this.fresh.delete(key);
12408
+ drop(fresh4) {
12409
+ for (const [key, note] of [...this.fresh]) if (note === fresh4) this.fresh.delete(key);
12266
12410
  }
12267
12411
  moved(from, to) {
12268
12412
  const source = normalizePath(from);
@@ -12271,10 +12415,10 @@ var FreshModules = class {
12271
12415
  for (const key of [...this.fresh.keys()]) {
12272
12416
  if (key === target || within6(key, target)) this.fresh.delete(key);
12273
12417
  }
12274
- for (const [key, fresh3] of [...this.fresh]) {
12418
+ for (const [key, fresh4] of [...this.fresh]) {
12275
12419
  if (key !== source && !within6(key, source)) continue;
12276
12420
  this.fresh.delete(key);
12277
- this.fresh.set(target + key.slice(source.length), fresh3);
12421
+ this.fresh.set(target + key.slice(source.length), fresh4);
12278
12422
  }
12279
12423
  }
12280
12424
  wrap(fs) {
@@ -12284,15 +12428,15 @@ var FreshModules = class {
12284
12428
  };
12285
12429
  const decide = async (given, known) => {
12286
12430
  const path = normalizePath(given);
12287
- const fresh3 = this.fresh.get(path);
12288
- if (!fresh3) return;
12431
+ const fresh4 = this.fresh.get(path);
12432
+ if (!fresh4) return;
12289
12433
  const bytes = known && known.length >= MAGIC.length ? known : await head(path);
12290
- const want = isModule(bytes) ? 73 & ~fresh3.umask : 0;
12291
- fresh3.setup = false;
12292
- if (want === fresh3.added) return;
12434
+ const want2 = isModule(bytes) ? 73 & ~fresh4.umask : 0;
12435
+ fresh4.setup = false;
12436
+ if (want2 === fresh4.added) return;
12293
12437
  const mode = (await fs.stat(path)).mode & 4095;
12294
- await fs.chmod(path, mode & ~fresh3.added | want);
12295
- fresh3.added = want;
12438
+ await fs.chmod(path, mode & ~fresh4.added | want2);
12439
+ fresh4.added = want2;
12296
12440
  };
12297
12441
  const overrides = {
12298
12442
  writeFile: async (path, content, owner) => {
@@ -12305,9 +12449,9 @@ var FreshModules = class {
12305
12449
  }
12306
12450
  },
12307
12451
  chmod: async (path, mode) => {
12308
- const fresh3 = this.fresh.get(normalizePath(path));
12309
- if (fresh3?.setup && (mode & 4095) === (438 & ~fresh3.umask)) fresh3.setup = false;
12310
- else if (fresh3) this.drop(fresh3);
12452
+ const fresh4 = this.fresh.get(normalizePath(path));
12453
+ if (fresh4?.setup && (mode & 4095) === (438 & ~fresh4.umask)) fresh4.setup = false;
12454
+ else if (fresh4) this.drop(fresh4);
12311
12455
  await fs.chmod(path, mode);
12312
12456
  },
12313
12457
  rm: async (path, options) => {
@@ -12324,16 +12468,16 @@ var FreshModules = class {
12324
12468
  link: async (from, to, follow2) => {
12325
12469
  await fs.link(from, to, follow2);
12326
12470
  const source = follow2 && fs.realpath ? await fs.realpath(from, true).catch(() => from) : from;
12327
- const fresh3 = this.fresh.get(normalizePath(source));
12328
- if (fresh3) this.fresh.set(normalizePath(to), fresh3);
12471
+ const fresh4 = this.fresh.get(normalizePath(source));
12472
+ if (fresh4) this.fresh.set(normalizePath(to), fresh4);
12329
12473
  }
12330
12474
  };
12331
12475
  const { append } = fs;
12332
12476
  if (append) {
12333
12477
  overrides.append = async (path, bytes, options) => {
12334
- const fresh3 = this.fresh.has(normalizePath(path));
12478
+ const fresh4 = this.fresh.has(normalizePath(path));
12335
12479
  const end = await append.call(fs, path, bytes, options);
12336
- if (fresh3 && end - bytes.length < MAGIC.length) await decide(path);
12480
+ if (fresh4 && end - bytes.length < MAGIC.length) await decide(path);
12337
12481
  return end;
12338
12482
  };
12339
12483
  }
@@ -12660,7 +12804,7 @@ var FsWatchers = class {
12660
12804
  };
12661
12805
  const content = (run2, types) => async (...args) => {
12662
12806
  const at = normalizePath(args[0]);
12663
- const fresh3 = this.covers(at) && !await fs.exists(at).catch(() => true);
12807
+ const fresh4 = this.covers(at) && !await fs.exists(at).catch(() => true);
12664
12808
  let done5 = false;
12665
12809
  try {
12666
12810
  await run2(...args);
@@ -12669,11 +12813,11 @@ var FsWatchers = class {
12669
12813
  const real = await realOf(at);
12670
12814
  const paths = real === at ? [at] : [at, real];
12671
12815
  this.report(paths);
12672
- if (done5) for (const type of types(fresh3, args)) this.emit(type, paths);
12816
+ if (done5) for (const type of types(fresh4, args)) this.emit(type, paths);
12673
12817
  }
12674
12818
  };
12675
- const written2 = (fresh3) => fresh3 ? ["rename", "change"] : ["change"];
12676
- const overwritten = (fresh3, args) => fresh3 || args[1].length === 0 ? written2(fresh3) : ["change", "change"];
12819
+ const written2 = (fresh4) => fresh4 ? ["rename", "change"] : ["change"];
12820
+ const overwritten = (fresh4, args) => fresh4 || args[1].length === 0 ? written2(fresh4) : ["change", "change"];
12677
12821
  const reshaping = (run2) => async (...args) => {
12678
12822
  this.reshaped();
12679
12823
  try {
@@ -12706,14 +12850,14 @@ var FsWatchers = class {
12706
12850
  if (created4.length > 0 || !await fs.exists(ancestor)) created4.push(ancestor);
12707
12851
  }
12708
12852
  }
12709
- const fresh3 = !options?.recursive || this.typed.size > 0 && !await fs.exists(path);
12853
+ const fresh4 = !options?.recursive || this.typed.size > 0 && !await fs.exists(path);
12710
12854
  let done5 = false;
12711
12855
  try {
12712
12856
  await fs.mkdir(path, options);
12713
12857
  done5 = true;
12714
12858
  } finally {
12715
12859
  this.report([...created4, path]);
12716
- if (done5) this.emit("rename", fresh3 ? [...created4, path] : created4);
12860
+ if (done5) this.emit("rename", fresh4 ? [...created4, path] : created4);
12717
12861
  }
12718
12862
  },
12719
12863
  rm: reshaping(async (path, options) => {
@@ -13223,15 +13367,15 @@ function embedderFailure(err) {
13223
13367
  return new KernelError(ERRNOS[String(name2)] ?? "EIO", lineOf2(err));
13224
13368
  }
13225
13369
  function headerOf(bytes) {
13226
- let parsed2;
13370
+ let parsed3;
13227
13371
  try {
13228
- parsed2 = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
13372
+ parsed3 = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
13229
13373
  } catch {
13230
13374
  throw new KernelError("EINVAL", "the request header is not JSON");
13231
13375
  }
13232
- if (typeof parsed2 !== "object" || parsed2 === null || Array.isArray(parsed2))
13376
+ if (typeof parsed3 !== "object" || parsed3 === null || Array.isArray(parsed3))
13233
13377
  throw new KernelError("EINVAL", "the request header is not an object");
13234
- const { kind, ...options } = parsed2;
13378
+ const { kind, ...options } = parsed3;
13235
13379
  if (typeof kind !== "string" || kind.length > 64 || !KIND.test(kind))
13236
13380
  throw new KernelError("EINVAL", "the request has no valid kind");
13237
13381
  return { kind, options };
@@ -13658,6 +13802,966 @@ var EmbedderBroker = class {
13658
13802
  }
13659
13803
  };
13660
13804
 
13805
+ // src/kernel/serial-ioctl.ts
13806
+ var IOCTL = {
13807
+ TCGETS: 21505,
13808
+ TCSETS: 21506,
13809
+ TCSETSW: 21507,
13810
+ TCSETSF: 21508,
13811
+ TCSBRK: 21513,
13812
+ TCFLSH: 21515,
13813
+ TIOCEXCL: 21516,
13814
+ TIOCNXCL: 21517,
13815
+ TIOCSCTTY: 21518,
13816
+ TIOCGPGRP: 21519,
13817
+ TIOCSPGRP: 21520,
13818
+ TIOCOUTQ: 21521,
13819
+ TIOCGWINSZ: 21523,
13820
+ TIOCSWINSZ: 21524,
13821
+ TIOCMGET: 21525,
13822
+ TIOCMBIS: 21526,
13823
+ TIOCMBIC: 21527,
13824
+ TIOCMSET: 21528,
13825
+ FIONREAD: 21531,
13826
+ TIOCNOTTY: 21538,
13827
+ TCSBRKP: 21541,
13828
+ TIOCSBRK: 21543,
13829
+ TIOCCBRK: 21544,
13830
+ TCGETS2: 2150388778,
13831
+ TCSETS2: 1076646955,
13832
+ TCSETSW2: 1076646956,
13833
+ TCSETSF2: 1076646957
13834
+ };
13835
+ var TERMIOS_GETS = /* @__PURE__ */ new Set([IOCTL.TCGETS, IOCTL.TCGETS2]);
13836
+ var TERMIOS_SETS = /* @__PURE__ */ new Set([
13837
+ IOCTL.TCSETS,
13838
+ IOCTL.TCSETSW,
13839
+ IOCTL.TCSETSF,
13840
+ IOCTL.TCSETS2,
13841
+ IOCTL.TCSETSW2,
13842
+ IOCTL.TCSETSF2
13843
+ ]);
13844
+ var INT_OUT = /* @__PURE__ */ new Set([
13845
+ IOCTL.TIOCMGET,
13846
+ IOCTL.FIONREAD,
13847
+ IOCTL.TIOCOUTQ
13848
+ ]);
13849
+ var INT_IN = /* @__PURE__ */ new Set([
13850
+ IOCTL.TIOCMBIS,
13851
+ IOCTL.TIOCMBIC,
13852
+ IOCTL.TIOCMSET
13853
+ ]);
13854
+ var INT_ARG = /* @__PURE__ */ new Set([IOCTL.TCSBRK, IOCTL.TCSBRKP, IOCTL.TCFLSH]);
13855
+ var TIOCM = {
13856
+ DTR: 2,
13857
+ RTS: 4,
13858
+ CTS: 32,
13859
+ CD: 64,
13860
+ RI: 128,
13861
+ DSR: 256
13862
+ };
13863
+ var CBAUD = 4111;
13864
+ var BOTHER = 4096;
13865
+ var IBSHIFT = 16;
13866
+ var CSIZE = 48;
13867
+ var CSTOPB = 64;
13868
+ var PARENB = 256;
13869
+ var PARODD = 512;
13870
+ var CRTSCTS = 2147483648;
13871
+ var SPEEDS = [
13872
+ 0,
13873
+ 50,
13874
+ 75,
13875
+ 110,
13876
+ 134,
13877
+ 150,
13878
+ 200,
13879
+ 300,
13880
+ 600,
13881
+ 1200,
13882
+ 1800,
13883
+ 2400,
13884
+ 4800,
13885
+ 9600,
13886
+ 19200,
13887
+ 38400
13888
+ ];
13889
+ var FAST = [
13890
+ 0,
13891
+ 57600,
13892
+ 115200,
13893
+ 230400,
13894
+ 460800,
13895
+ 5e5,
13896
+ 576e3,
13897
+ 921600,
13898
+ 1e6,
13899
+ 1152e3,
13900
+ 15e5,
13901
+ 2e6,
13902
+ 25e5,
13903
+ 3e6,
13904
+ 35e5,
13905
+ 4e6
13906
+ ];
13907
+ function speedOf(code) {
13908
+ if (code === BOTHER) return void 0;
13909
+ return code & BOTHER ? FAST[code & 15] : SPEEDS[code & 15];
13910
+ }
13911
+ function speedsOf(termios, current, extended) {
13912
+ const out = termios.c_cflag & CBAUD;
13913
+ const ospeed = speedOf(out) ?? (extended ? (termios.c_ospeed ?? current.ospeed) >>> 0 : current.ospeed);
13914
+ const inCode = termios.c_cflag >>> IBSHIFT & CBAUD;
13915
+ if (inCode === 0) return { ispeed: ospeed, ospeed };
13916
+ const ispeed = speedOf(inCode) ?? (extended ? (termios.c_ispeed ?? current.ispeed) >>> 0 : current.ispeed);
13917
+ return { ispeed, ospeed };
13918
+ }
13919
+ function settingsOf(cflag, baudRate) {
13920
+ return {
13921
+ baudRate,
13922
+ dataBits: 5 + ((cflag & CSIZE) >> 4),
13923
+ parity: cflag & PARENB ? cflag & PARODD ? "odd" : "even" : "none",
13924
+ stopBits: cflag & CSTOPB ? 2 : 1,
13925
+ flowControl: cflag & CRTSCTS ? "hardware" : "none"
13926
+ };
13927
+ }
13928
+ function sameSettings(a, b) {
13929
+ return a.baudRate === b.baudRate && a.dataBits === b.dataBits && a.parity === b.parity && a.stopBits === b.stopBits && a.flowControl === b.flowControl;
13930
+ }
13931
+ var SERIAL_CFLAG = 13 | 48 | 128 | 1024 | 2048;
13932
+ var SERIAL_LFLAG = 16 | 32 | 512 | 2048;
13933
+ var SERIAL_OFLAG = 4;
13934
+
13935
+ // src/kernel/tty.ts
13936
+ var ICRNL = 256;
13937
+ var IUTF8 = 16384;
13938
+ var OPOST = 1;
13939
+ var ONLCR = 4;
13940
+ var ISIG = 1;
13941
+ var ICANON = 2;
13942
+ var ECHO = 8;
13943
+ var ECHOE = 16;
13944
+ var ECHOCTL = 512;
13945
+ var NOFLSH = 128;
13946
+ var IEXTEN = 32768;
13947
+ var VINTR = 0;
13948
+ var VQUIT = 1;
13949
+ var VERASE = 2;
13950
+ var VKILL = 3;
13951
+ var VEOF = 4;
13952
+ var VSUSP = 10;
13953
+ var VWERASE = 14;
13954
+ function defaultTermios() {
13955
+ const cCc = new Array(32).fill(0);
13956
+ Object.assign(cCc, { [VINTR]: 3, [VQUIT]: 28, [VERASE]: 127, [VKILL]: 21 });
13957
+ Object.assign(cCc, { [VEOF]: 4, 6: 1, 8: 17, 9: 19, [VSUSP]: 26 });
13958
+ Object.assign(cCc, { 12: 18, 13: 15, [VWERASE]: 23, 15: 22 });
13959
+ return {
13960
+ c_iflag: ICRNL | 1024 | 8192 | IUTF8,
13961
+ c_oflag: OPOST | ONLCR,
13962
+ c_cflag: 15 | 48 | 128,
13963
+ c_lflag: ISIG | ICANON | ECHO | ECHOE | 32 | ECHOCTL | 2048 | IEXTEN,
13964
+ c_cc: cCc
13965
+ };
13966
+ }
13967
+ var encoder3 = new TextEncoder();
13968
+ function isContinuation(byte) {
13969
+ return (byte & 192) === 128;
13970
+ }
13971
+ var KernelTty = class {
13972
+ termios = defaultTermios();
13973
+ cols = 80;
13974
+ rows = 24;
13975
+ readable = [];
13976
+ line = [];
13977
+ waiters = [];
13978
+ hungUp = false;
13979
+ name;
13980
+ owner;
13981
+ onRead;
13982
+ readers = 0;
13983
+ screen;
13984
+ signal;
13985
+ constructor(screen, signal) {
13986
+ this.screen = screen;
13987
+ this.signal = signal;
13988
+ }
13989
+ receive(bytes) {
13990
+ const { c_iflag, c_lflag } = this.termios;
13991
+ if (!(c_iflag & ICRNL) && !(c_lflag & (ISIG | ICANON | ECHO))) {
13992
+ if (bytes.length > 0) this.readable.push(bytes.slice());
13993
+ } else for (const byte of bytes) this.key(byte);
13994
+ this.wake();
13995
+ }
13996
+ resize(cols, rows) {
13997
+ if (cols === this.cols && rows === this.rows) return;
13998
+ this.setSize(cols, rows);
13999
+ this.signal(SIG.WINCH);
14000
+ }
14001
+ hangup() {
14002
+ this.hungUp = true;
14003
+ this.wake();
14004
+ }
14005
+ signalHangup() {
14006
+ this.signal(SIG.HUP);
14007
+ }
14008
+ setSize(cols, rows) {
14009
+ this.cols = cols;
14010
+ this.rows = rows;
14011
+ }
14012
+ tcgets() {
14013
+ return { ...this.termios, c_cc: [...this.termios.c_cc] };
14014
+ }
14015
+ tcsets(termios, flush = false) {
14016
+ const wasCanonical = this.canonical;
14017
+ this.termios = { ...termios, c_cc: [...termios.c_cc] };
14018
+ if (flush) {
14019
+ this.line = [];
14020
+ this.readable = [];
14021
+ }
14022
+ if (wasCanonical && !this.canonical && this.line.length > 0) {
14023
+ this.readable.push(Uint8Array.from(this.line));
14024
+ this.line = [];
14025
+ this.wake();
14026
+ }
14027
+ }
14028
+ winsize() {
14029
+ return [this.rows, this.cols];
14030
+ }
14031
+ file() {
14032
+ return new OpenFile({
14033
+ read: (max, signal) => this.read(max, signal),
14034
+ write: async (bytes) => {
14035
+ if (this.hungUp) throw new KernelError("EIO");
14036
+ this.output(bytes);
14037
+ return bytes.length;
14038
+ },
14039
+ poll: () => ({
14040
+ readable: this.readable.length > 0 || this.hungUp,
14041
+ writable: true,
14042
+ hangup: this.hungUp,
14043
+ nread: this.readable.reduce((n, chunk2) => n + (chunk2?.length ?? 0), 0)
14044
+ }),
14045
+ changed: (signal) => this.changed(signal),
14046
+ close: () => {
14047
+ },
14048
+ tty: this
14049
+ });
14050
+ }
14051
+ get canonical() {
14052
+ return (this.termios.c_lflag & ICANON) !== 0;
14053
+ }
14054
+ cc(slot2) {
14055
+ return this.termios.c_cc[slot2] ?? 0;
14056
+ }
14057
+ key(input) {
14058
+ const { c_iflag, c_lflag } = this.termios;
14059
+ const byte = input === 13 && c_iflag & ICRNL ? 10 : input;
14060
+ if (c_lflag & ISIG && this.signalKey(byte)) return;
14061
+ if (!this.canonical) {
14062
+ this.readable.push(Uint8Array.of(byte));
14063
+ if (c_lflag & ECHO) this.echo(byte);
14064
+ return;
14065
+ }
14066
+ this.edit(byte);
14067
+ }
14068
+ signalKey(byte) {
14069
+ const sig = byte === this.cc(VINTR) ? SIG.INT : byte === this.cc(VQUIT) ? SIG.QUIT : byte === this.cc(VSUSP) ? SIG.TSTP : 0;
14070
+ if (!sig || byte === 0) return false;
14071
+ if (this.termios.c_lflag & ECHO) this.echo(byte);
14072
+ if (!(this.termios.c_lflag & NOFLSH)) {
14073
+ this.line = [];
14074
+ this.readable = [];
14075
+ }
14076
+ this.signal(sig);
14077
+ return true;
14078
+ }
14079
+ edit(byte) {
14080
+ const echo = (this.termios.c_lflag & ECHO) !== 0;
14081
+ if (byte === this.cc(VERASE) || byte === 8) {
14082
+ this.rubOut(this.lastCharLength());
14083
+ } else if (byte === this.cc(VKILL)) {
14084
+ this.rubOut(this.line.length);
14085
+ } else if (byte === this.cc(VWERASE)) {
14086
+ this.rubOut(this.lastWordLength());
14087
+ } else if (byte === this.cc(VEOF)) {
14088
+ this.readable.push(this.line.length > 0 ? Uint8Array.from(this.line) : null);
14089
+ this.line = [];
14090
+ } else if (byte === 10) {
14091
+ this.line.push(byte);
14092
+ this.readable.push(Uint8Array.from(this.line));
14093
+ this.line = [];
14094
+ if (echo) this.output(encoder3.encode("\n"));
14095
+ } else {
14096
+ this.line.push(byte);
14097
+ if (echo) this.echo(byte);
14098
+ }
14099
+ }
14100
+ lastWordLength() {
14101
+ let end = this.line.length;
14102
+ while (end > 0 && this.line[end - 1] === 32) end--;
14103
+ while (end > 0 && this.line[end - 1] !== 32) end--;
14104
+ return this.line.length - end;
14105
+ }
14106
+ lastCharLength() {
14107
+ const { line } = this;
14108
+ let n = Math.min(1, line.length);
14109
+ if (this.termios.c_iflag & IUTF8) {
14110
+ while (n < line.length && isContinuation(line[line.length - n] ?? 0)) n++;
14111
+ }
14112
+ return n;
14113
+ }
14114
+ rubOut(n) {
14115
+ const dropped2 = this.line.splice(this.line.length - n, n);
14116
+ const utf83 = (this.termios.c_iflag & IUTF8) !== 0;
14117
+ const cells = utf83 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
14118
+ if (cells > 0 && this.termios.c_lflag & ECHO) {
14119
+ this.output(encoder3.encode("\b \b".repeat(cells)), false);
14120
+ }
14121
+ }
14122
+ echo(byte) {
14123
+ if (byte < 32 && byte !== 10 && byte !== 9 && this.termios.c_lflag & ECHOCTL) {
14124
+ this.output(Uint8Array.of(94, byte + 64), false);
14125
+ return;
14126
+ }
14127
+ this.output(Uint8Array.of(byte));
14128
+ }
14129
+ output(bytes, translate2 = true) {
14130
+ const { c_oflag } = this.termios;
14131
+ if (!translate2 || !(c_oflag & OPOST && c_oflag & ONLCR) || !bytes.includes(10)) {
14132
+ this.screen.write(bytes);
14133
+ return;
14134
+ }
14135
+ const out = [];
14136
+ for (const byte of bytes) {
14137
+ if (byte === 10) out.push(13);
14138
+ out.push(byte);
14139
+ }
14140
+ this.screen.write(Uint8Array.from(out));
14141
+ }
14142
+ get reading() {
14143
+ return this.readers > 0;
14144
+ }
14145
+ async read(max, signal) {
14146
+ this.onRead?.();
14147
+ this.readers++;
14148
+ try {
14149
+ return await this.take(max, signal);
14150
+ } finally {
14151
+ this.readers--;
14152
+ }
14153
+ }
14154
+ async take(max, signal) {
14155
+ while (this.readable.length === 0) {
14156
+ if (this.hungUp) return new Uint8Array(0);
14157
+ await this.changed(signal);
14158
+ }
14159
+ const head = this.readable[0];
14160
+ if (head === null) {
14161
+ this.readable.shift();
14162
+ return new Uint8Array(0);
14163
+ }
14164
+ if (head === void 0) return new Uint8Array(0);
14165
+ const chunks = [];
14166
+ let n = 0;
14167
+ while (this.readable.length > 0 && n < max) {
14168
+ const next3 = this.readable[0];
14169
+ if (next3 === null || next3 === void 0) break;
14170
+ const take = next3.subarray(0, max - n);
14171
+ chunks.push(take);
14172
+ n += take.length;
14173
+ if (take.length < next3.length) this.readable[0] = next3.subarray(take.length);
14174
+ else this.readable.shift();
14175
+ if (this.canonical) break;
14176
+ }
14177
+ const out = new Uint8Array(n);
14178
+ let at = 0;
14179
+ for (const chunk2 of chunks) {
14180
+ out.set(chunk2, at);
14181
+ at += chunk2.length;
14182
+ }
14183
+ return out;
14184
+ }
14185
+ changed(signal) {
14186
+ if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
14187
+ return new Promise((resolve2, reject) => {
14188
+ const onAbort = () => {
14189
+ this.waiters = this.waiters.filter((w) => w !== waiter);
14190
+ reject(new KernelError("EINTR"));
14191
+ };
14192
+ const waiter = () => {
14193
+ signal?.removeEventListener("abort", onAbort);
14194
+ resolve2();
14195
+ };
14196
+ this.waiters.push(waiter);
14197
+ signal?.addEventListener("abort", onAbort, { once: true });
14198
+ });
14199
+ }
14200
+ wake() {
14201
+ const waiters = this.waiters;
14202
+ this.waiters = [];
14203
+ for (const resolve2 of waiters) resolve2();
14204
+ }
14205
+ };
14206
+
14207
+ // src/kernel/devices.ts
14208
+ var DEVICE_NAME = /^tty(USB|ACM)(\d{1,3})$/;
14209
+ var BY_ID = "/dev/serial/by-id";
14210
+ var LINK_NAME = /^[A-Za-z0-9._:+@=,-]{1,255}$/;
14211
+ var MAJORS = { USB: 188, ACM: 166 };
14212
+ var HIGH_WATER = 64 * 1024;
14213
+ var CLOSING_WAIT_MS = 3e4;
14214
+ var BREAK_MS = 250;
14215
+ var MAX_ID2 = 4294967294;
14216
+ var DEFAULT_SETTINGS = settingsOf(SERIAL_CFLAG, 9600);
14217
+ function refused2(code, text) {
14218
+ return Object.assign(new Error(`${code}: ${text}`), { code });
14219
+ }
14220
+ function invalid(what) {
14221
+ return refused2("EINVAL", what);
14222
+ }
14223
+ function nodePath(path) {
14224
+ if (typeof path !== "string") throw invalid("path must be a string");
14225
+ const name2 = path.startsWith("/dev/") ? path.slice(5) : path;
14226
+ const m = DEVICE_NAME.exec(name2);
14227
+ const minor = Number(m?.[2]);
14228
+ if (!m || minor > 255) throw invalid(`not a serial device name: ${path}`);
14229
+ return { path: `/dev/${name2}`, major: MAJORS[m[1]], minor };
14230
+ }
14231
+ function linkPath(link2) {
14232
+ if (typeof link2 !== "string") throw invalid("a link must be a string");
14233
+ const name2 = link2.startsWith(`${BY_ID}/`) ? link2.slice(BY_ID.length + 1) : link2;
14234
+ if (!LINK_NAME.test(name2) || name2 === "." || name2 === "..")
14235
+ throw invalid(`not a link name: ${link2}`);
14236
+ return `${BY_ID}/${name2}`;
14237
+ }
14238
+ function modeOf(mode) {
14239
+ if (mode === void 0) return 384;
14240
+ if (!Number.isInteger(mode) || mode < 0 || mode > 4095)
14241
+ throw invalid("mode must be permission bits");
14242
+ return mode;
14243
+ }
14244
+ async function idOf(value2, find) {
14245
+ if (value2 === void 0) return 0;
14246
+ if (typeof value2 === "number") {
14247
+ if (!Number.isInteger(value2) || value2 < 0 || value2 > MAX_ID2) throw invalid(`bad id ${value2}`);
14248
+ return value2;
14249
+ }
14250
+ const id2 = typeof value2 === "string" ? await find(value2) : void 0;
14251
+ if (id2 === void 0) throw invalid(`unknown owner or group ${String(value2)}`);
14252
+ return id2;
14253
+ }
14254
+ function quiet3() {
14255
+ return void 0;
14256
+ }
14257
+ function want(access2) {
14258
+ return access2 === "read" ? R_OK : access2 === "write" ? W_OK : R_OK | W_OK;
14259
+ }
14260
+ function checkMeta(node, change, uid, gid, guard2) {
14261
+ const { cred } = guard2;
14262
+ const owner = cred.euid === 0 || cred.euid === node.uid;
14263
+ const member = cred.egid === gid || cred.groups.includes(gid);
14264
+ const chown2 = uid !== node.uid || gid !== node.gid;
14265
+ const times = (change.atimeMs ?? change.mtimeMs ?? null) !== null;
14266
+ if (change.mode !== void 0 && !owner) deny(guard2, "chmod", node.path, "EPERM");
14267
+ if (chown2 && cred.euid !== 0 && (uid !== node.uid || !owner || !member))
14268
+ deny(guard2, "chown", node.path, "EPERM");
14269
+ if (times && !owner && !(change.now && permits(cred, node, W_OK, false)))
14270
+ deny(guard2, "utimes", node.path, change.now ? "EACCES" : "EPERM");
14271
+ }
14272
+ var DeviceNodes = class {
14273
+ hooks = /* @__PURE__ */ new Map();
14274
+ nodes = /* @__PURE__ */ new Map();
14275
+ links = /* @__PURE__ */ new Map();
14276
+ lookup;
14277
+ users;
14278
+ next = 0;
14279
+ constructor(users, lookup2) {
14280
+ this.users = users;
14281
+ this.lookup = lookup2;
14282
+ }
14283
+ register(client, hook) {
14284
+ this.hooks.set(client, hook);
14285
+ return () => {
14286
+ if (this.hooks.get(client) !== hook) return;
14287
+ this.hooks.delete(client);
14288
+ for (const node of [...this.nodes.values()]) if (node.client === client) this.drop(node);
14289
+ };
14290
+ }
14291
+ async add(spec, client) {
14292
+ if (typeof spec !== "object" || spec === null) throw invalid("add needs a spec");
14293
+ const { path, major, minor } = nodePath(spec.path);
14294
+ const mode = modeOf(spec.mode);
14295
+ if (spec.links !== void 0 && !Array.isArray(spec.links))
14296
+ throw invalid("links must be a list");
14297
+ const links = (spec.links ?? []).map(linkPath);
14298
+ const uid = await idOf(spec.owner, (name2) => this.lookup.userId(name2));
14299
+ const gid = await idOf(spec.group, (name2) => this.lookup.groupId(name2));
14300
+ if (!this.hooks.has(client)) throw refused2("ENOTSUP", "this client has no devices hook");
14301
+ if (this.nodes.has(path)) throw refused2("EEXIST", `${path} exists`);
14302
+ const taken = links.find((l, i) => this.links.has(l) || links.indexOf(l) !== i);
14303
+ if (taken !== void 0) throw refused2("EEXIST", `${taken} exists`);
14304
+ const node = {
14305
+ id: ++this.next,
14306
+ client,
14307
+ path,
14308
+ uid,
14309
+ gid,
14310
+ mode,
14311
+ major,
14312
+ minor,
14313
+ links,
14314
+ opening: false,
14315
+ atimeMs: Date.now(),
14316
+ mtimeMs: Date.now(),
14317
+ ctimeMs: Date.now()
14318
+ };
14319
+ this.nodes.set(path, node);
14320
+ for (const link2 of links) this.links.set(link2, node);
14321
+ return node.id;
14322
+ }
14323
+ remove(id2) {
14324
+ const node = [...this.nodes.values()].find((n) => n.id === id2);
14325
+ if (!node) return false;
14326
+ this.drop(node);
14327
+ return true;
14328
+ }
14329
+ drop(node) {
14330
+ this.nodes.delete(node.path);
14331
+ for (const link2 of node.links) this.links.delete(link2);
14332
+ node.channel?.lost(new KernelError("EIO", `${node.path} was removed`));
14333
+ }
14334
+ list() {
14335
+ return [...this.nodes.values()].sort((a, b) => a.path < b.path ? -1 : 1).map(({ path, uid, gid, mode, major, minor, links, atimeMs, mtimeMs, ctimeMs }) => ({
14336
+ path,
14337
+ uid,
14338
+ gid,
14339
+ mode,
14340
+ major,
14341
+ minor,
14342
+ links: [...links],
14343
+ atimeMs,
14344
+ mtimeMs,
14345
+ ctimeMs
14346
+ }));
14347
+ }
14348
+ meta(path, change, guard2) {
14349
+ const node = this.find(path);
14350
+ if (!node) throw new KernelError("ENOENT");
14351
+ const uid = change.uid === void 0 || change.uid === -1 ? node.uid : change.uid >>> 0;
14352
+ const gid = change.gid === void 0 || change.gid === -1 ? node.gid : change.gid >>> 0;
14353
+ if (guard2) checkMeta(node, change, uid, gid, guard2);
14354
+ const now = Date.now();
14355
+ if (change.mode !== void 0) node.mode = change.mode & 4095;
14356
+ node.uid = uid;
14357
+ node.gid = gid;
14358
+ const at = (ms, kept) => ms === void 0 || ms === null ? kept : change.now ? now : ms;
14359
+ node.atimeMs = at(change.atimeMs, node.atimeMs);
14360
+ node.mtimeMs = at(change.mtimeMs, node.mtimeMs);
14361
+ node.ctimeMs = now;
14362
+ if (node.channel) node.channel.tty.owner = { uid: node.uid, gid: node.gid, mode: node.mode };
14363
+ }
14364
+ find(path) {
14365
+ return this.nodes.get(path) ?? this.links.get(path);
14366
+ }
14367
+ openTty(path) {
14368
+ return this.nodes.get(path)?.channel?.tty;
14369
+ }
14370
+ async open(path, flags2, opener) {
14371
+ const node = this.find(path);
14372
+ if (!node) throw new KernelError("ENOENT");
14373
+ const access2 = (flags2 & 3) === 0 ? "read" : (flags2 & 3) === 1 ? "write" : "both";
14374
+ if (opener.guard && !permits(opener.guard.cred, node, want(access2), false))
14375
+ deny(opener.guard, "open", node.path);
14376
+ if (node.channel || node.opening) throw new KernelError("EBUSY");
14377
+ const hook = this.hooks.get(node.client);
14378
+ node.opening = true;
14379
+ const asked = Promise.resolve().then(
14380
+ () => hook.open({ path: node.path, flags: flags2 & 3, stamp: opener.stamp, signal: opener.signal })
14381
+ );
14382
+ let streams;
14383
+ try {
14384
+ streams = await interruptible2(asked, opener.signal);
14385
+ } catch (err) {
14386
+ if (err instanceof KernelError && err.code === "EINTR") {
14387
+ void asked.then(abandon, quiet3).finally(() => node.opening = false);
14388
+ } else node.opening = false;
14389
+ throw embedderFailure(err);
14390
+ }
14391
+ node.opening = false;
14392
+ if (!isStreams(streams)) throw new KernelError("EIO", "the devices hook gave no streams");
14393
+ if (this.nodes.get(node.path) !== node) {
14394
+ void abandon(streams);
14395
+ throw new KernelError("ENOENT");
14396
+ }
14397
+ const channel = new DeviceChannel(node.path, streams, node.saved, (saved) => {
14398
+ node.saved = saved;
14399
+ if (node.channel === channel) delete node.channel;
14400
+ });
14401
+ channel.tty.owner = { uid: node.uid, gid: node.gid, mode: node.mode };
14402
+ node.channel = channel;
14403
+ try {
14404
+ await channel.restore();
14405
+ } catch (err) {
14406
+ await channel.close();
14407
+ throw err;
14408
+ }
14409
+ return channel.file(access2);
14410
+ }
14411
+ };
14412
+ function isStreams(value2) {
14413
+ const s = value2;
14414
+ return !!s && s.readable instanceof ReadableStream && s.writable instanceof WritableStream && typeof s.control === "function";
14415
+ }
14416
+ async function abandon(streams) {
14417
+ if (!isStreams(streams)) return;
14418
+ await streams.readable.cancel("closed").catch(quiet3);
14419
+ await streams.writable.abort("closed").catch(quiet3);
14420
+ await Promise.resolve().then(() => streams.control("close")).catch(quiet3);
14421
+ }
14422
+ function serialTermios() {
14423
+ const base = defaultTermios();
14424
+ return {
14425
+ c_iflag: 0,
14426
+ c_oflag: SERIAL_OFLAG,
14427
+ c_cflag: SERIAL_CFLAG,
14428
+ c_lflag: SERIAL_LFLAG,
14429
+ c_cc: base.c_cc
14430
+ };
14431
+ }
14432
+ function sleep(ms, signal) {
14433
+ return interruptible2(new Promise((resolve2) => setTimeout(resolve2, ms)), signal);
14434
+ }
14435
+ var DeviceChannel = class {
14436
+ tty;
14437
+ path;
14438
+ inner;
14439
+ reader;
14440
+ writer;
14441
+ streams;
14442
+ onClosed;
14443
+ gone = Promise.withResolvers();
14444
+ failure;
14445
+ pending = /* @__PURE__ */ new Set();
14446
+ queued = 0;
14447
+ resume;
14448
+ waiters = [];
14449
+ closed = false;
14450
+ ispeed = 9600;
14451
+ ospeed = 9600;
14452
+ dtr = true;
14453
+ rts = true;
14454
+ constructor(path, streams, saved, onClosed) {
14455
+ this.path = path;
14456
+ this.streams = streams;
14457
+ this.onClosed = onClosed;
14458
+ this.gone.promise.catch(quiet3);
14459
+ this.tty = new KernelTty({ write: (bytes) => this.send(bytes) }, quiet3);
14460
+ this.tty.name = path;
14461
+ this.tty.setSize(0, 0);
14462
+ this.tty.tcsets(saved?.termios ?? serialTermios());
14463
+ if (saved) {
14464
+ this.ispeed = saved.ispeed;
14465
+ this.ospeed = saved.ospeed;
14466
+ this.dtr = this.rts = saved.ospeed !== 0;
14467
+ }
14468
+ this.inner = this.tty.file();
14469
+ this.reader = streams.readable.getReader();
14470
+ this.writer = streams.writable.getWriter();
14471
+ void this.pump();
14472
+ }
14473
+ file(access2) {
14474
+ return new OpenFile({
14475
+ ...access2 === "write" ? {} : { read: (max, signal) => this.read(max, signal) },
14476
+ ...access2 === "read" ? {} : { write: (bytes, signal) => this.write(bytes, signal) },
14477
+ poll: () => this.poll(),
14478
+ changed: (signal) => this.changed(signal),
14479
+ close: () => this.close(),
14480
+ tty: this.tty,
14481
+ device: this
14482
+ });
14483
+ }
14484
+ lost(err) {
14485
+ if (this.failure) return;
14486
+ this.failure = err;
14487
+ this.gone.reject(err);
14488
+ this.tty.hangup();
14489
+ this.wake();
14490
+ }
14491
+ control(op, args) {
14492
+ return Promise.resolve().then(() => this.streams.control(op, args));
14493
+ }
14494
+ buffered() {
14495
+ return this.inner.file.poll?.().nread ?? 0;
14496
+ }
14497
+ async pump() {
14498
+ for (; ; ) {
14499
+ while (this.buffered() >= HIGH_WATER && !this.closed && !this.failure) {
14500
+ await new Promise((resolve2) => {
14501
+ this.resume = resolve2;
14502
+ });
14503
+ }
14504
+ if (this.closed || this.failure) return;
14505
+ let got;
14506
+ try {
14507
+ got = await this.reader.read();
14508
+ } catch (err) {
14509
+ if (!this.closed) this.lost(new KernelError("EIO", lineOf2(err)));
14510
+ return;
14511
+ }
14512
+ if (got.done) {
14513
+ this.tty.hangup();
14514
+ this.wake();
14515
+ return;
14516
+ }
14517
+ this.tty.receive(got.value);
14518
+ this.wake();
14519
+ }
14520
+ }
14521
+ send(bytes) {
14522
+ if (this.closed || this.failure) return;
14523
+ const n = bytes.length;
14524
+ this.queued += n;
14525
+ const sent = this.writer.write(bytes.slice()).catch((err) => {
14526
+ this.lost(new KernelError("EIO", lineOf2(err)));
14527
+ });
14528
+ const tracked = sent.finally(() => {
14529
+ this.queued -= n;
14530
+ this.pending.delete(tracked);
14531
+ this.wake();
14532
+ });
14533
+ this.pending.add(tracked);
14534
+ }
14535
+ check() {
14536
+ if (this.failure) throw this.failure;
14537
+ }
14538
+ async read(max, signal) {
14539
+ this.check();
14540
+ const read = this.inner.file.read;
14541
+ const got = await read(max, signal);
14542
+ this.resume?.();
14543
+ this.resume = void 0;
14544
+ if (got.length === 0) this.check();
14545
+ return got;
14546
+ }
14547
+ async ready(signal) {
14548
+ this.check();
14549
+ try {
14550
+ await interruptible2(Promise.race([this.writer.ready, this.gone.promise]), signal);
14551
+ } catch (err) {
14552
+ if (err instanceof KernelError) throw err;
14553
+ throw new KernelError("EIO", lineOf2(err));
14554
+ }
14555
+ }
14556
+ async write(bytes, signal) {
14557
+ this.check();
14558
+ if (!((this.writer.desiredSize ?? 0) > 0)) await this.ready(signal);
14559
+ const write = this.inner.file.write;
14560
+ return write(bytes, signal);
14561
+ }
14562
+ poll() {
14563
+ if (this.failure)
14564
+ return { readable: true, writable: true, hangup: true, error: true, nread: 0 };
14565
+ const inner = this.inner.file.poll?.();
14566
+ const writable3 = (this.writer.desiredSize ?? 0) > 0;
14567
+ return { ...inner, writable: writable3 };
14568
+ }
14569
+ changed(signal) {
14570
+ const mine = new Promise((resolve2) => this.waiters.push(resolve2));
14571
+ const theirs = this.inner.file.changed?.(signal);
14572
+ return interruptible2(Promise.race([mine, theirs]), signal);
14573
+ }
14574
+ wake() {
14575
+ const waiters = this.waiters;
14576
+ this.waiters = [];
14577
+ for (const resolve2 of waiters) resolve2();
14578
+ }
14579
+ async drain(signal) {
14580
+ while (this.pending.size > 0) {
14581
+ this.check();
14582
+ await interruptible2(
14583
+ Promise.race([Promise.all([...this.pending]), this.gone.promise]),
14584
+ signal
14585
+ );
14586
+ }
14587
+ this.check();
14588
+ }
14589
+ async restore() {
14590
+ const settings = this.settings();
14591
+ if (settings.baudRate === 0)
14592
+ return this.signals({ dataTerminalReady: false, requestToSend: false });
14593
+ if (sameSettings(settings, DEFAULT_SETTINGS)) return;
14594
+ try {
14595
+ await this.control("configure", settings);
14596
+ } catch (err) {
14597
+ throw embedderFailure(err);
14598
+ }
14599
+ }
14600
+ settings() {
14601
+ return settingsOf(this.tty.tcgets().c_cflag, this.ospeed);
14602
+ }
14603
+ termios() {
14604
+ return { ...this.tty.tcgets(), c_ispeed: this.ispeed, c_ospeed: this.ospeed };
14605
+ }
14606
+ async setTermios(termios, how, signal) {
14607
+ this.check();
14608
+ if (how.drain) await this.drain(signal);
14609
+ const before = this.settings();
14610
+ const old = { termios: this.tty.tcgets(), ispeed: this.ispeed, ospeed: this.ospeed };
14611
+ const speeds = speedsOf(termios, this, how.extended === true);
14612
+ this.tty.tcsets(termios, how.flush === true);
14613
+ this.ispeed = speeds.ispeed;
14614
+ this.ospeed = speeds.ospeed;
14615
+ try {
14616
+ if (speeds.ospeed === 0) await this.setModem(this.bits() & ~(TIOCM.DTR | TIOCM.RTS));
14617
+ else {
14618
+ if (!sameSettings(before, this.settings()))
14619
+ await this.control("configure", this.settings());
14620
+ if (old.ospeed === 0) await this.setModem(this.bits() | TIOCM.DTR | TIOCM.RTS);
14621
+ }
14622
+ if (how.flush) await this.control("flush", { input: true, output: false });
14623
+ } catch (err) {
14624
+ this.tty.tcsets(old.termios);
14625
+ this.ispeed = old.ispeed;
14626
+ this.ospeed = old.ospeed;
14627
+ throw embedderFailure(err);
14628
+ }
14629
+ }
14630
+ bits() {
14631
+ return (this.dtr ? TIOCM.DTR : 0) | (this.rts ? TIOCM.RTS : 0);
14632
+ }
14633
+ async modem() {
14634
+ this.check();
14635
+ const s = await this.control("signals").catch((err) => {
14636
+ throw embedderFailure(err);
14637
+ });
14638
+ return this.bits() | (s?.clearToSend ? TIOCM.CTS : 0) | (s?.dataSetReady ? TIOCM.DSR : 0) | (s?.dataCarrierDetect ? TIOCM.CD : 0) | (s?.ringIndicator ? TIOCM.RI : 0);
14639
+ }
14640
+ async setModem(bits2) {
14641
+ this.check();
14642
+ const dataTerminalReady = (bits2 & TIOCM.DTR) !== 0;
14643
+ const requestToSend = (bits2 & TIOCM.RTS) !== 0;
14644
+ await this.signals({ dataTerminalReady, requestToSend });
14645
+ this.dtr = dataTerminalReady;
14646
+ this.rts = requestToSend;
14647
+ }
14648
+ async signals(args) {
14649
+ try {
14650
+ await this.control("setSignals", args);
14651
+ } catch (err) {
14652
+ throw embedderFailure(err);
14653
+ }
14654
+ }
14655
+ async sendBreak(ms, signal) {
14656
+ await this.drain(signal);
14657
+ await this.signals({ break: true });
14658
+ try {
14659
+ await sleep(ms, signal);
14660
+ } finally {
14661
+ await this.signals({ break: false });
14662
+ }
14663
+ }
14664
+ async flush(input, output) {
14665
+ this.check();
14666
+ if (input) this.tty.tcsets(this.tty.tcgets(), true);
14667
+ try {
14668
+ await this.control("flush", { input, output });
14669
+ } catch (err) {
14670
+ throw embedderFailure(err);
14671
+ }
14672
+ }
14673
+ async ioctl(request3, arg, signal) {
14674
+ this.check();
14675
+ switch (request3) {
14676
+ case IOCTL.TCGETS:
14677
+ case IOCTL.TCGETS2:
14678
+ return this.termios();
14679
+ case IOCTL.TCSETS:
14680
+ case IOCTL.TCSETSW:
14681
+ case IOCTL.TCSETSF:
14682
+ case IOCTL.TCSETS2:
14683
+ case IOCTL.TCSETSW2:
14684
+ case IOCTL.TCSETSF2: {
14685
+ const extended = request3 > 65535;
14686
+ const kind = (request3 & 255) - (extended ? 43 : 2);
14687
+ await this.setTermios(
14688
+ arg,
14689
+ { drain: kind > 0, flush: kind === 2, extended },
14690
+ signal
14691
+ );
14692
+ return null;
14693
+ }
14694
+ case IOCTL.TIOCMGET:
14695
+ return this.modem();
14696
+ case IOCTL.TIOCMSET:
14697
+ await this.setModem(Number(arg));
14698
+ return null;
14699
+ case IOCTL.TIOCMBIS:
14700
+ await this.setModem(this.bits() | Number(arg));
14701
+ return null;
14702
+ case IOCTL.TIOCMBIC:
14703
+ await this.setModem(this.bits() & ~Number(arg));
14704
+ return null;
14705
+ case IOCTL.TIOCSBRK:
14706
+ case IOCTL.TIOCCBRK:
14707
+ await this.signals({ break: request3 === IOCTL.TIOCSBRK });
14708
+ return null;
14709
+ case IOCTL.TCSBRK:
14710
+ if (Number(arg) === 0) await this.sendBreak(BREAK_MS, signal);
14711
+ else await this.drain(signal);
14712
+ return null;
14713
+ case IOCTL.TCSBRKP:
14714
+ await this.sendBreak(Number(arg) > 0 ? Number(arg) * 100 : BREAK_MS, signal);
14715
+ return null;
14716
+ case IOCTL.TCFLSH: {
14717
+ const queue = Number(arg);
14718
+ if (queue < 0 || queue > 2) throw new KernelError("EINVAL");
14719
+ await this.flush(queue !== 1, queue !== 0);
14720
+ return null;
14721
+ }
14722
+ case IOCTL.FIONREAD:
14723
+ return this.buffered();
14724
+ case IOCTL.TIOCOUTQ:
14725
+ return this.queued;
14726
+ case IOCTL.TIOCEXCL:
14727
+ case IOCTL.TIOCNXCL:
14728
+ return null;
14729
+ case IOCTL.TIOCGWINSZ: {
14730
+ const [rows, cols] = this.tty.winsize();
14731
+ return { rows, cols };
14732
+ }
14733
+ case IOCTL.TIOCSWINSZ: {
14734
+ const size = arg;
14735
+ this.tty.setSize(Number(size?.cols) & 65535, Number(size?.rows) & 65535);
14736
+ return null;
14737
+ }
14738
+ case IOCTL.TIOCSCTTY:
14739
+ throw new KernelError("EPERM");
14740
+ default:
14741
+ throw new KernelError("ENOTTY");
14742
+ }
14743
+ }
14744
+ async close() {
14745
+ if (this.closed) return;
14746
+ try {
14747
+ if (!this.failure) {
14748
+ await interruptible2(this.drain(), AbortSignal.timeout(CLOSING_WAIT_MS)).catch(quiet3);
14749
+ }
14750
+ this.closed = true;
14751
+ this.resume?.();
14752
+ this.wake();
14753
+ await this.reader.cancel("closed").catch(quiet3);
14754
+ this.reader.releaseLock();
14755
+ await this.writer.abort("closed").catch(quiet3);
14756
+ this.writer.releaseLock();
14757
+ await this.control("close").catch(quiet3);
14758
+ } finally {
14759
+ this.closed = true;
14760
+ this.onClosed({ termios: this.tty.tcgets(), ispeed: this.ispeed, ospeed: this.ospeed });
14761
+ }
14762
+ }
14763
+ };
14764
+
13661
14765
  // src/kernel/exec-ticket.ts
13662
14766
  var TICKET_ENV = "SLICC_SEALED_TICKET";
13663
14767
  var TICKET_TTL_MS = 3e4;
@@ -14030,8 +15134,8 @@ var DriverConnection = class _DriverConnection {
14030
15134
  } finally {
14031
15135
  clearTimeout(timer);
14032
15136
  }
14033
- const refused4 = driverVersionError(hello.protocol);
14034
- if (refused4) throw errnoError("EPROTO", refused4);
15137
+ const refused5 = driverVersionError(hello.protocol);
15138
+ if (refused5) throw errnoError("EPROTO", refused5);
14035
15139
  if (hello.error) throw errnoError(hello.errno ?? "EIO", hello.error);
14036
15140
  return new _DriverConnection(port, hello.capabilities ?? {}, waiting, timeoutMs, options);
14037
15141
  }
@@ -14077,7 +15181,7 @@ var DriverConnection = class _DriverConnection {
14077
15181
  var S_ISGID4 = 1024;
14078
15182
  var ID = /^\d{1,10}$/;
14079
15183
  var MASK = /^0*[0-7]{1,4}$/;
14080
- var MAX_ID2 = 4294967294;
15184
+ var MAX_ID3 = 4294967294;
14081
15185
  function idOption(value2) {
14082
15186
  return value2 === void 0 ? 0 : Number(value2);
14083
15187
  }
@@ -14089,7 +15193,7 @@ function checkIds(options) {
14089
15193
  for (const key of ["uid", "gid"]) {
14090
15194
  const value2 = options[key];
14091
15195
  if (value2 === void 0) continue;
14092
- if (!ID.test(value2) || Number(value2) > MAX_ID2) throw errnoError("EINVAL", `${key}=${value2}`);
15196
+ if (!ID.test(value2) || Number(value2) > MAX_ID3) throw errnoError("EINVAL", `${key}=${value2}`);
14093
15197
  }
14094
15198
  }
14095
15199
  function checkMasks(options) {
@@ -14101,7 +15205,7 @@ function checkMasks(options) {
14101
15205
  }
14102
15206
  }
14103
15207
  }
14104
- function modeOf(attr2, mount) {
15208
+ function modeOf2(attr2, mount) {
14105
15209
  const type = attr2.kind === "directory" ? 16384 : attr2.kind === "symlink" ? 40960 : attr2.kind === "fifo" ? 4096 : 32768;
14106
15210
  const fallback = type | (attr2.kind === "directory" ? 493 : attr2.kind === "symlink" ? 511 : 420);
14107
15211
  if (mount.caps.owners) return attr2.mode ?? fallback;
@@ -14262,9 +15366,9 @@ var HeldPaths = class extends Map {
14262
15366
  this.unlinked.set(path, file);
14263
15367
  }
14264
15368
  keep(path, file) {
14265
- const fresh3 = this.handlesAt(path).filter((id2) => !this.keptHandles.has(id2));
14266
- for (const id2 of fresh3) this.keptHandles.set(id2, file);
14267
- this.uses.set(file, Math.max(fresh3.length, 1));
15369
+ const fresh4 = this.handlesAt(path).filter((id2) => !this.keptHandles.has(id2));
15370
+ for (const id2 of fresh4) this.keptHandles.set(id2, file);
15371
+ this.uses.set(file, Math.max(fresh4.length, 1));
14268
15372
  this.unlinked.set(path, file);
14269
15373
  }
14270
15374
  unkeep(path, file) {
@@ -14385,7 +15489,7 @@ function statOf2(attr2, path, mount) {
14385
15489
  isSymbolicLink: attr2.kind === "symlink",
14386
15490
  ...attr2.kind === "fifo" ? { isFifo: true } : {},
14387
15491
  size: attr2.size,
14388
- mode: modeOf(attr2, mount),
15492
+ mode: modeOf2(attr2, mount),
14389
15493
  mtime,
14390
15494
  atime: attr2.atime === void 0 ? mtime : new Date(attr2.atime),
14391
15495
  ctime: mtime,
@@ -15476,8 +16580,8 @@ var MountFs = class _MountFs {
15476
16580
  }
15477
16581
  this.writable(mount, real);
15478
16582
  const { uid, gid, mode } = change;
15479
- const fresh3 = change.kind === "new" && { uid, gid, mode };
15480
- const set3 = fresh3 ? await this.driverOwner(found, real, fresh3, false) : change;
16583
+ const fresh4 = change.kind === "new" && { uid, gid, mode };
16584
+ const set3 = fresh4 ? await this.driverOwner(found, real, fresh4, false) : change;
15481
16585
  this.table.forget(mount, real);
15482
16586
  const attrs = Object.entries({ uid: set3.uid, gid: set3.gid, mode: set3.mode });
15483
16587
  await mount.conn.call({
@@ -15754,10 +16858,10 @@ async function runMkdir(entry, path, req) {
15754
16858
  const plain3 = plainDirMode(entry, req.mode);
15755
16859
  const parentsMode = !req.exclusive ? plainParentsMode(entry) : void 0;
15756
16860
  const fixParents = parentsMode !== void 0 ? await missingParents(fs, path, parentsMode) : void 0;
15757
- const fresh3 = plain3 !== void 0 && !await fs.exists(path);
16861
+ const fresh4 = plain3 !== void 0 && !await fs.exists(path);
15758
16862
  await (req.exclusive ? mkdirExclusive(entry, path, req.mode) : fs.mkdir(path, mkdirOptions(entry, req.mode)));
15759
16863
  await fixParents?.();
15760
- if (fresh3) await fs.chmod(path, plain3);
16864
+ if (fresh4) await fs.chmod(path, plain3);
15761
16865
  return done2;
15762
16866
  }
15763
16867
  async function shownAs(entry, path, st) {
@@ -15969,10 +17073,10 @@ async function guard(g, path, req, cwd) {
15969
17073
  }
15970
17074
  async function opening(entry, path, mode) {
15971
17075
  const g = guardOf(entry);
15972
- const want = (mode & HOLD_READ ? R_OK : 0) | (mode & HOLD_WRITE ? W_OK : 0);
17076
+ const want2 = (mode & HOLD_READ ? R_OK : 0) | (mode & HOLD_WRITE ? W_OK : 0);
15973
17077
  const created4 = (mode & HOLD_CREATED) !== 0 && entry.created?.take(path) === true;
15974
- if (g && want !== 0 && !created4) {
15975
- await need(g, "open", path, want, { directory: false });
17078
+ if (g && want2 !== 0 && !created4) {
17079
+ await need(g, "open", path, want2, { directory: false });
15976
17080
  }
15977
17081
  }
15978
17082
  async function stillThere(entry, path, req) {
@@ -15996,7 +17100,7 @@ async function holdOp(entry, path, req) {
15996
17100
  await entry.source?.open(path, real);
15997
17101
  await stillThere(entry, path, req);
15998
17102
  if (req.nofollow && !await ownEntry(entry, path, real ?? path)) continue;
15999
- const fid = req.ino === void 0 ? void 0 : await idOf(entry, real ?? path);
17103
+ const fid = req.ino === void 0 ? void 0 : await idOf2(entry, real ?? path);
16000
17104
  if (!r || !r.missed(path) && !r.changedSince(path, gen) && !r.inFlight?.(path))
16001
17105
  return json2(await heldOpen(entry, path, real, req, fid));
16002
17106
  }
@@ -16010,7 +17114,7 @@ async function heldOpen(entry, path, real, req, fid) {
16010
17114
  await entry.announceHold?.(real ?? path, fid, ((req.mode ?? 0) & HOLD_CREATED) !== 0);
16011
17115
  return real !== void 0 && real !== path ? { handle, real } : handle;
16012
17116
  }
16013
- async function idOf(entry, path) {
17117
+ async function idOf2(entry, path) {
16014
17118
  return await entry.fileId?.(path)?.catch(() => void 0) ?? void 0;
16015
17119
  }
16016
17120
  async function ownEntry(entry, path, real) {
@@ -16082,8 +17186,8 @@ async function guarded(entry, path, req) {
16082
17186
  return { paths };
16083
17187
  } catch (err) {
16084
17188
  const refusal2 = err.code === "EACCES";
16085
- const refused4 = req.op === "exists" && refusal2 ? json2(false) : toErrno(err);
16086
- return { refused: refused4 };
17189
+ const refused5 = req.op === "exists" && refusal2 ? json2(false) : toErrno(err);
17190
+ return { refused: refused5 };
16087
17191
  }
16088
17192
  }
16089
17193
  async function copyOp(entry, path, req) {
@@ -16184,7 +17288,7 @@ async function bound(entry, path, handle) {
16184
17288
  const now = await entry.fs.stat(path).catch(() => void 0);
16185
17289
  if (now === void 0 && bound3.fid !== void 0 && !await entry.fs.lstat(path).catch(() => void 0))
16186
17290
  throw syncError("ENOENT", path);
16187
- const same3 = bound3.fid === void 0 ? now?.ino === bound3.ino : now?.fid === bound3.fid || await idOf(entry, path) === bound3.fid;
17291
+ const same3 = bound3.fid === void 0 ? now?.ino === bound3.ino : now?.fid === bound3.fid || await idOf2(entry, path) === bound3.fid;
16188
17292
  if (same3) {
16189
17293
  entry.trust?.(handle, bound3.doubt);
16190
17294
  return;
@@ -16279,13 +17383,13 @@ async function created2(entry, req) {
16279
17383
  return seen === void 0 ? looked : json2({ ...looked.json, seen });
16280
17384
  }
16281
17385
  var DENIED = /* @__PURE__ */ new Set(["EACCES", "EPERM"]);
16282
- async function rmPermitted(entry, path, req, refused4) {
17386
+ async function rmPermitted(entry, path, req, refused5) {
16283
17387
  const st = await entry.fs.lstat(path).catch(() => void 0);
16284
- if (!st?.isDirectory) return refused4;
17388
+ if (!st?.isDirectory) return refused5;
16285
17389
  const unlisted = await guarded(entry, path, { ...req, op: "readdir" });
16286
17390
  if ("refused" in unlisted) return unlisted.refused;
16287
17391
  const names2 = await entry.fs.readdir(path).catch(() => void 0);
16288
- if (!names2) return refused4;
17392
+ if (!names2) return refused5;
16289
17393
  let first;
16290
17394
  for (const name2 of names2) {
16291
17395
  const child = await dispatchOne(entry, {
@@ -16336,14 +17440,14 @@ async function newDevName(entry, path, req) {
16336
17440
  const named = NAMING.has(req.op);
16337
17441
  if (!named && !CREATIONS.has(req.op)) return void 0;
16338
17442
  const target = named ? entry.fs.resolvePath(entry.cwd, req.arg2 ?? "") : path;
16339
- const refused4 = devCreation(target, entry.cred?.());
16340
- if (!refused4) return void 0;
17443
+ const refused5 = devCreation(target, entry.cred?.());
17444
+ if (!refused5) return void 0;
16341
17445
  const there = await entry.fs.lstat(target).then(
16342
17446
  () => true,
16343
17447
  () => false
16344
17448
  );
16345
17449
  if (there) return void 0;
16346
- return { ok: false, errno: refused4, message: `${target}: no new names in /dev or /proc` };
17450
+ return { ok: false, errno: refused5, message: `${target}: no new names in /dev or /proc` };
16347
17451
  }
16348
17452
  var MISS_OPS = /* @__PURE__ */ new Set(["stat", "lstat"]);
16349
17453
  async function linkFree(fs, path) {
@@ -16454,13 +17558,13 @@ var SAB_DEFAULT_WINDOW_BYTES = 1024 * 1024;
16454
17558
  var SAB_MIN_BYTES = SAB_HEADER_BYTES + 4096;
16455
17559
  var SYNC_SAB_REQ_MSG = "sync-sab-req";
16456
17560
  var SYNC_SAB_NEXT_MSG = "sync-sab-next";
16457
- var encoder3 = new TextEncoder();
17561
+ var encoder4 = new TextEncoder();
16458
17562
  var decoder2 = new TextDecoder();
16459
17563
  function encodeSabResult(result) {
16460
17564
  if (!result.ok) {
16461
17565
  return {
16462
17566
  status: SAB_STATUS_ERRNO,
16463
- payload: encoder3.encode(
17567
+ payload: encoder4.encode(
16464
17568
  JSON.stringify({
16465
17569
  errno: result.errno,
16466
17570
  message: result.message,
@@ -16475,7 +17579,7 @@ function encodeSabResult(result) {
16475
17579
  case "bytes":
16476
17580
  return { status: SAB_STATUS_BYTES, payload: result.bytes };
16477
17581
  case "json":
16478
- return { status: SAB_STATUS_JSON, payload: encoder3.encode(JSON.stringify(result.json)) };
17582
+ return { status: SAB_STATUS_JSON, payload: encoder4.encode(JSON.stringify(result.json)) };
16479
17583
  default:
16480
17584
  return { status: SAB_STATUS_VOID, payload: new Uint8Array(0) };
16481
17585
  }
@@ -16802,9 +17906,9 @@ var Pages = class {
16802
17906
  const page = (start2 + at) / PAGE;
16803
17907
  if (this.pages.has(page)) continue;
16804
17908
  const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
16805
- const fresh3 = this.take(part.length);
16806
- fresh3.set(part);
16807
- this.pages.set(page, fresh3);
17909
+ const fresh4 = this.take(part.length);
17910
+ fresh4.set(part);
17911
+ this.pages.set(page, fresh4);
16808
17912
  }
16809
17913
  }
16810
17914
  read(at, length2) {
@@ -17015,16 +18119,16 @@ var pinnedRoot;
17015
18119
  var rootKind = () => globalThis.navigator?.storage ? "opfs" : "memory";
17016
18120
  function mountTable(mounts = []) {
17017
18121
  const root = pinnedRoot ?? rootKind();
17018
- const lines2 = [`${root} / ${root} rw 0 0`, "devfs /dev devfs rw 0 0", "proc /proc proc rw 0 0"];
18122
+ const lines3 = [`${root} / ${root} rw 0 0`, "devfs /dev devfs rw 0 0", "proc /proc proc rw 0 0"];
17019
18123
  for (const m of mounts) {
17020
18124
  const options = Object.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
17021
18125
  if (m.state !== "ok") options.push(m.state);
17022
18126
  const access2 = options.includes("ro") ? [] : ["rw"];
17023
- lines2.push(
18127
+ lines3.push(
17024
18128
  `${field3(m.source)} ${field3(m.target)} ${m.type} ${[...access2, ...options].join(",")} 0 0`
17025
18129
  );
17026
18130
  }
17027
- return `${lines2.join("\n")}
18131
+ return `${lines3.join("\n")}
17028
18132
  `;
17029
18133
  }
17030
18134
 
@@ -18059,7 +19163,7 @@ function recordRefusal(opening2) {
18059
19163
  const found = headerClass(opening2);
18060
19164
  return found && new KernelError("EPROTO", `${found[0]}: ${found[1]}`);
18061
19165
  }
18062
- function refused2(engine, session, err, opening2) {
19166
+ function refused3(engine, session, err, opening2) {
18063
19167
  const flags2 = session.verifyResult;
18064
19168
  const code = verifyCode(flags2);
18065
19169
  if (code && flags2 !== -1)
@@ -18080,7 +19184,7 @@ async function handshake(conn, o, session, inbox) {
18080
19184
  done5 = session.handshake();
18081
19185
  } catch (err) {
18082
19186
  await send(conn, session).catch(() => void 0);
18083
- throw refused2(o.engine, session, err, inbox.opening);
19187
+ throw refused3(o.engine, session, err, inbox.opening);
18084
19188
  }
18085
19189
  await send(conn, session);
18086
19190
  if (done5) return;
@@ -18531,10 +19635,10 @@ async function anchorFor(engine, chain2, host, found) {
18531
19635
  var FRAGMENT = 16;
18532
19636
  var MIN_VALUE = 8;
18533
19637
  var MIN_BASE64 = 8;
18534
- var encoder4 = new TextEncoder();
19638
+ var encoder5 = new TextEncoder();
18535
19639
  var STD = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
18536
19640
  function utf8(text) {
18537
- return encoder4.encode(text);
19641
+ return encoder5.encode(text);
18538
19642
  }
18539
19643
  function base64(bytes, padded = true) {
18540
19644
  let out = "";
@@ -18608,14 +19712,73 @@ function encodings(bytes) {
18608
19712
  }
18609
19713
  return [...standard, ...standard.map(urlSafe)];
18610
19714
  }
18611
- function shapes(value2) {
19715
+ function windows(length2) {
19716
+ const out = [];
19717
+ for (let offset = 0; offset + FRAGMENT <= length2; offset++) out.push(offset);
19718
+ return out;
19719
+ }
19720
+ function strings(document) {
19721
+ const out = [];
19722
+ const stack = [document];
19723
+ while (stack.length > 0) {
19724
+ const item = stack.pop();
19725
+ if (typeof item === "string") out.push(item);
19726
+ if (typeof item !== "object" || item === null) continue;
19727
+ for (const child of Object.values(item)) stack.push(child);
19728
+ }
19729
+ return out;
19730
+ }
19731
+ var DOMAIN = "(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\\.)+[A-Za-z]{2,63}";
19732
+ var HOST = new RegExp(`^${DOMAIN}$`);
19733
+ var EMAIL = new RegExp(`^[^\\s@]+@${DOMAIN}$`);
19734
+ var URL_SHAPE = new RegExp(`^https?://${DOMAIN}(?::[0-9]{1,5})?((?:/[^/?#\\s]*)*)$`);
19735
+ var ARMOR = /^-----(?:BEGIN|END) [A-Z0-9 ]+-----$/;
19736
+ function unescaped(segment) {
19737
+ try {
19738
+ return decodeURIComponent(segment);
19739
+ } catch {
19740
+ return segment;
19741
+ }
19742
+ }
19743
+ function address(leaf) {
19744
+ if (HOST.test(leaf) || EMAIL.test(leaf)) return true;
19745
+ const url = URL_SHAPE.exec(leaf);
19746
+ if (!url) return false;
19747
+ return url[1].split("/").every((segment) => utf8(segment).length < FRAGMENT || EMAIL.test(unescaped(segment)));
19748
+ }
19749
+ function lines2(leaf) {
19750
+ if (address(leaf)) return [];
19751
+ return leaf.split(/[\r\n"\\]/).filter((line) => utf8(line).length >= FRAGMENT && !ARMOR.test(line));
19752
+ }
19753
+ function parsed2(value2) {
19754
+ try {
19755
+ const document = JSON.parse(value2);
19756
+ return typeof document === "object" && document !== null ? document : null;
19757
+ } catch {
19758
+ return null;
19759
+ }
19760
+ }
19761
+ function fragments(value2) {
19762
+ const raw2 = utf8(value2);
19763
+ const document = parsed2(value2);
19764
+ if (!document) return { pieces: [raw2], whole: true };
19765
+ return { pieces: [...new Set(strings(document).flatMap(lines2))].map(utf8), whole: false };
19766
+ }
19767
+ function shapes(value2, split3 = fragments(value2)) {
18612
19768
  const raw2 = utf8(value2);
18613
19769
  const seen = /* @__PURE__ */ new Set([value2]);
18614
19770
  const out = [];
18615
- if (raw2.length < FRAGMENT) out.push({ bytes: raw2, form: "raw" });
18616
- for (let offset = 0; raw2.length >= FRAGMENT && offset + FRAGMENT <= raw2.length; offset++) {
18617
- out.push({ bytes: raw2.subarray(offset, offset + FRAGMENT), form: "fragment", offset });
18618
- }
19771
+ if (raw2.length < FRAGMENT || !split3.whole) out.push({ bytes: raw2, form: "raw" });
19772
+ split3.pieces.forEach((bytes, piece) => {
19773
+ for (const offset of windows(bytes.length)) {
19774
+ out.push({
19775
+ bytes: bytes.subarray(offset, offset + FRAGMENT),
19776
+ form: "fragment",
19777
+ offset,
19778
+ piece
19779
+ });
19780
+ }
19781
+ });
18619
19782
  const add = (text, form) => {
18620
19783
  if (seen.has(text)) return;
18621
19784
  seen.add(text);
@@ -18748,12 +19911,19 @@ function prepare(secrets, sameLength) {
18748
19911
  const ids = /* @__PURE__ */ new Map();
18749
19912
  const wrapIds = /* @__PURE__ */ new Map();
18750
19913
  const values = secrets.map((secret) => utf8(secret.value));
19914
+ const splits = [];
18751
19915
  secrets.forEach((secret, index) => {
18752
19916
  if (typeof secret.value !== "string" || values[index].length < MIN_VALUE) {
18753
19917
  throw new RangeError(`the value of ${secret.name} is shorter than ${MIN_VALUE} bytes`);
18754
19918
  }
18755
- for (const shape of shapes(secret.value)) {
18756
- const source = { secret: index, form: shape.form, offset: shape.offset };
19919
+ splits.push(fragments(secret.value));
19920
+ for (const shape of shapes(secret.value, splits[index])) {
19921
+ const source = {
19922
+ secret: index,
19923
+ form: shape.form,
19924
+ offset: shape.offset,
19925
+ piece: shape.piece
19926
+ };
18757
19927
  register(main, sources, ids, shape.bytes, source);
18758
19928
  if (wrappable(shape.form)) register(wrap, wrapSources, wrapIds, shape.bytes, source);
18759
19929
  }
@@ -18764,7 +19934,16 @@ function prepare(secrets, sameLength) {
18764
19934
  utf8(secret.placeholder),
18765
19935
  utf8(`[redacted:${secret.name}]`)
18766
19936
  ]);
18767
- return { secrets, values, main, wrap, sources, wrapSources, replacements, sameLength };
19937
+ return {
19938
+ secrets,
19939
+ splits,
19940
+ main,
19941
+ wrap,
19942
+ sources,
19943
+ wrapSources,
19944
+ replacements,
19945
+ sameLength
19946
+ };
18768
19947
  }
18769
19948
  function filled(input, start2, end) {
18770
19949
  const out = new Uint8Array(end - start2);
@@ -18795,14 +19974,16 @@ function resolve(c, input, start2, end, source) {
18795
19974
  growing: false
18796
19975
  };
18797
19976
  }
18798
- const value2 = c.values[source.secret];
19977
+ const split3 = c.splits[source.secret];
19978
+ const value2 = split3.pieces[source.piece];
18799
19979
  let stop = end;
18800
19980
  let at = source.offset + FRAGMENT;
18801
19981
  while (stop < input.length && at < value2.length && input[stop] === value2[at]) {
18802
19982
  stop++;
18803
19983
  at++;
18804
19984
  }
18805
- const form = source.offset === 0 && at === value2.length ? "raw" : "fragment";
19985
+ const whole = split3.whole && source.offset === 0 && at === value2.length;
19986
+ const form = whole ? "raw" : "fragment";
18806
19987
  const growing = stop === input.length && at < value2.length;
18807
19988
  return { start: start2, end: stop, secret: source.secret, form, rank: RANK[form], growing };
18808
19989
  }
@@ -18956,9 +20137,9 @@ function compile(secrets, options = {}) {
18956
20137
  // src/kernel/scrub-file.ts
18957
20138
  var SCRUB_IDLE_MS = 50;
18958
20139
  var SCRUB_NONBLOCK_MAX = 64 * 1024;
18959
- var encoder5 = new TextEncoder();
20140
+ var encoder6 = new TextEncoder();
18960
20141
  function scrubberOf(secrets, sameLength = false) {
18961
- const usable3 = secrets.filter((s) => encoder5.encode(s.value).length >= MIN_VALUE);
20142
+ const usable3 = secrets.filter((s) => encoder6.encode(s.value).length >= MIN_VALUE);
18962
20143
  return usable3.length > 0 ? compile(usable3, { idleMs: SCRUB_IDLE_MS, sameLength }) : void 0;
18963
20144
  }
18964
20145
  async function writeAll2(file, bytes) {
@@ -18982,7 +20163,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
18982
20163
  let timer;
18983
20164
  let failed4;
18984
20165
  const waiters = /* @__PURE__ */ new Set();
18985
- const quiet2 = () => !busy && pending === 0;
20166
+ const quiet4 = () => !busy && pending === 0;
18986
20167
  const wake = () => {
18987
20168
  for (const waiter of [...waiters]) waiter();
18988
20169
  };
@@ -18999,7 +20180,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
18999
20180
  if (failed4 === void 0) failed4 = err;
19000
20181
  }).finally(() => {
19001
20182
  pending -= 1;
19002
- if (quiet2()) wake();
20183
+ if (quiet4()) wake();
19003
20184
  });
19004
20185
  };
19005
20186
  const settling = (signal) => new Promise((resolve2, reject) => {
@@ -19017,7 +20198,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
19017
20198
  signal?.addEventListener("abort", stop, { once: true });
19018
20199
  });
19019
20200
  const settled = async () => {
19020
- while (!quiet2()) await settling();
20201
+ while (!quiet4()) await settling();
19021
20202
  };
19022
20203
  const changed = async (signal) => {
19023
20204
  const round = new AbortController();
@@ -19033,7 +20214,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
19033
20214
  };
19034
20215
  const turn = async (signal) => {
19035
20216
  for (let waited = false; ; waited = true) {
19036
- if (quiet2() && (pollFile(file).writable || !file.changed)) {
20217
+ if (quiet4() && (pollFile(file).writable || !file.changed)) {
19037
20218
  busy = true;
19038
20219
  return;
19039
20220
  }
@@ -19062,13 +20243,14 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
19062
20243
  ...bound2(file, "stat"),
19063
20244
  poll: () => {
19064
20245
  const state = pollFile(file);
19065
- return quiet2() ? state : { ...state, writable: false };
20246
+ return quiet4() ? state : { ...state, writable: false };
19066
20247
  },
19067
20248
  changed,
19068
20249
  ...file.tty ? { tty: file.tty } : {},
19069
20250
  identity: file.identity ?? file,
19070
20251
  ...file.clipboard ? { clipboard: file.clipboard } : {},
19071
20252
  ...file.embedder ? { embedder: file.embedder } : {},
20253
+ ...file.device ? { device: file.device } : {},
19072
20254
  ...file.write ? {
19073
20255
  write: async (bytes, signal) => {
19074
20256
  await turn(signal);
@@ -19080,7 +20262,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
19080
20262
  await send2(stream.push(taken), signal);
19081
20263
  } finally {
19082
20264
  busy = false;
19083
- if (quiet2()) wake();
20265
+ if (quiet4()) wake();
19084
20266
  }
19085
20267
  timer = setTimeout(release, idleMs);
19086
20268
  return taken.length;
@@ -19139,9 +20321,9 @@ function scrubStdio(fds, scrubber) {
19139
20321
  // src/kernel/net/sealed-scrubs.ts
19140
20322
  function addTo(tree2, host, secrets) {
19141
20323
  const entry = tree2.get(host) ?? { secrets: [], version: 0, scrubber: void 0 };
19142
- const fresh3 = secrets.filter((s) => !entry.secrets.some((e) => e.value === s.value));
19143
- if (fresh3.length === 0) return;
19144
- entry.secrets = [...entry.secrets, ...fresh3];
20324
+ const fresh4 = secrets.filter((s) => !entry.secrets.some((e) => e.value === s.value));
20325
+ if (fresh4.length === 0) return;
20326
+ entry.secrets = [...entry.secrets, ...fresh4];
19145
20327
  entry.version += 1;
19146
20328
  entry.scrubber = void 0;
19147
20329
  tree2.set(host, entry);
@@ -19304,7 +20486,7 @@ var HEADER = new RegExp(`^header:${TOKEN3}$`);
19304
20486
  var METHOD = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
19305
20487
  var TARGET = /^[\x21-\x7e]+$/;
19306
20488
  var SESSION_EXIT = /^(tailscale|device:[A-Za-z0-9._:-]+)$/;
19307
- var HOST = /^[a-z0-9.:-]+$/;
20489
+ var HOST2 = /^[a-z0-9.:-]+$/;
19308
20490
  var UNSAFE = /[\r\n\0]/;
19309
20491
  var ANCHOR_ID = /^[A-Za-z0-9+/]{43}=$/;
19310
20492
  function verifiedByOk(f) {
@@ -19358,7 +20540,7 @@ function scramOk(f) {
19358
20540
  return texts.every((t) => typeof t === "string" && SCRAM_TEXT.test(t));
19359
20541
  }
19360
20542
  function hostOk(host) {
19361
- return typeof host === "string" && HOST.test(host) && boundHost(host) === host;
20543
+ return typeof host === "string" && HOST2.test(host) && boundHost(host) === host;
19362
20544
  }
19363
20545
  function sessionAskable(f, now) {
19364
20546
  if (!Object.hasOwn(POSITIONS, f.protocol) || typeof f.position !== "string") return false;
@@ -19448,20 +20630,20 @@ var ASK_FIELDS = /* @__PURE__ */ new Set([
19448
20630
  "caller",
19449
20631
  "requester"
19450
20632
  ]);
19451
- var encoder6 = new TextEncoder();
20633
+ var encoder7 = new TextEncoder();
19452
20634
  var decoder3 = new TextDecoder("utf-8", { fatal: true });
19453
20635
  function parseSealed(bytes) {
19454
20636
  try {
19455
20637
  if (bytes.length > SEALED_MAX_BYTES) throw new KernelError("EMSGSIZE");
19456
- let parsed2;
20638
+ let parsed3;
19457
20639
  try {
19458
- parsed2 = JSON.parse(decoder3.decode(bytes));
20640
+ parsed3 = JSON.parse(decoder3.decode(bytes));
19459
20641
  } catch {
19460
20642
  throw new KernelError("EINVAL");
19461
20643
  }
19462
- if (typeof parsed2 !== "object" || parsed2 === null || Array.isArray(parsed2))
20644
+ if (typeof parsed3 !== "object" || parsed3 === null || Array.isArray(parsed3))
19463
20645
  throw new KernelError("EINVAL");
19464
- const req = parsed2;
20646
+ const req = parsed3;
19465
20647
  if (req.v !== 1 || typeof req.op !== "string" || !SEALED_OPS.has(req.op))
19466
20648
  throw new KernelError("EINVAL");
19467
20649
  if (req.op === "materialize" && !materializable(req)) throw new KernelError("EINVAL");
@@ -19557,7 +20739,7 @@ var SealedChannel = class {
19557
20739
  await this.broker.tickets.record(req, reply, caller.pid);
19558
20740
  if (this.inflight !== flight) return;
19559
20741
  this.inflight = void 0;
19560
- if (reply) this.reply = encoder6.encode(serialized(reply));
20742
+ if (reply) this.reply = encoder7.encode(serialized(reply));
19561
20743
  });
19562
20744
  this.inflight = flight;
19563
20745
  return length2;
@@ -19833,7 +21015,7 @@ function shown(text) {
19833
21015
  const chars = [...safe];
19834
21016
  return `'${chars.length > SHOWN_MAX ? `${chars.slice(0, SHOWN_MAX - 1).join("")}\u2026` : safe}'`;
19835
21017
  }
19836
- function unescaped(double) {
21018
+ function unescaped2(double) {
19837
21019
  return double.replace(/\\(["\\$`])/g, "$1");
19838
21020
  }
19839
21021
  function splitWords(text) {
@@ -19849,7 +21031,7 @@ function splitWords(text) {
19849
21031
  word = void 0;
19850
21032
  continue;
19851
21033
  }
19852
- word = (word ?? "") + (single ?? (double !== void 0 ? unescaped(double) : escaped ?? plain3));
21034
+ word = (word ?? "") + (single ?? (double !== void 0 ? unescaped2(double) : escaped ?? plain3));
19853
21035
  }
19854
21036
  if (word !== void 0) words3.push(word);
19855
21037
  return words3;
@@ -19886,7 +21068,7 @@ function splitPatterns(text) {
19886
21068
  }
19887
21069
  word ??= [];
19888
21070
  if (plain3 !== void 0) addPlain(word, plain3);
19889
- else addPart(word, single ?? (double !== void 0 ? unescaped(double) : escaped));
21071
+ else addPart(word, single ?? (double !== void 0 ? unescaped2(double) : escaped));
19890
21072
  }
19891
21073
  if (word !== void 0) words3.push(word);
19892
21074
  return words3;
@@ -19937,13 +21119,13 @@ function commandMatches(cmnd, argv) {
19937
21119
  if (cmnd === "*") return true;
19938
21120
  const [path, ...args] = argv;
19939
21121
  if (path !== cmnd.path) return false;
19940
- const want = cmnd.args;
19941
- if (want === void 0) return true;
21122
+ const want2 = cmnd.args;
21123
+ if (want2 === void 0) return true;
19942
21124
  return globMatch(
19943
- want.length,
21125
+ want2.length,
19944
21126
  args.length,
19945
- (i) => isAny(want[i]),
19946
- (i, j) => wordMatch(want[i], args[j])
21127
+ (i) => isAny(want2[i]),
21128
+ (i, j) => wordMatch(want2[i], args[j])
19947
21129
  );
19948
21130
  }
19949
21131
  function commandText(cmnd) {
@@ -20240,11 +21422,11 @@ function parsePolicy(text, file) {
20240
21422
  const defaults2 = [];
20241
21423
  const errors = [];
20242
21424
  for (const [n, line] of text.split("\n").entries()) {
20243
- const parsed2 = parseLine(line, file);
20244
- if (parsed2 === null) continue;
20245
- if ("error" in parsed2) errors.push({ line: n + 1, message: parsed2.error });
20246
- else if ("defaults" in parsed2) defaults2.push(parsed2.defaults);
20247
- else rules.push(...parsed2.rules);
21425
+ const parsed3 = parseLine(line, file);
21426
+ if (parsed3 === null) continue;
21427
+ if ("error" in parsed3) errors.push({ line: n + 1, message: parsed3.error });
21428
+ else if ("defaults" in parsed3) defaults2.push(parsed3.defaults);
21429
+ else rules.push(...parsed3.rules);
20248
21430
  }
20249
21431
  return { rules, defaults: defaults2, errors };
20250
21432
  }
@@ -20297,11 +21479,11 @@ var REPLAY_TAGS_MAX = 64;
20297
21479
  var REPLAY_ARGV_MAX = 4096;
20298
21480
  var REPLAY_BYTES_MAX = 1024 * 1024;
20299
21481
  var NOT_A_REQUEST = "sudo: that isn't a sudo request this kernel can run";
20300
- function strings(value2, max) {
21482
+ function strings2(value2, max) {
20301
21483
  return Array.isArray(value2) && value2.length <= max && value2.every((s) => typeof s === "string");
20302
21484
  }
20303
21485
  function words(value2) {
20304
- if (!strings(value2, REPLAY_ARGV_MAX) || value2.length === 0) return false;
21486
+ if (!strings2(value2, REPLAY_ARGV_MAX) || value2.length === 0) return false;
20305
21487
  const bytes = value2.reduce((n, s) => n + s.length + 1, 0);
20306
21488
  return bytes <= REPLAY_BYTES_MAX && !value2.some((s) => s.includes("\0"));
20307
21489
  }
@@ -20336,7 +21518,7 @@ function replayOf(value2) {
20336
21518
  const bound3 = filesOf(r.userWritable);
20337
21519
  const named = (s) => typeof s === "string" && s !== "";
20338
21520
  const absolute2 = (s) => typeof s === "string" && s.startsWith("/") && s.length <= PATH_MAX && !s.includes("\0");
20339
- const valid = typeof id2 === "string" && REPLAY_ID.test(id2) && named(user) && named(target) && Number.isInteger(uid) && uid >= 0 && words(argv) && absolute2(path) && absolute2(cwd) && (stdin === "tty" || stdin === "none") && (tag === void 0 || typeof tag === "string") && (tags === void 0 || strings(tags, REPLAY_TAGS_MAX)) && rootOnly2 !== void 0 && bound3 !== void 0;
21521
+ const valid = typeof id2 === "string" && REPLAY_ID.test(id2) && named(user) && named(target) && Number.isInteger(uid) && uid >= 0 && words(argv) && absolute2(path) && absolute2(cwd) && (stdin === "tty" || stdin === "none") && (tag === void 0 || typeof tag === "string") && (tags === void 0 || strings2(tags, REPLAY_TAGS_MAX)) && rootOnly2 !== void 0 && bound3 !== void 0;
20340
21522
  if (!valid) return failed2("EINVAL", NOT_A_REQUEST);
20341
21523
  const all = tags ?? (tag === void 0 ? [] : [tag]);
20342
21524
  return {
@@ -20428,28 +21610,28 @@ function recordedCommand(argv) {
20428
21610
  const [path = "", ...args] = argv;
20429
21611
  return commandText({ path, args: args.map((a) => [a]) });
20430
21612
  }
20431
- function flags(parsed2, arg, next3) {
21613
+ function flags(parsed3, arg, next3) {
20432
21614
  for (let j = 1; j < arg.length; j++) {
20433
21615
  const flag = arg[j];
20434
- if (flag === "n") parsed2.nonInteractive = true;
20435
- else if (flag === "l") parsed2.list = true;
20436
- else if (flag === "v") parsed2.validate = true;
20437
- else if (flag === "k") parsed2.reset = true;
20438
- else if (flag === "K") parsed2.removeAll = true;
21616
+ if (flag === "n") parsed3.nonInteractive = true;
21617
+ else if (flag === "l") parsed3.list = true;
21618
+ else if (flag === "v") parsed3.validate = true;
21619
+ else if (flag === "k") parsed3.reset = true;
21620
+ else if (flag === "K") parsed3.removeAll = true;
20439
21621
  else if (flag !== "u") return `sudo: invalid option -- '${flag}'
20440
21622
  ${USAGE}`;
20441
21623
  else {
20442
21624
  const user = arg.slice(j + 1) || next3;
20443
21625
  if (!user) return `sudo: option requires an argument -- 'u'
20444
21626
  ${USAGE}`;
20445
- parsed2.user = user;
21627
+ parsed3.user = user;
20446
21628
  return arg.slice(j + 1) ? 0 : 1;
20447
21629
  }
20448
21630
  }
20449
21631
  return 0;
20450
21632
  }
20451
21633
  function parseArgs(args) {
20452
- const parsed2 = {
21634
+ const parsed3 = {
20453
21635
  list: false,
20454
21636
  nonInteractive: false,
20455
21637
  validate: false,
@@ -20466,19 +21648,19 @@ function parseArgs(args) {
20466
21648
  break;
20467
21649
  }
20468
21650
  if (!arg.startsWith("-") || arg === "-") break;
20469
- const used2 = flags(parsed2, arg, args[i + 1]);
21651
+ const used2 = flags(parsed3, arg, args[i + 1]);
20470
21652
  if (typeof used2 === "string") return used2;
20471
21653
  i += used2;
20472
21654
  }
20473
- parsed2.command = args.slice(i);
20474
- return usable(parsed2) ? parsed2 : USAGE;
21655
+ parsed3.command = args.slice(i);
21656
+ return usable(parsed3) ? parsed3 : USAGE;
20475
21657
  }
20476
- function usable(parsed2) {
20477
- const command = parsed2.command.length > 0;
20478
- const modes = [parsed2.list, parsed2.validate, parsed2.removeAll].filter(Boolean).length;
20479
- if (modes > 1 || parsed2.removeAll && parsed2.reset) return false;
21658
+ function usable(parsed3) {
21659
+ const command = parsed3.command.length > 0;
21660
+ const modes = [parsed3.list, parsed3.validate, parsed3.removeAll].filter(Boolean).length;
21661
+ if (modes > 1 || parsed3.removeAll && parsed3.reset) return false;
20480
21662
  if (modes === 1) return !command;
20481
- return command || parsed2.reset;
21663
+ return command || parsed3.reset;
20482
21664
  }
20483
21665
  function policyOf(settings) {
20484
21666
  return { type: settings.timestampType, minutes: settings.timestampTimeout };
@@ -20622,28 +21804,28 @@ var Sudo = class {
20622
21804
  };
20623
21805
  }
20624
21806
  async check(cred, call) {
20625
- const lines2 = [];
21807
+ const lines3 = [];
20626
21808
  let ok4 = true;
20627
21809
  const report = ({ path, problems, ok: read }) => {
20628
21810
  for (const problem of problems) {
20629
21811
  const where = problem.line === void 0 ? `${path}: sudo skips it:` : `${path}:${problem.line}:`;
20630
- lines2.push([2, `visudo: ${where} ${problem.message}`]);
21812
+ lines3.push([2, `visudo: ${where} ${problem.message}`]);
20631
21813
  }
20632
- if (read && problems.length === 0) lines2.push([1, `${path}: parsed OK`]);
21814
+ if (read && problems.length === 0) lines3.push([1, `${path}: parsed OK`]);
20633
21815
  if (problems.length > 0) ok4 = false;
20634
21816
  };
20635
21817
  const values = (defaults2) => {
20636
- for (const line of defaultsText(defaults2)) lines2.push([1, line]);
21818
+ for (const line of defaultsText(defaults2)) lines3.push([1, line]);
20637
21819
  };
20638
21820
  if (call.files !== void 0) {
20639
21821
  const defaults2 = [];
20640
21822
  for (const { name: name2, text } of call.files) {
20641
- const parsed2 = parsePolicy(text);
20642
- defaults2.push(...parsed2.defaults);
20643
- report({ path: name2, rules: [], problems: parsed2.errors, ok: true });
21823
+ const parsed3 = parsePolicy(text);
21824
+ defaults2.push(...parsed3.defaults);
21825
+ report({ path: name2, rules: [], problems: parsed3.errors, ok: true });
20644
21826
  }
20645
21827
  if (defaults2.length > 0) values(defaults2);
20646
- return { lines: lines2, ok: ok4 };
21828
+ return { lines: lines3, ok: ok4 };
20647
21829
  }
20648
21830
  if (this.o.mode === "enforce" && cred.euid !== 0) {
20649
21831
  const hint = "check the policy as root (sudo visudo -c), or a file with visudo -c -f <file>";
@@ -20652,7 +21834,7 @@ var Sudo = class {
20652
21834
  const files = await this.policy();
20653
21835
  for (const file of files) report(file);
20654
21836
  values(files.flatMap((f) => f.defaults ?? []));
20655
- return { lines: lines2, ok: ok4 };
21837
+ return { lines: lines3, ok: ok4 };
20656
21838
  }
20657
21839
  async groupIds() {
20658
21840
  return new Map((await this.o.users.groups()).map((g) => [g.name, g.gid]));
@@ -20717,13 +21899,13 @@ var Sudo = class {
20717
21899
  return void 0;
20718
21900
  }
20719
21901
  async request(caller, call, signal) {
20720
- const parsed2 = parseArgs(call.args);
20721
- if (typeof parsed2 === "string") return failed2("EINVAL", parsed2);
21902
+ const parsed3 = parseArgs(call.args);
21903
+ if (typeof parsed3 === "string") return failed2("EINVAL", parsed3);
20722
21904
  if (this.o.mode !== "enforce") {
20723
21905
  const mode = this.o.mode;
20724
- if (parsed2.list) return { list: `sudo: users are '${mode}': commands run as the caller
21906
+ if (parsed3.list) return { list: `sudo: users are '${mode}': commands run as the caller
20725
21907
  ` };
20726
- return parsed2.command.length > 0 ? { run: parsed2.command } : DONE;
21908
+ return parsed3.command.length > 0 ? { run: parsed3.command } : DONE;
20727
21909
  }
20728
21910
  const { cred } = caller;
20729
21911
  const policy = await this.policyRules(caller);
@@ -20736,22 +21918,22 @@ var Sudo = class {
20736
21918
  };
20737
21919
  const applies = await this.applies(self, cred);
20738
21920
  const settings = resolveDefaults(policy.defaults, applies);
20739
- if (parsed2.removeAll) {
21921
+ if (parsed3.removeAll) {
20740
21922
  this.timestamps.remove(owner.uid);
20741
21923
  return DONE;
20742
21924
  }
20743
- if (parsed2.reset && !parsed2.validate && !parsed2.list && parsed2.command.length === 0) {
21925
+ if (parsed3.reset && !parsed3.validate && !parsed3.list && parsed3.command.length === 0) {
20744
21926
  this.timestamps.invalidate(owner, settings.timestampType);
20745
21927
  return DONE;
20746
21928
  }
20747
- const target = await this.lookup(parsed2.user);
20748
- if (!target) return failed2("ENOENT", `sudo: unknown user ${parsed2.user}`);
21929
+ const target = await this.lookup(parsed3.user);
21930
+ if (!target) return failed2("ENOENT", `sudo: unknown user ${parsed3.user}`);
20749
21931
  const root = cred.euid === 0;
20750
21932
  const rules = root ? [] : policy.rules;
20751
21933
  const mine = rules.filter((rule) => applies(rule.who));
20752
- if (parsed2.list) return await this.listed(self, root, mine, settings.matching);
20753
- const session = { caller, self, target, owner, policy: policyOf(settings), parsed: parsed2, signal };
20754
- if (parsed2.validate) return this.validate(session, root, mine, call.cwd);
21934
+ if (parsed3.list) return await this.listed(self, root, mine, settings.matching);
21935
+ const session = { caller, self, target, owner, policy: policyOf(settings), parsed: parsed3, signal };
21936
+ if (parsed3.validate) return this.validate(session, root, mine, call.cwd);
20755
21937
  return this.command(session, root, mine, call);
20756
21938
  }
20757
21939
  async replay(value2) {
@@ -20810,9 +21992,9 @@ var Sudo = class {
20810
21992
  );
20811
21993
  }
20812
21994
  async command(s, root, mine, call) {
20813
- const { caller, self, target, parsed: parsed2 } = s;
21995
+ const { caller, self, target, parsed: parsed3 } = s;
20814
21996
  const { cred } = caller;
20815
- const [word = "", ...args] = parsed2.command;
21997
+ const [word = "", ...args] = parsed3.command;
20816
21998
  const cwd = await this.canonical(call.cwd);
20817
21999
  if (cwd === void 0 || !await this.reachable(cred, cwd)) {
20818
22000
  return failed2(
@@ -20827,7 +22009,7 @@ var Sudo = class {
20827
22009
  const matching = mine.filter(
20828
22010
  (rule) => (rule.target === "ALL" || rule.target === target.name) && (rule.cwd === void 0 || rule.cwd === cwd) && stdinMatches(rule, caller.stdin) && commandMatches(rule.cmnd, argv) && pinned(rule, bound3)
20829
22011
  );
20830
- const grant = () => this.grant(self, target, cred, parsed2.command, path, cwd, call.term, bound3);
22012
+ const grant = () => this.grant(self, target, cred, parsed3.command, path, cwd, call.term, bound3);
20831
22013
  if (root || matching.some((rule) => rule.nopasswd)) return grant();
20832
22014
  const quoted = argv.map(quoteWord).join(" ");
20833
22015
  if (matching.length === 0) {
@@ -20836,38 +22018,38 @@ var Sudo = class {
20836
22018
  `sudo: ${self.name} is not allowed to run ${quoted} as ${target.name}`
20837
22019
  );
20838
22020
  }
20839
- if (!parsed2.reset && this.timestamps.current(s.owner, s.policy, bound3)) {
22021
+ if (!parsed3.reset && this.timestamps.current(s.owner, s.policy, bound3)) {
20840
22022
  this.timestamps.stamp(s.owner, s.policy, bound3);
20841
22023
  this.logged(s, cwd, argv);
20842
22024
  return grant();
20843
22025
  }
20844
- if (parsed2.nonInteractive) return failed2("EPERM", "sudo: a password is required");
22026
+ if (parsed3.nonInteractive) return failed2("EPERM", "sudo: a password is required");
20845
22027
  return this.pendingFor(cred.ruid, async () => {
20846
- const answer2 = await this.ask(s, { argv: parsed2.command, path, cwd, bound: bound3 });
22028
+ const answer2 = await this.ask(s, { argv: parsed3.command, path, cwd, bound: bound3 });
20847
22029
  if ("error" in answer2) return answer2;
20848
22030
  if (await this.canonical(call.cwd) !== cwd || !await this.reachable(cred, cwd)) {
20849
22031
  return failed2("EACCES", `sudo: ${call.cwd} changed while the request waited`);
20850
22032
  }
20851
22033
  const now = await this.bound(path, cwd);
20852
22034
  if (!now) return failed2("ENOENT", `sudo: ${word}: command not found`);
20853
- const shown2 = recordable(cwd, [...parsed2.command, ...Object.keys(bound3)]);
22035
+ const shown2 = recordable(cwd, [...parsed3.command, ...Object.keys(bound3)]);
20854
22036
  const result = answer2.answer === "once" ? grant() : await this.always(self, target, { cwd, argv, bound: bound3 }, now, shown2, grant);
20855
22037
  if (!("error" in result)) this.stamped(s, answer2, bound3);
20856
22038
  return result;
20857
22039
  });
20858
22040
  }
20859
22041
  async validate(s, root, mine, dir) {
20860
- const { self, parsed: parsed2 } = s;
22042
+ const { self, parsed: parsed3 } = s;
20861
22043
  if (root) return DONE;
20862
22044
  if (mine.length === 0) {
20863
22045
  return failed2("EPERM", `Sorry, user ${self.name} may not run sudo on ${this.o.hostname}.`);
20864
22046
  }
20865
22047
  if (mine.every((rule) => rule.nopasswd)) return DONE;
20866
- if (!parsed2.reset && this.timestamps.current(s.owner, s.policy)) {
22048
+ if (!parsed3.reset && this.timestamps.current(s.owner, s.policy)) {
20867
22049
  this.timestamps.stamp(s.owner, s.policy);
20868
22050
  return DONE;
20869
22051
  }
20870
- if (parsed2.nonInteractive) return failed2("EPERM", "sudo: a password is required");
22052
+ if (parsed3.nonInteractive) return failed2("EPERM", "sudo: a password is required");
20871
22053
  const cwd = await this.canonical(dir) ?? normalizePath(dir);
20872
22054
  return this.pendingFor(s.caller.cred.ruid, async () => {
20873
22055
  const answer2 = await this.ask(s, { argv: [], path: "", cwd, bound: {}, validate: true });
@@ -20922,7 +22104,7 @@ var Sudo = class {
20922
22104
  }
20923
22105
  const shown2 = [];
20924
22106
  for (const rule of rules) shown2.push(described(rule, await this.writable(rule)));
20925
- const lines2 = shown2.join("\n");
22107
+ const lines3 = shown2.join("\n");
20926
22108
  const params = defaults2.flatMap((d) => d.params).join(", ");
20927
22109
  const matching = params === "" ? "" : `Matching Defaults entries for ${self.name} on ${host}:
20928
22110
  ${params}
@@ -20930,7 +22112,7 @@ var Sudo = class {
20930
22112
  `;
20931
22113
  return {
20932
22114
  list: `${matching}User ${self.name} may run the following commands on ${host}:
20933
- ${lines2}
22115
+ ${lines3}
20934
22116
  `
20935
22117
  };
20936
22118
  }
@@ -21347,7 +22529,7 @@ async function epollSyscall(req, proc) {
21347
22529
 
21348
22530
  // src/kernel/locks.ts
21349
22531
  var overlaps = (a, b) => a.start < b.end && b.start < a.end;
21350
- var clashes = (lock, owner, want) => lock.owner !== owner && overlaps(lock, want) && (lock.kind === "write" || want.kind === "write");
22532
+ var clashes = (lock, owner, want2) => lock.owner !== owner && overlaps(lock, want2) && (lock.kind === "write" || want2.kind === "write");
21351
22533
  function lockRange(start2, len) {
21352
22534
  if (!Number.isSafeInteger(start2) || !Number.isSafeInteger(len)) throw new KernelError("EINVAL");
21353
22535
  const from = len < 0 ? start2 + len : start2;
@@ -21433,8 +22615,8 @@ var LockTable = class {
21433
22615
  blocked = /* @__PURE__ */ new Map();
21434
22616
  wakers = /* @__PURE__ */ new Map();
21435
22617
  held = /* @__PURE__ */ new Map();
21436
- holder(key, owner, want) {
21437
- return this.records.get(key)?.find((lock) => clashes(lock, owner, want));
22618
+ holder(key, owner, want2) {
22619
+ return this.records.get(key)?.find((lock) => clashes(lock, owner, want2));
21438
22620
  }
21439
22621
  set(key, owner, kind, start2, end) {
21440
22622
  if (kind !== "unlock" && this.holder(key, owner, { key, kind, start: start2, end })) return false;
@@ -21453,10 +22635,10 @@ var LockTable = class {
21453
22635
  return true;
21454
22636
  }
21455
22637
  async wait(key, owner, kind, range2, blocking) {
21456
- const want = { key, kind, ...range2 };
22638
+ const want2 = { key, kind, ...range2 };
21457
22639
  let signal;
21458
22640
  for (; ; ) {
21459
- const holder = this.holder(key, owner, want);
22641
+ const holder = this.holder(key, owner, want2);
21460
22642
  if (!holder) {
21461
22643
  this.set(key, owner, kind, range2.start, range2.end);
21462
22644
  return;
@@ -21464,7 +22646,7 @@ var LockTable = class {
21464
22646
  if (this.waitsOn(holder.owner, owner)) throw new KernelError("EDEADLK");
21465
22647
  signal ??= blocking();
21466
22648
  if (signal.aborted) throw new KernelError("EINTR");
21467
- this.blocked.set(owner, want);
22649
+ this.blocked.set(owner, want2);
21468
22650
  try {
21469
22651
  await this.changed(key, signal);
21470
22652
  } finally {
@@ -21476,9 +22658,9 @@ var LockTable = class {
21476
22658
  const seen = /* @__PURE__ */ new Set();
21477
22659
  for (let at = from; at !== void 0 && !seen.has(at); ) {
21478
22660
  seen.add(at);
21479
- const want = this.blocked.get(at);
21480
- if (!want) return false;
21481
- const holder = this.holder(want.key, at, want);
22661
+ const want2 = this.blocked.get(at);
22662
+ if (!want2) return false;
22663
+ const holder = this.holder(want2.key, at, want2);
21482
22664
  if (!holder) return false;
21483
22665
  if (holder.owner === target) return true;
21484
22666
  at = holder.owner;
@@ -21843,24 +23025,24 @@ function errno2(code, message3) {
21843
23025
  }
21844
23026
  function requestHead(method, url, headers, extra, first = []) {
21845
23027
  if (!TOKEN4.test(method)) throw errno2("EINVAL", `method ${method}`);
21846
- const lines2 = [`${method} ${url.pathname}${url.search} HTTP/1.1`, `host: ${url.host}`];
21847
- for (const [name2, value2] of first) lines2.push(`${name2}: ${value2}`);
23028
+ const lines3 = [`${method} ${url.pathname}${url.search} HTTP/1.1`, `host: ${url.host}`];
23029
+ for (const [name2, value2] of first) lines3.push(`${name2}: ${value2}`);
21848
23030
  for (const [name2, value2] of headers) {
21849
23031
  if (OWN_HEADERS.has(name2.toLowerCase())) continue;
21850
23032
  if (!TOKEN4.test(name2) || !SAFE_VALUE.test(value2)) throw errno2("EINVAL", `header ${name2}`);
21851
- lines2.push(`${name2}: ${value2}`);
23033
+ lines3.push(`${name2}: ${value2}`);
21852
23034
  }
21853
- for (const [name2, value2] of extra) lines2.push(`${name2}: ${value2}`);
21854
- return latin1Bytes(`${lines2.join("\r\n")}\r
23035
+ for (const [name2, value2] of extra) lines3.push(`${name2}: ${value2}`);
23036
+ return latin1Bytes(`${lines3.join("\r\n")}\r
21855
23037
  \r
21856
23038
  `);
21857
23039
  }
21858
23040
  function parseHead(bytes) {
21859
- const [statusLine = "", ...lines2] = latin1(bytes).split(/\r?\n/);
23041
+ const [statusLine = "", ...lines3] = latin1(bytes).split(/\r?\n/);
21860
23042
  const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
21861
23043
  if (!match) throw errno2("EPROTO", `not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);
21862
23044
  const headers = [];
21863
- for (const line of lines2) {
23045
+ for (const line of lines3) {
21864
23046
  const colon = line.indexOf(":");
21865
23047
  if (colon > 0) headers.push([line.slice(0, colon).trim(), line.slice(colon + 1).trim()]);
21866
23048
  }
@@ -21950,8 +23132,8 @@ var KernelLoopback = class {
21950
23132
  );
21951
23133
  } catch (err) {
21952
23134
  socket.close();
21953
- const refused4 = err instanceof KernelError && err.code === "ECONNREFUSED";
21954
- throw refused4 ? errno2("ECONNREFUSED", `nothing listens on the kernel's port ${port}`) : err;
23135
+ const refused5 = err instanceof KernelError && err.code === "ECONNREFUSED";
23136
+ throw refused5 ? errno2("ECONNREFUSED", `nothing listens on the kernel's port ${port}`) : err;
21955
23137
  }
21956
23138
  signal.addEventListener("abort", () => socket.close(), { once: true });
21957
23139
  return socket;
@@ -22340,9 +23522,9 @@ function pem(der2) {
22340
23522
  for (let i = 0; i < der2.length; i += 32768) {
22341
23523
  b642 += String.fromCharCode(...der2.subarray(i, i + 32768));
22342
23524
  }
22343
- const lines2 = btoa(b642).match(/.{1,64}/g) ?? [];
23525
+ const lines3 = btoa(b642).match(/.{1,64}/g) ?? [];
22344
23526
  return `-----BEGIN CERTIFICATE-----
22345
- ${lines2.join("\n")}
23527
+ ${lines3.join("\n")}
22346
23528
  -----END CERTIFICATE-----
22347
23529
  `;
22348
23530
  }
@@ -23126,13 +24308,13 @@ var HOSTS_PATH = "/etc/hosts";
23126
24308
  function parseHosts(text) {
23127
24309
  const table = /* @__PURE__ */ new Map();
23128
24310
  for (const raw2 of text.split("\n")) {
23129
- const [address, ...names2] = raw2.replace(/#.*/, "").trim().split(/\s+/);
23130
- if (!address || names2.length === 0) continue;
23131
- if (ipv4Bytes(address) === void 0 && ipv6Groups(address) === void 0) continue;
24311
+ const [address2, ...names2] = raw2.replace(/#.*/, "").trim().split(/\s+/);
24312
+ if (!address2 || names2.length === 0) continue;
24313
+ if (ipv4Bytes(address2) === void 0 && ipv6Groups(address2) === void 0) continue;
23132
24314
  for (const name2 of names2) {
23133
24315
  const key = name2.toLowerCase().replace(/\.$/, "");
23134
24316
  const known = table.get(key) ?? [];
23135
- if (!known.includes(address)) known.push(address);
24317
+ if (!known.includes(address2)) known.push(address2);
23136
24318
  table.set(key, known);
23137
24319
  }
23138
24320
  }
@@ -23332,14 +24514,14 @@ var Resolver = class {
23332
24514
  if (hostsEntry(key, this.own) !== void 0) return true;
23333
24515
  return ((await this.hosts?.().catch(() => void 0))?.get(key)?.length ?? 0) > 0;
23334
24516
  }
23335
- async reverse(address) {
23336
- const v6 = ipv6Groups(address)?.join(":");
23337
- const same3 = (a) => a === address || v6 !== void 0 && ipv6Groups(a)?.join(":") === v6;
24517
+ async reverse(address2) {
24518
+ const v6 = ipv6Groups(address2)?.join(":");
24519
+ const same3 = (a) => a === address2 || v6 !== void 0 && ipv6Groups(a)?.join(":") === v6;
23338
24520
  const file = await this.hosts?.().catch(() => void 0);
23339
24521
  for (const table of [file, this.builtin]) {
23340
24522
  for (const [name2, addresses] of table ?? []) if (addresses.some(same3)) return name2;
23341
24523
  }
23342
- if (ipv4Bytes(address)?.[0] === 127) return "localhost";
24524
+ if (ipv4Bytes(address2)?.[0] === 127) return "localhost";
23343
24525
  return void 0;
23344
24526
  }
23345
24527
  async resolve(name2, family, blocking) {
@@ -23393,13 +24575,13 @@ var Resolver = class {
23393
24575
  if (addresses.length > 0) this.remember(id2, addresses, answer2.ttl);
23394
24576
  return addresses;
23395
24577
  }
23396
- usable(name2, address, family) {
23397
- const v4 = ipv4Bytes(address) !== void 0;
23398
- if (!v4 && (family === 4 || !ipv6Groups(address))) return false;
24578
+ usable(name2, address2, family) {
24579
+ const v4 = ipv4Bytes(address2) !== void 0;
24580
+ if (!v4 && (family === 4 || !ipv6Groups(address2))) return false;
23399
24581
  if (v4 && family === 6) return false;
23400
- const kind = this.routes.classify(address);
24582
+ const kind = this.routes.classify(address2);
23401
24583
  if (kind !== "loopback" && kind !== "host" && kind !== "reserved") return true;
23402
- this.log(`slicc-kernel: dropped the uplink's answer ${address} for ${name2}: it is ${kind}`);
24584
+ this.log(`slicc-kernel: dropped the uplink's answer ${address2} for ${name2}: it is ${kind}`);
23403
24585
  return false;
23404
24586
  }
23405
24587
  remember(id2, addresses, ttl) {
@@ -23433,275 +24615,6 @@ function waitFor(lookup2, signal) {
23433
24615
  });
23434
24616
  }
23435
24617
 
23436
- // src/kernel/tty.ts
23437
- var ICRNL = 256;
23438
- var IUTF8 = 16384;
23439
- var OPOST = 1;
23440
- var ONLCR = 4;
23441
- var ISIG = 1;
23442
- var ICANON = 2;
23443
- var ECHO = 8;
23444
- var ECHOE = 16;
23445
- var ECHOCTL = 512;
23446
- var NOFLSH = 128;
23447
- var IEXTEN = 32768;
23448
- var VINTR = 0;
23449
- var VQUIT = 1;
23450
- var VERASE = 2;
23451
- var VKILL = 3;
23452
- var VEOF = 4;
23453
- var VSUSP = 10;
23454
- var VWERASE = 14;
23455
- function defaultTermios() {
23456
- const cCc = new Array(32).fill(0);
23457
- Object.assign(cCc, { [VINTR]: 3, [VQUIT]: 28, [VERASE]: 127, [VKILL]: 21 });
23458
- Object.assign(cCc, { [VEOF]: 4, 6: 1, 8: 17, 9: 19, [VSUSP]: 26 });
23459
- Object.assign(cCc, { 12: 18, 13: 15, [VWERASE]: 23, 15: 22 });
23460
- return {
23461
- c_iflag: ICRNL | 1024 | 8192 | IUTF8,
23462
- c_oflag: OPOST | ONLCR,
23463
- c_cflag: 15 | 48 | 128,
23464
- c_lflag: ISIG | ICANON | ECHO | ECHOE | 32 | ECHOCTL | 2048 | IEXTEN,
23465
- c_cc: cCc
23466
- };
23467
- }
23468
- var encoder7 = new TextEncoder();
23469
- function isContinuation(byte) {
23470
- return (byte & 192) === 128;
23471
- }
23472
- var KernelTty = class {
23473
- termios = defaultTermios();
23474
- cols = 80;
23475
- rows = 24;
23476
- readable = [];
23477
- line = [];
23478
- waiters = [];
23479
- hungUp = false;
23480
- name;
23481
- owner;
23482
- onRead;
23483
- readers = 0;
23484
- screen;
23485
- signal;
23486
- constructor(screen, signal) {
23487
- this.screen = screen;
23488
- this.signal = signal;
23489
- }
23490
- receive(bytes) {
23491
- for (const byte of bytes) this.key(byte);
23492
- this.wake();
23493
- }
23494
- resize(cols, rows) {
23495
- if (cols === this.cols && rows === this.rows) return;
23496
- this.setSize(cols, rows);
23497
- this.signal(SIG.WINCH);
23498
- }
23499
- hangup() {
23500
- this.hungUp = true;
23501
- this.wake();
23502
- }
23503
- signalHangup() {
23504
- this.signal(SIG.HUP);
23505
- }
23506
- setSize(cols, rows) {
23507
- this.cols = cols;
23508
- this.rows = rows;
23509
- }
23510
- tcgets() {
23511
- return { ...this.termios, c_cc: [...this.termios.c_cc] };
23512
- }
23513
- tcsets(termios, flush = false) {
23514
- const wasCanonical = this.canonical;
23515
- this.termios = { ...termios, c_cc: [...termios.c_cc] };
23516
- if (flush) {
23517
- this.line = [];
23518
- this.readable = [];
23519
- }
23520
- if (wasCanonical && !this.canonical && this.line.length > 0) {
23521
- this.readable.push(Uint8Array.from(this.line));
23522
- this.line = [];
23523
- this.wake();
23524
- }
23525
- }
23526
- winsize() {
23527
- return [this.rows, this.cols];
23528
- }
23529
- file() {
23530
- return new OpenFile({
23531
- read: (max, signal) => this.read(max, signal),
23532
- write: async (bytes) => {
23533
- if (this.hungUp) throw new KernelError("EIO");
23534
- this.output(bytes);
23535
- return bytes.length;
23536
- },
23537
- poll: () => ({
23538
- readable: this.readable.length > 0 || this.hungUp,
23539
- writable: true,
23540
- hangup: this.hungUp,
23541
- nread: this.readable.reduce((n, chunk2) => n + (chunk2?.length ?? 0), 0)
23542
- }),
23543
- changed: (signal) => this.changed(signal),
23544
- close: () => {
23545
- },
23546
- tty: this
23547
- });
23548
- }
23549
- get canonical() {
23550
- return (this.termios.c_lflag & ICANON) !== 0;
23551
- }
23552
- cc(slot2) {
23553
- return this.termios.c_cc[slot2] ?? 0;
23554
- }
23555
- key(input) {
23556
- const { c_iflag, c_lflag } = this.termios;
23557
- const byte = input === 13 && c_iflag & ICRNL ? 10 : input;
23558
- if (c_lflag & ISIG && this.signalKey(byte)) return;
23559
- if (!this.canonical) {
23560
- this.readable.push(Uint8Array.of(byte));
23561
- if (c_lflag & ECHO) this.echo(byte);
23562
- return;
23563
- }
23564
- this.edit(byte);
23565
- }
23566
- signalKey(byte) {
23567
- const sig = byte === this.cc(VINTR) ? SIG.INT : byte === this.cc(VQUIT) ? SIG.QUIT : byte === this.cc(VSUSP) ? SIG.TSTP : 0;
23568
- if (!sig || byte === 0) return false;
23569
- if (this.termios.c_lflag & ECHO) this.echo(byte);
23570
- if (!(this.termios.c_lflag & NOFLSH)) {
23571
- this.line = [];
23572
- this.readable = [];
23573
- }
23574
- this.signal(sig);
23575
- return true;
23576
- }
23577
- edit(byte) {
23578
- const echo = (this.termios.c_lflag & ECHO) !== 0;
23579
- if (byte === this.cc(VERASE) || byte === 8) {
23580
- this.rubOut(this.lastCharLength());
23581
- } else if (byte === this.cc(VKILL)) {
23582
- this.rubOut(this.line.length);
23583
- } else if (byte === this.cc(VWERASE)) {
23584
- this.rubOut(this.lastWordLength());
23585
- } else if (byte === this.cc(VEOF)) {
23586
- this.readable.push(this.line.length > 0 ? Uint8Array.from(this.line) : null);
23587
- this.line = [];
23588
- } else if (byte === 10) {
23589
- this.line.push(byte);
23590
- this.readable.push(Uint8Array.from(this.line));
23591
- this.line = [];
23592
- if (echo) this.output(encoder7.encode("\n"));
23593
- } else {
23594
- this.line.push(byte);
23595
- if (echo) this.echo(byte);
23596
- }
23597
- }
23598
- lastWordLength() {
23599
- let end = this.line.length;
23600
- while (end > 0 && this.line[end - 1] === 32) end--;
23601
- while (end > 0 && this.line[end - 1] !== 32) end--;
23602
- return this.line.length - end;
23603
- }
23604
- lastCharLength() {
23605
- const { line } = this;
23606
- let n = Math.min(1, line.length);
23607
- if (this.termios.c_iflag & IUTF8) {
23608
- while (n < line.length && isContinuation(line[line.length - n] ?? 0)) n++;
23609
- }
23610
- return n;
23611
- }
23612
- rubOut(n) {
23613
- const dropped2 = this.line.splice(this.line.length - n, n);
23614
- const utf83 = (this.termios.c_iflag & IUTF8) !== 0;
23615
- const cells = utf83 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
23616
- if (cells > 0 && this.termios.c_lflag & ECHO) {
23617
- this.output(encoder7.encode("\b \b".repeat(cells)), false);
23618
- }
23619
- }
23620
- echo(byte) {
23621
- if (byte < 32 && byte !== 10 && byte !== 9 && this.termios.c_lflag & ECHOCTL) {
23622
- this.output(Uint8Array.of(94, byte + 64), false);
23623
- return;
23624
- }
23625
- this.output(Uint8Array.of(byte));
23626
- }
23627
- output(bytes, translate2 = true) {
23628
- const { c_oflag } = this.termios;
23629
- if (!translate2 || !(c_oflag & OPOST && c_oflag & ONLCR) || !bytes.includes(10)) {
23630
- this.screen.write(bytes);
23631
- return;
23632
- }
23633
- const out = [];
23634
- for (const byte of bytes) {
23635
- if (byte === 10) out.push(13);
23636
- out.push(byte);
23637
- }
23638
- this.screen.write(Uint8Array.from(out));
23639
- }
23640
- get reading() {
23641
- return this.readers > 0;
23642
- }
23643
- async read(max, signal) {
23644
- this.onRead?.();
23645
- this.readers++;
23646
- try {
23647
- return await this.take(max, signal);
23648
- } finally {
23649
- this.readers--;
23650
- }
23651
- }
23652
- async take(max, signal) {
23653
- while (this.readable.length === 0) {
23654
- if (this.hungUp) return new Uint8Array(0);
23655
- await this.changed(signal);
23656
- }
23657
- const head = this.readable[0];
23658
- if (head === null) {
23659
- this.readable.shift();
23660
- return new Uint8Array(0);
23661
- }
23662
- if (head === void 0) return new Uint8Array(0);
23663
- const chunks = [];
23664
- let n = 0;
23665
- while (this.readable.length > 0 && n < max) {
23666
- const next3 = this.readable[0];
23667
- if (next3 === null || next3 === void 0) break;
23668
- const take = next3.subarray(0, max - n);
23669
- chunks.push(take);
23670
- n += take.length;
23671
- if (take.length < next3.length) this.readable[0] = next3.subarray(take.length);
23672
- else this.readable.shift();
23673
- if (this.canonical) break;
23674
- }
23675
- const out = new Uint8Array(n);
23676
- let at = 0;
23677
- for (const chunk2 of chunks) {
23678
- out.set(chunk2, at);
23679
- at += chunk2.length;
23680
- }
23681
- return out;
23682
- }
23683
- changed(signal) {
23684
- if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
23685
- return new Promise((resolve2, reject) => {
23686
- const onAbort = () => {
23687
- this.waiters = this.waiters.filter((w) => w !== waiter);
23688
- reject(new KernelError("EINTR"));
23689
- };
23690
- const waiter = () => {
23691
- signal?.removeEventListener("abort", onAbort);
23692
- resolve2();
23693
- };
23694
- this.waiters.push(waiter);
23695
- signal?.addEventListener("abort", onAbort, { once: true });
23696
- });
23697
- }
23698
- wake() {
23699
- const waiters = this.waiters;
23700
- this.waiters = [];
23701
- for (const resolve2 of waiters) resolve2();
23702
- }
23703
- };
23704
-
23705
24618
  // src/kernel/pty.ts
23706
24619
  var PtyPair = class {
23707
24620
  slave;
@@ -23894,6 +24807,7 @@ function ptySyscall(req, ctx) {
23894
24807
  return done4;
23895
24808
  case "pty-ctty": {
23896
24809
  const tty = terminalOf(ctx, req.fd);
24810
+ if (ctx.fds.get(req.fd).file.device) throw new KernelError("EPERM");
23897
24811
  const leader = ctx.jobs?.getsid(ctx.pid, 0) === ctx.pid;
23898
24812
  if (leader && ctx.jobs?.controllingTerminal(ctx.pid) === tty) return done4;
23899
24813
  if (!ctx.jobs?.acquireTerminal(ctx.pid, tty)) throw new KernelError("EPERM");
@@ -24675,10 +25589,10 @@ function basicOf(value2) {
24675
25589
  function placeholdersIn(text) {
24676
25590
  return [...new Set(text.match(ANY_PLACEHOLDER) ?? [])];
24677
25591
  }
24678
- function scan(lines2) {
25592
+ function scan(lines3) {
24679
25593
  const found = [];
24680
- for (let i = 1; i < lines2.length; i++) {
24681
- const m = HEADER_LINE.exec(lines2[i]);
25594
+ for (let i = 1; i < lines3.length; i++) {
25595
+ const m = HEADER_LINE.exec(lines3[i]);
24682
25596
  if (!m) return void 0;
24683
25597
  const value2 = m[2];
24684
25598
  const basic = basicOf(value2);
@@ -24701,10 +25615,10 @@ function usable2(values) {
24701
25615
  }
24702
25616
  async function substituteHead(head, ask) {
24703
25617
  if (!head.endsWith("\r\n\r\n")) return void 0;
24704
- const lines2 = head.slice(0, -4).split("\r\n");
24705
- if (lines2.some((l) => l.includes("\n") || l.includes("\r"))) return void 0;
24706
- const request3 = REQUEST_LINE2.exec(lines2[0]);
24707
- const found = request3 ? scan(lines2) : void 0;
25618
+ const lines3 = head.slice(0, -4).split("\r\n");
25619
+ if (lines3.some((l) => l.includes("\n") || l.includes("\r"))) return void 0;
25620
+ const request3 = REQUEST_LINE2.exec(lines3[0]);
25621
+ const found = request3 ? scan(lines3) : void 0;
24708
25622
  if (!request3 || !found || found.length !== 1) return void 0;
24709
25623
  const at = found[0];
24710
25624
  if (at.placeholders.length > SESSION_PLACEHOLDERS) return void 0;
@@ -24716,8 +25630,8 @@ async function substituteHead(head, ask) {
24716
25630
  );
24717
25631
  if (Object.keys(values).length === 0) return void 0;
24718
25632
  const value2 = at.basic ? `${at.basic.scheme}${btoa(replaced(at.basic.decoded, values))}` : replaced(at.value, values);
24719
- const out = [...lines2];
24720
- out[at.line] = `${lines2[at.line].split(":")[0]}: ${value2}`;
25633
+ const out = [...lines3];
25634
+ out[at.line] = `${lines3[at.line].split(":")[0]}: ${value2}`;
24721
25635
  return { head: `${out.join("\r\n")}\r
24722
25636
  \r
24723
25637
  `, values };
@@ -25040,9 +25954,9 @@ function serverHeader(header4, server) {
25040
25954
  return server.plus && server.binding ? END_POINT : "n,,";
25041
25955
  }
25042
25956
  function rewritten(first, header4) {
25043
- const parsed2 = clientFirstOf(first.data);
25044
- if (!parsed2 || parsed2.header === header4) return first;
25045
- return { mechanism: header4 === END_POINT ? PLUS : PLAIN3, data: `${header4}${parsed2.bare}` };
25957
+ const parsed3 = clientFirstOf(first.data);
25958
+ if (!parsed3 || parsed3.header === header4) return first;
25959
+ return { mechanism: header4 === END_POINT ? PLUS : PLAIN3, data: `${header4}${parsed3.bare}` };
25046
25960
  }
25047
25961
  var PgClient = class {
25048
25962
  clientFirst;
@@ -25062,10 +25976,10 @@ var PgClient = class {
25062
25976
  }
25063
25977
  if (server.sasl && this.clientFirst === void 0) {
25064
25978
  const first = initial(message3);
25065
- const parsed2 = first && clientFirstOf(first.data);
25066
- if (!first || !parsed2) return { bytes: message3, done: true };
25979
+ const parsed3 = first && clientFirstOf(first.data);
25980
+ if (!first || !parsed3) return { bytes: message3, done: true };
25067
25981
  this.clientFirst = first.data;
25068
- this.header = serverHeader(parsed2.header, server);
25982
+ this.header = serverHeader(parsed3.header, server);
25069
25983
  const sent = rewritten(first, this.header);
25070
25984
  if (sent === first) return { bytes: message3, done: false };
25071
25985
  return {
@@ -25230,7 +26144,7 @@ function headEnd2(bytes) {
25230
26144
  }
25231
26145
  return -1;
25232
26146
  }
25233
- function modeOf2(start2) {
26147
+ function modeOf3(start2) {
25234
26148
  const sniffs = [
25235
26149
  ["http1", sniffHttp1(start2)],
25236
26150
  ["smtp", sniffSmtp(start2)],
@@ -25339,7 +26253,7 @@ var SessionWriter = class {
25339
26253
  joined5.set(bytes, this.buffer.length);
25340
26254
  this.buffer.fill(0);
25341
26255
  this.buffer = joined5;
25342
- if (this.mode === "sniff") this.mode = modeOf2(latin12(joined5.subarray(0, 8))) ?? "sniff";
26256
+ if (this.mode === "sniff") this.mode = modeOf3(latin12(joined5.subarray(0, 8))) ?? "sniff";
25343
26257
  if (this.mode === "pg" && !this.sawPg) {
25344
26258
  this.sawPg = true;
25345
26259
  if (!this.s.verified) void this.proof("pg");
@@ -25826,12 +26740,12 @@ function secure(prepared, seal) {
25826
26740
  prepared.own?.release();
25827
26741
  }
25828
26742
  };
25829
- const abandon = () => {
26743
+ const abandon2 = () => {
25830
26744
  if (ran) return;
25831
26745
  prepared.options.session.free();
25832
26746
  prepared.own?.release();
25833
26747
  };
25834
- return { make, abandon };
26748
+ return { make, abandon: abandon2 };
25835
26749
  }
25836
26750
  function supportOf(net) {
25837
26751
  const support = net.tlsClient;
@@ -25848,19 +26762,19 @@ async function uplinkAddress(proc, host) {
25848
26762
  async function connectSecure(proc, socket, host, port, req) {
25849
26763
  const servername = serverName(req.servername || host);
25850
26764
  if (isLoopbackHost(servername) || isLoopbackHost(host)) throw new KernelError("ENOTSUP");
25851
- const address = await uplinkAddress(proc, host);
25852
- const route2 = proc.net.uplinkFor(address);
26765
+ const address2 = await uplinkAddress(proc, host);
26766
+ const route2 = proc.net.uplinkFor(address2);
25853
26767
  if (!route2)
25854
26768
  throw new KernelError("ENOTSUP", "TLS in the kernel needs a destination the uplink routes");
25855
26769
  const support = supportOf(proc.net);
25856
26770
  let info;
25857
- const { make, abandon } = secure(await prepare2(support, req, servername), { proc, port });
26771
+ const { make, abandon: abandon2 } = secure(await prepare2(support, req, servername), { proc, port });
25858
26772
  try {
25859
- await socket.connectSecure({ family: "inet", host: address, port }, route2, async (conn) => {
26773
+ await socket.connectSecure({ family: "inet", host: address2, port }, route2, async (conn) => {
25860
26774
  info = await conn.upgrade(make);
25861
26775
  });
25862
26776
  } finally {
25863
- abandon();
26777
+ abandon2();
25864
26778
  }
25865
26779
  return sessionOf(info, servername);
25866
26780
  }
@@ -25893,14 +26807,14 @@ async function upgradeSecure(proc, socket, req) {
25893
26807
  const host = req.servername || (peer?.family === "inet" ? peer.host : "");
25894
26808
  const servername = serverName(host);
25895
26809
  const port = peer?.family === "inet" ? peer.port : 0;
25896
- const { make, abandon } = secure(await prepare2(supportOf(proc.net), req, servername), {
26810
+ const { make, abandon: abandon2 } = secure(await prepare2(supportOf(proc.net), req, servername), {
25897
26811
  proc,
25898
26812
  port
25899
26813
  });
25900
26814
  try {
25901
26815
  return sessionOf(await socket.upgradeSecure(make), servername);
25902
26816
  } finally {
25903
- abandon();
26817
+ abandon2();
25904
26818
  }
25905
26819
  }
25906
26820
 
@@ -27824,6 +28738,10 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
27824
28738
  "fd-open-clipboard",
27825
28739
  "fd-clipboard-abort",
27826
28740
  "fd-open-embedder",
28741
+ "fd-dev-nodes",
28742
+ "fd-open-dev",
28743
+ "fd-dev-ioctl",
28744
+ "fd-dev-meta",
27827
28745
  "tty-get",
27828
28746
  "tty-set",
27829
28747
  "tty-winsz",
@@ -28200,7 +29118,7 @@ var WasmProcess = class {
28200
29118
  async dispatch(req, cancelled) {
28201
29119
  try {
28202
29120
  if (isFdSyscall(req)) return await this.fdSyscall(req, cancelled);
28203
- if (isTtySyscall(req)) return this.ttySyscall(req);
29121
+ if (isTtySyscall(req)) return await this.ttySyscall(req, cancelled);
28204
29122
  if (isJobSyscall(req)) return this.jobSyscall(req);
28205
29123
  if (isSocketSyscall(req)) return await this.socketSyscall(req, cancelled);
28206
29124
  if (isSemSyscall(req)) return await this.semSyscall(req, cancelled);
@@ -28264,7 +29182,7 @@ var WasmProcess = class {
28264
29182
  return owner;
28265
29183
  }
28266
29184
  pathKeys = /* @__PURE__ */ new Map();
28267
- async lockPath(target, want) {
29185
+ async lockPath(target, want2) {
28268
29186
  if (target.fd !== void 0) {
28269
29187
  const st = await this.fds.get(target.fd).file.stat?.();
28270
29188
  if (!st) throw new KernelError("EBADF");
@@ -28274,9 +29192,9 @@ var WasmProcess = class {
28274
29192
  if (!target.path.startsWith("/")) throw new KernelError("EINVAL");
28275
29193
  const path = target.path;
28276
29194
  const g = this.guarded();
28277
- const checked = `${want}:${path}`;
28278
- if (g && want !== 0 && !this.lockChecked.has(checked)) {
28279
- await openable(g, "lock", path, want, false);
29195
+ const checked = `${want2}:${path}`;
29196
+ if (g && want2 !== 0 && !this.lockChecked.has(checked)) {
29197
+ await openable(g, "lock", path, want2, false);
28280
29198
  this.lockChecked.add(checked);
28281
29199
  }
28282
29200
  return path;
@@ -28289,8 +29207,8 @@ var WasmProcess = class {
28289
29207
  const known = file ? fileKeys.get(file) : this.pathKeys.get(target.path ?? "");
28290
29208
  if (kind === "close" && !file && target.path !== void 0) this.pathKeys.delete(target.path);
28291
29209
  if (access2 && !access2.stat) return objectKey(access2);
28292
- const want = kind === "read" ? R_OK : kind === "write" ? W_OK : 0;
28293
- const path = await this.lockPath(target, want);
29210
+ const want2 = kind === "read" ? R_OK : kind === "write" ? W_OK : 0;
29211
+ const path = await this.lockPath(target, want2);
28294
29212
  if (known !== void 0) return known;
28295
29213
  const st = await this.options.fs.stat?.(path).catch((err) => {
28296
29214
  if (!file) throw err;
@@ -28376,7 +29294,7 @@ var WasmProcess = class {
28376
29294
  const file = this.fds.get(req.fd).file;
28377
29295
  const read = req.peek ? file.peek : file.read;
28378
29296
  if (!read) throw new KernelError(req.peek && file.read ? "EOPNOTSUPP" : "EBADF");
28379
- if (file.tty) this.checkForeground(file.tty);
29297
+ if (file.tty && !file.device) this.checkForeground(file.tty);
28380
29298
  if (file.clipboard) return this.readClipboard(file.clipboard, req, cancelled);
28381
29299
  if (file.embedder) return this.readEmbedder(file, req, cancelled);
28382
29300
  if (file instanceof EventFd && req.max < EventFd.SIZE) throw new KernelError("EINVAL");
@@ -28473,6 +29391,46 @@ var WasmProcess = class {
28473
29391
  throw err;
28474
29392
  });
28475
29393
  }
29394
+ async openDev(path, flags2, cancelled) {
29395
+ const devices = this.options.devices;
29396
+ if (!devices) throw new KernelError("ENOENT");
29397
+ const shown2 = await this.options.identity?.();
29398
+ const tags = this.options.embedder?.tagsOf(this.pid);
29399
+ const stamp = {
29400
+ pid: shown2?.pid ?? this.pid,
29401
+ uid: this.cred.euid,
29402
+ gid: this.cred.egid,
29403
+ groups: [...this.cred.groups],
29404
+ users: devices.users,
29405
+ ...tags?.length ? { tag: tags[0], tags: [...tags] } : {}
29406
+ };
29407
+ const guard2 = this.guarded();
29408
+ const file = await devices.open(path, flags2, {
29409
+ stamp,
29410
+ ...guard2 ? { guard: guard2 } : {},
29411
+ signal: this.blockingSignal(cancelled)
29412
+ });
29413
+ return this.fds.install(this.options.outputFilter?.(file) ?? file, 3);
29414
+ }
29415
+ devMeta(path, change) {
29416
+ const devices = this.options.devices;
29417
+ if (!devices) throw new KernelError("ENOENT");
29418
+ devices.meta(path, change, this.guarded());
29419
+ return { ok: true, kind: "void" };
29420
+ }
29421
+ async devIoctl(req, cancelled) {
29422
+ const file = this.fds.get(req.fd).file;
29423
+ if (!file.device) {
29424
+ if (req.request >>> 0 === 21531) return pollFile(file).nread ?? 0;
29425
+ throw new KernelError("ENOTTY");
29426
+ }
29427
+ const result = await file.device.ioctl(
29428
+ req.request >>> 0,
29429
+ req.arg,
29430
+ this.blockingSignal(cancelled)
29431
+ );
29432
+ return result ?? null;
29433
+ }
28476
29434
  async openEmbedder(flags2) {
28477
29435
  const broker = this.options.embedder;
28478
29436
  if (!broker) throw new KernelError("ENOENT");
@@ -28560,11 +29518,11 @@ var WasmProcess = class {
28560
29518
  } else if (req.create) {
28561
29519
  await nodes.onEntry(path, async () => {
28562
29520
  const marks = this.options.owners || plain3 !== void 0;
28563
- const fresh3 = marks && !await present3(fs, path, true);
29521
+ const fresh4 = marks && !await present3(fs, path, true);
28564
29522
  this.fds.installAt(fd, this.openVfsFile(open2, await nodes.identify(path), fs));
28565
29523
  await this.fds.get(fd).file.stat?.();
28566
- if (fresh3 && plain3 !== void 0) await fs.chmod?.(path, plain3);
28567
- else if (fresh3) {
29524
+ if (fresh4 && plain3 !== void 0) await fs.chmod?.(path, plain3);
29525
+ else if (fresh4) {
28568
29526
  await fs.chown?.(path, { ...this.newOwner(req.mode), kind: "new" });
28569
29527
  }
28570
29528
  });
@@ -28663,8 +29621,8 @@ var WasmProcess = class {
28663
29621
  if (req.exclusive) return inParent(g, "open", req.path);
28664
29622
  if (req.created && guard2.created?.take(req.path)) return;
28665
29623
  const access2 = req.flags & WRITE_ACCESS;
28666
- const want = (access2 === 1 ? 0 : R_OK) | (access2 !== 0 || req.truncate ? W_OK : 0);
28667
- await openable(g, "open", req.path, want, req.create === true);
29624
+ const want2 = (access2 === 1 ? 0 : R_OK) | (access2 !== 0 || req.truncate ? W_OK : 0);
29625
+ await openable(g, "open", req.path, want2, req.create === true);
28668
29626
  }
28669
29627
  openTty(name2) {
28670
29628
  const tty = name2 === void 0 ? this.controllingTerminal() : this.options.jobs?.terminalNamed(name2);
@@ -28728,7 +29686,7 @@ var WasmProcess = class {
28728
29686
  }
28729
29687
  terminalByName(name2) {
28730
29688
  const n = ptsNumber(name2);
28731
- const tty = n === void 0 ? this.options.jobs?.terminalNamed(name2) : this.options.ptys?.get(n).slave;
29689
+ const tty = n === void 0 ? this.options.jobs?.terminalNamed(name2) ?? this.options.devices?.openTty(name2) : this.options.ptys?.get(n).slave;
28732
29690
  if (!tty) throw new KernelError("ENOENT");
28733
29691
  return tty;
28734
29692
  }
@@ -28850,6 +29808,16 @@ var WasmProcess = class {
28850
29808
  return { ok: true, kind: "json", json: await this.openClipboard(req.flags) };
28851
29809
  case "fd-open-embedder":
28852
29810
  return { ok: true, kind: "json", json: await this.openEmbedder(req.flags) };
29811
+ case "fd-dev-nodes":
29812
+ return { ok: true, kind: "json", json: this.options.devices?.list() ?? [] };
29813
+ case "fd-open-dev":
29814
+ return { ok: true, kind: "json", json: await this.openDev(req.path, req.flags, cancelled) };
29815
+ case "fd-dev-ioctl":
29816
+ return { ok: true, kind: "json", json: await this.devIoctl(req, cancelled) };
29817
+ case "fd-dev-meta": {
29818
+ const { op: _, path, ...change } = req;
29819
+ return this.devMeta(path, change);
29820
+ }
28853
29821
  case "fd-clipboard-abort": {
28854
29822
  const file = this.fds.get(req.fd);
28855
29823
  file.file.clipboard?.abort();
@@ -28894,6 +29862,11 @@ var WasmProcess = class {
28894
29862
  return { ok: true, kind: "void" };
28895
29863
  }
28896
29864
  const file = this.fds.get(req.fd).file;
29865
+ const device = file.device;
29866
+ if (device && (req.op === "fd-chmod" || req.op === "fd-chown" || req.op === "fd-utimes")) {
29867
+ const { op: _, fd: __, ...change } = req;
29868
+ return this.devMeta(device.path, change);
29869
+ }
28897
29870
  if (!file.pread || !file.pwrite || !file.resize || !file.stat) throw new KernelError("ESPIPE");
28898
29871
  switch (req.op) {
28899
29872
  case "fd-pread":
@@ -29063,14 +30036,19 @@ var WasmProcess = class {
29063
30036
  ...file.fifo !== void 0 ? { fifo: file.fifo } : {}
29064
30037
  };
29065
30038
  }
29066
- ttySyscall(req) {
30039
+ async ttySyscall(req, cancelled) {
29067
30040
  const tty = this.tty(req.fd);
29068
30041
  const jobs = this.options.jobs;
30042
+ const device = this.fds.get(req.fd).file.device;
30043
+ if (device && req.op.startsWith("tty-pgrp")) throw new KernelError("ENOTTY");
29069
30044
  switch (req.op) {
29070
30045
  case "tty-get":
29071
30046
  return { ok: true, kind: "json", json: tty.tcgets() };
29072
30047
  case "tty-set":
29073
- tty.tcsets(req.termios, req.flush === true);
30048
+ if (device) {
30049
+ const how = { flush: req.flush === true, drain: req.drain === true };
30050
+ await device.setTermios(req.termios, how, this.blockingSignal(cancelled));
30051
+ } else tty.tcsets(req.termios, req.flush === true);
29074
30052
  return { ok: true, kind: "void" };
29075
30053
  case "tty-winsz": {
29076
30054
  const size = tty.winsize();
@@ -29951,6 +30929,7 @@ function processOptions(opts, header4, hooks, created4) {
29951
30929
  ...opts.outputFilter ? { outputFilter: opts.outputFilter } : {},
29952
30930
  ...opts.clipboard ? { clipboard: opts.clipboard, ...clipboardName(opts) } : {},
29953
30931
  ...embedderOptions(opts),
30932
+ devices: opts.devices,
29954
30933
  ...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
29955
30934
  ...opts.source ? { source: opts.source } : {},
29956
30935
  noLinks: opts.noLinks,
@@ -30702,14 +31681,14 @@ var LockService = class {
30702
31681
  );
30703
31682
  });
30704
31683
  }
30705
- async holder(key, owner, want) {
31684
+ async holder(key, owner, want2) {
30706
31685
  return await this.ask({
30707
31686
  op: "holder",
30708
31687
  key,
30709
31688
  owner,
30710
- kind: want.kind,
30711
- start: want.start,
30712
- end: want.end
31689
+ kind: want2.kind,
31690
+ start: want2.start,
31691
+ end: want2.end
30713
31692
  }) ?? void 0;
30714
31693
  }
30715
31694
  async set(key, owner, kind, start2, end) {
@@ -30988,9 +31967,9 @@ function local(name2) {
30988
31967
  function literal2(name2) {
30989
31968
  return ipv4Bytes(name2) !== void 0 || ipv6Groups(name2) !== void 0;
30990
31969
  }
30991
- function addressData(address, type) {
30992
- if (type === TYPE_A) return ipv4Bytes(address);
30993
- const groups = type === TYPE_AAAA ? ipv6Groups(address) : void 0;
31970
+ function addressData(address2, type) {
31971
+ if (type === TYPE_A) return ipv4Bytes(address2);
31972
+ const groups = type === TYPE_AAAA ? ipv6Groups(address2) : void 0;
30994
31973
  return groups?.flatMap((g) => [g >> 8, g & 255]);
30995
31974
  }
30996
31975
  var DnsStub = class {
@@ -31003,10 +31982,10 @@ var DnsStub = class {
31003
31982
  this.timeoutMs = options.timeoutMs ?? DNS_FORWARD_TIMEOUT_MS;
31004
31983
  }
31005
31984
  async answer(query, udp) {
31006
- const parsed2 = parseQuery(query);
31007
- if (!parsed2) return void 0;
31008
- if (!parsed2.ok) return errorReply2(parsed2.id, parsed2.flags, parsed2.rcode);
31009
- const q = parsed2.question;
31985
+ const parsed3 = parseQuery(query);
31986
+ if (!parsed3) return void 0;
31987
+ if (!parsed3.ok) return errorReply2(parsed3.id, parsed3.flags, parsed3.rcode);
31988
+ const q = parsed3.question;
31010
31989
  const reply = await this.reply(query, q).catch(
31011
31990
  () => answerReply(query, q, SERVFAIL, [], false)
31012
31991
  );
@@ -31015,8 +31994,8 @@ var DnsStub = class {
31015
31994
  async reply(query, q) {
31016
31995
  if (q.klass !== CLASS_IN || literal2(q.name)) return this.forward(query, q);
31017
31996
  if (q.type === TYPE_PTR) {
31018
- const address = reverseAddress(q.name);
31019
- const name2 = address !== void 0 ? await this.reverse(address) : void 0;
31997
+ const address2 = reverseAddress(q.name);
31998
+ const name2 = address2 !== void 0 ? await this.reverse(address2) : void 0;
31020
31999
  if (name2 !== void 0)
31021
32000
  return answerReply(query, q, NOERROR, [record2(TYPE_PTR, encodeName(name2))], true);
31022
32001
  return this.forward(query, q);
@@ -31046,8 +32025,8 @@ var DnsStub = class {
31046
32025
  known(name2) {
31047
32026
  return this.resolver.known(name2).catch(() => false);
31048
32027
  }
31049
- reverse(address) {
31050
- return this.resolver.reverse(address).catch(() => void 0);
32028
+ reverse(address2) {
32029
+ return this.resolver.reverse(address2).catch(() => void 0);
31051
32030
  }
31052
32031
  async forward(query, q) {
31053
32032
  if (!this.forwarder) return answerReply(query, q, SERVFAIL, [], false);
@@ -31226,7 +32205,7 @@ function honors(traits, redirect) {
31226
32205
  const raw2 = traits.manualRedirects === true && (hops === void 0 || hops === false);
31227
32206
  return redirect === "manual" && raw2;
31228
32207
  }
31229
- function refused3(message3) {
32208
+ function refused4(message3) {
31230
32209
  return new TransportError(message3, 502, void 0, "transport");
31231
32210
  }
31232
32211
  var RemoteTransport = class {
@@ -31317,7 +32296,7 @@ var RemoteTransport = class {
31317
32296
  const { signal, redirect } = request3;
31318
32297
  signal.throwIfAborted();
31319
32298
  if (unfollowed2(redirect) && !honors(this.traits, redirect)) {
31320
- throw refused3(`this transport cannot honor redirect: ${redirect}`);
32299
+ throw refused4(`this transport cannot honor redirect: ${redirect}`);
31321
32300
  }
31322
32301
  let finished = false;
31323
32302
  const abort = () => {
@@ -31368,7 +32347,7 @@ var RemoteTransport = class {
31368
32347
  };
31369
32348
  if (unfollowed2(redirect) && (redirected2 || redirects?.length)) {
31370
32349
  await cancel();
31371
- throw refused3(`this transport followed a redirect despite redirect: ${redirect}`);
32350
+ throw refused4(`this transport followed a redirect despite redirect: ${redirect}`);
31372
32351
  }
31373
32352
  return {
31374
32353
  ...url !== void 0 ? { url } : {},
@@ -31858,6 +32837,7 @@ var SETUP_OPS = /* @__PURE__ */ new Set([
31858
32837
  "proc-list",
31859
32838
  "proc-identity",
31860
32839
  "fd-info",
32840
+ "fd-dev-nodes",
31861
32841
  "sig-mask",
31862
32842
  "sig-inherited",
31863
32843
  "proc-setpgid",
@@ -31954,8 +32934,8 @@ var WasmMemories = class {
31954
32934
  const learned = this.learned;
31955
32935
  if (!learned) return this.base;
31956
32936
  const steps = Math.floor((this.now() - learned.at) / DECAY_MS);
31957
- const raised = learned.ceiling + steps * DECAY_STEP;
31958
- if (raised < (this.base ?? DEFAULT_CEILING)) return raised;
32937
+ const raised2 = learned.ceiling + steps * DECAY_STEP;
32938
+ if (raised2 < (this.base ?? DEFAULT_CEILING)) return raised2;
31959
32939
  this.learned = void 0;
31960
32940
  return this.base;
31961
32941
  }
@@ -32098,9 +33078,9 @@ function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
32098
33078
  let open2 = false;
32099
33079
  const reply = (message3, transfer = []) => port.postMessage(message3, transfer);
32100
33080
  async function hello(message3) {
32101
- const refused4 = driverVersionError(message3.protocol);
32102
- if (refused4) {
32103
- reply({ hello: { protocol: DRIVER_PROTOCOL, error: refused4, errno: "EPROTO" } });
33081
+ const refused5 = driverVersionError(message3.protocol);
33082
+ if (refused5) {
33083
+ reply({ hello: { protocol: DRIVER_PROTOCOL, error: refused5, errno: "EPROTO" } });
32104
33084
  return;
32105
33085
  }
32106
33086
  try {
@@ -32406,7 +33386,7 @@ function unescape(field4) {
32406
33386
  );
32407
33387
  }
32408
33388
  function parseFstab(text) {
32409
- const lines2 = [];
33389
+ const lines3 = [];
32410
33390
  text.split("\n").forEach((raw2, index) => {
32411
33391
  const content = raw2.replace(/#.*$/, "").trim();
32412
33392
  if (!content) return;
@@ -32415,12 +33395,12 @@ function parseFstab(text) {
32415
33395
  if (options.split(",").includes("noauto")) return;
32416
33396
  const spec = { source, target, type, options: {} };
32417
33397
  try {
32418
- lines2.push({ line: index + 1, spec: { ...spec, options: parseMountOptions(options) } });
33398
+ lines3.push({ line: index + 1, spec: { ...spec, options: parseMountOptions(options) } });
32419
33399
  } catch (err) {
32420
- lines2.push({ line: index + 1, spec, invalid: err });
33400
+ lines3.push({ line: index + 1, spec, invalid: err });
32421
33401
  }
32422
33402
  });
32423
- return lines2;
33403
+ return lines3;
32424
33404
  }
32425
33405
  function codeOf6(err) {
32426
33406
  const code = err?.code;
@@ -32438,20 +33418,20 @@ function wait(ms, signal) {
32438
33418
  signal.addEventListener("abort", done5, { once: true });
32439
33419
  });
32440
33420
  }
32441
- async function mountFstab(lines2, mount, signal, retries = FSTAB_RETRIES, discard = () => {
33421
+ async function mountFstab(lines3, mount, signal, retries = FSTAB_RETRIES, discard = () => {
32442
33422
  }, settled = () => {
32443
33423
  }) {
32444
33424
  return Promise.all(
32445
- lines2.map(async (each) => {
33425
+ lines3.map(async (each) => {
32446
33426
  const result = await mountLine(each, mount, signal, retries, discard);
32447
33427
  settled(each, result);
32448
33428
  return result;
32449
33429
  })
32450
33430
  );
32451
33431
  }
32452
- async function mountLine({ line, spec, invalid }, mount, signal, retries, discard) {
32453
- let failed4 = invalid;
32454
- for (let attempt = 0; !invalid && attempt <= retries.length && !signal.aborted; attempt++) {
33432
+ async function mountLine({ line, spec, invalid: invalid2 }, mount, signal, retries, discard) {
33433
+ let failed4 = invalid2;
33434
+ for (let attempt = 0; !invalid2 && attempt <= retries.length && !signal.aborted; attempt++) {
32455
33435
  try {
32456
33436
  const entry = await mount(spec);
32457
33437
  if (!signal.aborted) return { line, target: spec.target, entry };
@@ -32619,14 +33599,14 @@ var RESTORE_AGAIN_MS = 3e4;
32619
33599
  function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
32620
33600
  const handles = /* @__PURE__ */ new Map();
32621
33601
  let nextFh = 0;
32622
- const fresh3 = (remote) => {
33602
+ const fresh4 = (remote) => {
32623
33603
  if (remote.client.token === remote.token && !remote.client.gone) return remote;
32624
33604
  throw fsError2("ESTALE", "the folder was granted again, so this open file is gone");
32625
33605
  };
32626
33606
  const live2 = (fh) => {
32627
33607
  const remote = handles.get(fh);
32628
33608
  if (!remote) throw fsError2("EBADF", `handle ${fh}`);
32629
- return fresh3(remote);
33609
+ return fresh4(remote);
32630
33610
  };
32631
33611
  const call = (op, body = {}) => current().call(op, body);
32632
33612
  const same3 = (was, mode, seen) => seen?.mode === (mode | 128) && (was.ino !== void 0 && seen.ino !== void 0 ? seen.ino === was.ino : seen.size === was.size && seen.mtime === was.mtime);
@@ -32708,7 +33688,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
32708
33688
  if (!remote) throw fsError2("EBADF", `handle ${fh}`);
32709
33689
  handles.delete(fh);
32710
33690
  try {
32711
- await fresh3(remote).client.call("release", { fh: remote.fh }, true);
33691
+ await fresh4(remote).client.call("release", { fh: remote.fh }, true);
32712
33692
  } finally {
32713
33693
  if (remote.restore) await settle3(current(), remote.restore);
32714
33694
  }
@@ -32758,7 +33738,7 @@ async function resize(h, path, size) {
32758
33738
  await h.release(fh);
32759
33739
  }
32760
33740
  }
32761
- function sleep(ms, signal) {
33741
+ function sleep2(ms, signal) {
32762
33742
  return new Promise((resolve2) => {
32763
33743
  const done5 = () => {
32764
33744
  clearTimeout(timer);
@@ -32786,22 +33766,22 @@ function keepWatching(loop, signal) {
32786
33766
  const stop = () => session2.abort();
32787
33767
  signal.addEventListener("abort", stop);
32788
33768
  let timer;
32789
- const quiet2 = () => {
33769
+ const quiet4 = () => {
32790
33770
  clearTimeout(timer);
32791
33771
  timer = setTimeout(stop, silenceMs);
32792
33772
  };
32793
33773
  let opened2 = false;
32794
33774
  try {
32795
33775
  const client = await loop.connect();
32796
- quiet2();
33776
+ quiet4();
32797
33777
  await client.watch(
32798
33778
  session2.signal,
32799
33779
  (event) => {
32800
- quiet2();
33780
+ quiet4();
32801
33781
  if (!event.ping) loop.event(event);
32802
33782
  },
32803
33783
  () => {
32804
- quiet2();
33784
+ quiet4();
32805
33785
  opened2 = true;
32806
33786
  up = true;
32807
33787
  loop.up(client);
@@ -32830,7 +33810,7 @@ function keepWatching(loop, signal) {
32830
33810
  loop.down();
32831
33811
  }
32832
33812
  ready2.resolve();
32833
- await sleep(wait2, signal);
33813
+ await sleep2(wait2, signal);
32834
33814
  wait2 = Math.min(wait2 * 2, most);
32835
33815
  }
32836
33816
  })();
@@ -34314,6 +35294,7 @@ var Launcher = class {
34314
35294
  scrubs = /* @__PURE__ */ new Map();
34315
35295
  clipboard;
34316
35296
  embedder;
35297
+ devices;
34317
35298
  rootFs;
34318
35299
  fileWatches;
34319
35300
  nextPid = 1e3;
@@ -34338,6 +35319,7 @@ var Launcher = class {
34338
35319
  );
34339
35320
  this.embedder = this.embedderBroker(options.embedder);
34340
35321
  this.embedder.denied = options.onDenial;
35322
+ this.devices = this.deviceNodes(options.devices);
34341
35323
  this.sealed = new SealedBroker(
34342
35324
  options.sealed,
34343
35325
  this.usersMode,
@@ -34553,16 +35535,16 @@ var Launcher = class {
34553
35535
  if (!found) {
34554
35536
  found = this.swaps.settled().then(() => this.trustedAppended()).then(async (appended2) => {
34555
35537
  const tiers = await this.tiers(scope, appended2);
34556
- const refused4 = [];
35538
+ const refused5 = [];
34557
35539
  const commands = await scanCommands(
34558
35540
  this.scanned,
34559
35541
  tiers,
34560
35542
  tiers.length - appended2.length,
34561
35543
  void 0,
34562
- refused4,
35544
+ refused5,
34563
35545
  await this.globalRoots(scope)
34564
35546
  );
34565
- this.refusedOf.set(commands, refused4.map(refusing));
35547
+ this.refusedOf.set(commands, refused5.map(refusing));
34566
35548
  return commands;
34567
35549
  }).then(
34568
35550
  (commands) => this.refusals(commands).set(SUDO_COMMAND.name, SUDO_COMMAND).set(VISUDO_COMMAND.name, VISUDO_COMMAND).set(CRONTAB_COMMAND.name, CRONTAB_COMMAND).set(SEALED_COMMAND.name, SEALED_COMMAND)
@@ -34769,14 +35751,14 @@ var Launcher = class {
34769
35751
  return await this.commandAt(path, scope) ?? await this.refusedAt(path, name2, scope);
34770
35752
  }
34771
35753
  async refusedAt(path, name2, scope) {
34772
- const refused4 = this.refusedOf.get(await this.commands(scope)) ?? [];
34773
- if (refused4.length === 0) return void 0;
35754
+ const refused5 = this.refusedOf.get(await this.commands(scope)) ?? [];
35755
+ if (refused5.length === 0) return void 0;
34774
35756
  const st = await this.base.stat(path).catch(() => void 0);
34775
35757
  if (!st?.isFile) return void 0;
34776
35758
  const real = await physicalPathAsync(this.base, path).catch(() => path);
34777
35759
  const same3 = async (own) => own === path || await physicalPathAsync(this.base, own).catch(() => own) === real;
34778
35760
  const owners = [];
34779
- for (const c of refused4) {
35761
+ for (const c of refused5) {
34780
35762
  const origin = c.origin;
34781
35763
  for (const own of [origin.glue, origin.wasm, ...origin.variants ?? []]) {
34782
35764
  if (!await same3(own)) continue;
@@ -34786,11 +35768,11 @@ var Launcher = class {
34786
35768
  }
34787
35769
  const stem = name2.replace(/\.wasm$/, "");
34788
35770
  const named = owners.find((c) => c.name === name2) ?? owners.find((c) => c.name === stem);
34789
- return named ?? owners[0] ?? await this.refusedCopy(real, name2, refused4);
35771
+ return named ?? owners[0] ?? await this.refusedCopy(real, name2, refused5);
34790
35772
  }
34791
- async refusedCopy(real, name2, refused4) {
35773
+ async refusedCopy(real, name2, refused5) {
34792
35774
  const dir = real.slice(0, real.lastIndexOf("/"));
34793
- for (const c of refused4) {
35775
+ for (const c of refused5) {
34794
35776
  const origin = c.origin;
34795
35777
  if (origin.abi !== "emscripten" || origin.script || c.name !== name2) continue;
34796
35778
  const own = await physicalPathAsync(this.base, origin.glue).catch(() => origin.glue);
@@ -34957,8 +35939,8 @@ var Launcher = class {
34957
35939
  const via = planned?.via ?? [];
34958
35940
  for (const [i, file] of via.entries()) {
34959
35941
  if (!byPath(file)) continue;
34960
- const want = i < via.length - 1 ? R_OK | X_OK : X_OK;
34961
- await need(g, "exec", this.fs.resolvePath(req.cwd, file), want);
35942
+ const want2 = i < via.length - 1 ? R_OK | X_OK : X_OK;
35943
+ await need(g, "exec", this.fs.resolvePath(req.cwd, file), want2);
34962
35944
  }
34963
35945
  for (const bin of planned?.bins ?? []) await need(g, "exec", bin, R_OK | X_OK);
34964
35946
  if (planned?.target.copy) await need(g, "exec", planned.target.copy, R_OK | X_OK);
@@ -34998,12 +35980,12 @@ var Launcher = class {
34998
35980
  return void 0;
34999
35981
  }
35000
35982
  async planOne(req, { ppid, cred, scope }, catalogOnly = false) {
35001
- const refused4 = (err) => {
35983
+ const refused5 = (err) => {
35002
35984
  throw codeOf8(err) === "EACCES" ? new SpawnError("EACCES") : err;
35003
35985
  };
35004
- const file = await this.execFile(req.file, req.cwd, this.execGuard(cred, ppid)).catch(refused4);
35986
+ const file = await this.execFile(req.file, req.cwd, this.execGuard(cred, ppid)).catch(refused5);
35005
35987
  const planned = await this.plan(file, req.argv, req.cwd, scope, req.argv0, catalogOnly);
35006
- await this.mayExec(cred, { ...req, file, pid: ppid }, planned).catch(refused4);
35988
+ await this.mayExec(cred, { ...req, file, pid: ppid }, planned).catch(refused5);
35007
35989
  if (planned) return planned;
35008
35990
  throw new SpawnError(catalogOnly ? "ENOENT" : await this.unrunnable(file, req.cwd, scope));
35009
35991
  }
@@ -35639,6 +36621,7 @@ var Launcher = class {
35639
36621
  ...scrubs ? { outputFilter: (file) => scrubbedAll(file, scrubs) } : {},
35640
36622
  clipboard: this.clipboard,
35641
36623
  embedder: { broker: this.embedder, session: () => this.sessionOf(pid) },
36624
+ devices: this.devices,
35642
36625
  ...this.dac ? { dac: this.dac } : {},
35643
36626
  program: req.program,
35644
36627
  changes: this.changes.sab,
@@ -35822,8 +36805,8 @@ var Launcher = class {
35822
36805
  this.sampledAt = Math.max(this.sampledAt, now);
35823
36806
  this.load = this.load.map((avg, i) => {
35824
36807
  const window = LOAD_WINDOWS_MS[i];
35825
- const fresh3 = 1 - Math.exp(-LOAD_SAMPLE_MS / window);
35826
- return avg * Math.exp(-elapsed / window) + running * fresh3;
36808
+ const fresh4 = 1 - Math.exp(-LOAD_SAMPLE_MS / window);
36809
+ return avg * Math.exp(-elapsed / window) + running * fresh4;
35827
36810
  });
35828
36811
  }
35829
36812
  idleSince;
@@ -36170,7 +37153,7 @@ var Launcher = class {
36170
37153
  forker(ppid, parent) {
36171
37154
  const { exec: _exec, pgid: _pgid, group: _group, pid: _pid, scrub: _scrub, ...image } = parent;
36172
37155
  return async (state, given) => {
36173
- const fresh3 = (fds, quietOom) => this.start({
37156
+ const fresh4 = (fds, quietOom) => this.start({
36174
37157
  ...image,
36175
37158
  quietOom,
36176
37159
  cwd: state.cwd ?? parent.cwd,
@@ -36186,7 +37169,7 @@ var Launcher = class {
36186
37169
  tagBase: this.tagBases.get(ppid),
36187
37170
  links: this.links.get(ppid)
36188
37171
  });
36189
- return childHandle(await this.admitted(parent.program.abi !== "js", given, fresh3));
37172
+ return childHandle(await this.admitted(parent.program.abi !== "js", given, fresh4));
36190
37173
  };
36191
37174
  }
36192
37175
  keySignal(tty, fallback, sig, send2) {
@@ -36227,6 +37210,14 @@ var Launcher = class {
36227
37210
  this.sealed.userOf = async (uid) => (await this.users.lookup(uid))?.name;
36228
37211
  this.sealed.factsOf = (pid) => this.processFacts(pid);
36229
37212
  }
37213
+ deviceNodes(hook) {
37214
+ const nodes = new DeviceNodes(this.usersMode, {
37215
+ groupId: async (name2) => (await this.users.groups()).find((g) => g.name === name2)?.gid,
37216
+ userId: async (name2) => (await this.users.lookup(name2))?.uid
37217
+ });
37218
+ if (hook) nodes.register(0, hook);
37219
+ return nodes;
37220
+ }
36230
37221
  embedderBroker(hook) {
36231
37222
  const broker = new EmbedderBroker(
36232
37223
  this.usersMode,
@@ -36516,8 +37507,8 @@ var Launcher = class {
36516
37507
  for (const [path, mode] of layout) {
36517
37508
  if (isProtected(this.prefixes, normalizePath(path))) continue;
36518
37509
  const real = await this.realLayoutPath(path);
36519
- const fresh3 = real !== void 0 && !await this.base.hasOwner?.(real);
36520
- if (fresh3) await this.base.chown?.(real, { uid: 0, gid: 0, mode, follow: false });
37510
+ const fresh4 = real !== void 0 && !await this.base.hasOwner?.(real);
37511
+ if (fresh4) await this.base.chown?.(real, { uid: 0, gid: 0, mode, follow: false });
36521
37512
  }
36522
37513
  }
36523
37514
  async realLayoutPath(given) {
@@ -36651,10 +37642,10 @@ var Launcher = class {
36651
37642
  }
36652
37643
  async mountFstab() {
36653
37644
  const text = await this.base.readFile(FSTAB_PATH).catch(() => "");
36654
- const lines2 = parseFstab(text);
36655
- for (const { spec } of lines2) this.mounts.note(spec, "pending");
37645
+ const lines3 = parseFstab(text);
37646
+ for (const { spec } of lines3) this.mounts.note(spec, "pending");
36656
37647
  return mountFstab(
36657
- lines2,
37648
+ lines3,
36658
37649
  (spec) => this.mount(spec).catch((err) => {
36659
37650
  this.mounts.note(spec, "pending", err?.message ?? String(err));
36660
37651
  throw err;
@@ -37551,7 +38542,7 @@ function isAttestRequest(req) {
37551
38542
  return ATTEST_OPS.has(req.op);
37552
38543
  }
37553
38544
  function isUsersRequest(req) {
37554
- return USERS_OPS.has(req.op);
38545
+ return USERS_OPS.has(req.op) || req.op === "devices-add" || req.op === "devices-remove";
37555
38546
  }
37556
38547
  function transferOf2(req, result) {
37557
38548
  if (result instanceof MessagePort) return [result];
@@ -37584,7 +38575,18 @@ function failure(err) {
37584
38575
  ...typeof code === "string" ? { code } : {}
37585
38576
  };
37586
38577
  }
38578
+ function pageReplied(data, requests, devices) {
38579
+ const embedderReply = embedderReplyOf(data);
38580
+ if (embedderReply) requests.answer(embedderReply);
38581
+ const devicesReply = devicesReplyOf(data);
38582
+ if (devicesReply) devices.answer(devicesReply);
38583
+ return embedderReply !== void 0 || devicesReply !== void 0;
38584
+ }
38585
+ function devicesOp(l, req) {
38586
+ return req.op === "devices-add" ? l.devices.add(req.spec, req.client) : l.devices.remove(req.node);
38587
+ }
37587
38588
  function usersOp(l, req) {
38589
+ if (req.op === "devices-add" || req.op === "devices-remove") return devicesOp(l, req);
37588
38590
  if (req.op === "users-add") return l.users.add(req.user);
37589
38591
  if (req.op === "users-remove") return l.users.remove(req.name, req.options);
37590
38592
  if (req.op === "groups-list") return l.users.groups();
@@ -37594,7 +38596,7 @@ function usersOp(l, req) {
37594
38596
  return l.users.list();
37595
38597
  }
37596
38598
  var clientIds = 0;
37597
- async function connectClient(deps, { user, tag, bind: bind2 }) {
38599
+ async function connectClient(deps, { user, tag, requests: bind2, devices }) {
37598
38600
  const l = await deps.ready();
37599
38601
  tagOption(tag);
37600
38602
  if (user !== void 0 && !await l.users.lookup(user)) {
@@ -37608,6 +38610,7 @@ async function connectClient(deps, { user, tag, bind: bind2 }) {
37608
38610
  signal: signalNumber,
37609
38611
  session: client,
37610
38612
  ...bind2 === "attach" ? { attachRequests: true } : {},
38613
+ ...devices === "attach" ? { attachDevices: true } : {},
37611
38614
  ...user !== void 0 ? { user } : {},
37612
38615
  ...tag !== void 0 ? { tag } : {},
37613
38616
  ...deps.locks ? { locks: deps.locks } : {},
@@ -37615,13 +38618,15 @@ async function connectClient(deps, { user, tag, bind: bind2 }) {
37615
38618
  });
37616
38619
  deps.clients.add(served2);
37617
38620
  const unbind = bind2 && bind2 !== "attach" ? l.embedder.register(client, deps.requests.hookFor(client, bind2)) : void 0;
38621
+ const unbindDevices = devices === true ? l.devices.register(client, deps.devices.hookFor(client)) : void 0;
37618
38622
  void served2.closed.then(() => {
37619
38623
  deps.clients.delete(served2);
37620
38624
  unbind?.();
38625
+ unbindDevices?.();
37621
38626
  });
37622
- return unbind ? { port: port2, client } : port2;
38627
+ return unbind || devices ? { port: port2, client } : port2;
37623
38628
  }
37624
- function hostOptions(req, port, sealed, sealedFetch, clipboard, lookup2, requests) {
38629
+ function hostOptions(req, port, sealed, sealedFetch, clipboard, lookup2, requests, devices) {
37625
38630
  return {
37626
38631
  cron: { lock: `slicc-kernel-cron:${req.metadata ?? META_DB}` },
37627
38632
  ...req.hostname !== void 0 ? { hostname: req.hostname } : {},
@@ -37633,7 +38638,8 @@ function hostOptions(req, port, sealed, sealedFetch, clipboard, lookup2, request
37633
38638
  ...req.sealed ? { sealed } : {},
37634
38639
  ...req.sealedFetch ? { sealedFetch } : {},
37635
38640
  ...req.clipboard ? { clipboard } : {},
37636
- ...req.requests ? { embedder: requests.hookFor(0, req.requests) } : {}
38641
+ ...req.requests ? { embedder: requests.hookFor(0, req.requests) } : {},
38642
+ ...req.devices ? { devices: devices.hookFor(0) } : {}
37637
38643
  };
37638
38644
  }
37639
38645
  function hostProtect(init, lookup2) {
@@ -37687,6 +38693,20 @@ function processOps(ready2, reply) {
37687
38693
  }
37688
38694
  return { terminal, sudoRun, act: onTerminal };
37689
38695
  }
38696
+ function lockHolder() {
38697
+ let holding;
38698
+ return (locks) => {
38699
+ holding ??= new Promise((resolve2) => {
38700
+ const name2 = `slicc-kernel:${crypto.randomUUID()}`;
38701
+ void locks.request(name2, () => {
38702
+ resolve2(name2);
38703
+ return new Promise(() => {
38704
+ });
38705
+ });
38706
+ });
38707
+ return holding;
38708
+ };
38709
+ }
37690
38710
  function serveKernel(port, deps) {
37691
38711
  let launcher;
37692
38712
  let remote;
@@ -37696,24 +38716,14 @@ function serveKernel(port, deps) {
37696
38716
  const sealed = sealedAsker(port);
37697
38717
  const clipboard = clipboardAsker(port);
37698
38718
  const requests = embedderAsker(port);
38719
+ const devices = devicesAsker(port);
37699
38720
  const integrity = integrityAsker(port);
37700
38721
  const approvals = sudoApprovals(port);
37701
38722
  const locks = deps.locks === null ? void 0 : deps.locks ?? locksOf();
37702
- let holding;
37703
- function holdLock(locks2) {
37704
- holding ??= new Promise((resolve2) => {
37705
- const name2 = `slicc-kernel:${crypto.randomUUID()}`;
37706
- void locks2.request(name2, () => {
37707
- resolve2(name2);
37708
- return new Promise(() => {
37709
- });
37710
- });
37711
- });
37712
- return holding;
37713
- }
37714
- const connect2 = (user, tag, bind2) => connectClient(
37715
- { ready: ready2, clients, requests, held: locks ? () => holdLock(locks) : void 0, locks },
37716
- { user, tag, bind: bind2 }
38723
+ const holdLock = lockHolder();
38724
+ const connect2 = (req) => connectClient(
38725
+ { ready: ready2, clients, requests, devices, held: locks && (() => holdLock(locks)), locks },
38726
+ req
37717
38727
  );
37718
38728
  async function ready2() {
37719
38729
  if (!launcher) throw new Error("the kernel is not initialized");
@@ -37744,7 +38754,8 @@ function serveKernel(port, deps) {
37744
38754
  sealed.fetch,
37745
38755
  clipboard.hook,
37746
38756
  integrity.lookup,
37747
- requests
38757
+ requests,
38758
+ devices
37748
38759
  ),
37749
38760
  ...req.approve ? { approve: approvals.ask } : {},
37750
38761
  ...net,
@@ -37776,7 +38787,7 @@ function serveKernel(port, deps) {
37776
38787
  if (req.op === "sealed-placeholders") return (await ready2()).setPlaceholders(req.list);
37777
38788
  if (req.op === "open-terminal") return processes2.terminal(req);
37778
38789
  if (req.op === "sudo-run") return processes2.sudoRun(req);
37779
- if (req.op === "connect") return connect2(req.user, req.tag, req.requests);
38790
+ if (req.op === "connect") return connect2(req);
37780
38791
  if (isUsersRequest(req)) return usersOp(await ready2(), req);
37781
38792
  if (req.op === "routes" || req.op === "transport-traits") {
37782
38793
  return networkOp(await ready2(), req, remote);
@@ -37813,8 +38824,7 @@ function serveKernel(port, deps) {
37813
38824
  if (sealedReply) return sealed.answer(sealedReply);
37814
38825
  const clipboardReply = event.data.clipboardReply;
37815
38826
  if (clipboardReply) return clipboard.answer(clipboardReply);
37816
- const embedderReply = embedderReplyOf(event.data);
37817
- if (embedderReply) return requests.answer(embedderReply);
38827
+ if (pageReplied(event.data, requests, devices)) return;
37818
38828
  const integrityReply = event.data.integrityReply;
37819
38829
  if (integrityReply) return integrity.answer(integrityReply);
37820
38830
  const grant = event.data.hostfsGrant;