@ai-ecoverse/slicc-kernel 1.181.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 (39) hide show
  1. package/README.md +62 -11
  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 +1727 -703
  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 +1897 -751
  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/cdp/types.d.ts +2 -0
  19. package/dist/types/client/attach.d.ts +2 -0
  20. package/dist/types/client/protocol.d.ts +1 -0
  21. package/dist/types/client/serve-client.d.ts +1 -0
  22. package/dist/types/devices-relay.d.ts +74 -0
  23. package/dist/types/index.d.ts +14 -0
  24. package/dist/types/kernel/devices.d.ts +155 -0
  25. package/dist/types/kernel/embedder.d.ts +2 -0
  26. package/dist/types/kernel/fd-table.d.ts +3 -1
  27. package/dist/types/kernel/host.d.ts +2 -0
  28. package/dist/types/kernel/process.d.ts +20 -0
  29. package/dist/types/kernel/serial-ioctl.d.ts +73 -0
  30. package/dist/types/kernel/socket.d.ts +2 -0
  31. package/dist/types/launcher.d.ts +4 -0
  32. package/dist/types/node.d.ts +6 -2
  33. package/dist/types/process/js/js-dev.d.ts +24 -0
  34. package/dist/types/process/kernel-streams.d.ts +11 -1
  35. package/dist/types/process/process-pty.d.ts +1 -0
  36. package/dist/types/process/wasi/wasi-fds.d.ts +1 -0
  37. package/dist/types/process/wasi/wasi-termios.d.ts +1 -0
  38. package/dist/types/serve.d.ts +14 -1
  39. package/package.json +3 -3
@@ -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");
@@ -4825,10 +4825,10 @@ var CdpFacade = class {
4825
4825
  );
4826
4826
  return;
4827
4827
  }
4828
- await this.write(conn, json(await this.list(runtime)));
4828
+ await this.write(conn, json(await this.list(runtime, conn.peerStamp)));
4829
4829
  }
4830
- async list(runtime) {
4831
- const conn = await this.host(runtime);
4830
+ async list(runtime, stamp) {
4831
+ const conn = await this.host(runtime, stamp);
4832
4832
  try {
4833
4833
  const reply = await new Promise(
4834
4834
  (resolve2, reject) => {
@@ -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
  }
@@ -4863,12 +4863,15 @@ var CdpFacade = class {
4863
4863
  conn.close();
4864
4864
  }
4865
4865
  }
4866
- host(runtime) {
4867
- return this.options.hosts.connect(runtime ? { runtime } : {});
4866
+ host(runtime, stamp) {
4867
+ return this.options.hosts.connect({
4868
+ ...runtime ? { runtime } : {},
4869
+ ...stamp ? { stamp } : {}
4870
+ });
4868
4871
  }
4869
4872
  async upgrade(conn, incoming, req, runtime) {
4870
4873
  const accept2 = await acceptKey(req.headers);
4871
- const host = await this.host(runtime);
4874
+ const host = await this.host(runtime, conn.peerStamp);
4872
4875
  const limits = {
4873
4876
  max: this.options.maxMessage ?? MAX_MESSAGE,
4874
4877
  closeWaitMs: this.options.closeWaitMs ?? CLOSE_WAIT_MS
@@ -4948,6 +4951,7 @@ function enableCdp(net, hosts) {
4948
4951
  let facade;
4949
4952
  net.kernelOnly(CDP_PORT, true);
4950
4953
  net.reserve(CDP_PORT);
4954
+ net.stampPeers(CDP_PORT);
4951
4955
  net.activate({ family: "inet", host: "127.0.0.1", port: CDP_PORT }, () => {
4952
4956
  const started = new CdpFacade({ net, hosts });
4953
4957
  facade = started;
@@ -4990,7 +4994,7 @@ var CdpHosts = class {
4990
4994
  if (!entry) throw new HttpError(503, NO_CDP_HOST);
4991
4995
  let conn;
4992
4996
  try {
4993
- conn = await entry.hook(request3.runtime ? { runtime: request3.runtime } : {});
4997
+ conn = await entry.hook(hookRequest(request3));
4994
4998
  } catch (e) {
4995
4999
  throw new HttpError(502, `CDP host: ${e instanceof Error ? e.message : String(e)}`);
4996
5000
  }
@@ -5010,6 +5014,12 @@ var CdpHosts = class {
5010
5014
  for (const entry of [this.embedder, ...this.clients]) if (entry) closeAll(entry);
5011
5015
  }
5012
5016
  };
5017
+ function hookRequest({ runtime, stamp }) {
5018
+ return {
5019
+ ...runtime ? { runtime } : {},
5020
+ ...stamp ? { stamp: structuredClone(stamp) } : {}
5021
+ };
5022
+ }
5013
5023
  function closeAll(entry) {
5014
5024
  for (const conn of [...entry.open]) {
5015
5025
  conn.close();
@@ -5065,10 +5075,129 @@ function portCdpHook(ask, gone) {
5065
5075
  });
5066
5076
  }
5067
5077
 
5068
- // 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
+ }
5069
5085
  function fresh2(bytes) {
5070
5086
  return bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength ? bytes : bytes.slice();
5071
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
+ }
5072
5201
  function embedderAsker(port) {
5073
5202
  const asks = /* @__PURE__ */ new Map();
5074
5203
  let next3 = 0;
@@ -5107,7 +5236,7 @@ function embedderAsker(port) {
5107
5236
  const got = await asked.body.read().catch(() => ({ done: true, value: void 0 }));
5108
5237
  if (got.done) post({ id: id2, bodyEnd: true });
5109
5238
  else {
5110
- const bytes = fresh2(got.value);
5239
+ const bytes = fresh3(got.value);
5111
5240
  post({ id: id2, body: bytes }, [bytes.buffer]);
5112
5241
  }
5113
5242
  };
@@ -5278,11 +5407,11 @@ function permOf(st, link2) {
5278
5407
  ...link2 !== void 0 ? { link: link2 } : {}
5279
5408
  };
5280
5409
  }
5281
- function permits(cred, perm, want, directory = true) {
5410
+ function permits(cred, perm, want2, directory = true) {
5282
5411
  const mode = perm.mode ?? (directory ? 493 : 420);
5283
- 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;
5284
5413
  const shift = cred.euid === perm.uid ? 6 : inGroup(cred, perm.gid) ? 3 : 0;
5285
- return (mode >> shift & want) === want;
5414
+ return (mode >> shift & want2) === want2;
5286
5415
  }
5287
5416
  function sticky(cred, dir, entry) {
5288
5417
  if (cred.euid === 0 || ((dir.mode ?? 0) & S_ISVTX) === 0) return true;
@@ -5357,27 +5486,27 @@ async function present(fs, path, directory) {
5357
5486
  const st = await fs.lstat(path).catch(() => void 0);
5358
5487
  return st !== void 0 && (!directory || st.isDirectory);
5359
5488
  }
5360
- async function finalOf(fs, walked, want, options) {
5489
+ async function finalOf(fs, walked, want2, options) {
5361
5490
  const perm = walked.perm;
5362
5491
  if (options.follow === false && perm.link !== void 0) {
5363
5492
  return [{ ...perm, mode: 511 }, false];
5364
5493
  }
5365
- if ((want & X_OK) === 0 || options.directory !== void 0) {
5494
+ if ((want2 & X_OK) === 0 || options.directory !== void 0) {
5366
5495
  return [perm, options.directory !== false];
5367
5496
  }
5368
5497
  const look = options.follow === false ? fs.lstat : fs.stat;
5369
5498
  const st = await look.call(fs, walked.path).catch(() => void 0);
5370
5499
  return st ? [permOf(st), st.isDirectory] : [perm, false];
5371
5500
  }
5372
- async function access(fs, cred, path, want, options = {}) {
5501
+ async function access(fs, cred, path, want2, options = {}) {
5373
5502
  const walked = await walk(fs, cred, path, options.follow !== false, options.links);
5374
5503
  if (walked.denied !== void 0) {
5375
5504
  if (walked.link || await present(fs, walked.denied, true)) return walked;
5376
5505
  return { path, perm: void 0, missing: true };
5377
5506
  }
5378
5507
  if (!walked.perm) return { ...walked, missing: true };
5379
- const [perm, directory] = await finalOf(fs, walked, want, options);
5380
- 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;
5381
5510
  if (await present(fs, walked.path, false)) return { ...walked, denied: walked.path };
5382
5511
  return { ...walked, missing: true };
5383
5512
  }
@@ -5483,11 +5612,11 @@ function ttyOpenable(g, tty) {
5483
5612
  if (!g || !tty.owner || permits(g.cred, tty.owner, R_OK | W_OK, false)) return;
5484
5613
  deny(g, "open", tty.name ?? "/dev/tty");
5485
5614
  }
5486
- async function need(g, op, path, want, options = {}, shown2 = path) {
5487
- if (want & W_OK && shown2 === path) await fence(g, op, path);
5488
- 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));
5489
5618
  if (walked.denied !== void 0) deny(g, op, walked.link ? walked.denied : shown2);
5490
- 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);
5491
5620
  return walked;
5492
5621
  }
5493
5622
  async function readsAs(g, op, path, shown2) {
@@ -5636,10 +5765,10 @@ async function creatable(g, op, path) {
5636
5765
  const target = await need(g, op, path, 0);
5637
5766
  if (target.denied === void 0) await need(g, op, parentOf5(target.path), W_OK | X_OK, DIR, path);
5638
5767
  }
5639
- async function openable(g, op, path, want, create) {
5768
+ async function openable(g, op, path, want2, create) {
5640
5769
  const st = create ? await g.fs.stat(path).catch(() => void 0) : void 0;
5641
5770
  if (create && !st) return creatable(g, op, path);
5642
- await need(g, op, path, want, { directory: st?.isDirectory ?? false });
5771
+ await need(g, op, path, want2, { directory: st?.isDirectory ?? false });
5643
5772
  }
5644
5773
 
5645
5774
  // src/kernel/cred.ts
@@ -6080,8 +6209,8 @@ var FdTable = class _FdTable {
6080
6209
  }
6081
6210
  stdioTerminal() {
6082
6211
  for (const fd of [0, 1, 2]) {
6083
- const tty = this.fds.get(fd)?.file.tty;
6084
- if (tty) return tty;
6212
+ const file = this.fds.get(fd)?.file;
6213
+ if (file?.tty && !file.device) return file.tty;
6085
6214
  }
6086
6215
  return void 0;
6087
6216
  }
@@ -6382,13 +6511,13 @@ function addressBits(host) {
6382
6511
  function parsePrefix(text) {
6383
6512
  const parts2 = text.trim().split("/");
6384
6513
  if (parts2.length > 2) return void 0;
6385
- const [address = "", size] = parts2;
6386
- const parsed2 = addressBits(address);
6387
- if (!parsed2) return void 0;
6388
- 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;
6389
6518
  const length2 = size === void 0 ? max : /^\d{1,3}$/.test(size) ? Number(size) : -1;
6390
6519
  if (length2 < 0 || length2 > max) return void 0;
6391
- return { ...parsed2, length: length2 };
6520
+ return { ...parsed3, length: length2 };
6392
6521
  }
6393
6522
  function within4(prefix, addr) {
6394
6523
  if (prefix.v6 !== addr.v6) return false;
@@ -6414,10 +6543,10 @@ var Routes = class {
6414
6543
  this.ipv6 = ipv62;
6415
6544
  }
6416
6545
  set(table) {
6417
- const parsed2 = (table.prefixes ?? []).map(parsePrefix);
6418
- const bad = (table.prefixes ?? []).find((_, i) => !parsed2[i]);
6546
+ const parsed3 = (table.prefixes ?? []).map(parsePrefix);
6547
+ const bad = (table.prefixes ?? []).find((_, i) => !parsed3[i]);
6419
6548
  if (bad !== void 0) throw new Error(`not an address prefix: ${bad}`);
6420
- this.table = parsed2;
6549
+ this.table = parsed3;
6421
6550
  this.exit = table.exit === true;
6422
6551
  }
6423
6552
  classify(host) {
@@ -6953,10 +7082,10 @@ var KernelSocket = class _KernelSocket {
6953
7082
  }
6954
7083
  };
6955
7084
  let made2;
6956
- const abandon = () => {
7085
+ const abandon2 = () => {
6957
7086
  if (!keeps(signal.reason)) view2.close();
6958
7087
  };
6959
- signal.addEventListener("abort", abandon, { once: true });
7088
+ signal.addEventListener("abort", abandon2, { once: true });
6960
7089
  try {
6961
7090
  made2 = await make(view2);
6962
7091
  } catch (err) {
@@ -6969,7 +7098,7 @@ var KernelSocket = class _KernelSocket {
6969
7098
  throw signal.aborted ? signal.reason : err;
6970
7099
  } finally {
6971
7100
  handshaking = false;
6972
- signal.removeEventListener("abort", abandon);
7101
+ signal.removeEventListener("abort", abandon2);
6973
7102
  }
6974
7103
  const plain3 = uplinkLink(made2.conn, (err) => {
6975
7104
  this.reset = err instanceof KernelError && err.code === "EPROTO" ? err : new KernelError("ECONNRESET");
@@ -7158,6 +7287,7 @@ var LoopbackNet = class {
7158
7287
  reserved = /* @__PURE__ */ new Set();
7159
7288
  facades = /* @__PURE__ */ new Map();
7160
7289
  proxyStamped;
7290
+ stamped = /* @__PURE__ */ new Set();
7161
7291
  nextEphemeral = EPHEMERAL_FIRST;
7162
7292
  route;
7163
7293
  origins;
@@ -7232,6 +7362,9 @@ var LoopbackNet = class {
7232
7362
  this.kernelOnly(port, true);
7233
7363
  this.facades.set(port, name2);
7234
7364
  }
7365
+ stampPeers(port) {
7366
+ this.stamped.add(port);
7367
+ }
7235
7368
  stampProxy(port) {
7236
7369
  this.proxyStamped = port;
7237
7370
  }
@@ -7240,7 +7373,7 @@ var LoopbackNet = class {
7240
7373
  }
7241
7374
  stampsPeers(addr) {
7242
7375
  if (addr.family !== "inet" || !isLoopback(canonicalHost(addr.host))) return false;
7243
- return this.facades.has(addr.port) || this.proxyStamped === addr.port;
7376
+ return this.facades.has(addr.port) || this.stamped.has(addr.port) || this.proxyStamped === addr.port;
7244
7377
  }
7245
7378
  activate(addr, start2) {
7246
7379
  this.activators.set(keyOf(addr), start2);
@@ -8114,7 +8247,7 @@ var UserDb = class {
8114
8247
  joined(name2, extra);
8115
8248
  group.push({ name: name2, gid: uid, members: [] });
8116
8249
  passwd.push(row);
8117
- const fresh3 = !await this.fs.exists(home);
8250
+ const fresh4 = !await this.fs.exists(home);
8118
8251
  await this.fs.mkdir(home, { recursive: true });
8119
8252
  try {
8120
8253
  await this.provision(row, this.umaskFor(options), shared2);
@@ -8122,7 +8255,7 @@ var UserDb = class {
8122
8255
  await this.fs.mkdir("/etc", { recursive: true });
8123
8256
  await this.commit(formatPasswd(passwd), formatGroup(group));
8124
8257
  } catch (err) {
8125
- if (fresh3) await this.fs.rm(home, { recursive: true, force: true });
8258
+ if (fresh4) await this.fs.rm(home, { recursive: true, force: true });
8126
8259
  throw err;
8127
8260
  }
8128
8261
  remember();
@@ -8206,7 +8339,7 @@ var UserDb = class {
8206
8339
  };
8207
8340
 
8208
8341
  // src/client/protocol.ts
8209
- var PROTOCOL = [1, 16];
8342
+ var PROTOCOL = [1, 18];
8210
8343
  var FS_METHODS = [
8211
8344
  "readFile",
8212
8345
  "writeFile",
@@ -8466,8 +8599,10 @@ function hostRegistries(send2, gone, port) {
8466
8599
  const cdps = cdpRegistry(send2, gone);
8467
8600
  const facades = facadeRegistry(send2, gone);
8468
8601
  const requests = requestBinding(send2, port);
8602
+ const devices = deviceBinding(send2, port);
8469
8603
  return {
8470
8604
  requests,
8605
+ devices,
8471
8606
  answer(l, req) {
8472
8607
  if (req.op === "serve-cdp") return cdps.serve(l, req);
8473
8608
  if (req.op === "unserve-cdp") return cdps.drop(req.registration);
@@ -8476,6 +8611,7 @@ function hostRegistries(send2, gone, port) {
8476
8611
  },
8477
8612
  close() {
8478
8613
  requests.close();
8614
+ devices.close();
8479
8615
  cdps.close();
8480
8616
  facades.close();
8481
8617
  }
@@ -8589,6 +8725,28 @@ function answered(pending, req, reply, detach) {
8589
8725
  }
8590
8726
  );
8591
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
+ }
8592
8750
  function serveClient(port, host) {
8593
8751
  let state = "new";
8594
8752
  let self;
@@ -8622,9 +8780,9 @@ function serveClient(port, host) {
8622
8780
  ended.resolve();
8623
8781
  }
8624
8782
  async function hello(message3) {
8625
- const refused4 = versionError(message3.protocol) ?? await hosts.requests.accept(host, message3);
8626
- if (refused4) {
8627
- send2({ hello: { protocol: PROTOCOL, error: refused4 } });
8783
+ const refused5 = await helloRefused(host, hosts, message3);
8784
+ if (refused5) {
8785
+ send2({ hello: { protocol: PROTOCOL, error: refused5 } });
8628
8786
  detach();
8629
8787
  return;
8630
8788
  }
@@ -8908,7 +9066,7 @@ function satisfies(version, range2) {
8908
9066
  }
8909
9067
 
8910
9068
  // src/version.ts
8911
- var KERNEL_VERSION = true ? "1.181.0" : "0.0.0-dev";
9069
+ var KERNEL_VERSION = true ? "1.183.0" : "0.0.0-dev";
8912
9070
  var OS_RELEASE = "6.0.0-slicc";
8913
9071
  function procVersion() {
8914
9072
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -9200,10 +9358,10 @@ async function tierPackages(fs, physical, roots, bins, kernel, global) {
9200
9358
  const own = commandsOf(pkg, manifest);
9201
9359
  const named = new Set(own.map((command) => command.name));
9202
9360
  const extra = bins ? await binCommandsOf(physical, pkg, manifest, named) : [];
9203
- const refused4 = engineRefusal(manifest, kernel);
9361
+ const refused5 = engineRefusal(manifest, kernel);
9204
9362
  const name2 = pkg.slice(root.length + 1);
9205
9363
  const dependency = direct?.has(name2) === false;
9206
- const marks = { ...refused4 ? { refused: refused4 } : {}, ...dependency ? { dependency } : {} };
9364
+ const marks = { ...refused5 ? { refused: refused5 } : {}, ...dependency ? { dependency } : {} };
9207
9365
  out.push({
9208
9366
  name: name2,
9209
9367
  pkg,
@@ -9235,7 +9393,7 @@ function winners(tier) {
9235
9393
  }
9236
9394
  return out;
9237
9395
  }
9238
- 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()) {
9239
9397
  const found = /* @__PURE__ */ new Map();
9240
9398
  const physical = physicalResolver(fs);
9241
9399
  const tiers = (typeof modules === "string" ? [modules] : modules).map(
@@ -9249,7 +9407,7 @@ async function scanCommands(fs, modules, binsFrom = Number.POSITIVE_INFINITY, ke
9249
9407
  const dependencies = [];
9250
9408
  for (const [index, roots] of tiers.entries()) {
9251
9409
  const tier = await tierPackages(fs, physical, roots, index >= binsFrom, kernel, global);
9252
- 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)));
9253
9411
  await take(tier.filter((entry) => !entry.dependency));
9254
9412
  dependencies.push(tier.filter((entry) => entry.dependency));
9255
9413
  }
@@ -9853,9 +10011,9 @@ var TAGS_VERSION = 1;
9853
10011
  async function readTags(fs) {
9854
10012
  const text = await fs.readFile(TAGS_PATH).catch(() => "");
9855
10013
  try {
9856
- const parsed2 = JSON.parse(text);
9857
- if (!parsed2 || typeof parsed2 !== "object") return {};
9858
- const { v: _, ...tables } = parsed2;
10014
+ const parsed3 = JSON.parse(text);
10015
+ if (!parsed3 || typeof parsed3 !== "object") return {};
10016
+ const { v: _, ...tables } = parsed3;
9859
10017
  return tables;
9860
10018
  } catch {
9861
10019
  return {};
@@ -9925,27 +10083,27 @@ var Crontabs = class {
9925
10083
  const json5 = JSON.stringify({ v: TAGS_VERSION, ...kept });
9926
10084
  if (json5 === await fs.readFile(TAGS_PATH).catch(() => "")) return;
9927
10085
  const owner = owners ? { uid: 0, gid: 0, mode: 384 } : void 0;
9928
- const fresh3 = `${TAGS_PATH}.${crypto.randomUUID()}.new`;
9929
- await fs.writeFile(fresh3, json5, owner);
9930
- 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);
9931
10089
  }
9932
10090
  async install(path, target, text, caller) {
9933
10091
  if (text.length > CRONTAB_MAX) return { error: "crontab: the table is too long" };
9934
10092
  const { errors } = parseCrontab(text, path);
9935
10093
  if (errors.length > 0) {
9936
- const lines2 = errors.map(({ line, message: message3 }) => `"-":${line}: ${message3}`);
9937
- 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") };
9938
10096
  }
9939
10097
  const { fs, owners } = this.o;
9940
10098
  if (!await fs.exists(SPOOL_DIR)) {
9941
10099
  await fs.mkdir(SPOOL_DIR, { recursive: true });
9942
10100
  if (owners) await fs.chown?.(SPOOL_DIR, { uid: 0, gid: 0, mode: 448, follow: false });
9943
10101
  }
9944
- const fresh3 = `${SPOOL_DIR}/.${target.name}.${crypto.randomUUID()}.new`;
10102
+ const fresh4 = `${SPOOL_DIR}/.${target.name}.${crypto.randomUUID()}.new`;
9945
10103
  const owner = owners ? { uid: target.uid, gid: target.gid, mode: 384 } : void 0;
9946
10104
  await this.retag(path, text, caller);
9947
- await fs.writeFile(fresh3, text, owner);
9948
- await fs.rename(fresh3, path);
10105
+ await fs.writeFile(fresh4, text, owner);
10106
+ await fs.rename(fresh4, path);
9949
10107
  return { text: "" };
9950
10108
  }
9951
10109
  };
@@ -10266,15 +10424,15 @@ var UnlinkedKeeper = class {
10266
10424
  const releases = [];
10267
10425
  const take = async () => {
10268
10426
  for (; ; ) {
10269
- const fresh3 = [...this.heldWithin(target).keys()].filter((path) => !paths.includes(path));
10270
- if (fresh3.length === 0) return;
10271
- fresh3.sort();
10272
- 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])) {
10273
10431
  for (const release of releases.splice(0)) release();
10274
- fresh3.push(...paths.splice(0));
10275
- fresh3.sort();
10432
+ fresh4.push(...paths.splice(0));
10433
+ fresh4.sort();
10276
10434
  }
10277
- for (const path of fresh3) {
10435
+ for (const path of fresh4) {
10278
10436
  paths.push(path);
10279
10437
  releases.push(await this.gate.acquire(path));
10280
10438
  }
@@ -10670,10 +10828,10 @@ var Output = class {
10670
10828
  };
10671
10829
  function readStamp(text) {
10672
10830
  try {
10673
- const parsed2 = JSON.parse(text ?? "");
10674
- const jobs = parsed2.jobs && typeof parsed2.jobs === "object" ? parsed2.jobs : {};
10675
- const reboot = parsed2.reboot && typeof parsed2.reboot === "object" ? parsed2.reboot : {};
10676
- 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 : {};
10677
10835
  return { jobs: { ...jobs }, reboot: { ...reboot }, when: { ...when } };
10678
10836
  } catch {
10679
10837
  return { jobs: {}, reboot: {}, when: {} };
@@ -10793,10 +10951,10 @@ var CronService = class {
10793
10951
  const text = JSON.stringify(stamp);
10794
10952
  const old = await fs.readFile(STAMP_PATH).catch(() => void 0);
10795
10953
  if (old === text || old === void 0 && text === EMPTY_STAMP) return;
10796
- const fresh3 = `${STAMP_PATH}.new`;
10954
+ const fresh4 = `${STAMP_PATH}.new`;
10797
10955
  const owner = owners ? { uid: 0, gid: 0, mode: 384 } : void 0;
10798
- await fs.writeFile(fresh3, text, owner);
10799
- await fs.rename(fresh3, STAMP_PATH);
10956
+ await fs.writeFile(fresh4, text, owner);
10957
+ await fs.rename(fresh4, STAMP_PATH);
10800
10958
  }
10801
10959
  async load() {
10802
10960
  const { fs } = this.host;
@@ -10849,10 +11007,10 @@ var CronService = class {
10849
11007
  await this.once(path, text, errors);
10850
11008
  return table;
10851
11009
  }
10852
- async once(path, version, lines2) {
11010
+ async once(path, version, lines3) {
10853
11011
  if (this.reported.get(path) === version) return;
10854
11012
  this.reported.set(path, version);
10855
- for (const line of lines2) await this.error(line);
11013
+ for (const line of lines3) await this.error(line);
10856
11014
  }
10857
11015
  async error(line) {
10858
11016
  if (this.stopping.signal.aborted) return;
@@ -11086,8 +11244,8 @@ function canonicalHost2(host) {
11086
11244
  function localV4(a, b) {
11087
11245
  return a === 127 || a === 0 || a === 169 && b === 254;
11088
11246
  }
11089
- function hextets(address) {
11090
- const [head, tail2, extra] = address.split("::");
11247
+ function hextets(address2) {
11248
+ const [head, tail2, extra] = address2.split("::");
11091
11249
  if (extra !== void 0) return void 0;
11092
11250
  const part = (text) => text ? text.split(":").map((h) => Number.parseInt(h, 16)) : [];
11093
11251
  const front = part(head);
@@ -11095,8 +11253,8 @@ function hextets(address) {
11095
11253
  const groups = tail2 === void 0 ? front : [...front, ...new Array(8 - front.length - back.length).fill(0), ...back];
11096
11254
  return groups.length === 8 && groups.every((g) => g >= 0 && g <= 65535) ? groups : void 0;
11097
11255
  }
11098
- function localV6(address) {
11099
- const g = hextets(address);
11256
+ function localV6(address2) {
11257
+ const g = hextets(address2);
11100
11258
  if (!g) return true;
11101
11259
  if ((g[0] & 65472) === 65152) return true;
11102
11260
  const zeroPrefix = g.slice(0, 5).every((x) => x === 0);
@@ -11469,8 +11627,8 @@ var RealmProxy = class {
11469
11627
  }
11470
11628
  async connect(conn, incoming, req, idle2) {
11471
11629
  const target = tunnelTarget(req);
11472
- const refused4 = target.port === 443 ? await this.tunnelReady(target) : void 0;
11473
- if (refused4) throw refused4;
11630
+ const refused5 = target.port === 443 ? await this.tunnelReady(target) : void 0;
11631
+ if (refused5) throw refused5;
11474
11632
  await conn.write(latin1Bytes("HTTP/1.1 200 Connection Established\r\n\r\n"), this.stop.signal);
11475
11633
  try {
11476
11634
  const first = await incoming.peek(this.stop.signal);
@@ -11914,15 +12072,15 @@ async function followLinks(fs, path) {
11914
12072
  }
11915
12073
  var NOBODY = { ruid: -1, euid: -1, suid: -1, rgid: -1, egid: -1, sgid: -1, groups: [] };
11916
12074
  var EMPTY2 = new Uint8Array(0);
11917
- function quiet(promise) {
12075
+ function quiet2(promise) {
11918
12076
  return promise.catch(() => void 0);
11919
12077
  }
11920
12078
  async function readsAs2(fs, cred, target) {
11921
- 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 }));
11922
12080
  return walked !== void 0 && walked.denied === void 0 && !walked.missing;
11923
12081
  }
11924
12082
  async function shownTarget(fs, target) {
11925
- const st = await quiet(fs.stat(target));
12083
+ const st = await quiet2(fs.stat(target));
11926
12084
  return st?.isFile && await readsAs2(fs, NOBODY, target) ? st : void 0;
11927
12085
  }
11928
12086
  async function madeFor(owner, virtual) {
@@ -12245,10 +12403,10 @@ var FreshModules = class {
12245
12403
  this.fresh.set(key, { umask: mask, pid, added: 0, setup: mask !== DEFAULT_UMASK });
12246
12404
  }
12247
12405
  forget(pid) {
12248
- 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);
12249
12407
  }
12250
- drop(fresh3) {
12251
- 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);
12252
12410
  }
12253
12411
  moved(from, to) {
12254
12412
  const source = normalizePath(from);
@@ -12257,10 +12415,10 @@ var FreshModules = class {
12257
12415
  for (const key of [...this.fresh.keys()]) {
12258
12416
  if (key === target || within6(key, target)) this.fresh.delete(key);
12259
12417
  }
12260
- for (const [key, fresh3] of [...this.fresh]) {
12418
+ for (const [key, fresh4] of [...this.fresh]) {
12261
12419
  if (key !== source && !within6(key, source)) continue;
12262
12420
  this.fresh.delete(key);
12263
- this.fresh.set(target + key.slice(source.length), fresh3);
12421
+ this.fresh.set(target + key.slice(source.length), fresh4);
12264
12422
  }
12265
12423
  }
12266
12424
  wrap(fs) {
@@ -12270,15 +12428,15 @@ var FreshModules = class {
12270
12428
  };
12271
12429
  const decide = async (given, known) => {
12272
12430
  const path = normalizePath(given);
12273
- const fresh3 = this.fresh.get(path);
12274
- if (!fresh3) return;
12431
+ const fresh4 = this.fresh.get(path);
12432
+ if (!fresh4) return;
12275
12433
  const bytes = known && known.length >= MAGIC.length ? known : await head(path);
12276
- const want = isModule(bytes) ? 73 & ~fresh3.umask : 0;
12277
- fresh3.setup = false;
12278
- if (want === fresh3.added) return;
12434
+ const want2 = isModule(bytes) ? 73 & ~fresh4.umask : 0;
12435
+ fresh4.setup = false;
12436
+ if (want2 === fresh4.added) return;
12279
12437
  const mode = (await fs.stat(path)).mode & 4095;
12280
- await fs.chmod(path, mode & ~fresh3.added | want);
12281
- fresh3.added = want;
12438
+ await fs.chmod(path, mode & ~fresh4.added | want2);
12439
+ fresh4.added = want2;
12282
12440
  };
12283
12441
  const overrides = {
12284
12442
  writeFile: async (path, content, owner) => {
@@ -12291,9 +12449,9 @@ var FreshModules = class {
12291
12449
  }
12292
12450
  },
12293
12451
  chmod: async (path, mode) => {
12294
- const fresh3 = this.fresh.get(normalizePath(path));
12295
- if (fresh3?.setup && (mode & 4095) === (438 & ~fresh3.umask)) fresh3.setup = false;
12296
- 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);
12297
12455
  await fs.chmod(path, mode);
12298
12456
  },
12299
12457
  rm: async (path, options) => {
@@ -12310,16 +12468,16 @@ var FreshModules = class {
12310
12468
  link: async (from, to, follow2) => {
12311
12469
  await fs.link(from, to, follow2);
12312
12470
  const source = follow2 && fs.realpath ? await fs.realpath(from, true).catch(() => from) : from;
12313
- const fresh3 = this.fresh.get(normalizePath(source));
12314
- 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);
12315
12473
  }
12316
12474
  };
12317
12475
  const { append } = fs;
12318
12476
  if (append) {
12319
12477
  overrides.append = async (path, bytes, options) => {
12320
- const fresh3 = this.fresh.has(normalizePath(path));
12478
+ const fresh4 = this.fresh.has(normalizePath(path));
12321
12479
  const end = await append.call(fs, path, bytes, options);
12322
- if (fresh3 && end - bytes.length < MAGIC.length) await decide(path);
12480
+ if (fresh4 && end - bytes.length < MAGIC.length) await decide(path);
12323
12481
  return end;
12324
12482
  };
12325
12483
  }
@@ -12646,7 +12804,7 @@ var FsWatchers = class {
12646
12804
  };
12647
12805
  const content = (run2, types) => async (...args) => {
12648
12806
  const at = normalizePath(args[0]);
12649
- const fresh3 = this.covers(at) && !await fs.exists(at).catch(() => true);
12807
+ const fresh4 = this.covers(at) && !await fs.exists(at).catch(() => true);
12650
12808
  let done5 = false;
12651
12809
  try {
12652
12810
  await run2(...args);
@@ -12655,11 +12813,11 @@ var FsWatchers = class {
12655
12813
  const real = await realOf(at);
12656
12814
  const paths = real === at ? [at] : [at, real];
12657
12815
  this.report(paths);
12658
- 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);
12659
12817
  }
12660
12818
  };
12661
- const written2 = (fresh3) => fresh3 ? ["rename", "change"] : ["change"];
12662
- 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"];
12663
12821
  const reshaping = (run2) => async (...args) => {
12664
12822
  this.reshaped();
12665
12823
  try {
@@ -12692,14 +12850,14 @@ var FsWatchers = class {
12692
12850
  if (created4.length > 0 || !await fs.exists(ancestor)) created4.push(ancestor);
12693
12851
  }
12694
12852
  }
12695
- 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);
12696
12854
  let done5 = false;
12697
12855
  try {
12698
12856
  await fs.mkdir(path, options);
12699
12857
  done5 = true;
12700
12858
  } finally {
12701
12859
  this.report([...created4, path]);
12702
- if (done5) this.emit("rename", fresh3 ? [...created4, path] : created4);
12860
+ if (done5) this.emit("rename", fresh4 ? [...created4, path] : created4);
12703
12861
  }
12704
12862
  },
12705
12863
  rm: reshaping(async (path, options) => {
@@ -13209,15 +13367,15 @@ function embedderFailure(err) {
13209
13367
  return new KernelError(ERRNOS[String(name2)] ?? "EIO", lineOf2(err));
13210
13368
  }
13211
13369
  function headerOf(bytes) {
13212
- let parsed2;
13370
+ let parsed3;
13213
13371
  try {
13214
- parsed2 = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
13372
+ parsed3 = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
13215
13373
  } catch {
13216
13374
  throw new KernelError("EINVAL", "the request header is not JSON");
13217
13375
  }
13218
- if (typeof parsed2 !== "object" || parsed2 === null || Array.isArray(parsed2))
13376
+ if (typeof parsed3 !== "object" || parsed3 === null || Array.isArray(parsed3))
13219
13377
  throw new KernelError("EINVAL", "the request header is not an object");
13220
- const { kind, ...options } = parsed2;
13378
+ const { kind, ...options } = parsed3;
13221
13379
  if (typeof kind !== "string" || kind.length > 64 || !KIND.test(kind))
13222
13380
  throw new KernelError("EINVAL", "the request has no valid kind");
13223
13381
  return { kind, options };
@@ -13644,6 +13802,966 @@ var EmbedderBroker = class {
13644
13802
  }
13645
13803
  };
13646
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
+
13647
14765
  // src/kernel/exec-ticket.ts
13648
14766
  var TICKET_ENV = "SLICC_SEALED_TICKET";
13649
14767
  var TICKET_TTL_MS = 3e4;
@@ -14016,8 +15134,8 @@ var DriverConnection = class _DriverConnection {
14016
15134
  } finally {
14017
15135
  clearTimeout(timer);
14018
15136
  }
14019
- const refused4 = driverVersionError(hello.protocol);
14020
- if (refused4) throw errnoError("EPROTO", refused4);
15137
+ const refused5 = driverVersionError(hello.protocol);
15138
+ if (refused5) throw errnoError("EPROTO", refused5);
14021
15139
  if (hello.error) throw errnoError(hello.errno ?? "EIO", hello.error);
14022
15140
  return new _DriverConnection(port, hello.capabilities ?? {}, waiting, timeoutMs, options);
14023
15141
  }
@@ -14063,7 +15181,7 @@ var DriverConnection = class _DriverConnection {
14063
15181
  var S_ISGID4 = 1024;
14064
15182
  var ID = /^\d{1,10}$/;
14065
15183
  var MASK = /^0*[0-7]{1,4}$/;
14066
- var MAX_ID2 = 4294967294;
15184
+ var MAX_ID3 = 4294967294;
14067
15185
  function idOption(value2) {
14068
15186
  return value2 === void 0 ? 0 : Number(value2);
14069
15187
  }
@@ -14075,7 +15193,7 @@ function checkIds(options) {
14075
15193
  for (const key of ["uid", "gid"]) {
14076
15194
  const value2 = options[key];
14077
15195
  if (value2 === void 0) continue;
14078
- 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}`);
14079
15197
  }
14080
15198
  }
14081
15199
  function checkMasks(options) {
@@ -14087,7 +15205,7 @@ function checkMasks(options) {
14087
15205
  }
14088
15206
  }
14089
15207
  }
14090
- function modeOf(attr2, mount) {
15208
+ function modeOf2(attr2, mount) {
14091
15209
  const type = attr2.kind === "directory" ? 16384 : attr2.kind === "symlink" ? 40960 : attr2.kind === "fifo" ? 4096 : 32768;
14092
15210
  const fallback = type | (attr2.kind === "directory" ? 493 : attr2.kind === "symlink" ? 511 : 420);
14093
15211
  if (mount.caps.owners) return attr2.mode ?? fallback;
@@ -14248,9 +15366,9 @@ var HeldPaths = class extends Map {
14248
15366
  this.unlinked.set(path, file);
14249
15367
  }
14250
15368
  keep(path, file) {
14251
- const fresh3 = this.handlesAt(path).filter((id2) => !this.keptHandles.has(id2));
14252
- for (const id2 of fresh3) this.keptHandles.set(id2, file);
14253
- 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));
14254
15372
  this.unlinked.set(path, file);
14255
15373
  }
14256
15374
  unkeep(path, file) {
@@ -14371,7 +15489,7 @@ function statOf2(attr2, path, mount) {
14371
15489
  isSymbolicLink: attr2.kind === "symlink",
14372
15490
  ...attr2.kind === "fifo" ? { isFifo: true } : {},
14373
15491
  size: attr2.size,
14374
- mode: modeOf(attr2, mount),
15492
+ mode: modeOf2(attr2, mount),
14375
15493
  mtime,
14376
15494
  atime: attr2.atime === void 0 ? mtime : new Date(attr2.atime),
14377
15495
  ctime: mtime,
@@ -15462,8 +16580,8 @@ var MountFs = class _MountFs {
15462
16580
  }
15463
16581
  this.writable(mount, real);
15464
16582
  const { uid, gid, mode } = change;
15465
- const fresh3 = change.kind === "new" && { uid, gid, mode };
15466
- 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;
15467
16585
  this.table.forget(mount, real);
15468
16586
  const attrs = Object.entries({ uid: set3.uid, gid: set3.gid, mode: set3.mode });
15469
16587
  await mount.conn.call({
@@ -15740,10 +16858,10 @@ async function runMkdir(entry, path, req) {
15740
16858
  const plain3 = plainDirMode(entry, req.mode);
15741
16859
  const parentsMode = !req.exclusive ? plainParentsMode(entry) : void 0;
15742
16860
  const fixParents = parentsMode !== void 0 ? await missingParents(fs, path, parentsMode) : void 0;
15743
- const fresh3 = plain3 !== void 0 && !await fs.exists(path);
16861
+ const fresh4 = plain3 !== void 0 && !await fs.exists(path);
15744
16862
  await (req.exclusive ? mkdirExclusive(entry, path, req.mode) : fs.mkdir(path, mkdirOptions(entry, req.mode)));
15745
16863
  await fixParents?.();
15746
- if (fresh3) await fs.chmod(path, plain3);
16864
+ if (fresh4) await fs.chmod(path, plain3);
15747
16865
  return done2;
15748
16866
  }
15749
16867
  async function shownAs(entry, path, st) {
@@ -15955,10 +17073,10 @@ async function guard(g, path, req, cwd) {
15955
17073
  }
15956
17074
  async function opening(entry, path, mode) {
15957
17075
  const g = guardOf(entry);
15958
- 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);
15959
17077
  const created4 = (mode & HOLD_CREATED) !== 0 && entry.created?.take(path) === true;
15960
- if (g && want !== 0 && !created4) {
15961
- await need(g, "open", path, want, { directory: false });
17078
+ if (g && want2 !== 0 && !created4) {
17079
+ await need(g, "open", path, want2, { directory: false });
15962
17080
  }
15963
17081
  }
15964
17082
  async function stillThere(entry, path, req) {
@@ -15982,7 +17100,7 @@ async function holdOp(entry, path, req) {
15982
17100
  await entry.source?.open(path, real);
15983
17101
  await stillThere(entry, path, req);
15984
17102
  if (req.nofollow && !await ownEntry(entry, path, real ?? path)) continue;
15985
- 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);
15986
17104
  if (!r || !r.missed(path) && !r.changedSince(path, gen) && !r.inFlight?.(path))
15987
17105
  return json2(await heldOpen(entry, path, real, req, fid));
15988
17106
  }
@@ -15996,7 +17114,7 @@ async function heldOpen(entry, path, real, req, fid) {
15996
17114
  await entry.announceHold?.(real ?? path, fid, ((req.mode ?? 0) & HOLD_CREATED) !== 0);
15997
17115
  return real !== void 0 && real !== path ? { handle, real } : handle;
15998
17116
  }
15999
- async function idOf(entry, path) {
17117
+ async function idOf2(entry, path) {
16000
17118
  return await entry.fileId?.(path)?.catch(() => void 0) ?? void 0;
16001
17119
  }
16002
17120
  async function ownEntry(entry, path, real) {
@@ -16068,8 +17186,8 @@ async function guarded(entry, path, req) {
16068
17186
  return { paths };
16069
17187
  } catch (err) {
16070
17188
  const refusal2 = err.code === "EACCES";
16071
- const refused4 = req.op === "exists" && refusal2 ? json2(false) : toErrno(err);
16072
- return { refused: refused4 };
17189
+ const refused5 = req.op === "exists" && refusal2 ? json2(false) : toErrno(err);
17190
+ return { refused: refused5 };
16073
17191
  }
16074
17192
  }
16075
17193
  async function copyOp(entry, path, req) {
@@ -16170,7 +17288,7 @@ async function bound(entry, path, handle) {
16170
17288
  const now = await entry.fs.stat(path).catch(() => void 0);
16171
17289
  if (now === void 0 && bound3.fid !== void 0 && !await entry.fs.lstat(path).catch(() => void 0))
16172
17290
  throw syncError("ENOENT", path);
16173
- 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;
16174
17292
  if (same3) {
16175
17293
  entry.trust?.(handle, bound3.doubt);
16176
17294
  return;
@@ -16265,13 +17383,13 @@ async function created2(entry, req) {
16265
17383
  return seen === void 0 ? looked : json2({ ...looked.json, seen });
16266
17384
  }
16267
17385
  var DENIED = /* @__PURE__ */ new Set(["EACCES", "EPERM"]);
16268
- async function rmPermitted(entry, path, req, refused4) {
17386
+ async function rmPermitted(entry, path, req, refused5) {
16269
17387
  const st = await entry.fs.lstat(path).catch(() => void 0);
16270
- if (!st?.isDirectory) return refused4;
17388
+ if (!st?.isDirectory) return refused5;
16271
17389
  const unlisted = await guarded(entry, path, { ...req, op: "readdir" });
16272
17390
  if ("refused" in unlisted) return unlisted.refused;
16273
17391
  const names2 = await entry.fs.readdir(path).catch(() => void 0);
16274
- if (!names2) return refused4;
17392
+ if (!names2) return refused5;
16275
17393
  let first;
16276
17394
  for (const name2 of names2) {
16277
17395
  const child = await dispatchOne(entry, {
@@ -16322,14 +17440,14 @@ async function newDevName(entry, path, req) {
16322
17440
  const named = NAMING.has(req.op);
16323
17441
  if (!named && !CREATIONS.has(req.op)) return void 0;
16324
17442
  const target = named ? entry.fs.resolvePath(entry.cwd, req.arg2 ?? "") : path;
16325
- const refused4 = devCreation(target, entry.cred?.());
16326
- if (!refused4) return void 0;
17443
+ const refused5 = devCreation(target, entry.cred?.());
17444
+ if (!refused5) return void 0;
16327
17445
  const there = await entry.fs.lstat(target).then(
16328
17446
  () => true,
16329
17447
  () => false
16330
17448
  );
16331
17449
  if (there) return void 0;
16332
- 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` };
16333
17451
  }
16334
17452
  var MISS_OPS = /* @__PURE__ */ new Set(["stat", "lstat"]);
16335
17453
  async function linkFree(fs, path) {
@@ -16440,13 +17558,13 @@ var SAB_DEFAULT_WINDOW_BYTES = 1024 * 1024;
16440
17558
  var SAB_MIN_BYTES = SAB_HEADER_BYTES + 4096;
16441
17559
  var SYNC_SAB_REQ_MSG = "sync-sab-req";
16442
17560
  var SYNC_SAB_NEXT_MSG = "sync-sab-next";
16443
- var encoder3 = new TextEncoder();
17561
+ var encoder4 = new TextEncoder();
16444
17562
  var decoder2 = new TextDecoder();
16445
17563
  function encodeSabResult(result) {
16446
17564
  if (!result.ok) {
16447
17565
  return {
16448
17566
  status: SAB_STATUS_ERRNO,
16449
- payload: encoder3.encode(
17567
+ payload: encoder4.encode(
16450
17568
  JSON.stringify({
16451
17569
  errno: result.errno,
16452
17570
  message: result.message,
@@ -16461,7 +17579,7 @@ function encodeSabResult(result) {
16461
17579
  case "bytes":
16462
17580
  return { status: SAB_STATUS_BYTES, payload: result.bytes };
16463
17581
  case "json":
16464
- 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)) };
16465
17583
  default:
16466
17584
  return { status: SAB_STATUS_VOID, payload: new Uint8Array(0) };
16467
17585
  }
@@ -16788,9 +17906,9 @@ var Pages = class {
16788
17906
  const page = (start2 + at) / PAGE;
16789
17907
  if (this.pages.has(page)) continue;
16790
17908
  const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
16791
- const fresh3 = this.take(part.length);
16792
- fresh3.set(part);
16793
- this.pages.set(page, fresh3);
17909
+ const fresh4 = this.take(part.length);
17910
+ fresh4.set(part);
17911
+ this.pages.set(page, fresh4);
16794
17912
  }
16795
17913
  }
16796
17914
  read(at, length2) {
@@ -17001,16 +18119,16 @@ var pinnedRoot;
17001
18119
  var rootKind = () => globalThis.navigator?.storage ? "opfs" : "memory";
17002
18120
  function mountTable(mounts = []) {
17003
18121
  const root = pinnedRoot ?? rootKind();
17004
- 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"];
17005
18123
  for (const m of mounts) {
17006
18124
  const options = Object.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
17007
18125
  if (m.state !== "ok") options.push(m.state);
17008
18126
  const access2 = options.includes("ro") ? [] : ["rw"];
17009
- lines2.push(
18127
+ lines3.push(
17010
18128
  `${field3(m.source)} ${field3(m.target)} ${m.type} ${[...access2, ...options].join(",")} 0 0`
17011
18129
  );
17012
18130
  }
17013
- return `${lines2.join("\n")}
18131
+ return `${lines3.join("\n")}
17014
18132
  `;
17015
18133
  }
17016
18134
 
@@ -18045,7 +19163,7 @@ function recordRefusal(opening2) {
18045
19163
  const found = headerClass(opening2);
18046
19164
  return found && new KernelError("EPROTO", `${found[0]}: ${found[1]}`);
18047
19165
  }
18048
- function refused2(engine, session, err, opening2) {
19166
+ function refused3(engine, session, err, opening2) {
18049
19167
  const flags2 = session.verifyResult;
18050
19168
  const code = verifyCode(flags2);
18051
19169
  if (code && flags2 !== -1)
@@ -18066,7 +19184,7 @@ async function handshake(conn, o, session, inbox) {
18066
19184
  done5 = session.handshake();
18067
19185
  } catch (err) {
18068
19186
  await send(conn, session).catch(() => void 0);
18069
- throw refused2(o.engine, session, err, inbox.opening);
19187
+ throw refused3(o.engine, session, err, inbox.opening);
18070
19188
  }
18071
19189
  await send(conn, session);
18072
19190
  if (done5) return;
@@ -18517,10 +19635,10 @@ async function anchorFor(engine, chain2, host, found) {
18517
19635
  var FRAGMENT = 16;
18518
19636
  var MIN_VALUE = 8;
18519
19637
  var MIN_BASE64 = 8;
18520
- var encoder4 = new TextEncoder();
19638
+ var encoder5 = new TextEncoder();
18521
19639
  var STD = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
18522
19640
  function utf8(text) {
18523
- return encoder4.encode(text);
19641
+ return encoder5.encode(text);
18524
19642
  }
18525
19643
  function base64(bytes, padded = true) {
18526
19644
  let out = "";
@@ -18594,14 +19712,73 @@ function encodings(bytes) {
18594
19712
  }
18595
19713
  return [...standard, ...standard.map(urlSafe)];
18596
19714
  }
18597
- 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)) {
18598
19768
  const raw2 = utf8(value2);
18599
19769
  const seen = /* @__PURE__ */ new Set([value2]);
18600
19770
  const out = [];
18601
- if (raw2.length < FRAGMENT) out.push({ bytes: raw2, form: "raw" });
18602
- for (let offset = 0; raw2.length >= FRAGMENT && offset + FRAGMENT <= raw2.length; offset++) {
18603
- out.push({ bytes: raw2.subarray(offset, offset + FRAGMENT), form: "fragment", offset });
18604
- }
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
+ });
18605
19782
  const add = (text, form) => {
18606
19783
  if (seen.has(text)) return;
18607
19784
  seen.add(text);
@@ -18734,12 +19911,19 @@ function prepare(secrets, sameLength) {
18734
19911
  const ids = /* @__PURE__ */ new Map();
18735
19912
  const wrapIds = /* @__PURE__ */ new Map();
18736
19913
  const values = secrets.map((secret) => utf8(secret.value));
19914
+ const splits = [];
18737
19915
  secrets.forEach((secret, index) => {
18738
19916
  if (typeof secret.value !== "string" || values[index].length < MIN_VALUE) {
18739
19917
  throw new RangeError(`the value of ${secret.name} is shorter than ${MIN_VALUE} bytes`);
18740
19918
  }
18741
- for (const shape of shapes(secret.value)) {
18742
- 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
+ };
18743
19927
  register(main, sources, ids, shape.bytes, source);
18744
19928
  if (wrappable(shape.form)) register(wrap, wrapSources, wrapIds, shape.bytes, source);
18745
19929
  }
@@ -18750,7 +19934,16 @@ function prepare(secrets, sameLength) {
18750
19934
  utf8(secret.placeholder),
18751
19935
  utf8(`[redacted:${secret.name}]`)
18752
19936
  ]);
18753
- 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
+ };
18754
19947
  }
18755
19948
  function filled(input, start2, end) {
18756
19949
  const out = new Uint8Array(end - start2);
@@ -18781,14 +19974,16 @@ function resolve(c, input, start2, end, source) {
18781
19974
  growing: false
18782
19975
  };
18783
19976
  }
18784
- const value2 = c.values[source.secret];
19977
+ const split3 = c.splits[source.secret];
19978
+ const value2 = split3.pieces[source.piece];
18785
19979
  let stop = end;
18786
19980
  let at = source.offset + FRAGMENT;
18787
19981
  while (stop < input.length && at < value2.length && input[stop] === value2[at]) {
18788
19982
  stop++;
18789
19983
  at++;
18790
19984
  }
18791
- 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";
18792
19987
  const growing = stop === input.length && at < value2.length;
18793
19988
  return { start: start2, end: stop, secret: source.secret, form, rank: RANK[form], growing };
18794
19989
  }
@@ -18942,9 +20137,9 @@ function compile(secrets, options = {}) {
18942
20137
  // src/kernel/scrub-file.ts
18943
20138
  var SCRUB_IDLE_MS = 50;
18944
20139
  var SCRUB_NONBLOCK_MAX = 64 * 1024;
18945
- var encoder5 = new TextEncoder();
20140
+ var encoder6 = new TextEncoder();
18946
20141
  function scrubberOf(secrets, sameLength = false) {
18947
- 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);
18948
20143
  return usable3.length > 0 ? compile(usable3, { idleMs: SCRUB_IDLE_MS, sameLength }) : void 0;
18949
20144
  }
18950
20145
  async function writeAll2(file, bytes) {
@@ -18968,7 +20163,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
18968
20163
  let timer;
18969
20164
  let failed4;
18970
20165
  const waiters = /* @__PURE__ */ new Set();
18971
- const quiet2 = () => !busy && pending === 0;
20166
+ const quiet4 = () => !busy && pending === 0;
18972
20167
  const wake = () => {
18973
20168
  for (const waiter of [...waiters]) waiter();
18974
20169
  };
@@ -18985,7 +20180,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
18985
20180
  if (failed4 === void 0) failed4 = err;
18986
20181
  }).finally(() => {
18987
20182
  pending -= 1;
18988
- if (quiet2()) wake();
20183
+ if (quiet4()) wake();
18989
20184
  });
18990
20185
  };
18991
20186
  const settling = (signal) => new Promise((resolve2, reject) => {
@@ -19003,7 +20198,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
19003
20198
  signal?.addEventListener("abort", stop, { once: true });
19004
20199
  });
19005
20200
  const settled = async () => {
19006
- while (!quiet2()) await settling();
20201
+ while (!quiet4()) await settling();
19007
20202
  };
19008
20203
  const changed = async (signal) => {
19009
20204
  const round = new AbortController();
@@ -19019,7 +20214,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
19019
20214
  };
19020
20215
  const turn = async (signal) => {
19021
20216
  for (let waited = false; ; waited = true) {
19022
- if (quiet2() && (pollFile(file).writable || !file.changed)) {
20217
+ if (quiet4() && (pollFile(file).writable || !file.changed)) {
19023
20218
  busy = true;
19024
20219
  return;
19025
20220
  }
@@ -19048,13 +20243,14 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
19048
20243
  ...bound2(file, "stat"),
19049
20244
  poll: () => {
19050
20245
  const state = pollFile(file);
19051
- return quiet2() ? state : { ...state, writable: false };
20246
+ return quiet4() ? state : { ...state, writable: false };
19052
20247
  },
19053
20248
  changed,
19054
20249
  ...file.tty ? { tty: file.tty } : {},
19055
20250
  identity: file.identity ?? file,
19056
20251
  ...file.clipboard ? { clipboard: file.clipboard } : {},
19057
20252
  ...file.embedder ? { embedder: file.embedder } : {},
20253
+ ...file.device ? { device: file.device } : {},
19058
20254
  ...file.write ? {
19059
20255
  write: async (bytes, signal) => {
19060
20256
  await turn(signal);
@@ -19066,7 +20262,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
19066
20262
  await send2(stream.push(taken), signal);
19067
20263
  } finally {
19068
20264
  busy = false;
19069
- if (quiet2()) wake();
20265
+ if (quiet4()) wake();
19070
20266
  }
19071
20267
  timer = setTimeout(release, idleMs);
19072
20268
  return taken.length;
@@ -19125,9 +20321,9 @@ function scrubStdio(fds, scrubber) {
19125
20321
  // src/kernel/net/sealed-scrubs.ts
19126
20322
  function addTo(tree2, host, secrets) {
19127
20323
  const entry = tree2.get(host) ?? { secrets: [], version: 0, scrubber: void 0 };
19128
- const fresh3 = secrets.filter((s) => !entry.secrets.some((e) => e.value === s.value));
19129
- if (fresh3.length === 0) return;
19130
- 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];
19131
20327
  entry.version += 1;
19132
20328
  entry.scrubber = void 0;
19133
20329
  tree2.set(host, entry);
@@ -19290,7 +20486,7 @@ var HEADER = new RegExp(`^header:${TOKEN3}$`);
19290
20486
  var METHOD = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
19291
20487
  var TARGET = /^[\x21-\x7e]+$/;
19292
20488
  var SESSION_EXIT = /^(tailscale|device:[A-Za-z0-9._:-]+)$/;
19293
- var HOST = /^[a-z0-9.:-]+$/;
20489
+ var HOST2 = /^[a-z0-9.:-]+$/;
19294
20490
  var UNSAFE = /[\r\n\0]/;
19295
20491
  var ANCHOR_ID = /^[A-Za-z0-9+/]{43}=$/;
19296
20492
  function verifiedByOk(f) {
@@ -19344,7 +20540,7 @@ function scramOk(f) {
19344
20540
  return texts.every((t) => typeof t === "string" && SCRAM_TEXT.test(t));
19345
20541
  }
19346
20542
  function hostOk(host) {
19347
- return typeof host === "string" && HOST.test(host) && boundHost(host) === host;
20543
+ return typeof host === "string" && HOST2.test(host) && boundHost(host) === host;
19348
20544
  }
19349
20545
  function sessionAskable(f, now) {
19350
20546
  if (!Object.hasOwn(POSITIONS, f.protocol) || typeof f.position !== "string") return false;
@@ -19434,20 +20630,20 @@ var ASK_FIELDS = /* @__PURE__ */ new Set([
19434
20630
  "caller",
19435
20631
  "requester"
19436
20632
  ]);
19437
- var encoder6 = new TextEncoder();
20633
+ var encoder7 = new TextEncoder();
19438
20634
  var decoder3 = new TextDecoder("utf-8", { fatal: true });
19439
20635
  function parseSealed(bytes) {
19440
20636
  try {
19441
20637
  if (bytes.length > SEALED_MAX_BYTES) throw new KernelError("EMSGSIZE");
19442
- let parsed2;
20638
+ let parsed3;
19443
20639
  try {
19444
- parsed2 = JSON.parse(decoder3.decode(bytes));
20640
+ parsed3 = JSON.parse(decoder3.decode(bytes));
19445
20641
  } catch {
19446
20642
  throw new KernelError("EINVAL");
19447
20643
  }
19448
- if (typeof parsed2 !== "object" || parsed2 === null || Array.isArray(parsed2))
20644
+ if (typeof parsed3 !== "object" || parsed3 === null || Array.isArray(parsed3))
19449
20645
  throw new KernelError("EINVAL");
19450
- const req = parsed2;
20646
+ const req = parsed3;
19451
20647
  if (req.v !== 1 || typeof req.op !== "string" || !SEALED_OPS.has(req.op))
19452
20648
  throw new KernelError("EINVAL");
19453
20649
  if (req.op === "materialize" && !materializable(req)) throw new KernelError("EINVAL");
@@ -19543,7 +20739,7 @@ var SealedChannel = class {
19543
20739
  await this.broker.tickets.record(req, reply, caller.pid);
19544
20740
  if (this.inflight !== flight) return;
19545
20741
  this.inflight = void 0;
19546
- if (reply) this.reply = encoder6.encode(serialized(reply));
20742
+ if (reply) this.reply = encoder7.encode(serialized(reply));
19547
20743
  });
19548
20744
  this.inflight = flight;
19549
20745
  return length2;
@@ -19819,7 +21015,7 @@ function shown(text) {
19819
21015
  const chars = [...safe];
19820
21016
  return `'${chars.length > SHOWN_MAX ? `${chars.slice(0, SHOWN_MAX - 1).join("")}\u2026` : safe}'`;
19821
21017
  }
19822
- function unescaped(double) {
21018
+ function unescaped2(double) {
19823
21019
  return double.replace(/\\(["\\$`])/g, "$1");
19824
21020
  }
19825
21021
  function splitWords(text) {
@@ -19835,7 +21031,7 @@ function splitWords(text) {
19835
21031
  word = void 0;
19836
21032
  continue;
19837
21033
  }
19838
- word = (word ?? "") + (single ?? (double !== void 0 ? unescaped(double) : escaped ?? plain3));
21034
+ word = (word ?? "") + (single ?? (double !== void 0 ? unescaped2(double) : escaped ?? plain3));
19839
21035
  }
19840
21036
  if (word !== void 0) words3.push(word);
19841
21037
  return words3;
@@ -19872,7 +21068,7 @@ function splitPatterns(text) {
19872
21068
  }
19873
21069
  word ??= [];
19874
21070
  if (plain3 !== void 0) addPlain(word, plain3);
19875
- else addPart(word, single ?? (double !== void 0 ? unescaped(double) : escaped));
21071
+ else addPart(word, single ?? (double !== void 0 ? unescaped2(double) : escaped));
19876
21072
  }
19877
21073
  if (word !== void 0) words3.push(word);
19878
21074
  return words3;
@@ -19923,13 +21119,13 @@ function commandMatches(cmnd, argv) {
19923
21119
  if (cmnd === "*") return true;
19924
21120
  const [path, ...args] = argv;
19925
21121
  if (path !== cmnd.path) return false;
19926
- const want = cmnd.args;
19927
- if (want === void 0) return true;
21122
+ const want2 = cmnd.args;
21123
+ if (want2 === void 0) return true;
19928
21124
  return globMatch(
19929
- want.length,
21125
+ want2.length,
19930
21126
  args.length,
19931
- (i) => isAny(want[i]),
19932
- (i, j) => wordMatch(want[i], args[j])
21127
+ (i) => isAny(want2[i]),
21128
+ (i, j) => wordMatch(want2[i], args[j])
19933
21129
  );
19934
21130
  }
19935
21131
  function commandText(cmnd) {
@@ -20226,11 +21422,11 @@ function parsePolicy(text, file) {
20226
21422
  const defaults2 = [];
20227
21423
  const errors = [];
20228
21424
  for (const [n, line] of text.split("\n").entries()) {
20229
- const parsed2 = parseLine(line, file);
20230
- if (parsed2 === null) continue;
20231
- if ("error" in parsed2) errors.push({ line: n + 1, message: parsed2.error });
20232
- else if ("defaults" in parsed2) defaults2.push(parsed2.defaults);
20233
- 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);
20234
21430
  }
20235
21431
  return { rules, defaults: defaults2, errors };
20236
21432
  }
@@ -20283,11 +21479,11 @@ var REPLAY_TAGS_MAX = 64;
20283
21479
  var REPLAY_ARGV_MAX = 4096;
20284
21480
  var REPLAY_BYTES_MAX = 1024 * 1024;
20285
21481
  var NOT_A_REQUEST = "sudo: that isn't a sudo request this kernel can run";
20286
- function strings(value2, max) {
21482
+ function strings2(value2, max) {
20287
21483
  return Array.isArray(value2) && value2.length <= max && value2.every((s) => typeof s === "string");
20288
21484
  }
20289
21485
  function words(value2) {
20290
- if (!strings(value2, REPLAY_ARGV_MAX) || value2.length === 0) return false;
21486
+ if (!strings2(value2, REPLAY_ARGV_MAX) || value2.length === 0) return false;
20291
21487
  const bytes = value2.reduce((n, s) => n + s.length + 1, 0);
20292
21488
  return bytes <= REPLAY_BYTES_MAX && !value2.some((s) => s.includes("\0"));
20293
21489
  }
@@ -20322,7 +21518,7 @@ function replayOf(value2) {
20322
21518
  const bound3 = filesOf(r.userWritable);
20323
21519
  const named = (s) => typeof s === "string" && s !== "";
20324
21520
  const absolute2 = (s) => typeof s === "string" && s.startsWith("/") && s.length <= PATH_MAX && !s.includes("\0");
20325
- 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;
20326
21522
  if (!valid) return failed2("EINVAL", NOT_A_REQUEST);
20327
21523
  const all = tags ?? (tag === void 0 ? [] : [tag]);
20328
21524
  return {
@@ -20414,28 +21610,28 @@ function recordedCommand(argv) {
20414
21610
  const [path = "", ...args] = argv;
20415
21611
  return commandText({ path, args: args.map((a) => [a]) });
20416
21612
  }
20417
- function flags(parsed2, arg, next3) {
21613
+ function flags(parsed3, arg, next3) {
20418
21614
  for (let j = 1; j < arg.length; j++) {
20419
21615
  const flag = arg[j];
20420
- if (flag === "n") parsed2.nonInteractive = true;
20421
- else if (flag === "l") parsed2.list = true;
20422
- else if (flag === "v") parsed2.validate = true;
20423
- else if (flag === "k") parsed2.reset = true;
20424
- 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;
20425
21621
  else if (flag !== "u") return `sudo: invalid option -- '${flag}'
20426
21622
  ${USAGE}`;
20427
21623
  else {
20428
21624
  const user = arg.slice(j + 1) || next3;
20429
21625
  if (!user) return `sudo: option requires an argument -- 'u'
20430
21626
  ${USAGE}`;
20431
- parsed2.user = user;
21627
+ parsed3.user = user;
20432
21628
  return arg.slice(j + 1) ? 0 : 1;
20433
21629
  }
20434
21630
  }
20435
21631
  return 0;
20436
21632
  }
20437
21633
  function parseArgs(args) {
20438
- const parsed2 = {
21634
+ const parsed3 = {
20439
21635
  list: false,
20440
21636
  nonInteractive: false,
20441
21637
  validate: false,
@@ -20452,19 +21648,19 @@ function parseArgs(args) {
20452
21648
  break;
20453
21649
  }
20454
21650
  if (!arg.startsWith("-") || arg === "-") break;
20455
- const used2 = flags(parsed2, arg, args[i + 1]);
21651
+ const used2 = flags(parsed3, arg, args[i + 1]);
20456
21652
  if (typeof used2 === "string") return used2;
20457
21653
  i += used2;
20458
21654
  }
20459
- parsed2.command = args.slice(i);
20460
- return usable(parsed2) ? parsed2 : USAGE;
21655
+ parsed3.command = args.slice(i);
21656
+ return usable(parsed3) ? parsed3 : USAGE;
20461
21657
  }
20462
- function usable(parsed2) {
20463
- const command = parsed2.command.length > 0;
20464
- const modes = [parsed2.list, parsed2.validate, parsed2.removeAll].filter(Boolean).length;
20465
- 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;
20466
21662
  if (modes === 1) return !command;
20467
- return command || parsed2.reset;
21663
+ return command || parsed3.reset;
20468
21664
  }
20469
21665
  function policyOf(settings) {
20470
21666
  return { type: settings.timestampType, minutes: settings.timestampTimeout };
@@ -20608,28 +21804,28 @@ var Sudo = class {
20608
21804
  };
20609
21805
  }
20610
21806
  async check(cred, call) {
20611
- const lines2 = [];
21807
+ const lines3 = [];
20612
21808
  let ok4 = true;
20613
21809
  const report = ({ path, problems, ok: read }) => {
20614
21810
  for (const problem of problems) {
20615
21811
  const where = problem.line === void 0 ? `${path}: sudo skips it:` : `${path}:${problem.line}:`;
20616
- lines2.push([2, `visudo: ${where} ${problem.message}`]);
21812
+ lines3.push([2, `visudo: ${where} ${problem.message}`]);
20617
21813
  }
20618
- if (read && problems.length === 0) lines2.push([1, `${path}: parsed OK`]);
21814
+ if (read && problems.length === 0) lines3.push([1, `${path}: parsed OK`]);
20619
21815
  if (problems.length > 0) ok4 = false;
20620
21816
  };
20621
21817
  const values = (defaults2) => {
20622
- for (const line of defaultsText(defaults2)) lines2.push([1, line]);
21818
+ for (const line of defaultsText(defaults2)) lines3.push([1, line]);
20623
21819
  };
20624
21820
  if (call.files !== void 0) {
20625
21821
  const defaults2 = [];
20626
21822
  for (const { name: name2, text } of call.files) {
20627
- const parsed2 = parsePolicy(text);
20628
- defaults2.push(...parsed2.defaults);
20629
- 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 });
20630
21826
  }
20631
21827
  if (defaults2.length > 0) values(defaults2);
20632
- return { lines: lines2, ok: ok4 };
21828
+ return { lines: lines3, ok: ok4 };
20633
21829
  }
20634
21830
  if (this.o.mode === "enforce" && cred.euid !== 0) {
20635
21831
  const hint = "check the policy as root (sudo visudo -c), or a file with visudo -c -f <file>";
@@ -20638,7 +21834,7 @@ var Sudo = class {
20638
21834
  const files = await this.policy();
20639
21835
  for (const file of files) report(file);
20640
21836
  values(files.flatMap((f) => f.defaults ?? []));
20641
- return { lines: lines2, ok: ok4 };
21837
+ return { lines: lines3, ok: ok4 };
20642
21838
  }
20643
21839
  async groupIds() {
20644
21840
  return new Map((await this.o.users.groups()).map((g) => [g.name, g.gid]));
@@ -20703,13 +21899,13 @@ var Sudo = class {
20703
21899
  return void 0;
20704
21900
  }
20705
21901
  async request(caller, call, signal) {
20706
- const parsed2 = parseArgs(call.args);
20707
- if (typeof parsed2 === "string") return failed2("EINVAL", parsed2);
21902
+ const parsed3 = parseArgs(call.args);
21903
+ if (typeof parsed3 === "string") return failed2("EINVAL", parsed3);
20708
21904
  if (this.o.mode !== "enforce") {
20709
21905
  const mode = this.o.mode;
20710
- 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
20711
21907
  ` };
20712
- return parsed2.command.length > 0 ? { run: parsed2.command } : DONE;
21908
+ return parsed3.command.length > 0 ? { run: parsed3.command } : DONE;
20713
21909
  }
20714
21910
  const { cred } = caller;
20715
21911
  const policy = await this.policyRules(caller);
@@ -20722,22 +21918,22 @@ var Sudo = class {
20722
21918
  };
20723
21919
  const applies = await this.applies(self, cred);
20724
21920
  const settings = resolveDefaults(policy.defaults, applies);
20725
- if (parsed2.removeAll) {
21921
+ if (parsed3.removeAll) {
20726
21922
  this.timestamps.remove(owner.uid);
20727
21923
  return DONE;
20728
21924
  }
20729
- if (parsed2.reset && !parsed2.validate && !parsed2.list && parsed2.command.length === 0) {
21925
+ if (parsed3.reset && !parsed3.validate && !parsed3.list && parsed3.command.length === 0) {
20730
21926
  this.timestamps.invalidate(owner, settings.timestampType);
20731
21927
  return DONE;
20732
21928
  }
20733
- const target = await this.lookup(parsed2.user);
20734
- 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}`);
20735
21931
  const root = cred.euid === 0;
20736
21932
  const rules = root ? [] : policy.rules;
20737
21933
  const mine = rules.filter((rule) => applies(rule.who));
20738
- if (parsed2.list) return await this.listed(self, root, mine, settings.matching);
20739
- const session = { caller, self, target, owner, policy: policyOf(settings), parsed: parsed2, signal };
20740
- 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);
20741
21937
  return this.command(session, root, mine, call);
20742
21938
  }
20743
21939
  async replay(value2) {
@@ -20796,9 +21992,9 @@ var Sudo = class {
20796
21992
  );
20797
21993
  }
20798
21994
  async command(s, root, mine, call) {
20799
- const { caller, self, target, parsed: parsed2 } = s;
21995
+ const { caller, self, target, parsed: parsed3 } = s;
20800
21996
  const { cred } = caller;
20801
- const [word = "", ...args] = parsed2.command;
21997
+ const [word = "", ...args] = parsed3.command;
20802
21998
  const cwd = await this.canonical(call.cwd);
20803
21999
  if (cwd === void 0 || !await this.reachable(cred, cwd)) {
20804
22000
  return failed2(
@@ -20813,7 +22009,7 @@ var Sudo = class {
20813
22009
  const matching = mine.filter(
20814
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)
20815
22011
  );
20816
- 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);
20817
22013
  if (root || matching.some((rule) => rule.nopasswd)) return grant();
20818
22014
  const quoted = argv.map(quoteWord).join(" ");
20819
22015
  if (matching.length === 0) {
@@ -20822,38 +22018,38 @@ var Sudo = class {
20822
22018
  `sudo: ${self.name} is not allowed to run ${quoted} as ${target.name}`
20823
22019
  );
20824
22020
  }
20825
- if (!parsed2.reset && this.timestamps.current(s.owner, s.policy, bound3)) {
22021
+ if (!parsed3.reset && this.timestamps.current(s.owner, s.policy, bound3)) {
20826
22022
  this.timestamps.stamp(s.owner, s.policy, bound3);
20827
22023
  this.logged(s, cwd, argv);
20828
22024
  return grant();
20829
22025
  }
20830
- if (parsed2.nonInteractive) return failed2("EPERM", "sudo: a password is required");
22026
+ if (parsed3.nonInteractive) return failed2("EPERM", "sudo: a password is required");
20831
22027
  return this.pendingFor(cred.ruid, async () => {
20832
- 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 });
20833
22029
  if ("error" in answer2) return answer2;
20834
22030
  if (await this.canonical(call.cwd) !== cwd || !await this.reachable(cred, cwd)) {
20835
22031
  return failed2("EACCES", `sudo: ${call.cwd} changed while the request waited`);
20836
22032
  }
20837
22033
  const now = await this.bound(path, cwd);
20838
22034
  if (!now) return failed2("ENOENT", `sudo: ${word}: command not found`);
20839
- const shown2 = recordable(cwd, [...parsed2.command, ...Object.keys(bound3)]);
22035
+ const shown2 = recordable(cwd, [...parsed3.command, ...Object.keys(bound3)]);
20840
22036
  const result = answer2.answer === "once" ? grant() : await this.always(self, target, { cwd, argv, bound: bound3 }, now, shown2, grant);
20841
22037
  if (!("error" in result)) this.stamped(s, answer2, bound3);
20842
22038
  return result;
20843
22039
  });
20844
22040
  }
20845
22041
  async validate(s, root, mine, dir) {
20846
- const { self, parsed: parsed2 } = s;
22042
+ const { self, parsed: parsed3 } = s;
20847
22043
  if (root) return DONE;
20848
22044
  if (mine.length === 0) {
20849
22045
  return failed2("EPERM", `Sorry, user ${self.name} may not run sudo on ${this.o.hostname}.`);
20850
22046
  }
20851
22047
  if (mine.every((rule) => rule.nopasswd)) return DONE;
20852
- if (!parsed2.reset && this.timestamps.current(s.owner, s.policy)) {
22048
+ if (!parsed3.reset && this.timestamps.current(s.owner, s.policy)) {
20853
22049
  this.timestamps.stamp(s.owner, s.policy);
20854
22050
  return DONE;
20855
22051
  }
20856
- if (parsed2.nonInteractive) return failed2("EPERM", "sudo: a password is required");
22052
+ if (parsed3.nonInteractive) return failed2("EPERM", "sudo: a password is required");
20857
22053
  const cwd = await this.canonical(dir) ?? normalizePath(dir);
20858
22054
  return this.pendingFor(s.caller.cred.ruid, async () => {
20859
22055
  const answer2 = await this.ask(s, { argv: [], path: "", cwd, bound: {}, validate: true });
@@ -20908,7 +22104,7 @@ var Sudo = class {
20908
22104
  }
20909
22105
  const shown2 = [];
20910
22106
  for (const rule of rules) shown2.push(described(rule, await this.writable(rule)));
20911
- const lines2 = shown2.join("\n");
22107
+ const lines3 = shown2.join("\n");
20912
22108
  const params = defaults2.flatMap((d) => d.params).join(", ");
20913
22109
  const matching = params === "" ? "" : `Matching Defaults entries for ${self.name} on ${host}:
20914
22110
  ${params}
@@ -20916,7 +22112,7 @@ var Sudo = class {
20916
22112
  `;
20917
22113
  return {
20918
22114
  list: `${matching}User ${self.name} may run the following commands on ${host}:
20919
- ${lines2}
22115
+ ${lines3}
20920
22116
  `
20921
22117
  };
20922
22118
  }
@@ -21333,7 +22529,7 @@ async function epollSyscall(req, proc) {
21333
22529
 
21334
22530
  // src/kernel/locks.ts
21335
22531
  var overlaps = (a, b) => a.start < b.end && b.start < a.end;
21336
- 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");
21337
22533
  function lockRange(start2, len) {
21338
22534
  if (!Number.isSafeInteger(start2) || !Number.isSafeInteger(len)) throw new KernelError("EINVAL");
21339
22535
  const from = len < 0 ? start2 + len : start2;
@@ -21419,8 +22615,8 @@ var LockTable = class {
21419
22615
  blocked = /* @__PURE__ */ new Map();
21420
22616
  wakers = /* @__PURE__ */ new Map();
21421
22617
  held = /* @__PURE__ */ new Map();
21422
- holder(key, owner, want) {
21423
- 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));
21424
22620
  }
21425
22621
  set(key, owner, kind, start2, end) {
21426
22622
  if (kind !== "unlock" && this.holder(key, owner, { key, kind, start: start2, end })) return false;
@@ -21439,10 +22635,10 @@ var LockTable = class {
21439
22635
  return true;
21440
22636
  }
21441
22637
  async wait(key, owner, kind, range2, blocking) {
21442
- const want = { key, kind, ...range2 };
22638
+ const want2 = { key, kind, ...range2 };
21443
22639
  let signal;
21444
22640
  for (; ; ) {
21445
- const holder = this.holder(key, owner, want);
22641
+ const holder = this.holder(key, owner, want2);
21446
22642
  if (!holder) {
21447
22643
  this.set(key, owner, kind, range2.start, range2.end);
21448
22644
  return;
@@ -21450,7 +22646,7 @@ var LockTable = class {
21450
22646
  if (this.waitsOn(holder.owner, owner)) throw new KernelError("EDEADLK");
21451
22647
  signal ??= blocking();
21452
22648
  if (signal.aborted) throw new KernelError("EINTR");
21453
- this.blocked.set(owner, want);
22649
+ this.blocked.set(owner, want2);
21454
22650
  try {
21455
22651
  await this.changed(key, signal);
21456
22652
  } finally {
@@ -21462,9 +22658,9 @@ var LockTable = class {
21462
22658
  const seen = /* @__PURE__ */ new Set();
21463
22659
  for (let at = from; at !== void 0 && !seen.has(at); ) {
21464
22660
  seen.add(at);
21465
- const want = this.blocked.get(at);
21466
- if (!want) return false;
21467
- 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);
21468
22664
  if (!holder) return false;
21469
22665
  if (holder.owner === target) return true;
21470
22666
  at = holder.owner;
@@ -21829,24 +23025,24 @@ function errno2(code, message3) {
21829
23025
  }
21830
23026
  function requestHead(method, url, headers, extra, first = []) {
21831
23027
  if (!TOKEN4.test(method)) throw errno2("EINVAL", `method ${method}`);
21832
- const lines2 = [`${method} ${url.pathname}${url.search} HTTP/1.1`, `host: ${url.host}`];
21833
- 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}`);
21834
23030
  for (const [name2, value2] of headers) {
21835
23031
  if (OWN_HEADERS.has(name2.toLowerCase())) continue;
21836
23032
  if (!TOKEN4.test(name2) || !SAFE_VALUE.test(value2)) throw errno2("EINVAL", `header ${name2}`);
21837
- lines2.push(`${name2}: ${value2}`);
23033
+ lines3.push(`${name2}: ${value2}`);
21838
23034
  }
21839
- for (const [name2, value2] of extra) lines2.push(`${name2}: ${value2}`);
21840
- 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
21841
23037
  \r
21842
23038
  `);
21843
23039
  }
21844
23040
  function parseHead(bytes) {
21845
- const [statusLine = "", ...lines2] = latin1(bytes).split(/\r?\n/);
23041
+ const [statusLine = "", ...lines3] = latin1(bytes).split(/\r?\n/);
21846
23042
  const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
21847
23043
  if (!match) throw errno2("EPROTO", `not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);
21848
23044
  const headers = [];
21849
- for (const line of lines2) {
23045
+ for (const line of lines3) {
21850
23046
  const colon = line.indexOf(":");
21851
23047
  if (colon > 0) headers.push([line.slice(0, colon).trim(), line.slice(colon + 1).trim()]);
21852
23048
  }
@@ -21936,8 +23132,8 @@ var KernelLoopback = class {
21936
23132
  );
21937
23133
  } catch (err) {
21938
23134
  socket.close();
21939
- const refused4 = err instanceof KernelError && err.code === "ECONNREFUSED";
21940
- 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;
21941
23137
  }
21942
23138
  signal.addEventListener("abort", () => socket.close(), { once: true });
21943
23139
  return socket;
@@ -22326,9 +23522,9 @@ function pem(der2) {
22326
23522
  for (let i = 0; i < der2.length; i += 32768) {
22327
23523
  b642 += String.fromCharCode(...der2.subarray(i, i + 32768));
22328
23524
  }
22329
- const lines2 = btoa(b642).match(/.{1,64}/g) ?? [];
23525
+ const lines3 = btoa(b642).match(/.{1,64}/g) ?? [];
22330
23526
  return `-----BEGIN CERTIFICATE-----
22331
- ${lines2.join("\n")}
23527
+ ${lines3.join("\n")}
22332
23528
  -----END CERTIFICATE-----
22333
23529
  `;
22334
23530
  }
@@ -23112,13 +24308,13 @@ var HOSTS_PATH = "/etc/hosts";
23112
24308
  function parseHosts(text) {
23113
24309
  const table = /* @__PURE__ */ new Map();
23114
24310
  for (const raw2 of text.split("\n")) {
23115
- const [address, ...names2] = raw2.replace(/#.*/, "").trim().split(/\s+/);
23116
- if (!address || names2.length === 0) continue;
23117
- 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;
23118
24314
  for (const name2 of names2) {
23119
24315
  const key = name2.toLowerCase().replace(/\.$/, "");
23120
24316
  const known = table.get(key) ?? [];
23121
- if (!known.includes(address)) known.push(address);
24317
+ if (!known.includes(address2)) known.push(address2);
23122
24318
  table.set(key, known);
23123
24319
  }
23124
24320
  }
@@ -23318,14 +24514,14 @@ var Resolver = class {
23318
24514
  if (hostsEntry(key, this.own) !== void 0) return true;
23319
24515
  return ((await this.hosts?.().catch(() => void 0))?.get(key)?.length ?? 0) > 0;
23320
24516
  }
23321
- async reverse(address) {
23322
- const v6 = ipv6Groups(address)?.join(":");
23323
- 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;
23324
24520
  const file = await this.hosts?.().catch(() => void 0);
23325
24521
  for (const table of [file, this.builtin]) {
23326
24522
  for (const [name2, addresses] of table ?? []) if (addresses.some(same3)) return name2;
23327
24523
  }
23328
- if (ipv4Bytes(address)?.[0] === 127) return "localhost";
24524
+ if (ipv4Bytes(address2)?.[0] === 127) return "localhost";
23329
24525
  return void 0;
23330
24526
  }
23331
24527
  async resolve(name2, family, blocking) {
@@ -23379,13 +24575,13 @@ var Resolver = class {
23379
24575
  if (addresses.length > 0) this.remember(id2, addresses, answer2.ttl);
23380
24576
  return addresses;
23381
24577
  }
23382
- usable(name2, address, family) {
23383
- const v4 = ipv4Bytes(address) !== void 0;
23384
- 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;
23385
24581
  if (v4 && family === 6) return false;
23386
- const kind = this.routes.classify(address);
24582
+ const kind = this.routes.classify(address2);
23387
24583
  if (kind !== "loopback" && kind !== "host" && kind !== "reserved") return true;
23388
- 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}`);
23389
24585
  return false;
23390
24586
  }
23391
24587
  remember(id2, addresses, ttl) {
@@ -23419,275 +24615,6 @@ function waitFor(lookup2, signal) {
23419
24615
  });
23420
24616
  }
23421
24617
 
23422
- // src/kernel/tty.ts
23423
- var ICRNL = 256;
23424
- var IUTF8 = 16384;
23425
- var OPOST = 1;
23426
- var ONLCR = 4;
23427
- var ISIG = 1;
23428
- var ICANON = 2;
23429
- var ECHO = 8;
23430
- var ECHOE = 16;
23431
- var ECHOCTL = 512;
23432
- var NOFLSH = 128;
23433
- var IEXTEN = 32768;
23434
- var VINTR = 0;
23435
- var VQUIT = 1;
23436
- var VERASE = 2;
23437
- var VKILL = 3;
23438
- var VEOF = 4;
23439
- var VSUSP = 10;
23440
- var VWERASE = 14;
23441
- function defaultTermios() {
23442
- const cCc = new Array(32).fill(0);
23443
- Object.assign(cCc, { [VINTR]: 3, [VQUIT]: 28, [VERASE]: 127, [VKILL]: 21 });
23444
- Object.assign(cCc, { [VEOF]: 4, 6: 1, 8: 17, 9: 19, [VSUSP]: 26 });
23445
- Object.assign(cCc, { 12: 18, 13: 15, [VWERASE]: 23, 15: 22 });
23446
- return {
23447
- c_iflag: ICRNL | 1024 | 8192 | IUTF8,
23448
- c_oflag: OPOST | ONLCR,
23449
- c_cflag: 15 | 48 | 128,
23450
- c_lflag: ISIG | ICANON | ECHO | ECHOE | 32 | ECHOCTL | 2048 | IEXTEN,
23451
- c_cc: cCc
23452
- };
23453
- }
23454
- var encoder7 = new TextEncoder();
23455
- function isContinuation(byte) {
23456
- return (byte & 192) === 128;
23457
- }
23458
- var KernelTty = class {
23459
- termios = defaultTermios();
23460
- cols = 80;
23461
- rows = 24;
23462
- readable = [];
23463
- line = [];
23464
- waiters = [];
23465
- hungUp = false;
23466
- name;
23467
- owner;
23468
- onRead;
23469
- readers = 0;
23470
- screen;
23471
- signal;
23472
- constructor(screen, signal) {
23473
- this.screen = screen;
23474
- this.signal = signal;
23475
- }
23476
- receive(bytes) {
23477
- for (const byte of bytes) this.key(byte);
23478
- this.wake();
23479
- }
23480
- resize(cols, rows) {
23481
- if (cols === this.cols && rows === this.rows) return;
23482
- this.setSize(cols, rows);
23483
- this.signal(SIG.WINCH);
23484
- }
23485
- hangup() {
23486
- this.hungUp = true;
23487
- this.wake();
23488
- }
23489
- signalHangup() {
23490
- this.signal(SIG.HUP);
23491
- }
23492
- setSize(cols, rows) {
23493
- this.cols = cols;
23494
- this.rows = rows;
23495
- }
23496
- tcgets() {
23497
- return { ...this.termios, c_cc: [...this.termios.c_cc] };
23498
- }
23499
- tcsets(termios, flush = false) {
23500
- const wasCanonical = this.canonical;
23501
- this.termios = { ...termios, c_cc: [...termios.c_cc] };
23502
- if (flush) {
23503
- this.line = [];
23504
- this.readable = [];
23505
- }
23506
- if (wasCanonical && !this.canonical && this.line.length > 0) {
23507
- this.readable.push(Uint8Array.from(this.line));
23508
- this.line = [];
23509
- this.wake();
23510
- }
23511
- }
23512
- winsize() {
23513
- return [this.rows, this.cols];
23514
- }
23515
- file() {
23516
- return new OpenFile({
23517
- read: (max, signal) => this.read(max, signal),
23518
- write: async (bytes) => {
23519
- if (this.hungUp) throw new KernelError("EIO");
23520
- this.output(bytes);
23521
- return bytes.length;
23522
- },
23523
- poll: () => ({
23524
- readable: this.readable.length > 0 || this.hungUp,
23525
- writable: true,
23526
- hangup: this.hungUp,
23527
- nread: this.readable.reduce((n, chunk2) => n + (chunk2?.length ?? 0), 0)
23528
- }),
23529
- changed: (signal) => this.changed(signal),
23530
- close: () => {
23531
- },
23532
- tty: this
23533
- });
23534
- }
23535
- get canonical() {
23536
- return (this.termios.c_lflag & ICANON) !== 0;
23537
- }
23538
- cc(slot2) {
23539
- return this.termios.c_cc[slot2] ?? 0;
23540
- }
23541
- key(input) {
23542
- const { c_iflag, c_lflag } = this.termios;
23543
- const byte = input === 13 && c_iflag & ICRNL ? 10 : input;
23544
- if (c_lflag & ISIG && this.signalKey(byte)) return;
23545
- if (!this.canonical) {
23546
- this.readable.push(Uint8Array.of(byte));
23547
- if (c_lflag & ECHO) this.echo(byte);
23548
- return;
23549
- }
23550
- this.edit(byte);
23551
- }
23552
- signalKey(byte) {
23553
- const sig = byte === this.cc(VINTR) ? SIG.INT : byte === this.cc(VQUIT) ? SIG.QUIT : byte === this.cc(VSUSP) ? SIG.TSTP : 0;
23554
- if (!sig || byte === 0) return false;
23555
- if (this.termios.c_lflag & ECHO) this.echo(byte);
23556
- if (!(this.termios.c_lflag & NOFLSH)) {
23557
- this.line = [];
23558
- this.readable = [];
23559
- }
23560
- this.signal(sig);
23561
- return true;
23562
- }
23563
- edit(byte) {
23564
- const echo = (this.termios.c_lflag & ECHO) !== 0;
23565
- if (byte === this.cc(VERASE) || byte === 8) {
23566
- this.rubOut(this.lastCharLength());
23567
- } else if (byte === this.cc(VKILL)) {
23568
- this.rubOut(this.line.length);
23569
- } else if (byte === this.cc(VWERASE)) {
23570
- this.rubOut(this.lastWordLength());
23571
- } else if (byte === this.cc(VEOF)) {
23572
- this.readable.push(this.line.length > 0 ? Uint8Array.from(this.line) : null);
23573
- this.line = [];
23574
- } else if (byte === 10) {
23575
- this.line.push(byte);
23576
- this.readable.push(Uint8Array.from(this.line));
23577
- this.line = [];
23578
- if (echo) this.output(encoder7.encode("\n"));
23579
- } else {
23580
- this.line.push(byte);
23581
- if (echo) this.echo(byte);
23582
- }
23583
- }
23584
- lastWordLength() {
23585
- let end = this.line.length;
23586
- while (end > 0 && this.line[end - 1] === 32) end--;
23587
- while (end > 0 && this.line[end - 1] !== 32) end--;
23588
- return this.line.length - end;
23589
- }
23590
- lastCharLength() {
23591
- const { line } = this;
23592
- let n = Math.min(1, line.length);
23593
- if (this.termios.c_iflag & IUTF8) {
23594
- while (n < line.length && isContinuation(line[line.length - n] ?? 0)) n++;
23595
- }
23596
- return n;
23597
- }
23598
- rubOut(n) {
23599
- const dropped2 = this.line.splice(this.line.length - n, n);
23600
- const utf83 = (this.termios.c_iflag & IUTF8) !== 0;
23601
- const cells = utf83 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
23602
- if (cells > 0 && this.termios.c_lflag & ECHO) {
23603
- this.output(encoder7.encode("\b \b".repeat(cells)), false);
23604
- }
23605
- }
23606
- echo(byte) {
23607
- if (byte < 32 && byte !== 10 && byte !== 9 && this.termios.c_lflag & ECHOCTL) {
23608
- this.output(Uint8Array.of(94, byte + 64), false);
23609
- return;
23610
- }
23611
- this.output(Uint8Array.of(byte));
23612
- }
23613
- output(bytes, translate2 = true) {
23614
- const { c_oflag } = this.termios;
23615
- if (!translate2 || !(c_oflag & OPOST && c_oflag & ONLCR) || !bytes.includes(10)) {
23616
- this.screen.write(bytes);
23617
- return;
23618
- }
23619
- const out = [];
23620
- for (const byte of bytes) {
23621
- if (byte === 10) out.push(13);
23622
- out.push(byte);
23623
- }
23624
- this.screen.write(Uint8Array.from(out));
23625
- }
23626
- get reading() {
23627
- return this.readers > 0;
23628
- }
23629
- async read(max, signal) {
23630
- this.onRead?.();
23631
- this.readers++;
23632
- try {
23633
- return await this.take(max, signal);
23634
- } finally {
23635
- this.readers--;
23636
- }
23637
- }
23638
- async take(max, signal) {
23639
- while (this.readable.length === 0) {
23640
- if (this.hungUp) return new Uint8Array(0);
23641
- await this.changed(signal);
23642
- }
23643
- const head = this.readable[0];
23644
- if (head === null) {
23645
- this.readable.shift();
23646
- return new Uint8Array(0);
23647
- }
23648
- if (head === void 0) return new Uint8Array(0);
23649
- const chunks = [];
23650
- let n = 0;
23651
- while (this.readable.length > 0 && n < max) {
23652
- const next3 = this.readable[0];
23653
- if (next3 === null || next3 === void 0) break;
23654
- const take = next3.subarray(0, max - n);
23655
- chunks.push(take);
23656
- n += take.length;
23657
- if (take.length < next3.length) this.readable[0] = next3.subarray(take.length);
23658
- else this.readable.shift();
23659
- if (this.canonical) break;
23660
- }
23661
- const out = new Uint8Array(n);
23662
- let at = 0;
23663
- for (const chunk2 of chunks) {
23664
- out.set(chunk2, at);
23665
- at += chunk2.length;
23666
- }
23667
- return out;
23668
- }
23669
- changed(signal) {
23670
- if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
23671
- return new Promise((resolve2, reject) => {
23672
- const onAbort = () => {
23673
- this.waiters = this.waiters.filter((w) => w !== waiter);
23674
- reject(new KernelError("EINTR"));
23675
- };
23676
- const waiter = () => {
23677
- signal?.removeEventListener("abort", onAbort);
23678
- resolve2();
23679
- };
23680
- this.waiters.push(waiter);
23681
- signal?.addEventListener("abort", onAbort, { once: true });
23682
- });
23683
- }
23684
- wake() {
23685
- const waiters = this.waiters;
23686
- this.waiters = [];
23687
- for (const resolve2 of waiters) resolve2();
23688
- }
23689
- };
23690
-
23691
24618
  // src/kernel/pty.ts
23692
24619
  var PtyPair = class {
23693
24620
  slave;
@@ -23880,6 +24807,7 @@ function ptySyscall(req, ctx) {
23880
24807
  return done4;
23881
24808
  case "pty-ctty": {
23882
24809
  const tty = terminalOf(ctx, req.fd);
24810
+ if (ctx.fds.get(req.fd).file.device) throw new KernelError("EPERM");
23883
24811
  const leader = ctx.jobs?.getsid(ctx.pid, 0) === ctx.pid;
23884
24812
  if (leader && ctx.jobs?.controllingTerminal(ctx.pid) === tty) return done4;
23885
24813
  if (!ctx.jobs?.acquireTerminal(ctx.pid, tty)) throw new KernelError("EPERM");
@@ -24661,10 +25589,10 @@ function basicOf(value2) {
24661
25589
  function placeholdersIn(text) {
24662
25590
  return [...new Set(text.match(ANY_PLACEHOLDER) ?? [])];
24663
25591
  }
24664
- function scan(lines2) {
25592
+ function scan(lines3) {
24665
25593
  const found = [];
24666
- for (let i = 1; i < lines2.length; i++) {
24667
- const m = HEADER_LINE.exec(lines2[i]);
25594
+ for (let i = 1; i < lines3.length; i++) {
25595
+ const m = HEADER_LINE.exec(lines3[i]);
24668
25596
  if (!m) return void 0;
24669
25597
  const value2 = m[2];
24670
25598
  const basic = basicOf(value2);
@@ -24687,10 +25615,10 @@ function usable2(values) {
24687
25615
  }
24688
25616
  async function substituteHead(head, ask) {
24689
25617
  if (!head.endsWith("\r\n\r\n")) return void 0;
24690
- const lines2 = head.slice(0, -4).split("\r\n");
24691
- if (lines2.some((l) => l.includes("\n") || l.includes("\r"))) return void 0;
24692
- const request3 = REQUEST_LINE2.exec(lines2[0]);
24693
- 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;
24694
25622
  if (!request3 || !found || found.length !== 1) return void 0;
24695
25623
  const at = found[0];
24696
25624
  if (at.placeholders.length > SESSION_PLACEHOLDERS) return void 0;
@@ -24702,8 +25630,8 @@ async function substituteHead(head, ask) {
24702
25630
  );
24703
25631
  if (Object.keys(values).length === 0) return void 0;
24704
25632
  const value2 = at.basic ? `${at.basic.scheme}${btoa(replaced(at.basic.decoded, values))}` : replaced(at.value, values);
24705
- const out = [...lines2];
24706
- out[at.line] = `${lines2[at.line].split(":")[0]}: ${value2}`;
25633
+ const out = [...lines3];
25634
+ out[at.line] = `${lines3[at.line].split(":")[0]}: ${value2}`;
24707
25635
  return { head: `${out.join("\r\n")}\r
24708
25636
  \r
24709
25637
  `, values };
@@ -25026,9 +25954,9 @@ function serverHeader(header4, server) {
25026
25954
  return server.plus && server.binding ? END_POINT : "n,,";
25027
25955
  }
25028
25956
  function rewritten(first, header4) {
25029
- const parsed2 = clientFirstOf(first.data);
25030
- if (!parsed2 || parsed2.header === header4) return first;
25031
- 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}` };
25032
25960
  }
25033
25961
  var PgClient = class {
25034
25962
  clientFirst;
@@ -25048,10 +25976,10 @@ var PgClient = class {
25048
25976
  }
25049
25977
  if (server.sasl && this.clientFirst === void 0) {
25050
25978
  const first = initial(message3);
25051
- const parsed2 = first && clientFirstOf(first.data);
25052
- if (!first || !parsed2) return { bytes: message3, done: true };
25979
+ const parsed3 = first && clientFirstOf(first.data);
25980
+ if (!first || !parsed3) return { bytes: message3, done: true };
25053
25981
  this.clientFirst = first.data;
25054
- this.header = serverHeader(parsed2.header, server);
25982
+ this.header = serverHeader(parsed3.header, server);
25055
25983
  const sent = rewritten(first, this.header);
25056
25984
  if (sent === first) return { bytes: message3, done: false };
25057
25985
  return {
@@ -25216,7 +26144,7 @@ function headEnd2(bytes) {
25216
26144
  }
25217
26145
  return -1;
25218
26146
  }
25219
- function modeOf2(start2) {
26147
+ function modeOf3(start2) {
25220
26148
  const sniffs = [
25221
26149
  ["http1", sniffHttp1(start2)],
25222
26150
  ["smtp", sniffSmtp(start2)],
@@ -25325,7 +26253,7 @@ var SessionWriter = class {
25325
26253
  joined5.set(bytes, this.buffer.length);
25326
26254
  this.buffer.fill(0);
25327
26255
  this.buffer = joined5;
25328
- 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";
25329
26257
  if (this.mode === "pg" && !this.sawPg) {
25330
26258
  this.sawPg = true;
25331
26259
  if (!this.s.verified) void this.proof("pg");
@@ -25812,12 +26740,12 @@ function secure(prepared, seal) {
25812
26740
  prepared.own?.release();
25813
26741
  }
25814
26742
  };
25815
- const abandon = () => {
26743
+ const abandon2 = () => {
25816
26744
  if (ran) return;
25817
26745
  prepared.options.session.free();
25818
26746
  prepared.own?.release();
25819
26747
  };
25820
- return { make, abandon };
26748
+ return { make, abandon: abandon2 };
25821
26749
  }
25822
26750
  function supportOf(net) {
25823
26751
  const support = net.tlsClient;
@@ -25834,19 +26762,19 @@ async function uplinkAddress(proc, host) {
25834
26762
  async function connectSecure(proc, socket, host, port, req) {
25835
26763
  const servername = serverName(req.servername || host);
25836
26764
  if (isLoopbackHost(servername) || isLoopbackHost(host)) throw new KernelError("ENOTSUP");
25837
- const address = await uplinkAddress(proc, host);
25838
- const route2 = proc.net.uplinkFor(address);
26765
+ const address2 = await uplinkAddress(proc, host);
26766
+ const route2 = proc.net.uplinkFor(address2);
25839
26767
  if (!route2)
25840
26768
  throw new KernelError("ENOTSUP", "TLS in the kernel needs a destination the uplink routes");
25841
26769
  const support = supportOf(proc.net);
25842
26770
  let info;
25843
- const { make, abandon } = secure(await prepare2(support, req, servername), { proc, port });
26771
+ const { make, abandon: abandon2 } = secure(await prepare2(support, req, servername), { proc, port });
25844
26772
  try {
25845
- await socket.connectSecure({ family: "inet", host: address, port }, route2, async (conn) => {
26773
+ await socket.connectSecure({ family: "inet", host: address2, port }, route2, async (conn) => {
25846
26774
  info = await conn.upgrade(make);
25847
26775
  });
25848
26776
  } finally {
25849
- abandon();
26777
+ abandon2();
25850
26778
  }
25851
26779
  return sessionOf(info, servername);
25852
26780
  }
@@ -25879,14 +26807,14 @@ async function upgradeSecure(proc, socket, req) {
25879
26807
  const host = req.servername || (peer?.family === "inet" ? peer.host : "");
25880
26808
  const servername = serverName(host);
25881
26809
  const port = peer?.family === "inet" ? peer.port : 0;
25882
- const { make, abandon } = secure(await prepare2(supportOf(proc.net), req, servername), {
26810
+ const { make, abandon: abandon2 } = secure(await prepare2(supportOf(proc.net), req, servername), {
25883
26811
  proc,
25884
26812
  port
25885
26813
  });
25886
26814
  try {
25887
26815
  return sessionOf(await socket.upgradeSecure(make), servername);
25888
26816
  } finally {
25889
- abandon();
26817
+ abandon2();
25890
26818
  }
25891
26819
  }
25892
26820
 
@@ -27810,6 +28738,10 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
27810
28738
  "fd-open-clipboard",
27811
28739
  "fd-clipboard-abort",
27812
28740
  "fd-open-embedder",
28741
+ "fd-dev-nodes",
28742
+ "fd-open-dev",
28743
+ "fd-dev-ioctl",
28744
+ "fd-dev-meta",
27813
28745
  "tty-get",
27814
28746
  "tty-set",
27815
28747
  "tty-winsz",
@@ -28186,7 +29118,7 @@ var WasmProcess = class {
28186
29118
  async dispatch(req, cancelled) {
28187
29119
  try {
28188
29120
  if (isFdSyscall(req)) return await this.fdSyscall(req, cancelled);
28189
- if (isTtySyscall(req)) return this.ttySyscall(req);
29121
+ if (isTtySyscall(req)) return await this.ttySyscall(req, cancelled);
28190
29122
  if (isJobSyscall(req)) return this.jobSyscall(req);
28191
29123
  if (isSocketSyscall(req)) return await this.socketSyscall(req, cancelled);
28192
29124
  if (isSemSyscall(req)) return await this.semSyscall(req, cancelled);
@@ -28250,7 +29182,7 @@ var WasmProcess = class {
28250
29182
  return owner;
28251
29183
  }
28252
29184
  pathKeys = /* @__PURE__ */ new Map();
28253
- async lockPath(target, want) {
29185
+ async lockPath(target, want2) {
28254
29186
  if (target.fd !== void 0) {
28255
29187
  const st = await this.fds.get(target.fd).file.stat?.();
28256
29188
  if (!st) throw new KernelError("EBADF");
@@ -28260,9 +29192,9 @@ var WasmProcess = class {
28260
29192
  if (!target.path.startsWith("/")) throw new KernelError("EINVAL");
28261
29193
  const path = target.path;
28262
29194
  const g = this.guarded();
28263
- const checked = `${want}:${path}`;
28264
- if (g && want !== 0 && !this.lockChecked.has(checked)) {
28265
- 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);
28266
29198
  this.lockChecked.add(checked);
28267
29199
  }
28268
29200
  return path;
@@ -28275,8 +29207,8 @@ var WasmProcess = class {
28275
29207
  const known = file ? fileKeys.get(file) : this.pathKeys.get(target.path ?? "");
28276
29208
  if (kind === "close" && !file && target.path !== void 0) this.pathKeys.delete(target.path);
28277
29209
  if (access2 && !access2.stat) return objectKey(access2);
28278
- const want = kind === "read" ? R_OK : kind === "write" ? W_OK : 0;
28279
- 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);
28280
29212
  if (known !== void 0) return known;
28281
29213
  const st = await this.options.fs.stat?.(path).catch((err) => {
28282
29214
  if (!file) throw err;
@@ -28362,7 +29294,7 @@ var WasmProcess = class {
28362
29294
  const file = this.fds.get(req.fd).file;
28363
29295
  const read = req.peek ? file.peek : file.read;
28364
29296
  if (!read) throw new KernelError(req.peek && file.read ? "EOPNOTSUPP" : "EBADF");
28365
- if (file.tty) this.checkForeground(file.tty);
29297
+ if (file.tty && !file.device) this.checkForeground(file.tty);
28366
29298
  if (file.clipboard) return this.readClipboard(file.clipboard, req, cancelled);
28367
29299
  if (file.embedder) return this.readEmbedder(file, req, cancelled);
28368
29300
  if (file instanceof EventFd && req.max < EventFd.SIZE) throw new KernelError("EINVAL");
@@ -28459,6 +29391,46 @@ var WasmProcess = class {
28459
29391
  throw err;
28460
29392
  });
28461
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
+ }
28462
29434
  async openEmbedder(flags2) {
28463
29435
  const broker = this.options.embedder;
28464
29436
  if (!broker) throw new KernelError("ENOENT");
@@ -28546,11 +29518,11 @@ var WasmProcess = class {
28546
29518
  } else if (req.create) {
28547
29519
  await nodes.onEntry(path, async () => {
28548
29520
  const marks = this.options.owners || plain3 !== void 0;
28549
- const fresh3 = marks && !await present3(fs, path, true);
29521
+ const fresh4 = marks && !await present3(fs, path, true);
28550
29522
  this.fds.installAt(fd, this.openVfsFile(open2, await nodes.identify(path), fs));
28551
29523
  await this.fds.get(fd).file.stat?.();
28552
- if (fresh3 && plain3 !== void 0) await fs.chmod?.(path, plain3);
28553
- else if (fresh3) {
29524
+ if (fresh4 && plain3 !== void 0) await fs.chmod?.(path, plain3);
29525
+ else if (fresh4) {
28554
29526
  await fs.chown?.(path, { ...this.newOwner(req.mode), kind: "new" });
28555
29527
  }
28556
29528
  });
@@ -28649,8 +29621,8 @@ var WasmProcess = class {
28649
29621
  if (req.exclusive) return inParent(g, "open", req.path);
28650
29622
  if (req.created && guard2.created?.take(req.path)) return;
28651
29623
  const access2 = req.flags & WRITE_ACCESS;
28652
- const want = (access2 === 1 ? 0 : R_OK) | (access2 !== 0 || req.truncate ? W_OK : 0);
28653
- 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);
28654
29626
  }
28655
29627
  openTty(name2) {
28656
29628
  const tty = name2 === void 0 ? this.controllingTerminal() : this.options.jobs?.terminalNamed(name2);
@@ -28714,7 +29686,7 @@ var WasmProcess = class {
28714
29686
  }
28715
29687
  terminalByName(name2) {
28716
29688
  const n = ptsNumber(name2);
28717
- 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;
28718
29690
  if (!tty) throw new KernelError("ENOENT");
28719
29691
  return tty;
28720
29692
  }
@@ -28836,6 +29808,16 @@ var WasmProcess = class {
28836
29808
  return { ok: true, kind: "json", json: await this.openClipboard(req.flags) };
28837
29809
  case "fd-open-embedder":
28838
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
+ }
28839
29821
  case "fd-clipboard-abort": {
28840
29822
  const file = this.fds.get(req.fd);
28841
29823
  file.file.clipboard?.abort();
@@ -28880,6 +29862,11 @@ var WasmProcess = class {
28880
29862
  return { ok: true, kind: "void" };
28881
29863
  }
28882
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
+ }
28883
29870
  if (!file.pread || !file.pwrite || !file.resize || !file.stat) throw new KernelError("ESPIPE");
28884
29871
  switch (req.op) {
28885
29872
  case "fd-pread":
@@ -29049,14 +30036,19 @@ var WasmProcess = class {
29049
30036
  ...file.fifo !== void 0 ? { fifo: file.fifo } : {}
29050
30037
  };
29051
30038
  }
29052
- ttySyscall(req) {
30039
+ async ttySyscall(req, cancelled) {
29053
30040
  const tty = this.tty(req.fd);
29054
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");
29055
30044
  switch (req.op) {
29056
30045
  case "tty-get":
29057
30046
  return { ok: true, kind: "json", json: tty.tcgets() };
29058
30047
  case "tty-set":
29059
- 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);
29060
30052
  return { ok: true, kind: "void" };
29061
30053
  case "tty-winsz": {
29062
30054
  const size = tty.winsize();
@@ -29937,6 +30929,7 @@ function processOptions(opts, header4, hooks, created4) {
29937
30929
  ...opts.outputFilter ? { outputFilter: opts.outputFilter } : {},
29938
30930
  ...opts.clipboard ? { clipboard: opts.clipboard, ...clipboardName(opts) } : {},
29939
30931
  ...embedderOptions(opts),
30932
+ devices: opts.devices,
29940
30933
  ...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
29941
30934
  ...opts.source ? { source: opts.source } : {},
29942
30935
  noLinks: opts.noLinks,
@@ -30688,14 +31681,14 @@ var LockService = class {
30688
31681
  );
30689
31682
  });
30690
31683
  }
30691
- async holder(key, owner, want) {
31684
+ async holder(key, owner, want2) {
30692
31685
  return await this.ask({
30693
31686
  op: "holder",
30694
31687
  key,
30695
31688
  owner,
30696
- kind: want.kind,
30697
- start: want.start,
30698
- end: want.end
31689
+ kind: want2.kind,
31690
+ start: want2.start,
31691
+ end: want2.end
30699
31692
  }) ?? void 0;
30700
31693
  }
30701
31694
  async set(key, owner, kind, start2, end) {
@@ -30974,9 +31967,9 @@ function local(name2) {
30974
31967
  function literal2(name2) {
30975
31968
  return ipv4Bytes(name2) !== void 0 || ipv6Groups(name2) !== void 0;
30976
31969
  }
30977
- function addressData(address, type) {
30978
- if (type === TYPE_A) return ipv4Bytes(address);
30979
- 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;
30980
31973
  return groups?.flatMap((g) => [g >> 8, g & 255]);
30981
31974
  }
30982
31975
  var DnsStub = class {
@@ -30989,10 +31982,10 @@ var DnsStub = class {
30989
31982
  this.timeoutMs = options.timeoutMs ?? DNS_FORWARD_TIMEOUT_MS;
30990
31983
  }
30991
31984
  async answer(query, udp) {
30992
- const parsed2 = parseQuery(query);
30993
- if (!parsed2) return void 0;
30994
- if (!parsed2.ok) return errorReply2(parsed2.id, parsed2.flags, parsed2.rcode);
30995
- 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;
30996
31989
  const reply = await this.reply(query, q).catch(
30997
31990
  () => answerReply(query, q, SERVFAIL, [], false)
30998
31991
  );
@@ -31001,8 +31994,8 @@ var DnsStub = class {
31001
31994
  async reply(query, q) {
31002
31995
  if (q.klass !== CLASS_IN || literal2(q.name)) return this.forward(query, q);
31003
31996
  if (q.type === TYPE_PTR) {
31004
- const address = reverseAddress(q.name);
31005
- 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;
31006
31999
  if (name2 !== void 0)
31007
32000
  return answerReply(query, q, NOERROR, [record2(TYPE_PTR, encodeName(name2))], true);
31008
32001
  return this.forward(query, q);
@@ -31032,8 +32025,8 @@ var DnsStub = class {
31032
32025
  known(name2) {
31033
32026
  return this.resolver.known(name2).catch(() => false);
31034
32027
  }
31035
- reverse(address) {
31036
- return this.resolver.reverse(address).catch(() => void 0);
32028
+ reverse(address2) {
32029
+ return this.resolver.reverse(address2).catch(() => void 0);
31037
32030
  }
31038
32031
  async forward(query, q) {
31039
32032
  if (!this.forwarder) return answerReply(query, q, SERVFAIL, [], false);
@@ -31212,7 +32205,7 @@ function honors(traits, redirect) {
31212
32205
  const raw2 = traits.manualRedirects === true && (hops === void 0 || hops === false);
31213
32206
  return redirect === "manual" && raw2;
31214
32207
  }
31215
- function refused3(message3) {
32208
+ function refused4(message3) {
31216
32209
  return new TransportError(message3, 502, void 0, "transport");
31217
32210
  }
31218
32211
  var RemoteTransport = class {
@@ -31303,7 +32296,7 @@ var RemoteTransport = class {
31303
32296
  const { signal, redirect } = request3;
31304
32297
  signal.throwIfAborted();
31305
32298
  if (unfollowed2(redirect) && !honors(this.traits, redirect)) {
31306
- throw refused3(`this transport cannot honor redirect: ${redirect}`);
32299
+ throw refused4(`this transport cannot honor redirect: ${redirect}`);
31307
32300
  }
31308
32301
  let finished = false;
31309
32302
  const abort = () => {
@@ -31354,7 +32347,7 @@ var RemoteTransport = class {
31354
32347
  };
31355
32348
  if (unfollowed2(redirect) && (redirected2 || redirects?.length)) {
31356
32349
  await cancel();
31357
- throw refused3(`this transport followed a redirect despite redirect: ${redirect}`);
32350
+ throw refused4(`this transport followed a redirect despite redirect: ${redirect}`);
31358
32351
  }
31359
32352
  return {
31360
32353
  ...url !== void 0 ? { url } : {},
@@ -31844,6 +32837,7 @@ var SETUP_OPS = /* @__PURE__ */ new Set([
31844
32837
  "proc-list",
31845
32838
  "proc-identity",
31846
32839
  "fd-info",
32840
+ "fd-dev-nodes",
31847
32841
  "sig-mask",
31848
32842
  "sig-inherited",
31849
32843
  "proc-setpgid",
@@ -31940,8 +32934,8 @@ var WasmMemories = class {
31940
32934
  const learned = this.learned;
31941
32935
  if (!learned) return this.base;
31942
32936
  const steps = Math.floor((this.now() - learned.at) / DECAY_MS);
31943
- const raised = learned.ceiling + steps * DECAY_STEP;
31944
- 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;
31945
32939
  this.learned = void 0;
31946
32940
  return this.base;
31947
32941
  }
@@ -32084,9 +33078,9 @@ function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
32084
33078
  let open2 = false;
32085
33079
  const reply = (message3, transfer = []) => port.postMessage(message3, transfer);
32086
33080
  async function hello(message3) {
32087
- const refused4 = driverVersionError(message3.protocol);
32088
- if (refused4) {
32089
- 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" } });
32090
33084
  return;
32091
33085
  }
32092
33086
  try {
@@ -32392,7 +33386,7 @@ function unescape(field4) {
32392
33386
  );
32393
33387
  }
32394
33388
  function parseFstab(text) {
32395
- const lines2 = [];
33389
+ const lines3 = [];
32396
33390
  text.split("\n").forEach((raw2, index) => {
32397
33391
  const content = raw2.replace(/#.*$/, "").trim();
32398
33392
  if (!content) return;
@@ -32401,12 +33395,12 @@ function parseFstab(text) {
32401
33395
  if (options.split(",").includes("noauto")) return;
32402
33396
  const spec = { source, target, type, options: {} };
32403
33397
  try {
32404
- lines2.push({ line: index + 1, spec: { ...spec, options: parseMountOptions(options) } });
33398
+ lines3.push({ line: index + 1, spec: { ...spec, options: parseMountOptions(options) } });
32405
33399
  } catch (err) {
32406
- lines2.push({ line: index + 1, spec, invalid: err });
33400
+ lines3.push({ line: index + 1, spec, invalid: err });
32407
33401
  }
32408
33402
  });
32409
- return lines2;
33403
+ return lines3;
32410
33404
  }
32411
33405
  function codeOf6(err) {
32412
33406
  const code = err?.code;
@@ -32424,20 +33418,20 @@ function wait(ms, signal) {
32424
33418
  signal.addEventListener("abort", done5, { once: true });
32425
33419
  });
32426
33420
  }
32427
- async function mountFstab(lines2, mount, signal, retries = FSTAB_RETRIES, discard = () => {
33421
+ async function mountFstab(lines3, mount, signal, retries = FSTAB_RETRIES, discard = () => {
32428
33422
  }, settled = () => {
32429
33423
  }) {
32430
33424
  return Promise.all(
32431
- lines2.map(async (each) => {
33425
+ lines3.map(async (each) => {
32432
33426
  const result = await mountLine(each, mount, signal, retries, discard);
32433
33427
  settled(each, result);
32434
33428
  return result;
32435
33429
  })
32436
33430
  );
32437
33431
  }
32438
- async function mountLine({ line, spec, invalid }, mount, signal, retries, discard) {
32439
- let failed4 = invalid;
32440
- 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++) {
32441
33435
  try {
32442
33436
  const entry = await mount(spec);
32443
33437
  if (!signal.aborted) return { line, target: spec.target, entry };
@@ -32605,14 +33599,14 @@ var RESTORE_AGAIN_MS = 3e4;
32605
33599
  function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
32606
33600
  const handles = /* @__PURE__ */ new Map();
32607
33601
  let nextFh = 0;
32608
- const fresh3 = (remote) => {
33602
+ const fresh4 = (remote) => {
32609
33603
  if (remote.client.token === remote.token && !remote.client.gone) return remote;
32610
33604
  throw fsError2("ESTALE", "the folder was granted again, so this open file is gone");
32611
33605
  };
32612
33606
  const live2 = (fh) => {
32613
33607
  const remote = handles.get(fh);
32614
33608
  if (!remote) throw fsError2("EBADF", `handle ${fh}`);
32615
- return fresh3(remote);
33609
+ return fresh4(remote);
32616
33610
  };
32617
33611
  const call = (op, body = {}) => current().call(op, body);
32618
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);
@@ -32694,7 +33688,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
32694
33688
  if (!remote) throw fsError2("EBADF", `handle ${fh}`);
32695
33689
  handles.delete(fh);
32696
33690
  try {
32697
- await fresh3(remote).client.call("release", { fh: remote.fh }, true);
33691
+ await fresh4(remote).client.call("release", { fh: remote.fh }, true);
32698
33692
  } finally {
32699
33693
  if (remote.restore) await settle3(current(), remote.restore);
32700
33694
  }
@@ -32744,7 +33738,7 @@ async function resize(h, path, size) {
32744
33738
  await h.release(fh);
32745
33739
  }
32746
33740
  }
32747
- function sleep(ms, signal) {
33741
+ function sleep2(ms, signal) {
32748
33742
  return new Promise((resolve2) => {
32749
33743
  const done5 = () => {
32750
33744
  clearTimeout(timer);
@@ -32772,22 +33766,22 @@ function keepWatching(loop, signal) {
32772
33766
  const stop = () => session2.abort();
32773
33767
  signal.addEventListener("abort", stop);
32774
33768
  let timer;
32775
- const quiet2 = () => {
33769
+ const quiet4 = () => {
32776
33770
  clearTimeout(timer);
32777
33771
  timer = setTimeout(stop, silenceMs);
32778
33772
  };
32779
33773
  let opened2 = false;
32780
33774
  try {
32781
33775
  const client = await loop.connect();
32782
- quiet2();
33776
+ quiet4();
32783
33777
  await client.watch(
32784
33778
  session2.signal,
32785
33779
  (event) => {
32786
- quiet2();
33780
+ quiet4();
32787
33781
  if (!event.ping) loop.event(event);
32788
33782
  },
32789
33783
  () => {
32790
- quiet2();
33784
+ quiet4();
32791
33785
  opened2 = true;
32792
33786
  up = true;
32793
33787
  loop.up(client);
@@ -32816,7 +33810,7 @@ function keepWatching(loop, signal) {
32816
33810
  loop.down();
32817
33811
  }
32818
33812
  ready2.resolve();
32819
- await sleep(wait2, signal);
33813
+ await sleep2(wait2, signal);
32820
33814
  wait2 = Math.min(wait2 * 2, most);
32821
33815
  }
32822
33816
  })();
@@ -34300,6 +35294,7 @@ var Launcher = class {
34300
35294
  scrubs = /* @__PURE__ */ new Map();
34301
35295
  clipboard;
34302
35296
  embedder;
35297
+ devices;
34303
35298
  rootFs;
34304
35299
  fileWatches;
34305
35300
  nextPid = 1e3;
@@ -34324,6 +35319,7 @@ var Launcher = class {
34324
35319
  );
34325
35320
  this.embedder = this.embedderBroker(options.embedder);
34326
35321
  this.embedder.denied = options.onDenial;
35322
+ this.devices = this.deviceNodes(options.devices);
34327
35323
  this.sealed = new SealedBroker(
34328
35324
  options.sealed,
34329
35325
  this.usersMode,
@@ -34539,16 +35535,16 @@ var Launcher = class {
34539
35535
  if (!found) {
34540
35536
  found = this.swaps.settled().then(() => this.trustedAppended()).then(async (appended2) => {
34541
35537
  const tiers = await this.tiers(scope, appended2);
34542
- const refused4 = [];
35538
+ const refused5 = [];
34543
35539
  const commands = await scanCommands(
34544
35540
  this.scanned,
34545
35541
  tiers,
34546
35542
  tiers.length - appended2.length,
34547
35543
  void 0,
34548
- refused4,
35544
+ refused5,
34549
35545
  await this.globalRoots(scope)
34550
35546
  );
34551
- this.refusedOf.set(commands, refused4.map(refusing));
35547
+ this.refusedOf.set(commands, refused5.map(refusing));
34552
35548
  return commands;
34553
35549
  }).then(
34554
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)
@@ -34755,14 +35751,14 @@ var Launcher = class {
34755
35751
  return await this.commandAt(path, scope) ?? await this.refusedAt(path, name2, scope);
34756
35752
  }
34757
35753
  async refusedAt(path, name2, scope) {
34758
- const refused4 = this.refusedOf.get(await this.commands(scope)) ?? [];
34759
- 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;
34760
35756
  const st = await this.base.stat(path).catch(() => void 0);
34761
35757
  if (!st?.isFile) return void 0;
34762
35758
  const real = await physicalPathAsync(this.base, path).catch(() => path);
34763
35759
  const same3 = async (own) => own === path || await physicalPathAsync(this.base, own).catch(() => own) === real;
34764
35760
  const owners = [];
34765
- for (const c of refused4) {
35761
+ for (const c of refused5) {
34766
35762
  const origin = c.origin;
34767
35763
  for (const own of [origin.glue, origin.wasm, ...origin.variants ?? []]) {
34768
35764
  if (!await same3(own)) continue;
@@ -34772,11 +35768,11 @@ var Launcher = class {
34772
35768
  }
34773
35769
  const stem = name2.replace(/\.wasm$/, "");
34774
35770
  const named = owners.find((c) => c.name === name2) ?? owners.find((c) => c.name === stem);
34775
- return named ?? owners[0] ?? await this.refusedCopy(real, name2, refused4);
35771
+ return named ?? owners[0] ?? await this.refusedCopy(real, name2, refused5);
34776
35772
  }
34777
- async refusedCopy(real, name2, refused4) {
35773
+ async refusedCopy(real, name2, refused5) {
34778
35774
  const dir = real.slice(0, real.lastIndexOf("/"));
34779
- for (const c of refused4) {
35775
+ for (const c of refused5) {
34780
35776
  const origin = c.origin;
34781
35777
  if (origin.abi !== "emscripten" || origin.script || c.name !== name2) continue;
34782
35778
  const own = await physicalPathAsync(this.base, origin.glue).catch(() => origin.glue);
@@ -34943,8 +35939,8 @@ var Launcher = class {
34943
35939
  const via = planned?.via ?? [];
34944
35940
  for (const [i, file] of via.entries()) {
34945
35941
  if (!byPath(file)) continue;
34946
- const want = i < via.length - 1 ? R_OK | X_OK : X_OK;
34947
- 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);
34948
35944
  }
34949
35945
  for (const bin of planned?.bins ?? []) await need(g, "exec", bin, R_OK | X_OK);
34950
35946
  if (planned?.target.copy) await need(g, "exec", planned.target.copy, R_OK | X_OK);
@@ -34984,12 +35980,12 @@ var Launcher = class {
34984
35980
  return void 0;
34985
35981
  }
34986
35982
  async planOne(req, { ppid, cred, scope }, catalogOnly = false) {
34987
- const refused4 = (err) => {
35983
+ const refused5 = (err) => {
34988
35984
  throw codeOf8(err) === "EACCES" ? new SpawnError("EACCES") : err;
34989
35985
  };
34990
- 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);
34991
35987
  const planned = await this.plan(file, req.argv, req.cwd, scope, req.argv0, catalogOnly);
34992
- await this.mayExec(cred, { ...req, file, pid: ppid }, planned).catch(refused4);
35988
+ await this.mayExec(cred, { ...req, file, pid: ppid }, planned).catch(refused5);
34993
35989
  if (planned) return planned;
34994
35990
  throw new SpawnError(catalogOnly ? "ENOENT" : await this.unrunnable(file, req.cwd, scope));
34995
35991
  }
@@ -35625,6 +36621,7 @@ var Launcher = class {
35625
36621
  ...scrubs ? { outputFilter: (file) => scrubbedAll(file, scrubs) } : {},
35626
36622
  clipboard: this.clipboard,
35627
36623
  embedder: { broker: this.embedder, session: () => this.sessionOf(pid) },
36624
+ devices: this.devices,
35628
36625
  ...this.dac ? { dac: this.dac } : {},
35629
36626
  program: req.program,
35630
36627
  changes: this.changes.sab,
@@ -35808,8 +36805,8 @@ var Launcher = class {
35808
36805
  this.sampledAt = Math.max(this.sampledAt, now);
35809
36806
  this.load = this.load.map((avg, i) => {
35810
36807
  const window = LOAD_WINDOWS_MS[i];
35811
- const fresh3 = 1 - Math.exp(-LOAD_SAMPLE_MS / window);
35812
- 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;
35813
36810
  });
35814
36811
  }
35815
36812
  idleSince;
@@ -36156,7 +37153,7 @@ var Launcher = class {
36156
37153
  forker(ppid, parent) {
36157
37154
  const { exec: _exec, pgid: _pgid, group: _group, pid: _pid, scrub: _scrub, ...image } = parent;
36158
37155
  return async (state, given) => {
36159
- const fresh3 = (fds, quietOom) => this.start({
37156
+ const fresh4 = (fds, quietOom) => this.start({
36160
37157
  ...image,
36161
37158
  quietOom,
36162
37159
  cwd: state.cwd ?? parent.cwd,
@@ -36172,7 +37169,7 @@ var Launcher = class {
36172
37169
  tagBase: this.tagBases.get(ppid),
36173
37170
  links: this.links.get(ppid)
36174
37171
  });
36175
- return childHandle(await this.admitted(parent.program.abi !== "js", given, fresh3));
37172
+ return childHandle(await this.admitted(parent.program.abi !== "js", given, fresh4));
36176
37173
  };
36177
37174
  }
36178
37175
  keySignal(tty, fallback, sig, send2) {
@@ -36213,6 +37210,14 @@ var Launcher = class {
36213
37210
  this.sealed.userOf = async (uid) => (await this.users.lookup(uid))?.name;
36214
37211
  this.sealed.factsOf = (pid) => this.processFacts(pid);
36215
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
+ }
36216
37221
  embedderBroker(hook) {
36217
37222
  const broker = new EmbedderBroker(
36218
37223
  this.usersMode,
@@ -36502,8 +37507,8 @@ var Launcher = class {
36502
37507
  for (const [path, mode] of layout) {
36503
37508
  if (isProtected(this.prefixes, normalizePath(path))) continue;
36504
37509
  const real = await this.realLayoutPath(path);
36505
- const fresh3 = real !== void 0 && !await this.base.hasOwner?.(real);
36506
- 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 });
36507
37512
  }
36508
37513
  }
36509
37514
  async realLayoutPath(given) {
@@ -36637,10 +37642,10 @@ var Launcher = class {
36637
37642
  }
36638
37643
  async mountFstab() {
36639
37644
  const text = await this.base.readFile(FSTAB_PATH).catch(() => "");
36640
- const lines2 = parseFstab(text);
36641
- 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");
36642
37647
  return mountFstab(
36643
- lines2,
37648
+ lines3,
36644
37649
  (spec) => this.mount(spec).catch((err) => {
36645
37650
  this.mounts.note(spec, "pending", err?.message ?? String(err));
36646
37651
  throw err;
@@ -37537,7 +38542,7 @@ function isAttestRequest(req) {
37537
38542
  return ATTEST_OPS.has(req.op);
37538
38543
  }
37539
38544
  function isUsersRequest(req) {
37540
- return USERS_OPS.has(req.op);
38545
+ return USERS_OPS.has(req.op) || req.op === "devices-add" || req.op === "devices-remove";
37541
38546
  }
37542
38547
  function transferOf2(req, result) {
37543
38548
  if (result instanceof MessagePort) return [result];
@@ -37570,7 +38575,18 @@ function failure(err) {
37570
38575
  ...typeof code === "string" ? { code } : {}
37571
38576
  };
37572
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
+ }
37573
38588
  function usersOp(l, req) {
38589
+ if (req.op === "devices-add" || req.op === "devices-remove") return devicesOp(l, req);
37574
38590
  if (req.op === "users-add") return l.users.add(req.user);
37575
38591
  if (req.op === "users-remove") return l.users.remove(req.name, req.options);
37576
38592
  if (req.op === "groups-list") return l.users.groups();
@@ -37580,7 +38596,7 @@ function usersOp(l, req) {
37580
38596
  return l.users.list();
37581
38597
  }
37582
38598
  var clientIds = 0;
37583
- async function connectClient(deps, { user, tag, bind: bind2 }) {
38599
+ async function connectClient(deps, { user, tag, requests: bind2, devices }) {
37584
38600
  const l = await deps.ready();
37585
38601
  tagOption(tag);
37586
38602
  if (user !== void 0 && !await l.users.lookup(user)) {
@@ -37594,6 +38610,7 @@ async function connectClient(deps, { user, tag, bind: bind2 }) {
37594
38610
  signal: signalNumber,
37595
38611
  session: client,
37596
38612
  ...bind2 === "attach" ? { attachRequests: true } : {},
38613
+ ...devices === "attach" ? { attachDevices: true } : {},
37597
38614
  ...user !== void 0 ? { user } : {},
37598
38615
  ...tag !== void 0 ? { tag } : {},
37599
38616
  ...deps.locks ? { locks: deps.locks } : {},
@@ -37601,13 +38618,15 @@ async function connectClient(deps, { user, tag, bind: bind2 }) {
37601
38618
  });
37602
38619
  deps.clients.add(served2);
37603
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;
37604
38622
  void served2.closed.then(() => {
37605
38623
  deps.clients.delete(served2);
37606
38624
  unbind?.();
38625
+ unbindDevices?.();
37607
38626
  });
37608
- return unbind ? { port: port2, client } : port2;
38627
+ return unbind || devices ? { port: port2, client } : port2;
37609
38628
  }
37610
- function hostOptions(req, port, sealed, sealedFetch, clipboard, lookup2, requests) {
38629
+ function hostOptions(req, port, sealed, sealedFetch, clipboard, lookup2, requests, devices) {
37611
38630
  return {
37612
38631
  cron: { lock: `slicc-kernel-cron:${req.metadata ?? META_DB}` },
37613
38632
  ...req.hostname !== void 0 ? { hostname: req.hostname } : {},
@@ -37619,7 +38638,8 @@ function hostOptions(req, port, sealed, sealedFetch, clipboard, lookup2, request
37619
38638
  ...req.sealed ? { sealed } : {},
37620
38639
  ...req.sealedFetch ? { sealedFetch } : {},
37621
38640
  ...req.clipboard ? { clipboard } : {},
37622
- ...req.requests ? { embedder: requests.hookFor(0, req.requests) } : {}
38641
+ ...req.requests ? { embedder: requests.hookFor(0, req.requests) } : {},
38642
+ ...req.devices ? { devices: devices.hookFor(0) } : {}
37623
38643
  };
37624
38644
  }
37625
38645
  function hostProtect(init, lookup2) {
@@ -37673,6 +38693,20 @@ function processOps(ready2, reply) {
37673
38693
  }
37674
38694
  return { terminal, sudoRun, act: onTerminal };
37675
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
+ }
37676
38710
  function serveKernel(port, deps) {
37677
38711
  let launcher;
37678
38712
  let remote;
@@ -37682,24 +38716,14 @@ function serveKernel(port, deps) {
37682
38716
  const sealed = sealedAsker(port);
37683
38717
  const clipboard = clipboardAsker(port);
37684
38718
  const requests = embedderAsker(port);
38719
+ const devices = devicesAsker(port);
37685
38720
  const integrity = integrityAsker(port);
37686
38721
  const approvals = sudoApprovals(port);
37687
38722
  const locks = deps.locks === null ? void 0 : deps.locks ?? locksOf();
37688
- let holding;
37689
- function holdLock(locks2) {
37690
- holding ??= new Promise((resolve2) => {
37691
- const name2 = `slicc-kernel:${crypto.randomUUID()}`;
37692
- void locks2.request(name2, () => {
37693
- resolve2(name2);
37694
- return new Promise(() => {
37695
- });
37696
- });
37697
- });
37698
- return holding;
37699
- }
37700
- const connect2 = (user, tag, bind2) => connectClient(
37701
- { ready: ready2, clients, requests, held: locks ? () => holdLock(locks) : void 0, locks },
37702
- { 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
37703
38727
  );
37704
38728
  async function ready2() {
37705
38729
  if (!launcher) throw new Error("the kernel is not initialized");
@@ -37730,7 +38754,8 @@ function serveKernel(port, deps) {
37730
38754
  sealed.fetch,
37731
38755
  clipboard.hook,
37732
38756
  integrity.lookup,
37733
- requests
38757
+ requests,
38758
+ devices
37734
38759
  ),
37735
38760
  ...req.approve ? { approve: approvals.ask } : {},
37736
38761
  ...net,
@@ -37762,7 +38787,7 @@ function serveKernel(port, deps) {
37762
38787
  if (req.op === "sealed-placeholders") return (await ready2()).setPlaceholders(req.list);
37763
38788
  if (req.op === "open-terminal") return processes2.terminal(req);
37764
38789
  if (req.op === "sudo-run") return processes2.sudoRun(req);
37765
- if (req.op === "connect") return connect2(req.user, req.tag, req.requests);
38790
+ if (req.op === "connect") return connect2(req);
37766
38791
  if (isUsersRequest(req)) return usersOp(await ready2(), req);
37767
38792
  if (req.op === "routes" || req.op === "transport-traits") {
37768
38793
  return networkOp(await ready2(), req, remote);
@@ -37799,8 +38824,7 @@ function serveKernel(port, deps) {
37799
38824
  if (sealedReply) return sealed.answer(sealedReply);
37800
38825
  const clipboardReply = event.data.clipboardReply;
37801
38826
  if (clipboardReply) return clipboard.answer(clipboardReply);
37802
- const embedderReply = embedderReplyOf(event.data);
37803
- if (embedderReply) return requests.answer(embedderReply);
38827
+ if (pageReplied(event.data, requests, devices)) return;
37804
38828
  const integrityReply = event.data.integrityReply;
37805
38829
  if (integrityReply) return integrity.answer(integrityReply);
37806
38830
  const grant = event.data.hostfsGrant;