@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
package/dist/node.js CHANGED
@@ -96,11 +96,231 @@ function portCdpHook(ask, gone) {
96
96
  });
97
97
  }
98
98
 
99
- // src/embedder-relay.ts
99
+ // src/devices-relay.ts
100
+ function quiet() {
101
+ return void 0;
102
+ }
103
+ function errorOf(err) {
104
+ const name2 = err?.name;
105
+ const message3 = err instanceof Error ? err.message : String(err);
106
+ return { name: typeof name2 === "string" ? name2 : "Error", message: message3 };
107
+ }
108
+ function raised(failure) {
109
+ return Object.assign(new Error(failure.message), { name: failure.name });
110
+ }
100
111
  function fresh2(bytes) {
101
112
  return bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength ? bytes : bytes.slice();
102
113
  }
103
- function errorOf(err) {
114
+ function devicesAsker(port) {
115
+ const remotes = /* @__PURE__ */ new Map();
116
+ let next3 = 0;
117
+ const post = (message3, transfer = []) => port.postMessage({ devices: message3 }, transfer);
118
+ const push = (remote, item) => {
119
+ remote.chunks.push(item);
120
+ remote.wake?.();
121
+ };
122
+ const streams = (id2, remote) => ({
123
+ readable: new ReadableStream(
124
+ {
125
+ pull: async (controller) => {
126
+ if (remote.chunks.length === 0) {
127
+ post({ id: id2, pull: true });
128
+ await new Promise((resolve2) => {
129
+ remote.wake = resolve2;
130
+ });
131
+ }
132
+ const item = remote.chunks.shift();
133
+ if (item instanceof Uint8Array) controller.enqueue(item);
134
+ else if (item === null || item === void 0) controller.close();
135
+ else controller.error(raised(item));
136
+ },
137
+ cancel: (reason) => {
138
+ post({ id: id2, cancel: String(reason ?? "closed") });
139
+ }
140
+ },
141
+ { highWaterMark: 0 }
142
+ ),
143
+ writable: new WritableStream({
144
+ write: (chunk2) => {
145
+ const seq2 = ++remote.seq;
146
+ const done4 = Promise.withResolvers();
147
+ remote.writes.set(seq2, done4);
148
+ const bytes = fresh2(chunk2);
149
+ post({ id: id2, write: bytes, seq: seq2 }, [bytes.buffer]);
150
+ return done4.promise;
151
+ },
152
+ abort: (reason) => {
153
+ post({ id: id2, cancel: String(reason ?? "closed") });
154
+ }
155
+ }),
156
+ control: (op, args) => {
157
+ const seq2 = ++remote.seq;
158
+ const done4 = Promise.withResolvers();
159
+ remote.controls.set(seq2, done4);
160
+ post({ id: id2, control: { seq: seq2, op, ...args ? { args } : {} } });
161
+ if (op === "close") void done4.promise.finally(() => remotes.delete(id2)).catch(quiet);
162
+ return done4.promise;
163
+ }
164
+ });
165
+ const hookFor = (client) => ({
166
+ open: async (request) => {
167
+ const id2 = ++next3;
168
+ const remote = {
169
+ opened: Promise.withResolvers(),
170
+ chunks: [],
171
+ wake: void 0,
172
+ writes: /* @__PURE__ */ new Map(),
173
+ controls: /* @__PURE__ */ new Map(),
174
+ seq: 0
175
+ };
176
+ remotes.set(id2, remote);
177
+ const abort = () => {
178
+ remotes.delete(id2);
179
+ post({ id: id2, cancel: "interrupted" });
180
+ remote.opened.reject(request.signal.reason);
181
+ };
182
+ request.signal.addEventListener("abort", abort, { once: true });
183
+ const { path, flags: flags2, stamp } = request;
184
+ post({ id: id2, open: { client, path, flags: flags2, stamp } });
185
+ try {
186
+ await remote.opened.promise;
187
+ } finally {
188
+ request.signal.removeEventListener("abort", abort);
189
+ }
190
+ return streams(id2, remote);
191
+ }
192
+ });
193
+ const answer2 = (message3) => {
194
+ const remote = remotes.get(message3.id);
195
+ if (!remote) return;
196
+ if ("opened" in message3) remote.opened.resolve();
197
+ else if ("failed" in message3) {
198
+ remotes.delete(message3.id);
199
+ remote.opened.reject(raised(message3.failed));
200
+ } else if ("chunk" in message3) push(remote, message3.chunk);
201
+ else if ("done" in message3) push(remote, null);
202
+ else if ("readError" in message3) push(remote, message3.readError);
203
+ else if ("wrote" in message3) {
204
+ remote.writes.get(message3.wrote)?.resolve();
205
+ remote.writes.delete(message3.wrote);
206
+ } else if ("writeError" in message3) {
207
+ remote.writes.get(message3.seq)?.reject(raised(message3.writeError));
208
+ remote.writes.delete(message3.seq);
209
+ } else if ("answered" in message3) {
210
+ remote.controls.get(message3.answered)?.resolve(message3.value);
211
+ remote.controls.delete(message3.answered);
212
+ } else {
213
+ remote.controls.get(message3.refused)?.reject(raised(message3.error));
214
+ remote.controls.delete(message3.refused);
215
+ }
216
+ };
217
+ return { hookFor, answer: answer2 };
218
+ }
219
+ function devicesAnswerer(port, hookFor) {
220
+ const locals = /* @__PURE__ */ new Map();
221
+ const post = (message3, transfer = []) => port.postMessage({ devicesReply: message3 }, transfer);
222
+ const open = async (id2, asked) => {
223
+ const local2 = { abort: new AbortController() };
224
+ locals.set(id2, local2);
225
+ try {
226
+ const hook = hookFor(asked.client);
227
+ if (!hook)
228
+ throw Object.assign(new Error("this page has no devices hook"), {
229
+ name: "NotSupportedError"
230
+ });
231
+ const streams = await hook.open({
232
+ path: asked.path,
233
+ flags: asked.flags,
234
+ stamp: asked.stamp,
235
+ signal: local2.abort.signal
236
+ });
237
+ if (!locals.has(id2)) {
238
+ await streams?.readable?.cancel("closed").catch(quiet);
239
+ await streams?.writable?.abort("closed").catch(quiet);
240
+ await Promise.resolve(streams?.control?.("close")).catch(quiet);
241
+ return;
242
+ }
243
+ local2.streams = streams;
244
+ local2.reader = streams.readable.getReader();
245
+ local2.writer = streams.writable.getWriter();
246
+ post({ id: id2, opened: true });
247
+ } catch (err) {
248
+ locals.delete(id2);
249
+ post({ id: id2, failed: errorOf(err) });
250
+ }
251
+ };
252
+ const pull = async (id2, local2) => {
253
+ try {
254
+ const got = await local2.reader.read();
255
+ if (got.done) return post({ id: id2, done: true });
256
+ const bytes = fresh2(got.value);
257
+ post({ id: id2, chunk: bytes }, [bytes.buffer]);
258
+ } catch (err) {
259
+ post({ id: id2, readError: errorOf(err) });
260
+ }
261
+ };
262
+ const write = async (id2, local2, bytes, seq2) => {
263
+ try {
264
+ await local2.writer.write(bytes);
265
+ post({ id: id2, wrote: seq2 });
266
+ } catch (err) {
267
+ post({ id: id2, writeError: errorOf(err), seq: seq2 });
268
+ }
269
+ };
270
+ const control = async (id2, local2, asked) => {
271
+ try {
272
+ await local2.closing;
273
+ const value2 = await local2.streams.control(asked.op, asked.args);
274
+ post({ id: id2, answered: asked.seq, ...value2 !== void 0 ? { value: value2 } : {} });
275
+ } catch (err) {
276
+ post({ id: id2, refused: asked.seq, error: errorOf(err) });
277
+ }
278
+ if (asked.op === "close") locals.delete(id2);
279
+ };
280
+ const release = async (local2, reason) => {
281
+ const { reader, writer } = local2;
282
+ local2.reader = void 0;
283
+ local2.writer = void 0;
284
+ await reader?.cancel(reason).catch(quiet);
285
+ reader?.releaseLock();
286
+ await writer?.abort(reason).catch(quiet);
287
+ writer?.releaseLock();
288
+ };
289
+ const cancel = (id2, local2, reason) => {
290
+ local2.abort.abort(new DOMException(reason, "AbortError"));
291
+ if (!local2.streams) {
292
+ locals.delete(id2);
293
+ return;
294
+ }
295
+ local2.closing = Promise.resolve(local2.closing).then(() => release(local2, reason));
296
+ };
297
+ return {
298
+ handle(data) {
299
+ const message3 = data?.devices;
300
+ if (!message3) return false;
301
+ if ("open" in message3) {
302
+ void open(message3.id, message3.open);
303
+ return true;
304
+ }
305
+ const local2 = locals.get(message3.id);
306
+ if (!local2) return true;
307
+ if ("pull" in message3) void pull(message3.id, local2);
308
+ else if ("write" in message3) void write(message3.id, local2, message3.write, message3.seq);
309
+ else if ("control" in message3) void control(message3.id, local2, message3.control);
310
+ else cancel(message3.id, local2, message3.cancel);
311
+ return true;
312
+ }
313
+ };
314
+ }
315
+ function devicesReplyOf(data) {
316
+ return data?.devicesReply;
317
+ }
318
+
319
+ // src/embedder-relay.ts
320
+ function fresh3(bytes) {
321
+ return bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength ? bytes : bytes.slice();
322
+ }
323
+ function errorOf2(err) {
104
324
  const name2 = err?.name;
105
325
  const message3 = err instanceof Error ? err.message : String(err);
106
326
  return { name: typeof name2 === "string" ? name2 : "Error", message: message3 };
@@ -143,7 +363,7 @@ function embedderAsker(port) {
143
363
  const got = await asked.body.read().catch(() => ({ done: true, value: void 0 }));
144
364
  if (got.done) post({ id: id2, bodyEnd: true });
145
365
  else {
146
- const bytes = fresh2(got.value);
366
+ const bytes = fresh3(got.value);
147
367
  post({ id: id2, body: bytes }, [bytes.buffer]);
148
368
  }
149
369
  };
@@ -228,7 +448,7 @@ function embedderAnswerer(port, hookFor) {
228
448
  const post = (message3, transfer = []) => port.postMessage({ embedderReply: message3 }, transfer);
229
449
  const fail3 = (id2, err) => {
230
450
  running.delete(id2);
231
- post({ id: id2, error: errorOf(err) });
451
+ post({ id: id2, error: errorOf2(err) });
232
452
  };
233
453
  const start2 = async (ask) => {
234
454
  const state = { abort: new AbortController(), body: [], wakeBody: void 0 };
@@ -263,7 +483,7 @@ function embedderAnswerer(port, hookFor) {
263
483
  post({ id: id2, done: true });
264
484
  return;
265
485
  }
266
- const bytes = fresh2(got.value);
486
+ const bytes = fresh3(got.value);
267
487
  post({ id: id2, chunk: bytes }, [bytes.buffer]);
268
488
  } catch (err) {
269
489
  fail3(id2, err);
@@ -574,11 +794,11 @@ function permOf(st, link2) {
574
794
  ...link2 !== void 0 ? { link: link2 } : {}
575
795
  };
576
796
  }
577
- function permits(cred, perm, want, directory = true) {
797
+ function permits(cred, perm, want2, directory = true) {
578
798
  const mode = perm.mode ?? (directory ? 493 : 420);
579
- if (cred.euid === 0) return (want & X_OK) === 0 || directory || (mode & 73) !== 0;
799
+ if (cred.euid === 0) return (want2 & X_OK) === 0 || directory || (mode & 73) !== 0;
580
800
  const shift = cred.euid === perm.uid ? 6 : inGroup(cred, perm.gid) ? 3 : 0;
581
- return (mode >> shift & want) === want;
801
+ return (mode >> shift & want2) === want2;
582
802
  }
583
803
  function sticky(cred, dir, entry) {
584
804
  if (cred.euid === 0 || ((dir.mode ?? 0) & S_ISVTX) === 0) return true;
@@ -653,27 +873,27 @@ async function present(fs, path, directory) {
653
873
  const st = await fs.lstat(path).catch(() => void 0);
654
874
  return st !== void 0 && (!directory || st.isDirectory);
655
875
  }
656
- async function finalOf(fs, walked, want, options) {
876
+ async function finalOf(fs, walked, want2, options) {
657
877
  const perm = walked.perm;
658
878
  if (options.follow === false && perm.link !== void 0) {
659
879
  return [{ ...perm, mode: 511 }, false];
660
880
  }
661
- if ((want & X_OK) === 0 || options.directory !== void 0) {
881
+ if ((want2 & X_OK) === 0 || options.directory !== void 0) {
662
882
  return [perm, options.directory !== false];
663
883
  }
664
884
  const look = options.follow === false ? fs.lstat : fs.stat;
665
885
  const st = await look.call(fs, walked.path).catch(() => void 0);
666
886
  return st ? [permOf(st), st.isDirectory] : [perm, false];
667
887
  }
668
- async function access(fs, cred, path, want, options = {}) {
888
+ async function access(fs, cred, path, want2, options = {}) {
669
889
  const walked = await walk(fs, cred, path, options.follow !== false, options.links);
670
890
  if (walked.denied !== void 0) {
671
891
  if (walked.link || await present(fs, walked.denied, true)) return walked;
672
892
  return { path, perm: void 0, missing: true };
673
893
  }
674
894
  if (!walked.perm) return { ...walked, missing: true };
675
- const [perm, directory] = await finalOf(fs, walked, want, options);
676
- if (want === 0 || permits(cred, perm, want, directory)) return walked;
895
+ const [perm, directory] = await finalOf(fs, walked, want2, options);
896
+ if (want2 === 0 || permits(cred, perm, want2, directory)) return walked;
677
897
  if (await present(fs, walked.path, false)) return { ...walked, denied: walked.path };
678
898
  return { ...walked, missing: true };
679
899
  }
@@ -779,11 +999,11 @@ function ttyOpenable(g, tty) {
779
999
  if (!g || !tty.owner || permits(g.cred, tty.owner, R_OK | W_OK, false)) return;
780
1000
  deny(g, "open", tty.name ?? "/dev/tty");
781
1001
  }
782
- async function need(g, op, path, want, options = {}, shown2 = path) {
783
- if (want & W_OK && shown2 === path) await fence(g, op, path);
784
- const walked = await access(g.fs, g.cred, path, want, linked(g, options));
1002
+ async function need(g, op, path, want2, options = {}, shown2 = path) {
1003
+ if (want2 & W_OK && shown2 === path) await fence(g, op, path);
1004
+ const walked = await access(g.fs, g.cred, path, want2, linked(g, options));
785
1005
  if (walked.denied !== void 0) deny(g, op, walked.link ? walked.denied : shown2);
786
- else if (want & R_OK && g.fs.commandTarget) await readsAs(g, op, walked.path, shown2);
1006
+ else if (want2 & R_OK && g.fs.commandTarget) await readsAs(g, op, walked.path, shown2);
787
1007
  return walked;
788
1008
  }
789
1009
  async function readsAs(g, op, path, shown2) {
@@ -932,10 +1152,10 @@ async function creatable(g, op, path) {
932
1152
  const target = await need(g, op, path, 0);
933
1153
  if (target.denied === void 0) await need(g, op, parentOf(target.path), W_OK | X_OK, DIR, path);
934
1154
  }
935
- async function openable(g, op, path, want, create) {
1155
+ async function openable(g, op, path, want2, create) {
936
1156
  const st = create ? await g.fs.stat(path).catch(() => void 0) : void 0;
937
1157
  if (create && !st) return creatable(g, op, path);
938
- await need(g, op, path, want, { directory: st?.isDirectory ?? false });
1158
+ await need(g, op, path, want2, { directory: st?.isDirectory ?? false });
939
1159
  }
940
1160
 
941
1161
  // src/kernel/cred.ts
@@ -1376,8 +1596,8 @@ var FdTable = class _FdTable {
1376
1596
  }
1377
1597
  stdioTerminal() {
1378
1598
  for (const fd of [0, 1, 2]) {
1379
- const tty = this.fds.get(fd)?.file.tty;
1380
- if (tty) return tty;
1599
+ const file = this.fds.get(fd)?.file;
1600
+ if (file?.tty && !file.device) return file.tty;
1381
1601
  }
1382
1602
  return void 0;
1383
1603
  }
@@ -1678,13 +1898,13 @@ function addressBits(host) {
1678
1898
  function parsePrefix(text) {
1679
1899
  const parts2 = text.trim().split("/");
1680
1900
  if (parts2.length > 2) return void 0;
1681
- const [address = "", size] = parts2;
1682
- const parsed2 = addressBits(address);
1683
- if (!parsed2) return void 0;
1684
- const max = parsed2.v6 ? 128 : 32;
1901
+ const [address2 = "", size] = parts2;
1902
+ const parsed3 = addressBits(address2);
1903
+ if (!parsed3) return void 0;
1904
+ const max = parsed3.v6 ? 128 : 32;
1685
1905
  const length2 = size === void 0 ? max : /^\d{1,3}$/.test(size) ? Number(size) : -1;
1686
1906
  if (length2 < 0 || length2 > max) return void 0;
1687
- return { ...parsed2, length: length2 };
1907
+ return { ...parsed3, length: length2 };
1688
1908
  }
1689
1909
  function within(prefix, addr) {
1690
1910
  if (prefix.v6 !== addr.v6) return false;
@@ -1710,10 +1930,10 @@ var Routes = class {
1710
1930
  this.ipv6 = ipv62;
1711
1931
  }
1712
1932
  set(table) {
1713
- const parsed2 = (table.prefixes ?? []).map(parsePrefix);
1714
- const bad = (table.prefixes ?? []).find((_, i) => !parsed2[i]);
1933
+ const parsed3 = (table.prefixes ?? []).map(parsePrefix);
1934
+ const bad = (table.prefixes ?? []).find((_, i) => !parsed3[i]);
1715
1935
  if (bad !== void 0) throw new Error(`not an address prefix: ${bad}`);
1716
- this.table = parsed2;
1936
+ this.table = parsed3;
1717
1937
  this.exit = table.exit === true;
1718
1938
  }
1719
1939
  classify(host) {
@@ -2249,10 +2469,10 @@ var KernelSocket = class _KernelSocket {
2249
2469
  }
2250
2470
  };
2251
2471
  let made2;
2252
- const abandon = () => {
2472
+ const abandon2 = () => {
2253
2473
  if (!keeps(signal.reason)) view2.close();
2254
2474
  };
2255
- signal.addEventListener("abort", abandon, { once: true });
2475
+ signal.addEventListener("abort", abandon2, { once: true });
2256
2476
  try {
2257
2477
  made2 = await make(view2);
2258
2478
  } catch (err) {
@@ -2265,7 +2485,7 @@ var KernelSocket = class _KernelSocket {
2265
2485
  throw signal.aborted ? signal.reason : err;
2266
2486
  } finally {
2267
2487
  handshaking = false;
2268
- signal.removeEventListener("abort", abandon);
2488
+ signal.removeEventListener("abort", abandon2);
2269
2489
  }
2270
2490
  const plain3 = uplinkLink(made2.conn, (err) => {
2271
2491
  this.reset = err instanceof KernelError && err.code === "EPROTO" ? err : new KernelError("ECONNRESET");
@@ -2454,6 +2674,7 @@ var LoopbackNet = class {
2454
2674
  reserved = /* @__PURE__ */ new Set();
2455
2675
  facades = /* @__PURE__ */ new Map();
2456
2676
  proxyStamped;
2677
+ stamped = /* @__PURE__ */ new Set();
2457
2678
  nextEphemeral = EPHEMERAL_FIRST;
2458
2679
  route;
2459
2680
  origins;
@@ -2528,6 +2749,9 @@ var LoopbackNet = class {
2528
2749
  this.kernelOnly(port, true);
2529
2750
  this.facades.set(port, name2);
2530
2751
  }
2752
+ stampPeers(port) {
2753
+ this.stamped.add(port);
2754
+ }
2531
2755
  stampProxy(port) {
2532
2756
  this.proxyStamped = port;
2533
2757
  }
@@ -2536,7 +2760,7 @@ var LoopbackNet = class {
2536
2760
  }
2537
2761
  stampsPeers(addr) {
2538
2762
  if (addr.family !== "inet" || !isLoopback(canonicalHost(addr.host))) return false;
2539
- return this.facades.has(addr.port) || this.proxyStamped === addr.port;
2763
+ return this.facades.has(addr.port) || this.stamped.has(addr.port) || this.proxyStamped === addr.port;
2540
2764
  }
2541
2765
  activate(addr, start2) {
2542
2766
  this.activators.set(keyOf(addr), start2);
@@ -2727,7 +2951,7 @@ function signalName(sig) {
2727
2951
  const name2 = Object.entries(SIG).find(([, n]) => n === sig)?.[0];
2728
2952
  return name2 === void 0 ? void 0 : `SIG${name2}`;
2729
2953
  }
2730
- function quiet(p) {
2954
+ function quiet2(p) {
2731
2955
  p.catch(() => void 0);
2732
2956
  return p;
2733
2957
  }
@@ -2961,21 +3185,21 @@ function latin1Bytes(text) {
2961
3185
  return out;
2962
3186
  }
2963
3187
  function parseRequestHead(bytes) {
2964
- const lines2 = latin1(bytes).split(/\r?\n/);
2965
- while (lines2.length > 0 && lines2[0] === "") lines2.shift();
2966
- const match = /^([!#$%&'*+\-.^_`|~0-9A-Za-z]+) (\S+) HTTP\/(\d)\.(\d)$/.exec(lines2.shift() ?? "");
3188
+ const lines3 = latin1(bytes).split(/\r?\n/);
3189
+ while (lines3.length > 0 && lines3[0] === "") lines3.shift();
3190
+ const match = /^([!#$%&'*+\-.^_`|~0-9A-Za-z]+) (\S+) HTTP\/(\d)\.(\d)$/.exec(lines3.shift() ?? "");
2967
3191
  if (!match) throw new HttpError(400, "malformed request line");
2968
3192
  if (match[3] !== "1") throw new HttpError(505, `HTTP/${match[3]}.${match[4]} is not supported`);
2969
3193
  return {
2970
3194
  method: match[1],
2971
3195
  target: match[2],
2972
3196
  minor: Number(match[4]),
2973
- headers: parseFields(lines2)
3197
+ headers: parseFields(lines3)
2974
3198
  };
2975
3199
  }
2976
- function parseFields(lines2) {
3200
+ function parseFields(lines3) {
2977
3201
  const headers = [];
2978
- for (const line of lines2) {
3202
+ for (const line of lines3) {
2979
3203
  if (line === "") continue;
2980
3204
  if (line.startsWith(" ") || line.startsWith(" ")) {
2981
3205
  throw new HttpError(400, "obsolete line folding");
@@ -3444,10 +3668,10 @@ var CdpFacade = class {
3444
3668
  );
3445
3669
  return;
3446
3670
  }
3447
- await this.write(conn, json(await this.list(runtime)));
3671
+ await this.write(conn, json(await this.list(runtime, conn.peerStamp)));
3448
3672
  }
3449
- async list(runtime) {
3450
- const conn = await this.host(runtime);
3673
+ async list(runtime, stamp) {
3674
+ const conn = await this.host(runtime, stamp);
3451
3675
  try {
3452
3676
  const reply = await new Promise(
3453
3677
  (resolve2, reject) => {
@@ -3460,10 +3684,10 @@ var CdpFacade = class {
3460
3684
  reject(new HttpError(502, `the CDP host closed: ${reason ?? "no reason"}`));
3461
3685
  };
3462
3686
  conn.onmessage = (message3) => {
3463
- const parsed2 = JSON.parse(message3);
3464
- if (parsed2.id !== 1) return;
3687
+ const parsed3 = JSON.parse(message3);
3688
+ if (parsed3.id !== 1) return;
3465
3689
  clearTimeout(timer);
3466
- resolve2(parsed2);
3690
+ resolve2(parsed3);
3467
3691
  };
3468
3692
  conn.send(JSON.stringify({ id: 1, method: "Target.getTargets" }));
3469
3693
  }
@@ -3482,12 +3706,15 @@ var CdpFacade = class {
3482
3706
  conn.close();
3483
3707
  }
3484
3708
  }
3485
- host(runtime) {
3486
- return this.options.hosts.connect(runtime ? { runtime } : {});
3709
+ host(runtime, stamp) {
3710
+ return this.options.hosts.connect({
3711
+ ...runtime ? { runtime } : {},
3712
+ ...stamp ? { stamp } : {}
3713
+ });
3487
3714
  }
3488
3715
  async upgrade(conn, incoming, req, runtime) {
3489
3716
  const accept2 = await acceptKey(req.headers);
3490
- const host = await this.host(runtime);
3717
+ const host = await this.host(runtime, conn.peerStamp);
3491
3718
  const limits = {
3492
3719
  max: this.options.maxMessage ?? MAX_MESSAGE,
3493
3720
  closeWaitMs: this.options.closeWaitMs ?? CLOSE_WAIT_MS
@@ -3567,6 +3794,7 @@ function enableCdp(net, hosts) {
3567
3794
  let facade;
3568
3795
  net.kernelOnly(CDP_PORT, true);
3569
3796
  net.reserve(CDP_PORT);
3797
+ net.stampPeers(CDP_PORT);
3570
3798
  net.activate({ family: "inet", host: "127.0.0.1", port: CDP_PORT }, () => {
3571
3799
  const started = new CdpFacade({ net, hosts });
3572
3800
  facade = started;
@@ -3609,7 +3837,7 @@ var CdpHosts = class {
3609
3837
  if (!entry) throw new HttpError(503, NO_CDP_HOST);
3610
3838
  let conn;
3611
3839
  try {
3612
- conn = await entry.hook(request.runtime ? { runtime: request.runtime } : {});
3840
+ conn = await entry.hook(hookRequest(request));
3613
3841
  } catch (e) {
3614
3842
  throw new HttpError(502, `CDP host: ${e instanceof Error ? e.message : String(e)}`);
3615
3843
  }
@@ -3629,6 +3857,12 @@ var CdpHosts = class {
3629
3857
  for (const entry of [this.embedder, ...this.clients]) if (entry) closeAll(entry);
3630
3858
  }
3631
3859
  };
3860
+ function hookRequest({ runtime, stamp }) {
3861
+ return {
3862
+ ...runtime ? { runtime } : {},
3863
+ ...stamp ? { stamp: structuredClone(stamp) } : {}
3864
+ };
3865
+ }
3632
3866
  function closeAll(entry) {
3633
3867
  for (const conn of [...entry.open]) {
3634
3868
  conn.close();
@@ -3787,7 +4021,7 @@ function satisfies(version, range2) {
3787
4021
  }
3788
4022
 
3789
4023
  // src/version.ts
3790
- var KERNEL_VERSION = true ? "1.181.0" : "0.0.0-dev";
4024
+ var KERNEL_VERSION = true ? "1.183.0" : "0.0.0-dev";
3791
4025
  var OS_RELEASE = "6.0.0-slicc";
3792
4026
  function procVersion() {
3793
4027
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -4079,10 +4313,10 @@ async function tierPackages(fs, physical, roots, bins, kernel, global) {
4079
4313
  const own = commandsOf(pkg, manifest);
4080
4314
  const named = new Set(own.map((command) => command.name));
4081
4315
  const extra = bins ? await binCommandsOf(physical, pkg, manifest, named) : [];
4082
- const refused4 = engineRefusal(manifest, kernel);
4316
+ const refused5 = engineRefusal(manifest, kernel);
4083
4317
  const name2 = pkg.slice(root.length + 1);
4084
4318
  const dependency = direct?.has(name2) === false;
4085
- const marks = { ...refused4 ? { refused: refused4 } : {}, ...dependency ? { dependency } : {} };
4319
+ const marks = { ...refused5 ? { refused: refused5 } : {}, ...dependency ? { dependency } : {} };
4086
4320
  out.push({
4087
4321
  name: name2,
4088
4322
  pkg,
@@ -4114,7 +4348,7 @@ function winners(tier) {
4114
4348
  }
4115
4349
  return out;
4116
4350
  }
4117
- async function scanCommands(fs, modules, binsFrom = Number.POSITIVE_INFINITY, kernel = KERNEL_VERSION, refused4, global = /* @__PURE__ */ new Set()) {
4351
+ async function scanCommands(fs, modules, binsFrom = Number.POSITIVE_INFINITY, kernel = KERNEL_VERSION, refused5, global = /* @__PURE__ */ new Set()) {
4118
4352
  const found = /* @__PURE__ */ new Map();
4119
4353
  const physical = physicalResolver(fs);
4120
4354
  const tiers = (typeof modules === "string" ? [modules] : modules).map(
@@ -4128,7 +4362,7 @@ async function scanCommands(fs, modules, binsFrom = Number.POSITIVE_INFINITY, ke
4128
4362
  const dependencies = [];
4129
4363
  for (const [index, roots] of tiers.entries()) {
4130
4364
  const tier = await tierPackages(fs, physical, roots, index >= binsFrom, kernel, global);
4131
- if (refused4) refused4.push(...tier.flatMap((entry) => entry.commands.filter((c) => c.refused)));
4365
+ if (refused5) refused5.push(...tier.flatMap((entry) => entry.commands.filter((c) => c.refused)));
4132
4366
  await take(tier.filter((entry) => !entry.dependency));
4133
4367
  dependencies.push(tier.filter((entry) => entry.dependency));
4134
4368
  }
@@ -4732,9 +4966,9 @@ var TAGS_VERSION = 1;
4732
4966
  async function readTags(fs) {
4733
4967
  const text = await fs.readFile(TAGS_PATH).catch(() => "");
4734
4968
  try {
4735
- const parsed2 = JSON.parse(text);
4736
- if (!parsed2 || typeof parsed2 !== "object") return {};
4737
- const { v: _, ...tables } = parsed2;
4969
+ const parsed3 = JSON.parse(text);
4970
+ if (!parsed3 || typeof parsed3 !== "object") return {};
4971
+ const { v: _, ...tables } = parsed3;
4738
4972
  return tables;
4739
4973
  } catch {
4740
4974
  return {};
@@ -4804,27 +5038,27 @@ var Crontabs = class {
4804
5038
  const json5 = JSON.stringify({ v: TAGS_VERSION, ...kept });
4805
5039
  if (json5 === await fs.readFile(TAGS_PATH).catch(() => "")) return;
4806
5040
  const owner = owners ? { uid: 0, gid: 0, mode: 384 } : void 0;
4807
- const fresh3 = `${TAGS_PATH}.${crypto.randomUUID()}.new`;
4808
- await fs.writeFile(fresh3, json5, owner);
4809
- await fs.rename(fresh3, TAGS_PATH);
5041
+ const fresh4 = `${TAGS_PATH}.${crypto.randomUUID()}.new`;
5042
+ await fs.writeFile(fresh4, json5, owner);
5043
+ await fs.rename(fresh4, TAGS_PATH);
4810
5044
  }
4811
5045
  async install(path, target, text, caller) {
4812
5046
  if (text.length > CRONTAB_MAX) return { error: "crontab: the table is too long" };
4813
5047
  const { errors } = parseCrontab(text, path);
4814
5048
  if (errors.length > 0) {
4815
- const lines2 = errors.map(({ line, message: message3 }) => `"-":${line}: ${message3}`);
4816
- return { error: ["crontab: errors in crontab file, cannot install", ...lines2].join("\n") };
5049
+ const lines3 = errors.map(({ line, message: message3 }) => `"-":${line}: ${message3}`);
5050
+ return { error: ["crontab: errors in crontab file, cannot install", ...lines3].join("\n") };
4817
5051
  }
4818
5052
  const { fs, owners } = this.o;
4819
5053
  if (!await fs.exists(SPOOL_DIR)) {
4820
5054
  await fs.mkdir(SPOOL_DIR, { recursive: true });
4821
5055
  if (owners) await fs.chown?.(SPOOL_DIR, { uid: 0, gid: 0, mode: 448, follow: false });
4822
5056
  }
4823
- const fresh3 = `${SPOOL_DIR}/.${target.name}.${crypto.randomUUID()}.new`;
5057
+ const fresh4 = `${SPOOL_DIR}/.${target.name}.${crypto.randomUUID()}.new`;
4824
5058
  const owner = owners ? { uid: target.uid, gid: target.gid, mode: 384 } : void 0;
4825
5059
  await this.retag(path, text, caller);
4826
- await fs.writeFile(fresh3, text, owner);
4827
- await fs.rename(fresh3, path);
5060
+ await fs.writeFile(fresh4, text, owner);
5061
+ await fs.rename(fresh4, path);
4828
5062
  return { text: "" };
4829
5063
  }
4830
5064
  };
@@ -5158,15 +5392,15 @@ var UnlinkedKeeper = class {
5158
5392
  const releases = [];
5159
5393
  const take = async () => {
5160
5394
  for (; ; ) {
5161
- const fresh3 = [...this.heldWithin(target).keys()].filter((path) => !paths.includes(path));
5162
- if (fresh3.length === 0) return;
5163
- fresh3.sort();
5164
- if (paths.some((path) => path > fresh3[0])) {
5395
+ const fresh4 = [...this.heldWithin(target).keys()].filter((path) => !paths.includes(path));
5396
+ if (fresh4.length === 0) return;
5397
+ fresh4.sort();
5398
+ if (paths.some((path) => path > fresh4[0])) {
5165
5399
  for (const release of releases.splice(0)) release();
5166
- fresh3.push(...paths.splice(0));
5167
- fresh3.sort();
5400
+ fresh4.push(...paths.splice(0));
5401
+ fresh4.sort();
5168
5402
  }
5169
- for (const path of fresh3) {
5403
+ for (const path of fresh4) {
5170
5404
  paths.push(path);
5171
5405
  releases.push(await this.gate.acquire(path));
5172
5406
  }
@@ -5562,10 +5796,10 @@ var Output = class {
5562
5796
  };
5563
5797
  function readStamp(text) {
5564
5798
  try {
5565
- const parsed2 = JSON.parse(text ?? "");
5566
- const jobs = parsed2.jobs && typeof parsed2.jobs === "object" ? parsed2.jobs : {};
5567
- const reboot = parsed2.reboot && typeof parsed2.reboot === "object" ? parsed2.reboot : {};
5568
- const when = parsed2.when && typeof parsed2.when === "object" ? parsed2.when : {};
5799
+ const parsed3 = JSON.parse(text ?? "");
5800
+ const jobs = parsed3.jobs && typeof parsed3.jobs === "object" ? parsed3.jobs : {};
5801
+ const reboot = parsed3.reboot && typeof parsed3.reboot === "object" ? parsed3.reboot : {};
5802
+ const when = parsed3.when && typeof parsed3.when === "object" ? parsed3.when : {};
5569
5803
  return { jobs: { ...jobs }, reboot: { ...reboot }, when: { ...when } };
5570
5804
  } catch {
5571
5805
  return { jobs: {}, reboot: {}, when: {} };
@@ -5685,10 +5919,10 @@ var CronService = class {
5685
5919
  const text = JSON.stringify(stamp);
5686
5920
  const old = await fs.readFile(STAMP_PATH).catch(() => void 0);
5687
5921
  if (old === text || old === void 0 && text === EMPTY_STAMP) return;
5688
- const fresh3 = `${STAMP_PATH}.new`;
5922
+ const fresh4 = `${STAMP_PATH}.new`;
5689
5923
  const owner = owners ? { uid: 0, gid: 0, mode: 384 } : void 0;
5690
- await fs.writeFile(fresh3, text, owner);
5691
- await fs.rename(fresh3, STAMP_PATH);
5924
+ await fs.writeFile(fresh4, text, owner);
5925
+ await fs.rename(fresh4, STAMP_PATH);
5692
5926
  }
5693
5927
  async load() {
5694
5928
  const { fs } = this.host;
@@ -5741,10 +5975,10 @@ var CronService = class {
5741
5975
  await this.once(path, text, errors);
5742
5976
  return table;
5743
5977
  }
5744
- async once(path, version, lines2) {
5978
+ async once(path, version, lines3) {
5745
5979
  if (this.reported.get(path) === version) return;
5746
5980
  this.reported.set(path, version);
5747
- for (const line of lines2) await this.error(line);
5981
+ for (const line of lines3) await this.error(line);
5748
5982
  }
5749
5983
  async error(line) {
5750
5984
  if (this.stopping.signal.aborted) return;
@@ -6046,8 +6280,8 @@ function canonicalHost2(host) {
6046
6280
  function localV4(a, b) {
6047
6281
  return a === 127 || a === 0 || a === 169 && b === 254;
6048
6282
  }
6049
- function hextets(address) {
6050
- const [head, tail2, extra] = address.split("::");
6283
+ function hextets(address2) {
6284
+ const [head, tail2, extra] = address2.split("::");
6051
6285
  if (extra !== void 0) return void 0;
6052
6286
  const part = (text) => text ? text.split(":").map((h) => Number.parseInt(h, 16)) : [];
6053
6287
  const front = part(head);
@@ -6055,8 +6289,8 @@ function hextets(address) {
6055
6289
  const groups = tail2 === void 0 ? front : [...front, ...new Array(8 - front.length - back.length).fill(0), ...back];
6056
6290
  return groups.length === 8 && groups.every((g) => g >= 0 && g <= 65535) ? groups : void 0;
6057
6291
  }
6058
- function localV6(address) {
6059
- const g = hextets(address);
6292
+ function localV6(address2) {
6293
+ const g = hextets(address2);
6060
6294
  if (!g) return true;
6061
6295
  if ((g[0] & 65472) === 65152) return true;
6062
6296
  const zeroPrefix = g.slice(0, 5).every((x) => x === 0);
@@ -6429,8 +6663,8 @@ var RealmProxy = class {
6429
6663
  }
6430
6664
  async connect(conn, incoming, req, idle2) {
6431
6665
  const target = tunnelTarget(req);
6432
- const refused4 = target.port === 443 ? await this.tunnelReady(target) : void 0;
6433
- if (refused4) throw refused4;
6666
+ const refused5 = target.port === 443 ? await this.tunnelReady(target) : void 0;
6667
+ if (refused5) throw refused5;
6434
6668
  await conn.write(latin1Bytes("HTTP/1.1 200 Connection Established\r\n\r\n"), this.stop.signal);
6435
6669
  try {
6436
6670
  const first = await incoming.peek(this.stop.signal);
@@ -6986,15 +7220,15 @@ async function followLinks(fs, path) {
6986
7220
  }
6987
7221
  var NOBODY = { ruid: -1, euid: -1, suid: -1, rgid: -1, egid: -1, sgid: -1, groups: [] };
6988
7222
  var EMPTY2 = new Uint8Array(0);
6989
- function quiet2(promise) {
7223
+ function quiet3(promise) {
6990
7224
  return promise.catch(() => void 0);
6991
7225
  }
6992
7226
  async function readsAs2(fs, cred, target) {
6993
- const walked = await quiet2(access(fs, cred, target, R_OK, { directory: false }));
7227
+ const walked = await quiet3(access(fs, cred, target, R_OK, { directory: false }));
6994
7228
  return walked !== void 0 && walked.denied === void 0 && !walked.missing;
6995
7229
  }
6996
7230
  async function shownTarget(fs, target) {
6997
- const st = await quiet2(fs.stat(target));
7231
+ const st = await quiet3(fs.stat(target));
6998
7232
  return st?.isFile && await readsAs2(fs, NOBODY, target) ? st : void 0;
6999
7233
  }
7000
7234
  async function madeFor(owner, virtual) {
@@ -7317,10 +7551,10 @@ var FreshModules = class {
7317
7551
  this.fresh.set(key, { umask: mask, pid, added: 0, setup: mask !== DEFAULT_UMASK });
7318
7552
  }
7319
7553
  forget(pid) {
7320
- for (const [path, fresh3] of this.fresh) if (fresh3.pid === pid) this.fresh.delete(path);
7554
+ for (const [path, fresh4] of this.fresh) if (fresh4.pid === pid) this.fresh.delete(path);
7321
7555
  }
7322
- drop(fresh3) {
7323
- for (const [key, note] of [...this.fresh]) if (note === fresh3) this.fresh.delete(key);
7556
+ drop(fresh4) {
7557
+ for (const [key, note] of [...this.fresh]) if (note === fresh4) this.fresh.delete(key);
7324
7558
  }
7325
7559
  moved(from, to) {
7326
7560
  const source = normalizePath(from);
@@ -7329,10 +7563,10 @@ var FreshModules = class {
7329
7563
  for (const key of [...this.fresh.keys()]) {
7330
7564
  if (key === target || within3(key, target)) this.fresh.delete(key);
7331
7565
  }
7332
- for (const [key, fresh3] of [...this.fresh]) {
7566
+ for (const [key, fresh4] of [...this.fresh]) {
7333
7567
  if (key !== source && !within3(key, source)) continue;
7334
7568
  this.fresh.delete(key);
7335
- this.fresh.set(target + key.slice(source.length), fresh3);
7569
+ this.fresh.set(target + key.slice(source.length), fresh4);
7336
7570
  }
7337
7571
  }
7338
7572
  wrap(fs) {
@@ -7342,15 +7576,15 @@ var FreshModules = class {
7342
7576
  };
7343
7577
  const decide = async (given, known) => {
7344
7578
  const path = normalizePath(given);
7345
- const fresh3 = this.fresh.get(path);
7346
- if (!fresh3) return;
7579
+ const fresh4 = this.fresh.get(path);
7580
+ if (!fresh4) return;
7347
7581
  const bytes = known && known.length >= MAGIC.length ? known : await head(path);
7348
- const want = isModule(bytes) ? 73 & ~fresh3.umask : 0;
7349
- fresh3.setup = false;
7350
- if (want === fresh3.added) return;
7582
+ const want2 = isModule(bytes) ? 73 & ~fresh4.umask : 0;
7583
+ fresh4.setup = false;
7584
+ if (want2 === fresh4.added) return;
7351
7585
  const mode = (await fs.stat(path)).mode & 4095;
7352
- await fs.chmod(path, mode & ~fresh3.added | want);
7353
- fresh3.added = want;
7586
+ await fs.chmod(path, mode & ~fresh4.added | want2);
7587
+ fresh4.added = want2;
7354
7588
  };
7355
7589
  const overrides = {
7356
7590
  writeFile: async (path, content, owner) => {
@@ -7363,9 +7597,9 @@ var FreshModules = class {
7363
7597
  }
7364
7598
  },
7365
7599
  chmod: async (path, mode) => {
7366
- const fresh3 = this.fresh.get(normalizePath(path));
7367
- if (fresh3?.setup && (mode & 4095) === (438 & ~fresh3.umask)) fresh3.setup = false;
7368
- else if (fresh3) this.drop(fresh3);
7600
+ const fresh4 = this.fresh.get(normalizePath(path));
7601
+ if (fresh4?.setup && (mode & 4095) === (438 & ~fresh4.umask)) fresh4.setup = false;
7602
+ else if (fresh4) this.drop(fresh4);
7369
7603
  await fs.chmod(path, mode);
7370
7604
  },
7371
7605
  rm: async (path, options) => {
@@ -7382,16 +7616,16 @@ var FreshModules = class {
7382
7616
  link: async (from, to, follow2) => {
7383
7617
  await fs.link(from, to, follow2);
7384
7618
  const source = follow2 && fs.realpath ? await fs.realpath(from, true).catch(() => from) : from;
7385
- const fresh3 = this.fresh.get(normalizePath(source));
7386
- if (fresh3) this.fresh.set(normalizePath(to), fresh3);
7619
+ const fresh4 = this.fresh.get(normalizePath(source));
7620
+ if (fresh4) this.fresh.set(normalizePath(to), fresh4);
7387
7621
  }
7388
7622
  };
7389
7623
  const { append } = fs;
7390
7624
  if (append) {
7391
7625
  overrides.append = async (path, bytes, options) => {
7392
- const fresh3 = this.fresh.has(normalizePath(path));
7626
+ const fresh4 = this.fresh.has(normalizePath(path));
7393
7627
  const end = await append.call(fs, path, bytes, options);
7394
- if (fresh3 && end - bytes.length < MAGIC.length) await decide(path);
7628
+ if (fresh4 && end - bytes.length < MAGIC.length) await decide(path);
7395
7629
  return end;
7396
7630
  };
7397
7631
  }
@@ -8246,7 +8480,7 @@ var FsWatchers = class {
8246
8480
  };
8247
8481
  const content = (run2, types) => async (...args) => {
8248
8482
  const at = normalizePath(args[0]);
8249
- const fresh3 = this.covers(at) && !await fs.exists(at).catch(() => true);
8483
+ const fresh4 = this.covers(at) && !await fs.exists(at).catch(() => true);
8250
8484
  let done4 = false;
8251
8485
  try {
8252
8486
  await run2(...args);
@@ -8255,11 +8489,11 @@ var FsWatchers = class {
8255
8489
  const real = await realOf(at);
8256
8490
  const paths = real === at ? [at] : [at, real];
8257
8491
  this.report(paths);
8258
- if (done4) for (const type of types(fresh3, args)) this.emit(type, paths);
8492
+ if (done4) for (const type of types(fresh4, args)) this.emit(type, paths);
8259
8493
  }
8260
8494
  };
8261
- const written2 = (fresh3) => fresh3 ? ["rename", "change"] : ["change"];
8262
- const overwritten = (fresh3, args) => fresh3 || args[1].length === 0 ? written2(fresh3) : ["change", "change"];
8495
+ const written2 = (fresh4) => fresh4 ? ["rename", "change"] : ["change"];
8496
+ const overwritten = (fresh4, args) => fresh4 || args[1].length === 0 ? written2(fresh4) : ["change", "change"];
8263
8497
  const reshaping = (run2) => async (...args) => {
8264
8498
  this.reshaped();
8265
8499
  try {
@@ -8292,14 +8526,14 @@ var FsWatchers = class {
8292
8526
  if (created4.length > 0 || !await fs.exists(ancestor)) created4.push(ancestor);
8293
8527
  }
8294
8528
  }
8295
- const fresh3 = !options?.recursive || this.typed.size > 0 && !await fs.exists(path);
8529
+ const fresh4 = !options?.recursive || this.typed.size > 0 && !await fs.exists(path);
8296
8530
  let done4 = false;
8297
8531
  try {
8298
8532
  await fs.mkdir(path, options);
8299
8533
  done4 = true;
8300
8534
  } finally {
8301
8535
  this.report([...created4, path]);
8302
- if (done4) this.emit("rename", fresh3 ? [...created4, path] : created4);
8536
+ if (done4) this.emit("rename", fresh4 ? [...created4, path] : created4);
8303
8537
  }
8304
8538
  },
8305
8539
  rm: reshaping(async (path, options) => {
@@ -8809,15 +9043,15 @@ function embedderFailure(err) {
8809
9043
  return new KernelError(ERRNOS[String(name2)] ?? "EIO", lineOf2(err));
8810
9044
  }
8811
9045
  function headerOf(bytes) {
8812
- let parsed2;
9046
+ let parsed3;
8813
9047
  try {
8814
- parsed2 = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
9048
+ parsed3 = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
8815
9049
  } catch {
8816
9050
  throw new KernelError("EINVAL", "the request header is not JSON");
8817
9051
  }
8818
- if (typeof parsed2 !== "object" || parsed2 === null || Array.isArray(parsed2))
9052
+ if (typeof parsed3 !== "object" || parsed3 === null || Array.isArray(parsed3))
8819
9053
  throw new KernelError("EINVAL", "the request header is not an object");
8820
- const { kind, ...options } = parsed2;
9054
+ const { kind, ...options } = parsed3;
8821
9055
  if (typeof kind !== "string" || kind.length > 64 || !KIND.test(kind))
8822
9056
  throw new KernelError("EINVAL", "the request has no valid kind");
8823
9057
  return { kind, options };
@@ -9258,6 +9492,970 @@ function kindsOf(hook) {
9258
9492
  );
9259
9493
  }
9260
9494
 
9495
+ // src/kernel/serial-ioctl.ts
9496
+ var IOCTL = {
9497
+ TCGETS: 21505,
9498
+ TCSETS: 21506,
9499
+ TCSETSW: 21507,
9500
+ TCSETSF: 21508,
9501
+ TCSBRK: 21513,
9502
+ TCFLSH: 21515,
9503
+ TIOCEXCL: 21516,
9504
+ TIOCNXCL: 21517,
9505
+ TIOCSCTTY: 21518,
9506
+ TIOCGPGRP: 21519,
9507
+ TIOCSPGRP: 21520,
9508
+ TIOCOUTQ: 21521,
9509
+ TIOCGWINSZ: 21523,
9510
+ TIOCSWINSZ: 21524,
9511
+ TIOCMGET: 21525,
9512
+ TIOCMBIS: 21526,
9513
+ TIOCMBIC: 21527,
9514
+ TIOCMSET: 21528,
9515
+ FIONREAD: 21531,
9516
+ TIOCNOTTY: 21538,
9517
+ TCSBRKP: 21541,
9518
+ TIOCSBRK: 21543,
9519
+ TIOCCBRK: 21544,
9520
+ TCGETS2: 2150388778,
9521
+ TCSETS2: 1076646955,
9522
+ TCSETSW2: 1076646956,
9523
+ TCSETSF2: 1076646957
9524
+ };
9525
+ var TERMIOS_GETS = /* @__PURE__ */ new Set([IOCTL.TCGETS, IOCTL.TCGETS2]);
9526
+ var TERMIOS_SETS = /* @__PURE__ */ new Set([
9527
+ IOCTL.TCSETS,
9528
+ IOCTL.TCSETSW,
9529
+ IOCTL.TCSETSF,
9530
+ IOCTL.TCSETS2,
9531
+ IOCTL.TCSETSW2,
9532
+ IOCTL.TCSETSF2
9533
+ ]);
9534
+ var INT_OUT = /* @__PURE__ */ new Set([
9535
+ IOCTL.TIOCMGET,
9536
+ IOCTL.FIONREAD,
9537
+ IOCTL.TIOCOUTQ
9538
+ ]);
9539
+ var INT_IN = /* @__PURE__ */ new Set([
9540
+ IOCTL.TIOCMBIS,
9541
+ IOCTL.TIOCMBIC,
9542
+ IOCTL.TIOCMSET
9543
+ ]);
9544
+ var INT_ARG = /* @__PURE__ */ new Set([IOCTL.TCSBRK, IOCTL.TCSBRKP, IOCTL.TCFLSH]);
9545
+ var TIOCM = {
9546
+ DTR: 2,
9547
+ RTS: 4,
9548
+ CTS: 32,
9549
+ CD: 64,
9550
+ RI: 128,
9551
+ DSR: 256
9552
+ };
9553
+ var CBAUD = 4111;
9554
+ var BOTHER = 4096;
9555
+ var IBSHIFT = 16;
9556
+ var CSIZE = 48;
9557
+ var CSTOPB = 64;
9558
+ var PARENB = 256;
9559
+ var PARODD = 512;
9560
+ var CRTSCTS = 2147483648;
9561
+ var SPEEDS = [
9562
+ 0,
9563
+ 50,
9564
+ 75,
9565
+ 110,
9566
+ 134,
9567
+ 150,
9568
+ 200,
9569
+ 300,
9570
+ 600,
9571
+ 1200,
9572
+ 1800,
9573
+ 2400,
9574
+ 4800,
9575
+ 9600,
9576
+ 19200,
9577
+ 38400
9578
+ ];
9579
+ var FAST = [
9580
+ 0,
9581
+ 57600,
9582
+ 115200,
9583
+ 230400,
9584
+ 460800,
9585
+ 5e5,
9586
+ 576e3,
9587
+ 921600,
9588
+ 1e6,
9589
+ 1152e3,
9590
+ 15e5,
9591
+ 2e6,
9592
+ 25e5,
9593
+ 3e6,
9594
+ 35e5,
9595
+ 4e6
9596
+ ];
9597
+ function speedOf(code) {
9598
+ if (code === BOTHER) return void 0;
9599
+ return code & BOTHER ? FAST[code & 15] : SPEEDS[code & 15];
9600
+ }
9601
+ function speedsOf(termios, current, extended) {
9602
+ const out = termios.c_cflag & CBAUD;
9603
+ const ospeed = speedOf(out) ?? (extended ? (termios.c_ospeed ?? current.ospeed) >>> 0 : current.ospeed);
9604
+ const inCode = termios.c_cflag >>> IBSHIFT & CBAUD;
9605
+ if (inCode === 0) return { ispeed: ospeed, ospeed };
9606
+ const ispeed = speedOf(inCode) ?? (extended ? (termios.c_ispeed ?? current.ispeed) >>> 0 : current.ispeed);
9607
+ return { ispeed, ospeed };
9608
+ }
9609
+ function settingsOf(cflag, baudRate) {
9610
+ return {
9611
+ baudRate,
9612
+ dataBits: 5 + ((cflag & CSIZE) >> 4),
9613
+ parity: cflag & PARENB ? cflag & PARODD ? "odd" : "even" : "none",
9614
+ stopBits: cflag & CSTOPB ? 2 : 1,
9615
+ flowControl: cflag & CRTSCTS ? "hardware" : "none"
9616
+ };
9617
+ }
9618
+ function sameSettings(a, b) {
9619
+ return a.baudRate === b.baudRate && a.dataBits === b.dataBits && a.parity === b.parity && a.stopBits === b.stopBits && a.flowControl === b.flowControl;
9620
+ }
9621
+ var SERIAL_CFLAG = 13 | 48 | 128 | 1024 | 2048;
9622
+ var SERIAL_LFLAG = 16 | 32 | 512 | 2048;
9623
+ var SERIAL_OFLAG = 4;
9624
+
9625
+ // src/kernel/tty.ts
9626
+ var ICRNL = 256;
9627
+ var IUTF8 = 16384;
9628
+ var OPOST = 1;
9629
+ var ONLCR = 4;
9630
+ var ISIG = 1;
9631
+ var ICANON = 2;
9632
+ var ECHO = 8;
9633
+ var ECHOE = 16;
9634
+ var ECHOCTL = 512;
9635
+ var NOFLSH = 128;
9636
+ var IEXTEN = 32768;
9637
+ var VINTR = 0;
9638
+ var VQUIT = 1;
9639
+ var VERASE = 2;
9640
+ var VKILL = 3;
9641
+ var VEOF = 4;
9642
+ var VSUSP = 10;
9643
+ var VWERASE = 14;
9644
+ function defaultTermios() {
9645
+ const cCc = new Array(32).fill(0);
9646
+ Object.assign(cCc, { [VINTR]: 3, [VQUIT]: 28, [VERASE]: 127, [VKILL]: 21 });
9647
+ Object.assign(cCc, { [VEOF]: 4, 6: 1, 8: 17, 9: 19, [VSUSP]: 26 });
9648
+ Object.assign(cCc, { 12: 18, 13: 15, [VWERASE]: 23, 15: 22 });
9649
+ return {
9650
+ c_iflag: ICRNL | 1024 | 8192 | IUTF8,
9651
+ c_oflag: OPOST | ONLCR,
9652
+ c_cflag: 15 | 48 | 128,
9653
+ c_lflag: ISIG | ICANON | ECHO | ECHOE | 32 | ECHOCTL | 2048 | IEXTEN,
9654
+ c_cc: cCc
9655
+ };
9656
+ }
9657
+ var encoder3 = new TextEncoder();
9658
+ function isContinuation(byte) {
9659
+ return (byte & 192) === 128;
9660
+ }
9661
+ var KernelTty = class {
9662
+ termios = defaultTermios();
9663
+ cols = 80;
9664
+ rows = 24;
9665
+ readable = [];
9666
+ line = [];
9667
+ waiters = [];
9668
+ hungUp = false;
9669
+ name;
9670
+ owner;
9671
+ onRead;
9672
+ readers = 0;
9673
+ screen;
9674
+ signal;
9675
+ constructor(screen, signal) {
9676
+ this.screen = screen;
9677
+ this.signal = signal;
9678
+ }
9679
+ receive(bytes) {
9680
+ const { c_iflag, c_lflag } = this.termios;
9681
+ if (!(c_iflag & ICRNL) && !(c_lflag & (ISIG | ICANON | ECHO))) {
9682
+ if (bytes.length > 0) this.readable.push(bytes.slice());
9683
+ } else for (const byte of bytes) this.key(byte);
9684
+ this.wake();
9685
+ }
9686
+ resize(cols, rows) {
9687
+ if (cols === this.cols && rows === this.rows) return;
9688
+ this.setSize(cols, rows);
9689
+ this.signal(SIG.WINCH);
9690
+ }
9691
+ hangup() {
9692
+ this.hungUp = true;
9693
+ this.wake();
9694
+ }
9695
+ signalHangup() {
9696
+ this.signal(SIG.HUP);
9697
+ }
9698
+ setSize(cols, rows) {
9699
+ this.cols = cols;
9700
+ this.rows = rows;
9701
+ }
9702
+ tcgets() {
9703
+ return { ...this.termios, c_cc: [...this.termios.c_cc] };
9704
+ }
9705
+ tcsets(termios, flush = false) {
9706
+ const wasCanonical = this.canonical;
9707
+ this.termios = { ...termios, c_cc: [...termios.c_cc] };
9708
+ if (flush) {
9709
+ this.line = [];
9710
+ this.readable = [];
9711
+ }
9712
+ if (wasCanonical && !this.canonical && this.line.length > 0) {
9713
+ this.readable.push(Uint8Array.from(this.line));
9714
+ this.line = [];
9715
+ this.wake();
9716
+ }
9717
+ }
9718
+ winsize() {
9719
+ return [this.rows, this.cols];
9720
+ }
9721
+ file() {
9722
+ return new OpenFile({
9723
+ read: (max, signal) => this.read(max, signal),
9724
+ write: async (bytes) => {
9725
+ if (this.hungUp) throw new KernelError("EIO");
9726
+ this.output(bytes);
9727
+ return bytes.length;
9728
+ },
9729
+ poll: () => ({
9730
+ readable: this.readable.length > 0 || this.hungUp,
9731
+ writable: true,
9732
+ hangup: this.hungUp,
9733
+ nread: this.readable.reduce((n, chunk2) => n + (chunk2?.length ?? 0), 0)
9734
+ }),
9735
+ changed: (signal) => this.changed(signal),
9736
+ close: () => {
9737
+ },
9738
+ tty: this
9739
+ });
9740
+ }
9741
+ get canonical() {
9742
+ return (this.termios.c_lflag & ICANON) !== 0;
9743
+ }
9744
+ cc(slot2) {
9745
+ return this.termios.c_cc[slot2] ?? 0;
9746
+ }
9747
+ key(input) {
9748
+ const { c_iflag, c_lflag } = this.termios;
9749
+ const byte = input === 13 && c_iflag & ICRNL ? 10 : input;
9750
+ if (c_lflag & ISIG && this.signalKey(byte)) return;
9751
+ if (!this.canonical) {
9752
+ this.readable.push(Uint8Array.of(byte));
9753
+ if (c_lflag & ECHO) this.echo(byte);
9754
+ return;
9755
+ }
9756
+ this.edit(byte);
9757
+ }
9758
+ signalKey(byte) {
9759
+ const sig = byte === this.cc(VINTR) ? SIG.INT : byte === this.cc(VQUIT) ? SIG.QUIT : byte === this.cc(VSUSP) ? SIG.TSTP : 0;
9760
+ if (!sig || byte === 0) return false;
9761
+ if (this.termios.c_lflag & ECHO) this.echo(byte);
9762
+ if (!(this.termios.c_lflag & NOFLSH)) {
9763
+ this.line = [];
9764
+ this.readable = [];
9765
+ }
9766
+ this.signal(sig);
9767
+ return true;
9768
+ }
9769
+ edit(byte) {
9770
+ const echo = (this.termios.c_lflag & ECHO) !== 0;
9771
+ if (byte === this.cc(VERASE) || byte === 8) {
9772
+ this.rubOut(this.lastCharLength());
9773
+ } else if (byte === this.cc(VKILL)) {
9774
+ this.rubOut(this.line.length);
9775
+ } else if (byte === this.cc(VWERASE)) {
9776
+ this.rubOut(this.lastWordLength());
9777
+ } else if (byte === this.cc(VEOF)) {
9778
+ this.readable.push(this.line.length > 0 ? Uint8Array.from(this.line) : null);
9779
+ this.line = [];
9780
+ } else if (byte === 10) {
9781
+ this.line.push(byte);
9782
+ this.readable.push(Uint8Array.from(this.line));
9783
+ this.line = [];
9784
+ if (echo) this.output(encoder3.encode("\n"));
9785
+ } else {
9786
+ this.line.push(byte);
9787
+ if (echo) this.echo(byte);
9788
+ }
9789
+ }
9790
+ lastWordLength() {
9791
+ let end = this.line.length;
9792
+ while (end > 0 && this.line[end - 1] === 32) end--;
9793
+ while (end > 0 && this.line[end - 1] !== 32) end--;
9794
+ return this.line.length - end;
9795
+ }
9796
+ lastCharLength() {
9797
+ const { line } = this;
9798
+ let n = Math.min(1, line.length);
9799
+ if (this.termios.c_iflag & IUTF8) {
9800
+ while (n < line.length && isContinuation(line[line.length - n] ?? 0)) n++;
9801
+ }
9802
+ return n;
9803
+ }
9804
+ rubOut(n) {
9805
+ const dropped2 = this.line.splice(this.line.length - n, n);
9806
+ const utf83 = (this.termios.c_iflag & IUTF8) !== 0;
9807
+ const cells = utf83 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
9808
+ if (cells > 0 && this.termios.c_lflag & ECHO) {
9809
+ this.output(encoder3.encode("\b \b".repeat(cells)), false);
9810
+ }
9811
+ }
9812
+ echo(byte) {
9813
+ if (byte < 32 && byte !== 10 && byte !== 9 && this.termios.c_lflag & ECHOCTL) {
9814
+ this.output(Uint8Array.of(94, byte + 64), false);
9815
+ return;
9816
+ }
9817
+ this.output(Uint8Array.of(byte));
9818
+ }
9819
+ output(bytes, translate2 = true) {
9820
+ const { c_oflag } = this.termios;
9821
+ if (!translate2 || !(c_oflag & OPOST && c_oflag & ONLCR) || !bytes.includes(10)) {
9822
+ this.screen.write(bytes);
9823
+ return;
9824
+ }
9825
+ const out = [];
9826
+ for (const byte of bytes) {
9827
+ if (byte === 10) out.push(13);
9828
+ out.push(byte);
9829
+ }
9830
+ this.screen.write(Uint8Array.from(out));
9831
+ }
9832
+ get reading() {
9833
+ return this.readers > 0;
9834
+ }
9835
+ async read(max, signal) {
9836
+ this.onRead?.();
9837
+ this.readers++;
9838
+ try {
9839
+ return await this.take(max, signal);
9840
+ } finally {
9841
+ this.readers--;
9842
+ }
9843
+ }
9844
+ async take(max, signal) {
9845
+ while (this.readable.length === 0) {
9846
+ if (this.hungUp) return new Uint8Array(0);
9847
+ await this.changed(signal);
9848
+ }
9849
+ const head = this.readable[0];
9850
+ if (head === null) {
9851
+ this.readable.shift();
9852
+ return new Uint8Array(0);
9853
+ }
9854
+ if (head === void 0) return new Uint8Array(0);
9855
+ const chunks2 = [];
9856
+ let n = 0;
9857
+ while (this.readable.length > 0 && n < max) {
9858
+ const next3 = this.readable[0];
9859
+ if (next3 === null || next3 === void 0) break;
9860
+ const take = next3.subarray(0, max - n);
9861
+ chunks2.push(take);
9862
+ n += take.length;
9863
+ if (take.length < next3.length) this.readable[0] = next3.subarray(take.length);
9864
+ else this.readable.shift();
9865
+ if (this.canonical) break;
9866
+ }
9867
+ const out = new Uint8Array(n);
9868
+ let at = 0;
9869
+ for (const chunk2 of chunks2) {
9870
+ out.set(chunk2, at);
9871
+ at += chunk2.length;
9872
+ }
9873
+ return out;
9874
+ }
9875
+ changed(signal) {
9876
+ if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
9877
+ return new Promise((resolve2, reject) => {
9878
+ const onAbort = () => {
9879
+ this.waiters = this.waiters.filter((w) => w !== waiter);
9880
+ reject(new KernelError("EINTR"));
9881
+ };
9882
+ const waiter = () => {
9883
+ signal?.removeEventListener("abort", onAbort);
9884
+ resolve2();
9885
+ };
9886
+ this.waiters.push(waiter);
9887
+ signal?.addEventListener("abort", onAbort, { once: true });
9888
+ });
9889
+ }
9890
+ wake() {
9891
+ const waiters = this.waiters;
9892
+ this.waiters = [];
9893
+ for (const resolve2 of waiters) resolve2();
9894
+ }
9895
+ };
9896
+
9897
+ // src/kernel/devices.ts
9898
+ var DEVICE_NAME = /^tty(USB|ACM)(\d{1,3})$/;
9899
+ var BY_ID = "/dev/serial/by-id";
9900
+ var LINK_NAME = /^[A-Za-z0-9._:+@=,-]{1,255}$/;
9901
+ var MAJORS = { USB: 188, ACM: 166 };
9902
+ var HIGH_WATER = 64 * 1024;
9903
+ var CLOSING_WAIT_MS = 3e4;
9904
+ var BREAK_MS = 250;
9905
+ var MAX_ID2 = 4294967294;
9906
+ var DEFAULT_SETTINGS = settingsOf(SERIAL_CFLAG, 9600);
9907
+ function refused2(code, text) {
9908
+ return Object.assign(new Error(`${code}: ${text}`), { code });
9909
+ }
9910
+ function invalid(what) {
9911
+ return refused2("EINVAL", what);
9912
+ }
9913
+ function nodePath(path) {
9914
+ if (typeof path !== "string") throw invalid("path must be a string");
9915
+ const name2 = path.startsWith("/dev/") ? path.slice(5) : path;
9916
+ const m = DEVICE_NAME.exec(name2);
9917
+ const minor = Number(m?.[2]);
9918
+ if (!m || minor > 255) throw invalid(`not a serial device name: ${path}`);
9919
+ return { path: `/dev/${name2}`, major: MAJORS[m[1]], minor };
9920
+ }
9921
+ function linkPath(link2) {
9922
+ if (typeof link2 !== "string") throw invalid("a link must be a string");
9923
+ const name2 = link2.startsWith(`${BY_ID}/`) ? link2.slice(BY_ID.length + 1) : link2;
9924
+ if (!LINK_NAME.test(name2) || name2 === "." || name2 === "..")
9925
+ throw invalid(`not a link name: ${link2}`);
9926
+ return `${BY_ID}/${name2}`;
9927
+ }
9928
+ function modeOf(mode) {
9929
+ if (mode === void 0) return 384;
9930
+ if (!Number.isInteger(mode) || mode < 0 || mode > 4095)
9931
+ throw invalid("mode must be permission bits");
9932
+ return mode;
9933
+ }
9934
+ async function idOf(value2, find) {
9935
+ if (value2 === void 0) return 0;
9936
+ if (typeof value2 === "number") {
9937
+ if (!Number.isInteger(value2) || value2 < 0 || value2 > MAX_ID2) throw invalid(`bad id ${value2}`);
9938
+ return value2;
9939
+ }
9940
+ const id2 = typeof value2 === "string" ? await find(value2) : void 0;
9941
+ if (id2 === void 0) throw invalid(`unknown owner or group ${String(value2)}`);
9942
+ return id2;
9943
+ }
9944
+ function quiet4() {
9945
+ return void 0;
9946
+ }
9947
+ function want(access2) {
9948
+ return access2 === "read" ? R_OK : access2 === "write" ? W_OK : R_OK | W_OK;
9949
+ }
9950
+ function devicesHook(hook) {
9951
+ if (typeof hook?.open !== "function") throw new TypeError("devices needs open");
9952
+ return { open: (request) => hook.open(request) };
9953
+ }
9954
+ function checkMeta(node, change, uid, gid, guard2) {
9955
+ const { cred } = guard2;
9956
+ const owner = cred.euid === 0 || cred.euid === node.uid;
9957
+ const member = cred.egid === gid || cred.groups.includes(gid);
9958
+ const chown2 = uid !== node.uid || gid !== node.gid;
9959
+ const times = (change.atimeMs ?? change.mtimeMs ?? null) !== null;
9960
+ if (change.mode !== void 0 && !owner) deny(guard2, "chmod", node.path, "EPERM");
9961
+ if (chown2 && cred.euid !== 0 && (uid !== node.uid || !owner || !member))
9962
+ deny(guard2, "chown", node.path, "EPERM");
9963
+ if (times && !owner && !(change.now && permits(cred, node, W_OK, false)))
9964
+ deny(guard2, "utimes", node.path, change.now ? "EACCES" : "EPERM");
9965
+ }
9966
+ var DeviceNodes = class {
9967
+ hooks = /* @__PURE__ */ new Map();
9968
+ nodes = /* @__PURE__ */ new Map();
9969
+ links = /* @__PURE__ */ new Map();
9970
+ lookup;
9971
+ users;
9972
+ next = 0;
9973
+ constructor(users, lookup4) {
9974
+ this.users = users;
9975
+ this.lookup = lookup4;
9976
+ }
9977
+ register(client, hook) {
9978
+ this.hooks.set(client, hook);
9979
+ return () => {
9980
+ if (this.hooks.get(client) !== hook) return;
9981
+ this.hooks.delete(client);
9982
+ for (const node of [...this.nodes.values()]) if (node.client === client) this.drop(node);
9983
+ };
9984
+ }
9985
+ async add(spec, client) {
9986
+ if (typeof spec !== "object" || spec === null) throw invalid("add needs a spec");
9987
+ const { path, major, minor } = nodePath(spec.path);
9988
+ const mode = modeOf(spec.mode);
9989
+ if (spec.links !== void 0 && !Array.isArray(spec.links))
9990
+ throw invalid("links must be a list");
9991
+ const links = (spec.links ?? []).map(linkPath);
9992
+ const uid = await idOf(spec.owner, (name2) => this.lookup.userId(name2));
9993
+ const gid = await idOf(spec.group, (name2) => this.lookup.groupId(name2));
9994
+ if (!this.hooks.has(client)) throw refused2("ENOTSUP", "this client has no devices hook");
9995
+ if (this.nodes.has(path)) throw refused2("EEXIST", `${path} exists`);
9996
+ const taken = links.find((l, i) => this.links.has(l) || links.indexOf(l) !== i);
9997
+ if (taken !== void 0) throw refused2("EEXIST", `${taken} exists`);
9998
+ const node = {
9999
+ id: ++this.next,
10000
+ client,
10001
+ path,
10002
+ uid,
10003
+ gid,
10004
+ mode,
10005
+ major,
10006
+ minor,
10007
+ links,
10008
+ opening: false,
10009
+ atimeMs: Date.now(),
10010
+ mtimeMs: Date.now(),
10011
+ ctimeMs: Date.now()
10012
+ };
10013
+ this.nodes.set(path, node);
10014
+ for (const link2 of links) this.links.set(link2, node);
10015
+ return node.id;
10016
+ }
10017
+ remove(id2) {
10018
+ const node = [...this.nodes.values()].find((n) => n.id === id2);
10019
+ if (!node) return false;
10020
+ this.drop(node);
10021
+ return true;
10022
+ }
10023
+ drop(node) {
10024
+ this.nodes.delete(node.path);
10025
+ for (const link2 of node.links) this.links.delete(link2);
10026
+ node.channel?.lost(new KernelError("EIO", `${node.path} was removed`));
10027
+ }
10028
+ list() {
10029
+ return [...this.nodes.values()].sort((a, b) => a.path < b.path ? -1 : 1).map(({ path, uid, gid, mode, major, minor, links, atimeMs, mtimeMs, ctimeMs }) => ({
10030
+ path,
10031
+ uid,
10032
+ gid,
10033
+ mode,
10034
+ major,
10035
+ minor,
10036
+ links: [...links],
10037
+ atimeMs,
10038
+ mtimeMs,
10039
+ ctimeMs
10040
+ }));
10041
+ }
10042
+ meta(path, change, guard2) {
10043
+ const node = this.find(path);
10044
+ if (!node) throw new KernelError("ENOENT");
10045
+ const uid = change.uid === void 0 || change.uid === -1 ? node.uid : change.uid >>> 0;
10046
+ const gid = change.gid === void 0 || change.gid === -1 ? node.gid : change.gid >>> 0;
10047
+ if (guard2) checkMeta(node, change, uid, gid, guard2);
10048
+ const now = Date.now();
10049
+ if (change.mode !== void 0) node.mode = change.mode & 4095;
10050
+ node.uid = uid;
10051
+ node.gid = gid;
10052
+ const at = (ms, kept) => ms === void 0 || ms === null ? kept : change.now ? now : ms;
10053
+ node.atimeMs = at(change.atimeMs, node.atimeMs);
10054
+ node.mtimeMs = at(change.mtimeMs, node.mtimeMs);
10055
+ node.ctimeMs = now;
10056
+ if (node.channel) node.channel.tty.owner = { uid: node.uid, gid: node.gid, mode: node.mode };
10057
+ }
10058
+ find(path) {
10059
+ return this.nodes.get(path) ?? this.links.get(path);
10060
+ }
10061
+ openTty(path) {
10062
+ return this.nodes.get(path)?.channel?.tty;
10063
+ }
10064
+ async open(path, flags2, opener) {
10065
+ const node = this.find(path);
10066
+ if (!node) throw new KernelError("ENOENT");
10067
+ const access2 = (flags2 & 3) === 0 ? "read" : (flags2 & 3) === 1 ? "write" : "both";
10068
+ if (opener.guard && !permits(opener.guard.cred, node, want(access2), false))
10069
+ deny(opener.guard, "open", node.path);
10070
+ if (node.channel || node.opening) throw new KernelError("EBUSY");
10071
+ const hook = this.hooks.get(node.client);
10072
+ node.opening = true;
10073
+ const asked = Promise.resolve().then(
10074
+ () => hook.open({ path: node.path, flags: flags2 & 3, stamp: opener.stamp, signal: opener.signal })
10075
+ );
10076
+ let streams;
10077
+ try {
10078
+ streams = await interruptible2(asked, opener.signal);
10079
+ } catch (err) {
10080
+ if (err instanceof KernelError && err.code === "EINTR") {
10081
+ void asked.then(abandon, quiet4).finally(() => node.opening = false);
10082
+ } else node.opening = false;
10083
+ throw embedderFailure(err);
10084
+ }
10085
+ node.opening = false;
10086
+ if (!isStreams(streams)) throw new KernelError("EIO", "the devices hook gave no streams");
10087
+ if (this.nodes.get(node.path) !== node) {
10088
+ void abandon(streams);
10089
+ throw new KernelError("ENOENT");
10090
+ }
10091
+ const channel = new DeviceChannel(node.path, streams, node.saved, (saved) => {
10092
+ node.saved = saved;
10093
+ if (node.channel === channel) delete node.channel;
10094
+ });
10095
+ channel.tty.owner = { uid: node.uid, gid: node.gid, mode: node.mode };
10096
+ node.channel = channel;
10097
+ try {
10098
+ await channel.restore();
10099
+ } catch (err) {
10100
+ await channel.close();
10101
+ throw err;
10102
+ }
10103
+ return channel.file(access2);
10104
+ }
10105
+ };
10106
+ function isStreams(value2) {
10107
+ const s = value2;
10108
+ return !!s && s.readable instanceof ReadableStream && s.writable instanceof WritableStream && typeof s.control === "function";
10109
+ }
10110
+ async function abandon(streams) {
10111
+ if (!isStreams(streams)) return;
10112
+ await streams.readable.cancel("closed").catch(quiet4);
10113
+ await streams.writable.abort("closed").catch(quiet4);
10114
+ await Promise.resolve().then(() => streams.control("close")).catch(quiet4);
10115
+ }
10116
+ function serialTermios() {
10117
+ const base = defaultTermios();
10118
+ return {
10119
+ c_iflag: 0,
10120
+ c_oflag: SERIAL_OFLAG,
10121
+ c_cflag: SERIAL_CFLAG,
10122
+ c_lflag: SERIAL_LFLAG,
10123
+ c_cc: base.c_cc
10124
+ };
10125
+ }
10126
+ function sleep(ms, signal) {
10127
+ return interruptible2(new Promise((resolve2) => setTimeout(resolve2, ms)), signal);
10128
+ }
10129
+ var DeviceChannel = class {
10130
+ tty;
10131
+ path;
10132
+ inner;
10133
+ reader;
10134
+ writer;
10135
+ streams;
10136
+ onClosed;
10137
+ gone = Promise.withResolvers();
10138
+ failure;
10139
+ pending = /* @__PURE__ */ new Set();
10140
+ queued = 0;
10141
+ resume;
10142
+ waiters = [];
10143
+ closed = false;
10144
+ ispeed = 9600;
10145
+ ospeed = 9600;
10146
+ dtr = true;
10147
+ rts = true;
10148
+ constructor(path, streams, saved, onClosed) {
10149
+ this.path = path;
10150
+ this.streams = streams;
10151
+ this.onClosed = onClosed;
10152
+ this.gone.promise.catch(quiet4);
10153
+ this.tty = new KernelTty({ write: (bytes) => this.send(bytes) }, quiet4);
10154
+ this.tty.name = path;
10155
+ this.tty.setSize(0, 0);
10156
+ this.tty.tcsets(saved?.termios ?? serialTermios());
10157
+ if (saved) {
10158
+ this.ispeed = saved.ispeed;
10159
+ this.ospeed = saved.ospeed;
10160
+ this.dtr = this.rts = saved.ospeed !== 0;
10161
+ }
10162
+ this.inner = this.tty.file();
10163
+ this.reader = streams.readable.getReader();
10164
+ this.writer = streams.writable.getWriter();
10165
+ void this.pump();
10166
+ }
10167
+ file(access2) {
10168
+ return new OpenFile({
10169
+ ...access2 === "write" ? {} : { read: (max, signal) => this.read(max, signal) },
10170
+ ...access2 === "read" ? {} : { write: (bytes, signal) => this.write(bytes, signal) },
10171
+ poll: () => this.poll(),
10172
+ changed: (signal) => this.changed(signal),
10173
+ close: () => this.close(),
10174
+ tty: this.tty,
10175
+ device: this
10176
+ });
10177
+ }
10178
+ lost(err) {
10179
+ if (this.failure) return;
10180
+ this.failure = err;
10181
+ this.gone.reject(err);
10182
+ this.tty.hangup();
10183
+ this.wake();
10184
+ }
10185
+ control(op, args) {
10186
+ return Promise.resolve().then(() => this.streams.control(op, args));
10187
+ }
10188
+ buffered() {
10189
+ return this.inner.file.poll?.().nread ?? 0;
10190
+ }
10191
+ async pump() {
10192
+ for (; ; ) {
10193
+ while (this.buffered() >= HIGH_WATER && !this.closed && !this.failure) {
10194
+ await new Promise((resolve2) => {
10195
+ this.resume = resolve2;
10196
+ });
10197
+ }
10198
+ if (this.closed || this.failure) return;
10199
+ let got;
10200
+ try {
10201
+ got = await this.reader.read();
10202
+ } catch (err) {
10203
+ if (!this.closed) this.lost(new KernelError("EIO", lineOf2(err)));
10204
+ return;
10205
+ }
10206
+ if (got.done) {
10207
+ this.tty.hangup();
10208
+ this.wake();
10209
+ return;
10210
+ }
10211
+ this.tty.receive(got.value);
10212
+ this.wake();
10213
+ }
10214
+ }
10215
+ send(bytes) {
10216
+ if (this.closed || this.failure) return;
10217
+ const n = bytes.length;
10218
+ this.queued += n;
10219
+ const sent = this.writer.write(bytes.slice()).catch((err) => {
10220
+ this.lost(new KernelError("EIO", lineOf2(err)));
10221
+ });
10222
+ const tracked = sent.finally(() => {
10223
+ this.queued -= n;
10224
+ this.pending.delete(tracked);
10225
+ this.wake();
10226
+ });
10227
+ this.pending.add(tracked);
10228
+ }
10229
+ check() {
10230
+ if (this.failure) throw this.failure;
10231
+ }
10232
+ async read(max, signal) {
10233
+ this.check();
10234
+ const read = this.inner.file.read;
10235
+ const got = await read(max, signal);
10236
+ this.resume?.();
10237
+ this.resume = void 0;
10238
+ if (got.length === 0) this.check();
10239
+ return got;
10240
+ }
10241
+ async ready(signal) {
10242
+ this.check();
10243
+ try {
10244
+ await interruptible2(Promise.race([this.writer.ready, this.gone.promise]), signal);
10245
+ } catch (err) {
10246
+ if (err instanceof KernelError) throw err;
10247
+ throw new KernelError("EIO", lineOf2(err));
10248
+ }
10249
+ }
10250
+ async write(bytes, signal) {
10251
+ this.check();
10252
+ if (!((this.writer.desiredSize ?? 0) > 0)) await this.ready(signal);
10253
+ const write = this.inner.file.write;
10254
+ return write(bytes, signal);
10255
+ }
10256
+ poll() {
10257
+ if (this.failure)
10258
+ return { readable: true, writable: true, hangup: true, error: true, nread: 0 };
10259
+ const inner = this.inner.file.poll?.();
10260
+ const writable3 = (this.writer.desiredSize ?? 0) > 0;
10261
+ return { ...inner, writable: writable3 };
10262
+ }
10263
+ changed(signal) {
10264
+ const mine = new Promise((resolve2) => this.waiters.push(resolve2));
10265
+ const theirs = this.inner.file.changed?.(signal);
10266
+ return interruptible2(Promise.race([mine, theirs]), signal);
10267
+ }
10268
+ wake() {
10269
+ const waiters = this.waiters;
10270
+ this.waiters = [];
10271
+ for (const resolve2 of waiters) resolve2();
10272
+ }
10273
+ async drain(signal) {
10274
+ while (this.pending.size > 0) {
10275
+ this.check();
10276
+ await interruptible2(
10277
+ Promise.race([Promise.all([...this.pending]), this.gone.promise]),
10278
+ signal
10279
+ );
10280
+ }
10281
+ this.check();
10282
+ }
10283
+ async restore() {
10284
+ const settings = this.settings();
10285
+ if (settings.baudRate === 0)
10286
+ return this.signals({ dataTerminalReady: false, requestToSend: false });
10287
+ if (sameSettings(settings, DEFAULT_SETTINGS)) return;
10288
+ try {
10289
+ await this.control("configure", settings);
10290
+ } catch (err) {
10291
+ throw embedderFailure(err);
10292
+ }
10293
+ }
10294
+ settings() {
10295
+ return settingsOf(this.tty.tcgets().c_cflag, this.ospeed);
10296
+ }
10297
+ termios() {
10298
+ return { ...this.tty.tcgets(), c_ispeed: this.ispeed, c_ospeed: this.ospeed };
10299
+ }
10300
+ async setTermios(termios, how, signal) {
10301
+ this.check();
10302
+ if (how.drain) await this.drain(signal);
10303
+ const before = this.settings();
10304
+ const old = { termios: this.tty.tcgets(), ispeed: this.ispeed, ospeed: this.ospeed };
10305
+ const speeds = speedsOf(termios, this, how.extended === true);
10306
+ this.tty.tcsets(termios, how.flush === true);
10307
+ this.ispeed = speeds.ispeed;
10308
+ this.ospeed = speeds.ospeed;
10309
+ try {
10310
+ if (speeds.ospeed === 0) await this.setModem(this.bits() & ~(TIOCM.DTR | TIOCM.RTS));
10311
+ else {
10312
+ if (!sameSettings(before, this.settings()))
10313
+ await this.control("configure", this.settings());
10314
+ if (old.ospeed === 0) await this.setModem(this.bits() | TIOCM.DTR | TIOCM.RTS);
10315
+ }
10316
+ if (how.flush) await this.control("flush", { input: true, output: false });
10317
+ } catch (err) {
10318
+ this.tty.tcsets(old.termios);
10319
+ this.ispeed = old.ispeed;
10320
+ this.ospeed = old.ospeed;
10321
+ throw embedderFailure(err);
10322
+ }
10323
+ }
10324
+ bits() {
10325
+ return (this.dtr ? TIOCM.DTR : 0) | (this.rts ? TIOCM.RTS : 0);
10326
+ }
10327
+ async modem() {
10328
+ this.check();
10329
+ const s = await this.control("signals").catch((err) => {
10330
+ throw embedderFailure(err);
10331
+ });
10332
+ return this.bits() | (s?.clearToSend ? TIOCM.CTS : 0) | (s?.dataSetReady ? TIOCM.DSR : 0) | (s?.dataCarrierDetect ? TIOCM.CD : 0) | (s?.ringIndicator ? TIOCM.RI : 0);
10333
+ }
10334
+ async setModem(bits2) {
10335
+ this.check();
10336
+ const dataTerminalReady = (bits2 & TIOCM.DTR) !== 0;
10337
+ const requestToSend = (bits2 & TIOCM.RTS) !== 0;
10338
+ await this.signals({ dataTerminalReady, requestToSend });
10339
+ this.dtr = dataTerminalReady;
10340
+ this.rts = requestToSend;
10341
+ }
10342
+ async signals(args) {
10343
+ try {
10344
+ await this.control("setSignals", args);
10345
+ } catch (err) {
10346
+ throw embedderFailure(err);
10347
+ }
10348
+ }
10349
+ async sendBreak(ms, signal) {
10350
+ await this.drain(signal);
10351
+ await this.signals({ break: true });
10352
+ try {
10353
+ await sleep(ms, signal);
10354
+ } finally {
10355
+ await this.signals({ break: false });
10356
+ }
10357
+ }
10358
+ async flush(input, output) {
10359
+ this.check();
10360
+ if (input) this.tty.tcsets(this.tty.tcgets(), true);
10361
+ try {
10362
+ await this.control("flush", { input, output });
10363
+ } catch (err) {
10364
+ throw embedderFailure(err);
10365
+ }
10366
+ }
10367
+ async ioctl(request, arg, signal) {
10368
+ this.check();
10369
+ switch (request) {
10370
+ case IOCTL.TCGETS:
10371
+ case IOCTL.TCGETS2:
10372
+ return this.termios();
10373
+ case IOCTL.TCSETS:
10374
+ case IOCTL.TCSETSW:
10375
+ case IOCTL.TCSETSF:
10376
+ case IOCTL.TCSETS2:
10377
+ case IOCTL.TCSETSW2:
10378
+ case IOCTL.TCSETSF2: {
10379
+ const extended = request > 65535;
10380
+ const kind = (request & 255) - (extended ? 43 : 2);
10381
+ await this.setTermios(
10382
+ arg,
10383
+ { drain: kind > 0, flush: kind === 2, extended },
10384
+ signal
10385
+ );
10386
+ return null;
10387
+ }
10388
+ case IOCTL.TIOCMGET:
10389
+ return this.modem();
10390
+ case IOCTL.TIOCMSET:
10391
+ await this.setModem(Number(arg));
10392
+ return null;
10393
+ case IOCTL.TIOCMBIS:
10394
+ await this.setModem(this.bits() | Number(arg));
10395
+ return null;
10396
+ case IOCTL.TIOCMBIC:
10397
+ await this.setModem(this.bits() & ~Number(arg));
10398
+ return null;
10399
+ case IOCTL.TIOCSBRK:
10400
+ case IOCTL.TIOCCBRK:
10401
+ await this.signals({ break: request === IOCTL.TIOCSBRK });
10402
+ return null;
10403
+ case IOCTL.TCSBRK:
10404
+ if (Number(arg) === 0) await this.sendBreak(BREAK_MS, signal);
10405
+ else await this.drain(signal);
10406
+ return null;
10407
+ case IOCTL.TCSBRKP:
10408
+ await this.sendBreak(Number(arg) > 0 ? Number(arg) * 100 : BREAK_MS, signal);
10409
+ return null;
10410
+ case IOCTL.TCFLSH: {
10411
+ const queue = Number(arg);
10412
+ if (queue < 0 || queue > 2) throw new KernelError("EINVAL");
10413
+ await this.flush(queue !== 1, queue !== 0);
10414
+ return null;
10415
+ }
10416
+ case IOCTL.FIONREAD:
10417
+ return this.buffered();
10418
+ case IOCTL.TIOCOUTQ:
10419
+ return this.queued;
10420
+ case IOCTL.TIOCEXCL:
10421
+ case IOCTL.TIOCNXCL:
10422
+ return null;
10423
+ case IOCTL.TIOCGWINSZ: {
10424
+ const [rows, cols] = this.tty.winsize();
10425
+ return { rows, cols };
10426
+ }
10427
+ case IOCTL.TIOCSWINSZ: {
10428
+ const size = arg;
10429
+ this.tty.setSize(Number(size?.cols) & 65535, Number(size?.rows) & 65535);
10430
+ return null;
10431
+ }
10432
+ case IOCTL.TIOCSCTTY:
10433
+ throw new KernelError("EPERM");
10434
+ default:
10435
+ throw new KernelError("ENOTTY");
10436
+ }
10437
+ }
10438
+ async close() {
10439
+ if (this.closed) return;
10440
+ try {
10441
+ if (!this.failure) {
10442
+ await interruptible2(this.drain(), AbortSignal.timeout(CLOSING_WAIT_MS)).catch(quiet4);
10443
+ }
10444
+ this.closed = true;
10445
+ this.resume?.();
10446
+ this.wake();
10447
+ await this.reader.cancel("closed").catch(quiet4);
10448
+ this.reader.releaseLock();
10449
+ await this.writer.abort("closed").catch(quiet4);
10450
+ this.writer.releaseLock();
10451
+ await this.control("close").catch(quiet4);
10452
+ } finally {
10453
+ this.closed = true;
10454
+ this.onClosed({ termios: this.tty.tcgets(), ispeed: this.ispeed, ospeed: this.ospeed });
10455
+ }
10456
+ }
10457
+ };
10458
+
9261
10459
  // src/kernel/exec-ticket.ts
9262
10460
  var TICKET_ENV = "SLICC_SEALED_TICKET";
9263
10461
  var TICKET_TTL_MS = 3e4;
@@ -9647,8 +10845,8 @@ var DriverConnection = class _DriverConnection {
9647
10845
  } finally {
9648
10846
  clearTimeout(timer);
9649
10847
  }
9650
- const refused4 = driverVersionError(hello.protocol);
9651
- if (refused4) throw errnoError("EPROTO", refused4);
10848
+ const refused5 = driverVersionError(hello.protocol);
10849
+ if (refused5) throw errnoError("EPROTO", refused5);
9652
10850
  if (hello.error) throw errnoError(hello.errno ?? "EIO", hello.error);
9653
10851
  return new _DriverConnection(port, hello.capabilities ?? {}, waiting, timeoutMs, options);
9654
10852
  }
@@ -9694,7 +10892,7 @@ var DriverConnection = class _DriverConnection {
9694
10892
  var S_ISGID3 = 1024;
9695
10893
  var ID = /^\d{1,10}$/;
9696
10894
  var MASK = /^0*[0-7]{1,4}$/;
9697
- var MAX_ID2 = 4294967294;
10895
+ var MAX_ID3 = 4294967294;
9698
10896
  function idOption(value2) {
9699
10897
  return value2 === void 0 ? 0 : Number(value2);
9700
10898
  }
@@ -9706,7 +10904,7 @@ function checkIds(options) {
9706
10904
  for (const key of ["uid", "gid"]) {
9707
10905
  const value2 = options[key];
9708
10906
  if (value2 === void 0) continue;
9709
- if (!ID.test(value2) || Number(value2) > MAX_ID2) throw errnoError("EINVAL", `${key}=${value2}`);
10907
+ if (!ID.test(value2) || Number(value2) > MAX_ID3) throw errnoError("EINVAL", `${key}=${value2}`);
9710
10908
  }
9711
10909
  }
9712
10910
  function checkMasks(options) {
@@ -9718,7 +10916,7 @@ function checkMasks(options) {
9718
10916
  }
9719
10917
  }
9720
10918
  }
9721
- function modeOf(attr2, mount) {
10919
+ function modeOf2(attr2, mount) {
9722
10920
  const type = attr2.kind === "directory" ? 16384 : attr2.kind === "symlink" ? 40960 : attr2.kind === "fifo" ? 4096 : 32768;
9723
10921
  const fallback = type | (attr2.kind === "directory" ? 493 : attr2.kind === "symlink" ? 511 : 420);
9724
10922
  if (mount.caps.owners) return attr2.mode ?? fallback;
@@ -9879,9 +11077,9 @@ var HeldPaths = class extends Map {
9879
11077
  this.unlinked.set(path, file);
9880
11078
  }
9881
11079
  keep(path, file) {
9882
- const fresh3 = this.handlesAt(path).filter((id2) => !this.keptHandles.has(id2));
9883
- for (const id2 of fresh3) this.keptHandles.set(id2, file);
9884
- this.uses.set(file, Math.max(fresh3.length, 1));
11080
+ const fresh4 = this.handlesAt(path).filter((id2) => !this.keptHandles.has(id2));
11081
+ for (const id2 of fresh4) this.keptHandles.set(id2, file);
11082
+ this.uses.set(file, Math.max(fresh4.length, 1));
9885
11083
  this.unlinked.set(path, file);
9886
11084
  }
9887
11085
  unkeep(path, file) {
@@ -10002,7 +11200,7 @@ function statOf2(attr2, path, mount) {
10002
11200
  isSymbolicLink: attr2.kind === "symlink",
10003
11201
  ...attr2.kind === "fifo" ? { isFifo: true } : {},
10004
11202
  size: attr2.size,
10005
- mode: modeOf(attr2, mount),
11203
+ mode: modeOf2(attr2, mount),
10006
11204
  mtime,
10007
11205
  atime: attr2.atime === void 0 ? mtime : new Date(attr2.atime),
10008
11206
  ctime: mtime,
@@ -11093,8 +12291,8 @@ var MountFs = class _MountFs {
11093
12291
  }
11094
12292
  this.writable(mount, real);
11095
12293
  const { uid, gid, mode } = change;
11096
- const fresh3 = change.kind === "new" && { uid, gid, mode };
11097
- const set3 = fresh3 ? await this.driverOwner(found, real, fresh3, false) : change;
12294
+ const fresh4 = change.kind === "new" && { uid, gid, mode };
12295
+ const set3 = fresh4 ? await this.driverOwner(found, real, fresh4, false) : change;
11098
12296
  this.table.forget(mount, real);
11099
12297
  const attrs = Object.entries({ uid: set3.uid, gid: set3.gid, mode: set3.mode });
11100
12298
  await mount.conn.call({
@@ -11371,10 +12569,10 @@ async function runMkdir(entry, path, req) {
11371
12569
  const plain3 = plainDirMode(entry, req.mode);
11372
12570
  const parentsMode = !req.exclusive ? plainParentsMode(entry) : void 0;
11373
12571
  const fixParents = parentsMode !== void 0 ? await missingParents(fs, path, parentsMode) : void 0;
11374
- const fresh3 = plain3 !== void 0 && !await fs.exists(path);
12572
+ const fresh4 = plain3 !== void 0 && !await fs.exists(path);
11375
12573
  await (req.exclusive ? mkdirExclusive(entry, path, req.mode) : fs.mkdir(path, mkdirOptions(entry, req.mode)));
11376
12574
  await fixParents?.();
11377
- if (fresh3) await fs.chmod(path, plain3);
12575
+ if (fresh4) await fs.chmod(path, plain3);
11378
12576
  return done;
11379
12577
  }
11380
12578
  async function shownAs(entry, path, st) {
@@ -11586,10 +12784,10 @@ async function guard(g, path, req, cwd) {
11586
12784
  }
11587
12785
  async function opening(entry, path, mode) {
11588
12786
  const g = guardOf(entry);
11589
- const want = (mode & HOLD_READ ? R_OK : 0) | (mode & HOLD_WRITE ? W_OK : 0);
12787
+ const want2 = (mode & HOLD_READ ? R_OK : 0) | (mode & HOLD_WRITE ? W_OK : 0);
11590
12788
  const created4 = (mode & HOLD_CREATED) !== 0 && entry.created?.take(path) === true;
11591
- if (g && want !== 0 && !created4) {
11592
- await need(g, "open", path, want, { directory: false });
12789
+ if (g && want2 !== 0 && !created4) {
12790
+ await need(g, "open", path, want2, { directory: false });
11593
12791
  }
11594
12792
  }
11595
12793
  async function stillThere(entry, path, req) {
@@ -11613,7 +12811,7 @@ async function holdOp(entry, path, req) {
11613
12811
  await entry.source?.open(path, real);
11614
12812
  await stillThere(entry, path, req);
11615
12813
  if (req.nofollow && !await ownEntry(entry, path, real ?? path)) continue;
11616
- const fid = req.ino === void 0 ? void 0 : await idOf(entry, real ?? path);
12814
+ const fid = req.ino === void 0 ? void 0 : await idOf2(entry, real ?? path);
11617
12815
  if (!r || !r.missed(path) && !r.changedSince(path, gen) && !r.inFlight?.(path))
11618
12816
  return json2(await heldOpen(entry, path, real, req, fid));
11619
12817
  }
@@ -11627,7 +12825,7 @@ async function heldOpen(entry, path, real, req, fid) {
11627
12825
  await entry.announceHold?.(real ?? path, fid, ((req.mode ?? 0) & HOLD_CREATED) !== 0);
11628
12826
  return real !== void 0 && real !== path ? { handle, real } : handle;
11629
12827
  }
11630
- async function idOf(entry, path) {
12828
+ async function idOf2(entry, path) {
11631
12829
  return await entry.fileId?.(path)?.catch(() => void 0) ?? void 0;
11632
12830
  }
11633
12831
  async function ownEntry(entry, path, real) {
@@ -11699,8 +12897,8 @@ async function guarded(entry, path, req) {
11699
12897
  return { paths };
11700
12898
  } catch (err) {
11701
12899
  const refusal3 = err.code === "EACCES";
11702
- const refused4 = req.op === "exists" && refusal3 ? json2(false) : toErrno(err);
11703
- return { refused: refused4 };
12900
+ const refused5 = req.op === "exists" && refusal3 ? json2(false) : toErrno(err);
12901
+ return { refused: refused5 };
11704
12902
  }
11705
12903
  }
11706
12904
  async function copyOp(entry, path, req) {
@@ -11801,7 +12999,7 @@ async function bound(entry, path, handle) {
11801
12999
  const now = await entry.fs.stat(path).catch(() => void 0);
11802
13000
  if (now === void 0 && bound3.fid !== void 0 && !await entry.fs.lstat(path).catch(() => void 0))
11803
13001
  throw syncError("ENOENT", path);
11804
- const same2 = bound3.fid === void 0 ? now?.ino === bound3.ino : now?.fid === bound3.fid || await idOf(entry, path) === bound3.fid;
13002
+ const same2 = bound3.fid === void 0 ? now?.ino === bound3.ino : now?.fid === bound3.fid || await idOf2(entry, path) === bound3.fid;
11805
13003
  if (same2) {
11806
13004
  entry.trust?.(handle, bound3.doubt);
11807
13005
  return;
@@ -11896,13 +13094,13 @@ async function created(entry, req) {
11896
13094
  return seen === void 0 ? looked : json2({ ...looked.json, seen });
11897
13095
  }
11898
13096
  var DENIED = /* @__PURE__ */ new Set(["EACCES", "EPERM"]);
11899
- async function rmPermitted(entry, path, req, refused4) {
13097
+ async function rmPermitted(entry, path, req, refused5) {
11900
13098
  const st = await entry.fs.lstat(path).catch(() => void 0);
11901
- if (!st?.isDirectory) return refused4;
13099
+ if (!st?.isDirectory) return refused5;
11902
13100
  const unlisted = await guarded(entry, path, { ...req, op: "readdir" });
11903
13101
  if ("refused" in unlisted) return unlisted.refused;
11904
13102
  const names2 = await entry.fs.readdir(path).catch(() => void 0);
11905
- if (!names2) return refused4;
13103
+ if (!names2) return refused5;
11906
13104
  let first;
11907
13105
  for (const name2 of names2) {
11908
13106
  const child = await dispatchOne(entry, {
@@ -11953,14 +13151,14 @@ async function newDevName(entry, path, req) {
11953
13151
  const named = NAMING.has(req.op);
11954
13152
  if (!named && !CREATIONS.has(req.op)) return void 0;
11955
13153
  const target = named ? entry.fs.resolvePath(entry.cwd, req.arg2 ?? "") : path;
11956
- const refused4 = devCreation(target, entry.cred?.());
11957
- if (!refused4) return void 0;
13154
+ const refused5 = devCreation(target, entry.cred?.());
13155
+ if (!refused5) return void 0;
11958
13156
  const there = await entry.fs.lstat(target).then(
11959
13157
  () => true,
11960
13158
  () => false
11961
13159
  );
11962
13160
  if (there) return void 0;
11963
- return { ok: false, errno: refused4, message: `${target}: no new names in /dev or /proc` };
13161
+ return { ok: false, errno: refused5, message: `${target}: no new names in /dev or /proc` };
11964
13162
  }
11965
13163
  var MISS_OPS = /* @__PURE__ */ new Set(["stat", "lstat"]);
11966
13164
  async function linkFree(fs, path) {
@@ -12071,13 +13269,13 @@ var SAB_DEFAULT_WINDOW_BYTES = 1024 * 1024;
12071
13269
  var SAB_MIN_BYTES = SAB_HEADER_BYTES + 4096;
12072
13270
  var SYNC_SAB_REQ_MSG = "sync-sab-req";
12073
13271
  var SYNC_SAB_NEXT_MSG = "sync-sab-next";
12074
- var encoder3 = new TextEncoder();
13272
+ var encoder4 = new TextEncoder();
12075
13273
  var decoder2 = new TextDecoder();
12076
13274
  function encodeSabResult(result) {
12077
13275
  if (!result.ok) {
12078
13276
  return {
12079
13277
  status: SAB_STATUS_ERRNO,
12080
- payload: encoder3.encode(
13278
+ payload: encoder4.encode(
12081
13279
  JSON.stringify({
12082
13280
  errno: result.errno,
12083
13281
  message: result.message,
@@ -12092,7 +13290,7 @@ function encodeSabResult(result) {
12092
13290
  case "bytes":
12093
13291
  return { status: SAB_STATUS_BYTES, payload: result.bytes };
12094
13292
  case "json":
12095
- return { status: SAB_STATUS_JSON, payload: encoder3.encode(JSON.stringify(result.json)) };
13293
+ return { status: SAB_STATUS_JSON, payload: encoder4.encode(JSON.stringify(result.json)) };
12096
13294
  default:
12097
13295
  return { status: SAB_STATUS_VOID, payload: new Uint8Array(0) };
12098
13296
  }
@@ -12419,9 +13617,9 @@ var Pages = class {
12419
13617
  const page = (start2 + at) / PAGE;
12420
13618
  if (this.pages.has(page)) continue;
12421
13619
  const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
12422
- const fresh3 = this.take(part.length);
12423
- fresh3.set(part);
12424
- this.pages.set(page, fresh3);
13620
+ const fresh4 = this.take(part.length);
13621
+ fresh4.set(part);
13622
+ this.pages.set(page, fresh4);
12425
13623
  }
12426
13624
  }
12427
13625
  read(at, length2) {
@@ -12632,16 +13830,16 @@ var pinnedRoot;
12632
13830
  var rootKind = () => globalThis.navigator?.storage ? "opfs" : "memory";
12633
13831
  function mountTable(mounts = []) {
12634
13832
  const root = pinnedRoot ?? rootKind();
12635
- const lines2 = [`${root} / ${root} rw 0 0`, "devfs /dev devfs rw 0 0", "proc /proc proc rw 0 0"];
13833
+ const lines3 = [`${root} / ${root} rw 0 0`, "devfs /dev devfs rw 0 0", "proc /proc proc rw 0 0"];
12636
13834
  for (const m of mounts) {
12637
13835
  const options = Object.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
12638
13836
  if (m.state !== "ok") options.push(m.state);
12639
13837
  const access2 = options.includes("ro") ? [] : ["rw"];
12640
- lines2.push(
13838
+ lines3.push(
12641
13839
  `${field3(m.source)} ${field3(m.target)} ${m.type} ${[...access2, ...options].join(",")} 0 0`
12642
13840
  );
12643
13841
  }
12644
- return `${lines2.join("\n")}
13842
+ return `${lines3.join("\n")}
12645
13843
  `;
12646
13844
  }
12647
13845
 
@@ -13676,7 +14874,7 @@ function recordRefusal(opening2) {
13676
14874
  const found = headerClass(opening2);
13677
14875
  return found && new KernelError("EPROTO", `${found[0]}: ${found[1]}`);
13678
14876
  }
13679
- function refused2(engine, session, err, opening2) {
14877
+ function refused3(engine, session, err, opening2) {
13680
14878
  const flags2 = session.verifyResult;
13681
14879
  const code = verifyCode(flags2);
13682
14880
  if (code && flags2 !== -1)
@@ -13697,7 +14895,7 @@ async function handshake(conn, o, session, inbox) {
13697
14895
  done4 = session.handshake();
13698
14896
  } catch (err) {
13699
14897
  await send(conn, session).catch(() => void 0);
13700
- throw refused2(o.engine, session, err, inbox.opening);
14898
+ throw refused3(o.engine, session, err, inbox.opening);
13701
14899
  }
13702
14900
  await send(conn, session);
13703
14901
  if (done4) return;
@@ -14148,10 +15346,10 @@ async function anchorFor(engine, chain2, host, found) {
14148
15346
  var FRAGMENT = 16;
14149
15347
  var MIN_VALUE = 8;
14150
15348
  var MIN_BASE64 = 8;
14151
- var encoder4 = new TextEncoder();
15349
+ var encoder5 = new TextEncoder();
14152
15350
  var STD = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
14153
15351
  function utf8(text) {
14154
- return encoder4.encode(text);
15352
+ return encoder5.encode(text);
14155
15353
  }
14156
15354
  function base64(bytes, padded = true) {
14157
15355
  let out = "";
@@ -14225,14 +15423,73 @@ function encodings(bytes) {
14225
15423
  }
14226
15424
  return [...standard, ...standard.map(urlSafe)];
14227
15425
  }
14228
- function shapes(value2) {
15426
+ function windows(length2) {
15427
+ const out = [];
15428
+ for (let offset = 0; offset + FRAGMENT <= length2; offset++) out.push(offset);
15429
+ return out;
15430
+ }
15431
+ function strings(document) {
15432
+ const out = [];
15433
+ const stack = [document];
15434
+ while (stack.length > 0) {
15435
+ const item = stack.pop();
15436
+ if (typeof item === "string") out.push(item);
15437
+ if (typeof item !== "object" || item === null) continue;
15438
+ for (const child of Object.values(item)) stack.push(child);
15439
+ }
15440
+ return out;
15441
+ }
15442
+ var DOMAIN = "(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\\.)+[A-Za-z]{2,63}";
15443
+ var HOST = new RegExp(`^${DOMAIN}$`);
15444
+ var EMAIL = new RegExp(`^[^\\s@]+@${DOMAIN}$`);
15445
+ var URL_SHAPE = new RegExp(`^https?://${DOMAIN}(?::[0-9]{1,5})?((?:/[^/?#\\s]*)*)$`);
15446
+ var ARMOR = /^-----(?:BEGIN|END) [A-Z0-9 ]+-----$/;
15447
+ function unescaped(segment) {
15448
+ try {
15449
+ return decodeURIComponent(segment);
15450
+ } catch {
15451
+ return segment;
15452
+ }
15453
+ }
15454
+ function address(leaf) {
15455
+ if (HOST.test(leaf) || EMAIL.test(leaf)) return true;
15456
+ const url = URL_SHAPE.exec(leaf);
15457
+ if (!url) return false;
15458
+ return url[1].split("/").every((segment) => utf8(segment).length < FRAGMENT || EMAIL.test(unescaped(segment)));
15459
+ }
15460
+ function lines(leaf) {
15461
+ if (address(leaf)) return [];
15462
+ return leaf.split(/[\r\n"\\]/).filter((line) => utf8(line).length >= FRAGMENT && !ARMOR.test(line));
15463
+ }
15464
+ function parsed(value2) {
15465
+ try {
15466
+ const document = JSON.parse(value2);
15467
+ return typeof document === "object" && document !== null ? document : null;
15468
+ } catch {
15469
+ return null;
15470
+ }
15471
+ }
15472
+ function fragments(value2) {
15473
+ const raw2 = utf8(value2);
15474
+ const document = parsed(value2);
15475
+ if (!document) return { pieces: [raw2], whole: true };
15476
+ return { pieces: [...new Set(strings(document).flatMap(lines))].map(utf8), whole: false };
15477
+ }
15478
+ function shapes(value2, split3 = fragments(value2)) {
14229
15479
  const raw2 = utf8(value2);
14230
15480
  const seen = /* @__PURE__ */ new Set([value2]);
14231
15481
  const out = [];
14232
- if (raw2.length < FRAGMENT) out.push({ bytes: raw2, form: "raw" });
14233
- for (let offset = 0; raw2.length >= FRAGMENT && offset + FRAGMENT <= raw2.length; offset++) {
14234
- out.push({ bytes: raw2.subarray(offset, offset + FRAGMENT), form: "fragment", offset });
14235
- }
15482
+ if (raw2.length < FRAGMENT || !split3.whole) out.push({ bytes: raw2, form: "raw" });
15483
+ split3.pieces.forEach((bytes, piece) => {
15484
+ for (const offset of windows(bytes.length)) {
15485
+ out.push({
15486
+ bytes: bytes.subarray(offset, offset + FRAGMENT),
15487
+ form: "fragment",
15488
+ offset,
15489
+ piece
15490
+ });
15491
+ }
15492
+ });
14236
15493
  const add = (text, form) => {
14237
15494
  if (seen.has(text)) return;
14238
15495
  seen.add(text);
@@ -14365,12 +15622,19 @@ function prepare(secrets, sameLength) {
14365
15622
  const ids = /* @__PURE__ */ new Map();
14366
15623
  const wrapIds = /* @__PURE__ */ new Map();
14367
15624
  const values = secrets.map((secret) => utf8(secret.value));
15625
+ const splits = [];
14368
15626
  secrets.forEach((secret, index) => {
14369
15627
  if (typeof secret.value !== "string" || values[index].length < MIN_VALUE) {
14370
15628
  throw new RangeError(`the value of ${secret.name} is shorter than ${MIN_VALUE} bytes`);
14371
15629
  }
14372
- for (const shape of shapes(secret.value)) {
14373
- const source = { secret: index, form: shape.form, offset: shape.offset };
15630
+ splits.push(fragments(secret.value));
15631
+ for (const shape of shapes(secret.value, splits[index])) {
15632
+ const source = {
15633
+ secret: index,
15634
+ form: shape.form,
15635
+ offset: shape.offset,
15636
+ piece: shape.piece
15637
+ };
14374
15638
  register(main, sources, ids, shape.bytes, source);
14375
15639
  if (wrappable(shape.form)) register(wrap, wrapSources, wrapIds, shape.bytes, source);
14376
15640
  }
@@ -14381,7 +15645,16 @@ function prepare(secrets, sameLength) {
14381
15645
  utf8(secret.placeholder),
14382
15646
  utf8(`[redacted:${secret.name}]`)
14383
15647
  ]);
14384
- return { secrets, values, main, wrap, sources, wrapSources, replacements, sameLength };
15648
+ return {
15649
+ secrets,
15650
+ splits,
15651
+ main,
15652
+ wrap,
15653
+ sources,
15654
+ wrapSources,
15655
+ replacements,
15656
+ sameLength
15657
+ };
14385
15658
  }
14386
15659
  function filled(input, start2, end) {
14387
15660
  const out = new Uint8Array(end - start2);
@@ -14412,14 +15685,16 @@ function resolve(c, input, start2, end, source) {
14412
15685
  growing: false
14413
15686
  };
14414
15687
  }
14415
- const value2 = c.values[source.secret];
15688
+ const split3 = c.splits[source.secret];
15689
+ const value2 = split3.pieces[source.piece];
14416
15690
  let stop = end;
14417
15691
  let at = source.offset + FRAGMENT;
14418
15692
  while (stop < input.length && at < value2.length && input[stop] === value2[at]) {
14419
15693
  stop++;
14420
15694
  at++;
14421
15695
  }
14422
- const form = source.offset === 0 && at === value2.length ? "raw" : "fragment";
15696
+ const whole = split3.whole && source.offset === 0 && at === value2.length;
15697
+ const form = whole ? "raw" : "fragment";
14423
15698
  const growing = stop === input.length && at < value2.length;
14424
15699
  return { start: start2, end: stop, secret: source.secret, form, rank: RANK[form], growing };
14425
15700
  }
@@ -14573,9 +15848,9 @@ function compile(secrets, options = {}) {
14573
15848
  // src/kernel/scrub-file.ts
14574
15849
  var SCRUB_IDLE_MS = 50;
14575
15850
  var SCRUB_NONBLOCK_MAX = 64 * 1024;
14576
- var encoder5 = new TextEncoder();
15851
+ var encoder6 = new TextEncoder();
14577
15852
  function scrubberOf(secrets, sameLength = false) {
14578
- const usable3 = secrets.filter((s) => encoder5.encode(s.value).length >= MIN_VALUE);
15853
+ const usable3 = secrets.filter((s) => encoder6.encode(s.value).length >= MIN_VALUE);
14579
15854
  return usable3.length > 0 ? compile(usable3, { idleMs: SCRUB_IDLE_MS, sameLength }) : void 0;
14580
15855
  }
14581
15856
  async function writeAll(file, bytes) {
@@ -14599,7 +15874,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
14599
15874
  let timer;
14600
15875
  let failed5;
14601
15876
  const waiters = /* @__PURE__ */ new Set();
14602
- const quiet3 = () => !busy && pending === 0;
15877
+ const quiet5 = () => !busy && pending === 0;
14603
15878
  const wake = () => {
14604
15879
  for (const waiter of [...waiters]) waiter();
14605
15880
  };
@@ -14616,7 +15891,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
14616
15891
  if (failed5 === void 0) failed5 = err;
14617
15892
  }).finally(() => {
14618
15893
  pending -= 1;
14619
- if (quiet3()) wake();
15894
+ if (quiet5()) wake();
14620
15895
  });
14621
15896
  };
14622
15897
  const settling = (signal) => new Promise((resolve2, reject) => {
@@ -14634,7 +15909,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
14634
15909
  signal?.addEventListener("abort", stop, { once: true });
14635
15910
  });
14636
15911
  const settled = async () => {
14637
- while (!quiet3()) await settling();
15912
+ while (!quiet5()) await settling();
14638
15913
  };
14639
15914
  const changed = async (signal) => {
14640
15915
  const round = new AbortController();
@@ -14650,7 +15925,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
14650
15925
  };
14651
15926
  const turn = async (signal) => {
14652
15927
  for (let waited = false; ; waited = true) {
14653
- if (quiet3() && (pollFile(file).writable || !file.changed)) {
15928
+ if (quiet5() && (pollFile(file).writable || !file.changed)) {
14654
15929
  busy = true;
14655
15930
  return;
14656
15931
  }
@@ -14679,13 +15954,14 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
14679
15954
  ...bound2(file, "stat"),
14680
15955
  poll: () => {
14681
15956
  const state = pollFile(file);
14682
- return quiet3() ? state : { ...state, writable: false };
15957
+ return quiet5() ? state : { ...state, writable: false };
14683
15958
  },
14684
15959
  changed,
14685
15960
  ...file.tty ? { tty: file.tty } : {},
14686
15961
  identity: file.identity ?? file,
14687
15962
  ...file.clipboard ? { clipboard: file.clipboard } : {},
14688
15963
  ...file.embedder ? { embedder: file.embedder } : {},
15964
+ ...file.device ? { device: file.device } : {},
14689
15965
  ...file.write ? {
14690
15966
  write: async (bytes, signal) => {
14691
15967
  await turn(signal);
@@ -14697,7 +15973,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
14697
15973
  await send2(stream2.push(taken), signal);
14698
15974
  } finally {
14699
15975
  busy = false;
14700
- if (quiet3()) wake();
15976
+ if (quiet5()) wake();
14701
15977
  }
14702
15978
  timer = setTimeout(release, idleMs);
14703
15979
  return taken.length;
@@ -14756,9 +16032,9 @@ function scrubStdio(fds, scrubber) {
14756
16032
  // src/kernel/net/sealed-scrubs.ts
14757
16033
  function addTo(tree2, host, secrets) {
14758
16034
  const entry = tree2.get(host) ?? { secrets: [], version: 0, scrubber: void 0 };
14759
- const fresh3 = secrets.filter((s) => !entry.secrets.some((e) => e.value === s.value));
14760
- if (fresh3.length === 0) return;
14761
- entry.secrets = [...entry.secrets, ...fresh3];
16035
+ const fresh4 = secrets.filter((s) => !entry.secrets.some((e) => e.value === s.value));
16036
+ if (fresh4.length === 0) return;
16037
+ entry.secrets = [...entry.secrets, ...fresh4];
14762
16038
  entry.version += 1;
14763
16039
  entry.scrubber = void 0;
14764
16040
  tree2.set(host, entry);
@@ -14921,7 +16197,7 @@ var HEADER = new RegExp(`^header:${TOKEN3}$`);
14921
16197
  var METHOD = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
14922
16198
  var TARGET = /^[\x21-\x7e]+$/;
14923
16199
  var SESSION_EXIT = /^(tailscale|device:[A-Za-z0-9._:-]+)$/;
14924
- var HOST = /^[a-z0-9.:-]+$/;
16200
+ var HOST2 = /^[a-z0-9.:-]+$/;
14925
16201
  var UNSAFE = /[\r\n\0]/;
14926
16202
  var ANCHOR_ID = /^[A-Za-z0-9+/]{43}=$/;
14927
16203
  function verifiedByOk(f) {
@@ -14975,7 +16251,7 @@ function scramOk(f) {
14975
16251
  return texts.every((t) => typeof t === "string" && SCRAM_TEXT.test(t));
14976
16252
  }
14977
16253
  function hostOk(host) {
14978
- return typeof host === "string" && HOST.test(host) && boundHost(host) === host;
16254
+ return typeof host === "string" && HOST2.test(host) && boundHost(host) === host;
14979
16255
  }
14980
16256
  function sessionAskable(f, now) {
14981
16257
  if (!Object.hasOwn(POSITIONS, f.protocol) || typeof f.position !== "string") return false;
@@ -15065,20 +16341,20 @@ var ASK_FIELDS = /* @__PURE__ */ new Set([
15065
16341
  "caller",
15066
16342
  "requester"
15067
16343
  ]);
15068
- var encoder6 = new TextEncoder();
16344
+ var encoder7 = new TextEncoder();
15069
16345
  var decoder3 = new TextDecoder("utf-8", { fatal: true });
15070
16346
  function parseSealed(bytes) {
15071
16347
  try {
15072
16348
  if (bytes.length > SEALED_MAX_BYTES) throw new KernelError("EMSGSIZE");
15073
- let parsed2;
16349
+ let parsed3;
15074
16350
  try {
15075
- parsed2 = JSON.parse(decoder3.decode(bytes));
16351
+ parsed3 = JSON.parse(decoder3.decode(bytes));
15076
16352
  } catch {
15077
16353
  throw new KernelError("EINVAL");
15078
16354
  }
15079
- if (typeof parsed2 !== "object" || parsed2 === null || Array.isArray(parsed2))
16355
+ if (typeof parsed3 !== "object" || parsed3 === null || Array.isArray(parsed3))
15080
16356
  throw new KernelError("EINVAL");
15081
- const req = parsed2;
16357
+ const req = parsed3;
15082
16358
  if (req.v !== 1 || typeof req.op !== "string" || !SEALED_OPS.has(req.op))
15083
16359
  throw new KernelError("EINVAL");
15084
16360
  if (req.op === "materialize" && !materializable(req)) throw new KernelError("EINVAL");
@@ -15174,7 +16450,7 @@ var SealedChannel = class {
15174
16450
  await this.broker.tickets.record(req, reply, caller.pid);
15175
16451
  if (this.inflight !== flight) return;
15176
16452
  this.inflight = void 0;
15177
- if (reply) this.reply = encoder6.encode(serialized(reply));
16453
+ if (reply) this.reply = encoder7.encode(serialized(reply));
15178
16454
  });
15179
16455
  this.inflight = flight;
15180
16456
  return length2;
@@ -15450,7 +16726,7 @@ function shown(text) {
15450
16726
  const chars = [...safe];
15451
16727
  return `'${chars.length > SHOWN_MAX ? `${chars.slice(0, SHOWN_MAX - 1).join("")}\u2026` : safe}'`;
15452
16728
  }
15453
- function unescaped(double) {
16729
+ function unescaped2(double) {
15454
16730
  return double.replace(/\\(["\\$`])/g, "$1");
15455
16731
  }
15456
16732
  function splitWords(text) {
@@ -15466,7 +16742,7 @@ function splitWords(text) {
15466
16742
  word = void 0;
15467
16743
  continue;
15468
16744
  }
15469
- word = (word ?? "") + (single ?? (double !== void 0 ? unescaped(double) : escaped ?? plain3));
16745
+ word = (word ?? "") + (single ?? (double !== void 0 ? unescaped2(double) : escaped ?? plain3));
15470
16746
  }
15471
16747
  if (word !== void 0) words3.push(word);
15472
16748
  return words3;
@@ -15503,7 +16779,7 @@ function splitPatterns(text) {
15503
16779
  }
15504
16780
  word ??= [];
15505
16781
  if (plain3 !== void 0) addPlain(word, plain3);
15506
- else addPart(word, single ?? (double !== void 0 ? unescaped(double) : escaped));
16782
+ else addPart(word, single ?? (double !== void 0 ? unescaped2(double) : escaped));
15507
16783
  }
15508
16784
  if (word !== void 0) words3.push(word);
15509
16785
  return words3;
@@ -15554,13 +16830,13 @@ function commandMatches(cmnd, argv) {
15554
16830
  if (cmnd === "*") return true;
15555
16831
  const [path, ...args] = argv;
15556
16832
  if (path !== cmnd.path) return false;
15557
- const want = cmnd.args;
15558
- if (want === void 0) return true;
16833
+ const want2 = cmnd.args;
16834
+ if (want2 === void 0) return true;
15559
16835
  return globMatch(
15560
- want.length,
16836
+ want2.length,
15561
16837
  args.length,
15562
- (i) => isAny(want[i]),
15563
- (i, j) => wordMatch(want[i], args[j])
16838
+ (i) => isAny(want2[i]),
16839
+ (i, j) => wordMatch(want2[i], args[j])
15564
16840
  );
15565
16841
  }
15566
16842
  function commandText(cmnd) {
@@ -15857,11 +17133,11 @@ function parsePolicy(text, file) {
15857
17133
  const defaults2 = [];
15858
17134
  const errors = [];
15859
17135
  for (const [n, line] of text.split("\n").entries()) {
15860
- const parsed2 = parseLine(line, file);
15861
- if (parsed2 === null) continue;
15862
- if ("error" in parsed2) errors.push({ line: n + 1, message: parsed2.error });
15863
- else if ("defaults" in parsed2) defaults2.push(parsed2.defaults);
15864
- else rules.push(...parsed2.rules);
17136
+ const parsed3 = parseLine(line, file);
17137
+ if (parsed3 === null) continue;
17138
+ if ("error" in parsed3) errors.push({ line: n + 1, message: parsed3.error });
17139
+ else if ("defaults" in parsed3) defaults2.push(parsed3.defaults);
17140
+ else rules.push(...parsed3.rules);
15865
17141
  }
15866
17142
  return { rules, defaults: defaults2, errors };
15867
17143
  }
@@ -15881,18 +17157,18 @@ var HOME_DIR = /^\/home\/[^/]+$/;
15881
17157
  var SYSTEM_PNPM_HOME = "/usr/local/share/pnpm";
15882
17158
  var PNPM_CONFIG = "minimumReleaseAge: 1440\n";
15883
17159
  var isId = (text) => text !== void 0 && /^\d+$/.test(text);
15884
- function lines(text) {
17160
+ function lines2(text) {
15885
17161
  return text.split("\n").filter((line, i, all) => line !== "" || i < all.length - 1);
15886
17162
  }
15887
17163
  function parsePasswd(text) {
15888
- return lines(text).map((line) => {
17164
+ return lines2(text).map((line) => {
15889
17165
  const [name2, , uid, gid, gecos = "", home = "/", shell = "/bin/sh"] = line.split(":");
15890
17166
  if (!name2 || !isId(uid) || !isId(gid)) return line;
15891
17167
  return { name: name2, uid: Number(uid), gid: Number(gid), gecos, home, shell };
15892
17168
  });
15893
17169
  }
15894
17170
  function parseGroup(text) {
15895
- return lines(text).map((line) => {
17171
+ return lines2(text).map((line) => {
15896
17172
  const [name2, , gid, members = ""] = line.split(":");
15897
17173
  if (!name2 || !isId(gid)) return line;
15898
17174
  return { name: name2, gid: Number(gid), members: members.split(",").filter(Boolean) };
@@ -15908,7 +17184,7 @@ function formatGroup(rows) {
15908
17184
  return `${rows.map((r) => typeof r === "string" ? r : line(r)).join("\n")}
15909
17185
  `;
15910
17186
  }
15911
- var parsed = (rows) => rows.filter((r) => typeof r !== "string");
17187
+ var parsed2 = (rows) => rows.filter((r) => typeof r !== "string");
15912
17188
  function homeOf(home) {
15913
17189
  if (typeof home !== "string" || !home.startsWith("/") || /[:\r\n]/.test(home)) {
15914
17190
  throw fsError("EINVAL", `not a home directory: ${String(home)}`);
@@ -16101,20 +17377,20 @@ var UserDb = class {
16101
17377
  return (await this.lookupTerminal(user))?.account;
16102
17378
  }
16103
17379
  async ttyGroup() {
16104
- return parsed(parseGroup(await this.read(GROUP, DEFAULT_GROUP))).find((g) => g.name === "tty")?.gid;
17380
+ return parsed2(parseGroup(await this.read(GROUP, DEFAULT_GROUP))).find((g) => g.name === "tty")?.gid;
16105
17381
  }
16106
17382
  async lookupTerminal(user) {
16107
17383
  const { passwd, group } = await this.tables();
16108
17384
  const key = typeof user === "number" ? "uid" : "name";
16109
- const row = parsed(passwd).find((r) => r[key] === user);
16110
- const groups = parsed(group);
17385
+ const row = parsed2(passwd).find((r) => r[key] === user);
17386
+ const groups = parsed2(group);
16111
17387
  const tty = groups.find((g) => g.name === "tty")?.gid;
16112
17388
  return row && { account: this.account(row, groups), tty };
16113
17389
  }
16114
17390
  async list() {
16115
17391
  const { passwd, group } = await this.tables();
16116
- const groups = parsed(group);
16117
- return parsed(passwd).map((row) => this.account(row, groups));
17392
+ const groups = parsed2(group);
17393
+ return parsed2(passwd).map((row) => this.account(row, groups));
16118
17394
  }
16119
17395
  add(options) {
16120
17396
  return this.serial(async () => {
@@ -16126,8 +17402,8 @@ var UserDb = class {
16126
17402
  if (options.umask !== void 0) this.umasks.set(name2, options.umask & 511);
16127
17403
  };
16128
17404
  const { passwd, group } = await this.tables();
16129
- const users = parsed(passwd);
16130
- const groups = parsed(group);
17405
+ const users = parsed2(passwd);
17406
+ const groups = parsed2(group);
16131
17407
  const found = users.find((r) => r.name === name2);
16132
17408
  const shared2 = homeGroupOf(groups, options.homeGroup);
16133
17409
  const homeMode = homeModeOf(options.homeMode);
@@ -16152,7 +17428,7 @@ var UserDb = class {
16152
17428
  joined3(name2, extra);
16153
17429
  group.push({ name: name2, gid: uid, members: [] });
16154
17430
  passwd.push(row);
16155
- const fresh3 = !await this.fs.exists(home);
17431
+ const fresh4 = !await this.fs.exists(home);
16156
17432
  await this.fs.mkdir(home, { recursive: true });
16157
17433
  try {
16158
17434
  await this.provision(row, this.umaskFor(options), shared2);
@@ -16160,16 +17436,16 @@ var UserDb = class {
16160
17436
  await this.fs.mkdir("/etc", { recursive: true });
16161
17437
  await this.commit(formatPasswd(passwd), formatGroup(group));
16162
17438
  } catch (err) {
16163
- if (fresh3) await this.fs.rm(home, { recursive: true, force: true });
17439
+ if (fresh4) await this.fs.rm(home, { recursive: true, force: true });
16164
17440
  throw err;
16165
17441
  }
16166
17442
  remember();
16167
- return this.account(row, parsed(group));
17443
+ return this.account(row, parsed2(group));
16168
17444
  });
16169
17445
  }
16170
17446
  async groups() {
16171
17447
  const { group } = await this.tables();
16172
- return parsed(group).map(({ name: name2, gid, members }) => ({ name: name2, gid, members: [...members] }));
17448
+ return parsed2(group).map(({ name: name2, gid, members }) => ({ name: name2, gid, members: [...members] }));
16173
17449
  }
16174
17450
  addGroup(name2, gid) {
16175
17451
  return this.serial(async () => {
@@ -16180,7 +17456,7 @@ var UserDb = class {
16180
17456
  throw fsError("EINVAL", `not a group id: ${String(gid)}`);
16181
17457
  }
16182
17458
  const { group } = await this.tables();
16183
- const rows = parsed(group);
17459
+ const rows = parsed2(group);
16184
17460
  const found = rows.find((g) => g.name === name2);
16185
17461
  if (found) return { name: found.name, gid: found.gid, members: [...found.members] };
16186
17462
  const taken = rows.find((g) => g.gid === gid);
@@ -16200,9 +17476,9 @@ var UserDb = class {
16200
17476
  member(name2, user, on) {
16201
17477
  return this.serial(async () => {
16202
17478
  const { passwd, group } = await this.tables();
16203
- const row = parsed(group).find((g) => g.name === name2);
17479
+ const row = parsed2(group).find((g) => g.name === name2);
16204
17480
  if (!row) throw fsError("ENOENT", `no group ${String(name2)}`);
16205
- const who = parsed(passwd).find((u) => u.name === user);
17481
+ const who = parsed2(passwd).find((u) => u.name === user);
16206
17482
  if (!who) throw fsError("ENOENT", `no user ${String(user)}`);
16207
17483
  if (who.gid === row.gid) throw fsError("EINVAL", `${name2} is the own group of ${user}`);
16208
17484
  if (row.members.includes(user) !== on) {
@@ -16215,7 +17491,7 @@ var UserDb = class {
16215
17491
  remove(name2, options = {}) {
16216
17492
  return this.serial(async () => {
16217
17493
  const { passwd, group } = await this.tables();
16218
- const row = parsed(passwd).find((r) => r.name === name2);
17494
+ const row = parsed2(passwd).find((r) => r.name === name2);
16219
17495
  if (!row) return false;
16220
17496
  if (row.uid < FIRST_UID || row.uid > LAST_UID) {
16221
17497
  throw fsError("EPERM", `${name2} is a system account`);
@@ -16223,9 +17499,9 @@ var UserDb = class {
16223
17499
  const keptGroups = group.filter(
16224
17500
  (r) => typeof r === "string" || !(r.name === name2 && r.gid === row.gid)
16225
17501
  );
16226
- for (const r of parsed(keptGroups)) r.members = r.members.filter((m) => m !== name2);
17502
+ for (const r of parsed2(keptGroups)) r.members = r.members.filter((m) => m !== name2);
16227
17503
  const home = this.fs.resolvePath("/", row.home);
16228
- const shared2 = parsed(passwd).some(
17504
+ const shared2 = parsed2(passwd).some(
16229
17505
  (r) => r !== row && this.fs.resolvePath("/", r.home) === home
16230
17506
  );
16231
17507
  const removeHome = !options.keepHome && !shared2 && HOME_DIR.test(home) && await this.fs.exists(home);
@@ -16291,11 +17567,11 @@ var REPLAY_TAGS_MAX = 64;
16291
17567
  var REPLAY_ARGV_MAX = 4096;
16292
17568
  var REPLAY_BYTES_MAX = 1024 * 1024;
16293
17569
  var NOT_A_REQUEST = "sudo: that isn't a sudo request this kernel can run";
16294
- function strings(value2, max) {
17570
+ function strings2(value2, max) {
16295
17571
  return Array.isArray(value2) && value2.length <= max && value2.every((s) => typeof s === "string");
16296
17572
  }
16297
17573
  function words(value2) {
16298
- if (!strings(value2, REPLAY_ARGV_MAX) || value2.length === 0) return false;
17574
+ if (!strings2(value2, REPLAY_ARGV_MAX) || value2.length === 0) return false;
16299
17575
  const bytes = value2.reduce((n, s) => n + s.length + 1, 0);
16300
17576
  return bytes <= REPLAY_BYTES_MAX && !value2.some((s) => s.includes("\0"));
16301
17577
  }
@@ -16330,7 +17606,7 @@ function replayOf(value2) {
16330
17606
  const bound3 = filesOf(r.userWritable);
16331
17607
  const named = (s) => typeof s === "string" && s !== "";
16332
17608
  const absolute2 = (s) => typeof s === "string" && s.startsWith("/") && s.length <= PATH_MAX && !s.includes("\0");
16333
- const valid2 = 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;
17609
+ const valid2 = 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;
16334
17610
  if (!valid2) return failed2("EINVAL", NOT_A_REQUEST);
16335
17611
  const all = tags ?? (tag === void 0 ? [] : [tag]);
16336
17612
  return {
@@ -16422,28 +17698,28 @@ function recordedCommand(argv) {
16422
17698
  const [path = "", ...args] = argv;
16423
17699
  return commandText({ path, args: args.map((a) => [a]) });
16424
17700
  }
16425
- function flags(parsed2, arg, next3) {
17701
+ function flags(parsed3, arg, next3) {
16426
17702
  for (let j = 1; j < arg.length; j++) {
16427
17703
  const flag = arg[j];
16428
- if (flag === "n") parsed2.nonInteractive = true;
16429
- else if (flag === "l") parsed2.list = true;
16430
- else if (flag === "v") parsed2.validate = true;
16431
- else if (flag === "k") parsed2.reset = true;
16432
- else if (flag === "K") parsed2.removeAll = true;
17704
+ if (flag === "n") parsed3.nonInteractive = true;
17705
+ else if (flag === "l") parsed3.list = true;
17706
+ else if (flag === "v") parsed3.validate = true;
17707
+ else if (flag === "k") parsed3.reset = true;
17708
+ else if (flag === "K") parsed3.removeAll = true;
16433
17709
  else if (flag !== "u") return `sudo: invalid option -- '${flag}'
16434
17710
  ${USAGE}`;
16435
17711
  else {
16436
17712
  const user = arg.slice(j + 1) || next3;
16437
17713
  if (!user) return `sudo: option requires an argument -- 'u'
16438
17714
  ${USAGE}`;
16439
- parsed2.user = user;
17715
+ parsed3.user = user;
16440
17716
  return arg.slice(j + 1) ? 0 : 1;
16441
17717
  }
16442
17718
  }
16443
17719
  return 0;
16444
17720
  }
16445
17721
  function parseArgs(args) {
16446
- const parsed2 = {
17722
+ const parsed3 = {
16447
17723
  list: false,
16448
17724
  nonInteractive: false,
16449
17725
  validate: false,
@@ -16460,19 +17736,19 @@ function parseArgs(args) {
16460
17736
  break;
16461
17737
  }
16462
17738
  if (!arg.startsWith("-") || arg === "-") break;
16463
- const used = flags(parsed2, arg, args[i + 1]);
17739
+ const used = flags(parsed3, arg, args[i + 1]);
16464
17740
  if (typeof used === "string") return used;
16465
17741
  i += used;
16466
17742
  }
16467
- parsed2.command = args.slice(i);
16468
- return usable(parsed2) ? parsed2 : USAGE;
17743
+ parsed3.command = args.slice(i);
17744
+ return usable(parsed3) ? parsed3 : USAGE;
16469
17745
  }
16470
- function usable(parsed2) {
16471
- const command = parsed2.command.length > 0;
16472
- const modes = [parsed2.list, parsed2.validate, parsed2.removeAll].filter(Boolean).length;
16473
- if (modes > 1 || parsed2.removeAll && parsed2.reset) return false;
17746
+ function usable(parsed3) {
17747
+ const command = parsed3.command.length > 0;
17748
+ const modes = [parsed3.list, parsed3.validate, parsed3.removeAll].filter(Boolean).length;
17749
+ if (modes > 1 || parsed3.removeAll && parsed3.reset) return false;
16474
17750
  if (modes === 1) return !command;
16475
- return command || parsed2.reset;
17751
+ return command || parsed3.reset;
16476
17752
  }
16477
17753
  function policyOf(settings) {
16478
17754
  return { type: settings.timestampType, minutes: settings.timestampTimeout };
@@ -16616,28 +17892,28 @@ var Sudo = class {
16616
17892
  };
16617
17893
  }
16618
17894
  async check(cred, call) {
16619
- const lines2 = [];
17895
+ const lines3 = [];
16620
17896
  let ok4 = true;
16621
17897
  const report = ({ path, problems, ok: read }) => {
16622
17898
  for (const problem of problems) {
16623
17899
  const where = problem.line === void 0 ? `${path}: sudo skips it:` : `${path}:${problem.line}:`;
16624
- lines2.push([2, `visudo: ${where} ${problem.message}`]);
17900
+ lines3.push([2, `visudo: ${where} ${problem.message}`]);
16625
17901
  }
16626
- if (read && problems.length === 0) lines2.push([1, `${path}: parsed OK`]);
17902
+ if (read && problems.length === 0) lines3.push([1, `${path}: parsed OK`]);
16627
17903
  if (problems.length > 0) ok4 = false;
16628
17904
  };
16629
17905
  const values = (defaults2) => {
16630
- for (const line of defaultsText(defaults2)) lines2.push([1, line]);
17906
+ for (const line of defaultsText(defaults2)) lines3.push([1, line]);
16631
17907
  };
16632
17908
  if (call.files !== void 0) {
16633
17909
  const defaults2 = [];
16634
17910
  for (const { name: name2, text } of call.files) {
16635
- const parsed2 = parsePolicy(text);
16636
- defaults2.push(...parsed2.defaults);
16637
- report({ path: name2, rules: [], problems: parsed2.errors, ok: true });
17911
+ const parsed3 = parsePolicy(text);
17912
+ defaults2.push(...parsed3.defaults);
17913
+ report({ path: name2, rules: [], problems: parsed3.errors, ok: true });
16638
17914
  }
16639
17915
  if (defaults2.length > 0) values(defaults2);
16640
- return { lines: lines2, ok: ok4 };
17916
+ return { lines: lines3, ok: ok4 };
16641
17917
  }
16642
17918
  if (this.o.mode === "enforce" && cred.euid !== 0) {
16643
17919
  const hint = "check the policy as root (sudo visudo -c), or a file with visudo -c -f <file>";
@@ -16646,7 +17922,7 @@ var Sudo = class {
16646
17922
  const files = await this.policy();
16647
17923
  for (const file of files) report(file);
16648
17924
  values(files.flatMap((f) => f.defaults ?? []));
16649
- return { lines: lines2, ok: ok4 };
17925
+ return { lines: lines3, ok: ok4 };
16650
17926
  }
16651
17927
  async groupIds() {
16652
17928
  return new Map((await this.o.users.groups()).map((g) => [g.name, g.gid]));
@@ -16711,13 +17987,13 @@ var Sudo = class {
16711
17987
  return void 0;
16712
17988
  }
16713
17989
  async request(caller, call, signal) {
16714
- const parsed2 = parseArgs(call.args);
16715
- if (typeof parsed2 === "string") return failed2("EINVAL", parsed2);
17990
+ const parsed3 = parseArgs(call.args);
17991
+ if (typeof parsed3 === "string") return failed2("EINVAL", parsed3);
16716
17992
  if (this.o.mode !== "enforce") {
16717
17993
  const mode = this.o.mode;
16718
- if (parsed2.list) return { list: `sudo: users are '${mode}': commands run as the caller
17994
+ if (parsed3.list) return { list: `sudo: users are '${mode}': commands run as the caller
16719
17995
  ` };
16720
- return parsed2.command.length > 0 ? { run: parsed2.command } : DONE;
17996
+ return parsed3.command.length > 0 ? { run: parsed3.command } : DONE;
16721
17997
  }
16722
17998
  const { cred } = caller;
16723
17999
  const policy = await this.policyRules(caller);
@@ -16730,22 +18006,22 @@ var Sudo = class {
16730
18006
  };
16731
18007
  const applies = await this.applies(self, cred);
16732
18008
  const settings = resolveDefaults(policy.defaults, applies);
16733
- if (parsed2.removeAll) {
18009
+ if (parsed3.removeAll) {
16734
18010
  this.timestamps.remove(owner.uid);
16735
18011
  return DONE;
16736
18012
  }
16737
- if (parsed2.reset && !parsed2.validate && !parsed2.list && parsed2.command.length === 0) {
18013
+ if (parsed3.reset && !parsed3.validate && !parsed3.list && parsed3.command.length === 0) {
16738
18014
  this.timestamps.invalidate(owner, settings.timestampType);
16739
18015
  return DONE;
16740
18016
  }
16741
- const target = await this.lookup(parsed2.user);
16742
- if (!target) return failed2("ENOENT", `sudo: unknown user ${parsed2.user}`);
18017
+ const target = await this.lookup(parsed3.user);
18018
+ if (!target) return failed2("ENOENT", `sudo: unknown user ${parsed3.user}`);
16743
18019
  const root = cred.euid === 0;
16744
18020
  const rules = root ? [] : policy.rules;
16745
18021
  const mine = rules.filter((rule) => applies(rule.who));
16746
- if (parsed2.list) return await this.listed(self, root, mine, settings.matching);
16747
- const session = { caller, self, target, owner, policy: policyOf(settings), parsed: parsed2, signal };
16748
- if (parsed2.validate) return this.validate(session, root, mine, call.cwd);
18022
+ if (parsed3.list) return await this.listed(self, root, mine, settings.matching);
18023
+ const session = { caller, self, target, owner, policy: policyOf(settings), parsed: parsed3, signal };
18024
+ if (parsed3.validate) return this.validate(session, root, mine, call.cwd);
16749
18025
  return this.command(session, root, mine, call);
16750
18026
  }
16751
18027
  async replay(value2) {
@@ -16804,9 +18080,9 @@ var Sudo = class {
16804
18080
  );
16805
18081
  }
16806
18082
  async command(s, root, mine, call) {
16807
- const { caller, self, target, parsed: parsed2 } = s;
18083
+ const { caller, self, target, parsed: parsed3 } = s;
16808
18084
  const { cred } = caller;
16809
- const [word = "", ...args] = parsed2.command;
18085
+ const [word = "", ...args] = parsed3.command;
16810
18086
  const cwd = await this.canonical(call.cwd);
16811
18087
  if (cwd === void 0 || !await this.reachable(cred, cwd)) {
16812
18088
  return failed2(
@@ -16821,7 +18097,7 @@ var Sudo = class {
16821
18097
  const matching = mine.filter(
16822
18098
  (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)
16823
18099
  );
16824
- const grant = () => this.grant(self, target, cred, parsed2.command, path, cwd, call.term, bound3);
18100
+ const grant = () => this.grant(self, target, cred, parsed3.command, path, cwd, call.term, bound3);
16825
18101
  if (root || matching.some((rule) => rule.nopasswd)) return grant();
16826
18102
  const quoted = argv.map(quoteWord).join(" ");
16827
18103
  if (matching.length === 0) {
@@ -16830,38 +18106,38 @@ var Sudo = class {
16830
18106
  `sudo: ${self.name} is not allowed to run ${quoted} as ${target.name}`
16831
18107
  );
16832
18108
  }
16833
- if (!parsed2.reset && this.timestamps.current(s.owner, s.policy, bound3)) {
18109
+ if (!parsed3.reset && this.timestamps.current(s.owner, s.policy, bound3)) {
16834
18110
  this.timestamps.stamp(s.owner, s.policy, bound3);
16835
18111
  this.logged(s, cwd, argv);
16836
18112
  return grant();
16837
18113
  }
16838
- if (parsed2.nonInteractive) return failed2("EPERM", "sudo: a password is required");
18114
+ if (parsed3.nonInteractive) return failed2("EPERM", "sudo: a password is required");
16839
18115
  return this.pendingFor(cred.ruid, async () => {
16840
- const answer2 = await this.ask(s, { argv: parsed2.command, path, cwd, bound: bound3 });
18116
+ const answer2 = await this.ask(s, { argv: parsed3.command, path, cwd, bound: bound3 });
16841
18117
  if ("error" in answer2) return answer2;
16842
18118
  if (await this.canonical(call.cwd) !== cwd || !await this.reachable(cred, cwd)) {
16843
18119
  return failed2("EACCES", `sudo: ${call.cwd} changed while the request waited`);
16844
18120
  }
16845
18121
  const now = await this.bound(path, cwd);
16846
18122
  if (!now) return failed2("ENOENT", `sudo: ${word}: command not found`);
16847
- const shown2 = recordable(cwd, [...parsed2.command, ...Object.keys(bound3)]);
18123
+ const shown2 = recordable(cwd, [...parsed3.command, ...Object.keys(bound3)]);
16848
18124
  const result = answer2.answer === "once" ? grant() : await this.always(self, target, { cwd, argv, bound: bound3 }, now, shown2, grant);
16849
18125
  if (!("error" in result)) this.stamped(s, answer2, bound3);
16850
18126
  return result;
16851
18127
  });
16852
18128
  }
16853
18129
  async validate(s, root, mine, dir) {
16854
- const { self, parsed: parsed2 } = s;
18130
+ const { self, parsed: parsed3 } = s;
16855
18131
  if (root) return DONE;
16856
18132
  if (mine.length === 0) {
16857
18133
  return failed2("EPERM", `Sorry, user ${self.name} may not run sudo on ${this.o.hostname}.`);
16858
18134
  }
16859
18135
  if (mine.every((rule) => rule.nopasswd)) return DONE;
16860
- if (!parsed2.reset && this.timestamps.current(s.owner, s.policy)) {
18136
+ if (!parsed3.reset && this.timestamps.current(s.owner, s.policy)) {
16861
18137
  this.timestamps.stamp(s.owner, s.policy);
16862
18138
  return DONE;
16863
18139
  }
16864
- if (parsed2.nonInteractive) return failed2("EPERM", "sudo: a password is required");
18140
+ if (parsed3.nonInteractive) return failed2("EPERM", "sudo: a password is required");
16865
18141
  const cwd = await this.canonical(dir) ?? normalizePath(dir);
16866
18142
  return this.pendingFor(s.caller.cred.ruid, async () => {
16867
18143
  const answer2 = await this.ask(s, { argv: [], path: "", cwd, bound: {}, validate: true });
@@ -16916,7 +18192,7 @@ var Sudo = class {
16916
18192
  }
16917
18193
  const shown2 = [];
16918
18194
  for (const rule of rules) shown2.push(described(rule, await this.writable(rule)));
16919
- const lines2 = shown2.join("\n");
18195
+ const lines3 = shown2.join("\n");
16920
18196
  const params = defaults2.flatMap((d) => d.params).join(", ");
16921
18197
  const matching = params === "" ? "" : `Matching Defaults entries for ${self.name} on ${host}:
16922
18198
  ${params}
@@ -16924,7 +18200,7 @@ var Sudo = class {
16924
18200
  `;
16925
18201
  return {
16926
18202
  list: `${matching}User ${self.name} may run the following commands on ${host}:
16927
- ${lines2}
18203
+ ${lines3}
16928
18204
  `
16929
18205
  };
16930
18206
  }
@@ -17341,7 +18617,7 @@ async function epollSyscall(req, proc) {
17341
18617
 
17342
18618
  // src/kernel/locks.ts
17343
18619
  var overlaps = (a, b) => a.start < b.end && b.start < a.end;
17344
- var clashes = (lock, owner, want) => lock.owner !== owner && overlaps(lock, want) && (lock.kind === "write" || want.kind === "write");
18620
+ var clashes = (lock, owner, want2) => lock.owner !== owner && overlaps(lock, want2) && (lock.kind === "write" || want2.kind === "write");
17345
18621
  function lockRange(start2, len) {
17346
18622
  if (!Number.isSafeInteger(start2) || !Number.isSafeInteger(len)) throw new KernelError("EINVAL");
17347
18623
  const from = len < 0 ? start2 + len : start2;
@@ -17427,8 +18703,8 @@ var LockTable = class {
17427
18703
  blocked = /* @__PURE__ */ new Map();
17428
18704
  wakers = /* @__PURE__ */ new Map();
17429
18705
  held = /* @__PURE__ */ new Map();
17430
- holder(key, owner, want) {
17431
- return this.records.get(key)?.find((lock) => clashes(lock, owner, want));
18706
+ holder(key, owner, want2) {
18707
+ return this.records.get(key)?.find((lock) => clashes(lock, owner, want2));
17432
18708
  }
17433
18709
  set(key, owner, kind, start2, end) {
17434
18710
  if (kind !== "unlock" && this.holder(key, owner, { key, kind, start: start2, end })) return false;
@@ -17447,10 +18723,10 @@ var LockTable = class {
17447
18723
  return true;
17448
18724
  }
17449
18725
  async wait(key, owner, kind, range2, blocking) {
17450
- const want = { key, kind, ...range2 };
18726
+ const want2 = { key, kind, ...range2 };
17451
18727
  let signal;
17452
18728
  for (; ; ) {
17453
- const holder = this.holder(key, owner, want);
18729
+ const holder = this.holder(key, owner, want2);
17454
18730
  if (!holder) {
17455
18731
  this.set(key, owner, kind, range2.start, range2.end);
17456
18732
  return;
@@ -17458,7 +18734,7 @@ var LockTable = class {
17458
18734
  if (this.waitsOn(holder.owner, owner)) throw new KernelError("EDEADLK");
17459
18735
  signal ??= blocking();
17460
18736
  if (signal.aborted) throw new KernelError("EINTR");
17461
- this.blocked.set(owner, want);
18737
+ this.blocked.set(owner, want2);
17462
18738
  try {
17463
18739
  await this.changed(key, signal);
17464
18740
  } finally {
@@ -17470,9 +18746,9 @@ var LockTable = class {
17470
18746
  const seen = /* @__PURE__ */ new Set();
17471
18747
  for (let at = from; at !== void 0 && !seen.has(at); ) {
17472
18748
  seen.add(at);
17473
- const want = this.blocked.get(at);
17474
- if (!want) return false;
17475
- const holder = this.holder(want.key, at, want);
18749
+ const want2 = this.blocked.get(at);
18750
+ if (!want2) return false;
18751
+ const holder = this.holder(want2.key, at, want2);
17476
18752
  if (!holder) return false;
17477
18753
  if (holder.owner === target) return true;
17478
18754
  at = holder.owner;
@@ -17837,24 +19113,24 @@ function errno2(code, message3) {
17837
19113
  }
17838
19114
  function requestHead(method, url, headers, extra, first = []) {
17839
19115
  if (!TOKEN4.test(method)) throw errno2("EINVAL", `method ${method}`);
17840
- const lines2 = [`${method} ${url.pathname}${url.search} HTTP/1.1`, `host: ${url.host}`];
17841
- for (const [name2, value2] of first) lines2.push(`${name2}: ${value2}`);
19116
+ const lines3 = [`${method} ${url.pathname}${url.search} HTTP/1.1`, `host: ${url.host}`];
19117
+ for (const [name2, value2] of first) lines3.push(`${name2}: ${value2}`);
17842
19118
  for (const [name2, value2] of headers) {
17843
19119
  if (OWN_HEADERS.has(name2.toLowerCase())) continue;
17844
19120
  if (!TOKEN4.test(name2) || !SAFE_VALUE.test(value2)) throw errno2("EINVAL", `header ${name2}`);
17845
- lines2.push(`${name2}: ${value2}`);
19121
+ lines3.push(`${name2}: ${value2}`);
17846
19122
  }
17847
- for (const [name2, value2] of extra) lines2.push(`${name2}: ${value2}`);
17848
- return latin1Bytes(`${lines2.join("\r\n")}\r
19123
+ for (const [name2, value2] of extra) lines3.push(`${name2}: ${value2}`);
19124
+ return latin1Bytes(`${lines3.join("\r\n")}\r
17849
19125
  \r
17850
19126
  `);
17851
19127
  }
17852
19128
  function parseHead(bytes) {
17853
- const [statusLine = "", ...lines2] = latin1(bytes).split(/\r?\n/);
19129
+ const [statusLine = "", ...lines3] = latin1(bytes).split(/\r?\n/);
17854
19130
  const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
17855
19131
  if (!match) throw errno2("EPROTO", `not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);
17856
19132
  const headers = [];
17857
- for (const line of lines2) {
19133
+ for (const line of lines3) {
17858
19134
  const colon = line.indexOf(":");
17859
19135
  if (colon > 0) headers.push([line.slice(0, colon).trim(), line.slice(colon + 1).trim()]);
17860
19136
  }
@@ -17944,8 +19220,8 @@ var KernelLoopback = class {
17944
19220
  );
17945
19221
  } catch (err) {
17946
19222
  socket.close();
17947
- const refused4 = err instanceof KernelError && err.code === "ECONNREFUSED";
17948
- throw refused4 ? errno2("ECONNREFUSED", `nothing listens on the kernel's port ${port}`) : err;
19223
+ const refused5 = err instanceof KernelError && err.code === "ECONNREFUSED";
19224
+ throw refused5 ? errno2("ECONNREFUSED", `nothing listens on the kernel's port ${port}`) : err;
17949
19225
  }
17950
19226
  signal.addEventListener("abort", () => socket.close(), { once: true });
17951
19227
  return socket;
@@ -18334,9 +19610,9 @@ function pem(der2) {
18334
19610
  for (let i = 0; i < der2.length; i += 32768) {
18335
19611
  b642 += String.fromCharCode(...der2.subarray(i, i + 32768));
18336
19612
  }
18337
- const lines2 = btoa(b642).match(/.{1,64}/g) ?? [];
19613
+ const lines3 = btoa(b642).match(/.{1,64}/g) ?? [];
18338
19614
  return `-----BEGIN CERTIFICATE-----
18339
- ${lines2.join("\n")}
19615
+ ${lines3.join("\n")}
18340
19616
  -----END CERTIFICATE-----
18341
19617
  `;
18342
19618
  }
@@ -19075,13 +20351,13 @@ var HOSTS_PATH = "/etc/hosts";
19075
20351
  function parseHosts(text) {
19076
20352
  const table = /* @__PURE__ */ new Map();
19077
20353
  for (const raw2 of text.split("\n")) {
19078
- const [address, ...names2] = raw2.replace(/#.*/, "").trim().split(/\s+/);
19079
- if (!address || names2.length === 0) continue;
19080
- if (ipv4Bytes(address) === void 0 && ipv6Groups(address) === void 0) continue;
20354
+ const [address2, ...names2] = raw2.replace(/#.*/, "").trim().split(/\s+/);
20355
+ if (!address2 || names2.length === 0) continue;
20356
+ if (ipv4Bytes(address2) === void 0 && ipv6Groups(address2) === void 0) continue;
19081
20357
  for (const name2 of names2) {
19082
20358
  const key = name2.toLowerCase().replace(/\.$/, "");
19083
20359
  const known = table.get(key) ?? [];
19084
- if (!known.includes(address)) known.push(address);
20360
+ if (!known.includes(address2)) known.push(address2);
19085
20361
  table.set(key, known);
19086
20362
  }
19087
20363
  }
@@ -19169,14 +20445,14 @@ var Resolver = class {
19169
20445
  if (hostsEntry(key, this.own) !== void 0) return true;
19170
20446
  return ((await this.hosts?.().catch(() => void 0))?.get(key)?.length ?? 0) > 0;
19171
20447
  }
19172
- async reverse(address) {
19173
- const v6 = ipv6Groups(address)?.join(":");
19174
- const same2 = (a) => a === address || v6 !== void 0 && ipv6Groups(a)?.join(":") === v6;
20448
+ async reverse(address2) {
20449
+ const v6 = ipv6Groups(address2)?.join(":");
20450
+ const same2 = (a) => a === address2 || v6 !== void 0 && ipv6Groups(a)?.join(":") === v6;
19175
20451
  const file = await this.hosts?.().catch(() => void 0);
19176
20452
  for (const table of [file, this.builtin]) {
19177
20453
  for (const [name2, addresses] of table ?? []) if (addresses.some(same2)) return name2;
19178
20454
  }
19179
- if (ipv4Bytes(address)?.[0] === 127) return "localhost";
20455
+ if (ipv4Bytes(address2)?.[0] === 127) return "localhost";
19180
20456
  return void 0;
19181
20457
  }
19182
20458
  async resolve(name2, family, blocking) {
@@ -19230,13 +20506,13 @@ var Resolver = class {
19230
20506
  if (addresses.length > 0) this.remember(id2, addresses, answer2.ttl);
19231
20507
  return addresses;
19232
20508
  }
19233
- usable(name2, address, family) {
19234
- const v4 = ipv4Bytes(address) !== void 0;
19235
- if (!v4 && (family === 4 || !ipv6Groups(address))) return false;
20509
+ usable(name2, address2, family) {
20510
+ const v4 = ipv4Bytes(address2) !== void 0;
20511
+ if (!v4 && (family === 4 || !ipv6Groups(address2))) return false;
19236
20512
  if (v4 && family === 6) return false;
19237
- const kind = this.routes.classify(address);
20513
+ const kind = this.routes.classify(address2);
19238
20514
  if (kind !== "loopback" && kind !== "host" && kind !== "reserved") return true;
19239
- this.log(`slicc-kernel: dropped the uplink's answer ${address} for ${name2}: it is ${kind}`);
20515
+ this.log(`slicc-kernel: dropped the uplink's answer ${address2} for ${name2}: it is ${kind}`);
19240
20516
  return false;
19241
20517
  }
19242
20518
  remember(id2, addresses, ttl) {
@@ -19270,275 +20546,6 @@ function waitFor(lookup4, signal) {
19270
20546
  });
19271
20547
  }
19272
20548
 
19273
- // src/kernel/tty.ts
19274
- var ICRNL = 256;
19275
- var IUTF8 = 16384;
19276
- var OPOST = 1;
19277
- var ONLCR = 4;
19278
- var ISIG = 1;
19279
- var ICANON = 2;
19280
- var ECHO = 8;
19281
- var ECHOE = 16;
19282
- var ECHOCTL = 512;
19283
- var NOFLSH = 128;
19284
- var IEXTEN = 32768;
19285
- var VINTR = 0;
19286
- var VQUIT = 1;
19287
- var VERASE = 2;
19288
- var VKILL = 3;
19289
- var VEOF = 4;
19290
- var VSUSP = 10;
19291
- var VWERASE = 14;
19292
- function defaultTermios() {
19293
- const cCc = new Array(32).fill(0);
19294
- Object.assign(cCc, { [VINTR]: 3, [VQUIT]: 28, [VERASE]: 127, [VKILL]: 21 });
19295
- Object.assign(cCc, { [VEOF]: 4, 6: 1, 8: 17, 9: 19, [VSUSP]: 26 });
19296
- Object.assign(cCc, { 12: 18, 13: 15, [VWERASE]: 23, 15: 22 });
19297
- return {
19298
- c_iflag: ICRNL | 1024 | 8192 | IUTF8,
19299
- c_oflag: OPOST | ONLCR,
19300
- c_cflag: 15 | 48 | 128,
19301
- c_lflag: ISIG | ICANON | ECHO | ECHOE | 32 | ECHOCTL | 2048 | IEXTEN,
19302
- c_cc: cCc
19303
- };
19304
- }
19305
- var encoder7 = new TextEncoder();
19306
- function isContinuation(byte) {
19307
- return (byte & 192) === 128;
19308
- }
19309
- var KernelTty = class {
19310
- termios = defaultTermios();
19311
- cols = 80;
19312
- rows = 24;
19313
- readable = [];
19314
- line = [];
19315
- waiters = [];
19316
- hungUp = false;
19317
- name;
19318
- owner;
19319
- onRead;
19320
- readers = 0;
19321
- screen;
19322
- signal;
19323
- constructor(screen, signal) {
19324
- this.screen = screen;
19325
- this.signal = signal;
19326
- }
19327
- receive(bytes) {
19328
- for (const byte of bytes) this.key(byte);
19329
- this.wake();
19330
- }
19331
- resize(cols, rows) {
19332
- if (cols === this.cols && rows === this.rows) return;
19333
- this.setSize(cols, rows);
19334
- this.signal(SIG.WINCH);
19335
- }
19336
- hangup() {
19337
- this.hungUp = true;
19338
- this.wake();
19339
- }
19340
- signalHangup() {
19341
- this.signal(SIG.HUP);
19342
- }
19343
- setSize(cols, rows) {
19344
- this.cols = cols;
19345
- this.rows = rows;
19346
- }
19347
- tcgets() {
19348
- return { ...this.termios, c_cc: [...this.termios.c_cc] };
19349
- }
19350
- tcsets(termios, flush = false) {
19351
- const wasCanonical = this.canonical;
19352
- this.termios = { ...termios, c_cc: [...termios.c_cc] };
19353
- if (flush) {
19354
- this.line = [];
19355
- this.readable = [];
19356
- }
19357
- if (wasCanonical && !this.canonical && this.line.length > 0) {
19358
- this.readable.push(Uint8Array.from(this.line));
19359
- this.line = [];
19360
- this.wake();
19361
- }
19362
- }
19363
- winsize() {
19364
- return [this.rows, this.cols];
19365
- }
19366
- file() {
19367
- return new OpenFile({
19368
- read: (max, signal) => this.read(max, signal),
19369
- write: async (bytes) => {
19370
- if (this.hungUp) throw new KernelError("EIO");
19371
- this.output(bytes);
19372
- return bytes.length;
19373
- },
19374
- poll: () => ({
19375
- readable: this.readable.length > 0 || this.hungUp,
19376
- writable: true,
19377
- hangup: this.hungUp,
19378
- nread: this.readable.reduce((n, chunk2) => n + (chunk2?.length ?? 0), 0)
19379
- }),
19380
- changed: (signal) => this.changed(signal),
19381
- close: () => {
19382
- },
19383
- tty: this
19384
- });
19385
- }
19386
- get canonical() {
19387
- return (this.termios.c_lflag & ICANON) !== 0;
19388
- }
19389
- cc(slot2) {
19390
- return this.termios.c_cc[slot2] ?? 0;
19391
- }
19392
- key(input) {
19393
- const { c_iflag, c_lflag } = this.termios;
19394
- const byte = input === 13 && c_iflag & ICRNL ? 10 : input;
19395
- if (c_lflag & ISIG && this.signalKey(byte)) return;
19396
- if (!this.canonical) {
19397
- this.readable.push(Uint8Array.of(byte));
19398
- if (c_lflag & ECHO) this.echo(byte);
19399
- return;
19400
- }
19401
- this.edit(byte);
19402
- }
19403
- signalKey(byte) {
19404
- const sig = byte === this.cc(VINTR) ? SIG.INT : byte === this.cc(VQUIT) ? SIG.QUIT : byte === this.cc(VSUSP) ? SIG.TSTP : 0;
19405
- if (!sig || byte === 0) return false;
19406
- if (this.termios.c_lflag & ECHO) this.echo(byte);
19407
- if (!(this.termios.c_lflag & NOFLSH)) {
19408
- this.line = [];
19409
- this.readable = [];
19410
- }
19411
- this.signal(sig);
19412
- return true;
19413
- }
19414
- edit(byte) {
19415
- const echo = (this.termios.c_lflag & ECHO) !== 0;
19416
- if (byte === this.cc(VERASE) || byte === 8) {
19417
- this.rubOut(this.lastCharLength());
19418
- } else if (byte === this.cc(VKILL)) {
19419
- this.rubOut(this.line.length);
19420
- } else if (byte === this.cc(VWERASE)) {
19421
- this.rubOut(this.lastWordLength());
19422
- } else if (byte === this.cc(VEOF)) {
19423
- this.readable.push(this.line.length > 0 ? Uint8Array.from(this.line) : null);
19424
- this.line = [];
19425
- } else if (byte === 10) {
19426
- this.line.push(byte);
19427
- this.readable.push(Uint8Array.from(this.line));
19428
- this.line = [];
19429
- if (echo) this.output(encoder7.encode("\n"));
19430
- } else {
19431
- this.line.push(byte);
19432
- if (echo) this.echo(byte);
19433
- }
19434
- }
19435
- lastWordLength() {
19436
- let end = this.line.length;
19437
- while (end > 0 && this.line[end - 1] === 32) end--;
19438
- while (end > 0 && this.line[end - 1] !== 32) end--;
19439
- return this.line.length - end;
19440
- }
19441
- lastCharLength() {
19442
- const { line } = this;
19443
- let n = Math.min(1, line.length);
19444
- if (this.termios.c_iflag & IUTF8) {
19445
- while (n < line.length && isContinuation(line[line.length - n] ?? 0)) n++;
19446
- }
19447
- return n;
19448
- }
19449
- rubOut(n) {
19450
- const dropped2 = this.line.splice(this.line.length - n, n);
19451
- const utf83 = (this.termios.c_iflag & IUTF8) !== 0;
19452
- const cells = utf83 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
19453
- if (cells > 0 && this.termios.c_lflag & ECHO) {
19454
- this.output(encoder7.encode("\b \b".repeat(cells)), false);
19455
- }
19456
- }
19457
- echo(byte) {
19458
- if (byte < 32 && byte !== 10 && byte !== 9 && this.termios.c_lflag & ECHOCTL) {
19459
- this.output(Uint8Array.of(94, byte + 64), false);
19460
- return;
19461
- }
19462
- this.output(Uint8Array.of(byte));
19463
- }
19464
- output(bytes, translate2 = true) {
19465
- const { c_oflag } = this.termios;
19466
- if (!translate2 || !(c_oflag & OPOST && c_oflag & ONLCR) || !bytes.includes(10)) {
19467
- this.screen.write(bytes);
19468
- return;
19469
- }
19470
- const out = [];
19471
- for (const byte of bytes) {
19472
- if (byte === 10) out.push(13);
19473
- out.push(byte);
19474
- }
19475
- this.screen.write(Uint8Array.from(out));
19476
- }
19477
- get reading() {
19478
- return this.readers > 0;
19479
- }
19480
- async read(max, signal) {
19481
- this.onRead?.();
19482
- this.readers++;
19483
- try {
19484
- return await this.take(max, signal);
19485
- } finally {
19486
- this.readers--;
19487
- }
19488
- }
19489
- async take(max, signal) {
19490
- while (this.readable.length === 0) {
19491
- if (this.hungUp) return new Uint8Array(0);
19492
- await this.changed(signal);
19493
- }
19494
- const head = this.readable[0];
19495
- if (head === null) {
19496
- this.readable.shift();
19497
- return new Uint8Array(0);
19498
- }
19499
- if (head === void 0) return new Uint8Array(0);
19500
- const chunks2 = [];
19501
- let n = 0;
19502
- while (this.readable.length > 0 && n < max) {
19503
- const next3 = this.readable[0];
19504
- if (next3 === null || next3 === void 0) break;
19505
- const take = next3.subarray(0, max - n);
19506
- chunks2.push(take);
19507
- n += take.length;
19508
- if (take.length < next3.length) this.readable[0] = next3.subarray(take.length);
19509
- else this.readable.shift();
19510
- if (this.canonical) break;
19511
- }
19512
- const out = new Uint8Array(n);
19513
- let at = 0;
19514
- for (const chunk2 of chunks2) {
19515
- out.set(chunk2, at);
19516
- at += chunk2.length;
19517
- }
19518
- return out;
19519
- }
19520
- changed(signal) {
19521
- if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
19522
- return new Promise((resolve2, reject) => {
19523
- const onAbort = () => {
19524
- this.waiters = this.waiters.filter((w) => w !== waiter);
19525
- reject(new KernelError("EINTR"));
19526
- };
19527
- const waiter = () => {
19528
- signal?.removeEventListener("abort", onAbort);
19529
- resolve2();
19530
- };
19531
- this.waiters.push(waiter);
19532
- signal?.addEventListener("abort", onAbort, { once: true });
19533
- });
19534
- }
19535
- wake() {
19536
- const waiters = this.waiters;
19537
- this.waiters = [];
19538
- for (const resolve2 of waiters) resolve2();
19539
- }
19540
- };
19541
-
19542
20549
  // src/kernel/pty.ts
19543
20550
  var PtyPair = class {
19544
20551
  slave;
@@ -19731,6 +20738,7 @@ function ptySyscall(req, ctx) {
19731
20738
  return done3;
19732
20739
  case "pty-ctty": {
19733
20740
  const tty = terminalOf(ctx, req.fd);
20741
+ if (ctx.fds.get(req.fd).file.device) throw new KernelError("EPERM");
19734
20742
  const leader = ctx.jobs?.getsid(ctx.pid, 0) === ctx.pid;
19735
20743
  if (leader && ctx.jobs?.controllingTerminal(ctx.pid) === tty) return done3;
19736
20744
  if (!ctx.jobs?.acquireTerminal(ctx.pid, tty)) throw new KernelError("EPERM");
@@ -20512,10 +21520,10 @@ function basicOf(value2) {
20512
21520
  function placeholdersIn(text) {
20513
21521
  return [...new Set(text.match(ANY_PLACEHOLDER) ?? [])];
20514
21522
  }
20515
- function scan(lines2) {
21523
+ function scan(lines3) {
20516
21524
  const found = [];
20517
- for (let i = 1; i < lines2.length; i++) {
20518
- const m = HEADER_LINE.exec(lines2[i]);
21525
+ for (let i = 1; i < lines3.length; i++) {
21526
+ const m = HEADER_LINE.exec(lines3[i]);
20519
21527
  if (!m) return void 0;
20520
21528
  const value2 = m[2];
20521
21529
  const basic = basicOf(value2);
@@ -20538,10 +21546,10 @@ function usable2(values) {
20538
21546
  }
20539
21547
  async function substituteHead(head, ask) {
20540
21548
  if (!head.endsWith("\r\n\r\n")) return void 0;
20541
- const lines2 = head.slice(0, -4).split("\r\n");
20542
- if (lines2.some((l) => l.includes("\n") || l.includes("\r"))) return void 0;
20543
- const request = REQUEST_LINE2.exec(lines2[0]);
20544
- const found = request ? scan(lines2) : void 0;
21549
+ const lines3 = head.slice(0, -4).split("\r\n");
21550
+ if (lines3.some((l) => l.includes("\n") || l.includes("\r"))) return void 0;
21551
+ const request = REQUEST_LINE2.exec(lines3[0]);
21552
+ const found = request ? scan(lines3) : void 0;
20545
21553
  if (!request || !found || found.length !== 1) return void 0;
20546
21554
  const at = found[0];
20547
21555
  if (at.placeholders.length > SESSION_PLACEHOLDERS) return void 0;
@@ -20553,8 +21561,8 @@ async function substituteHead(head, ask) {
20553
21561
  );
20554
21562
  if (Object.keys(values).length === 0) return void 0;
20555
21563
  const value2 = at.basic ? `${at.basic.scheme}${btoa(replaced(at.basic.decoded, values))}` : replaced(at.value, values);
20556
- const out = [...lines2];
20557
- out[at.line] = `${lines2[at.line].split(":")[0]}: ${value2}`;
21564
+ const out = [...lines3];
21565
+ out[at.line] = `${lines3[at.line].split(":")[0]}: ${value2}`;
20558
21566
  return { head: `${out.join("\r\n")}\r
20559
21567
  \r
20560
21568
  `, values };
@@ -20877,9 +21885,9 @@ function serverHeader(header5, server) {
20877
21885
  return server.plus && server.binding ? END_POINT : "n,,";
20878
21886
  }
20879
21887
  function rewritten(first, header5) {
20880
- const parsed2 = clientFirstOf(first.data);
20881
- if (!parsed2 || parsed2.header === header5) return first;
20882
- return { mechanism: header5 === END_POINT ? PLUS : PLAIN2, data: `${header5}${parsed2.bare}` };
21888
+ const parsed3 = clientFirstOf(first.data);
21889
+ if (!parsed3 || parsed3.header === header5) return first;
21890
+ return { mechanism: header5 === END_POINT ? PLUS : PLAIN2, data: `${header5}${parsed3.bare}` };
20883
21891
  }
20884
21892
  var PgClient = class {
20885
21893
  clientFirst;
@@ -20899,10 +21907,10 @@ var PgClient = class {
20899
21907
  }
20900
21908
  if (server.sasl && this.clientFirst === void 0) {
20901
21909
  const first = initial(message3);
20902
- const parsed2 = first && clientFirstOf(first.data);
20903
- if (!first || !parsed2) return { bytes: message3, done: true };
21910
+ const parsed3 = first && clientFirstOf(first.data);
21911
+ if (!first || !parsed3) return { bytes: message3, done: true };
20904
21912
  this.clientFirst = first.data;
20905
- this.header = serverHeader(parsed2.header, server);
21913
+ this.header = serverHeader(parsed3.header, server);
20906
21914
  const sent = rewritten(first, this.header);
20907
21915
  if (sent === first) return { bytes: message3, done: false };
20908
21916
  return {
@@ -21067,7 +22075,7 @@ function headEnd2(bytes) {
21067
22075
  }
21068
22076
  return -1;
21069
22077
  }
21070
- function modeOf2(start2) {
22078
+ function modeOf3(start2) {
21071
22079
  const sniffs = [
21072
22080
  ["http1", sniffHttp1(start2)],
21073
22081
  ["smtp", sniffSmtp(start2)],
@@ -21176,7 +22184,7 @@ var SessionWriter = class {
21176
22184
  joined5.set(bytes, this.buffer.length);
21177
22185
  this.buffer.fill(0);
21178
22186
  this.buffer = joined5;
21179
- if (this.mode === "sniff") this.mode = modeOf2(latin12(joined5.subarray(0, 8))) ?? "sniff";
22187
+ if (this.mode === "sniff") this.mode = modeOf3(latin12(joined5.subarray(0, 8))) ?? "sniff";
21180
22188
  if (this.mode === "pg" && !this.sawPg) {
21181
22189
  this.sawPg = true;
21182
22190
  if (!this.s.verified) void this.proof("pg");
@@ -21663,12 +22671,12 @@ function secure(prepared, seal) {
21663
22671
  prepared.own?.release();
21664
22672
  }
21665
22673
  };
21666
- const abandon = () => {
22674
+ const abandon2 = () => {
21667
22675
  if (ran) return;
21668
22676
  prepared.options.session.free();
21669
22677
  prepared.own?.release();
21670
22678
  };
21671
- return { make, abandon };
22679
+ return { make, abandon: abandon2 };
21672
22680
  }
21673
22681
  function supportOf(net) {
21674
22682
  const support = net.tlsClient;
@@ -21685,19 +22693,19 @@ async function uplinkAddress(proc, host) {
21685
22693
  async function connectSecure(proc, socket, host, port, req) {
21686
22694
  const servername = serverName(req.servername || host);
21687
22695
  if (isLoopbackHost(servername) || isLoopbackHost(host)) throw new KernelError("ENOTSUP");
21688
- const address = await uplinkAddress(proc, host);
21689
- const route2 = proc.net.uplinkFor(address);
22696
+ const address2 = await uplinkAddress(proc, host);
22697
+ const route2 = proc.net.uplinkFor(address2);
21690
22698
  if (!route2)
21691
22699
  throw new KernelError("ENOTSUP", "TLS in the kernel needs a destination the uplink routes");
21692
22700
  const support = supportOf(proc.net);
21693
22701
  let info;
21694
- const { make, abandon } = secure(await prepare2(support, req, servername), { proc, port });
22702
+ const { make, abandon: abandon2 } = secure(await prepare2(support, req, servername), { proc, port });
21695
22703
  try {
21696
- await socket.connectSecure({ family: "inet", host: address, port }, route2, async (conn) => {
22704
+ await socket.connectSecure({ family: "inet", host: address2, port }, route2, async (conn) => {
21697
22705
  info = await conn.upgrade(make);
21698
22706
  });
21699
22707
  } finally {
21700
- abandon();
22708
+ abandon2();
21701
22709
  }
21702
22710
  return sessionOf(info, servername);
21703
22711
  }
@@ -21730,14 +22738,14 @@ async function upgradeSecure(proc, socket, req) {
21730
22738
  const host = req.servername || (peer?.family === "inet" ? peer.host : "");
21731
22739
  const servername = serverName(host);
21732
22740
  const port = peer?.family === "inet" ? peer.port : 0;
21733
- const { make, abandon } = secure(await prepare2(supportOf(proc.net), req, servername), {
22741
+ const { make, abandon: abandon2 } = secure(await prepare2(supportOf(proc.net), req, servername), {
21734
22742
  proc,
21735
22743
  port
21736
22744
  });
21737
22745
  try {
21738
22746
  return sessionOf(await socket.upgradeSecure(make), servername);
21739
22747
  } finally {
21740
- abandon();
22748
+ abandon2();
21741
22749
  }
21742
22750
  }
21743
22751
 
@@ -23661,6 +24669,10 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
23661
24669
  "fd-open-clipboard",
23662
24670
  "fd-clipboard-abort",
23663
24671
  "fd-open-embedder",
24672
+ "fd-dev-nodes",
24673
+ "fd-open-dev",
24674
+ "fd-dev-ioctl",
24675
+ "fd-dev-meta",
23664
24676
  "tty-get",
23665
24677
  "tty-set",
23666
24678
  "tty-winsz",
@@ -24037,7 +25049,7 @@ var WasmProcess = class {
24037
25049
  async dispatch(req, cancelled) {
24038
25050
  try {
24039
25051
  if (isFdSyscall(req)) return await this.fdSyscall(req, cancelled);
24040
- if (isTtySyscall(req)) return this.ttySyscall(req);
25052
+ if (isTtySyscall(req)) return await this.ttySyscall(req, cancelled);
24041
25053
  if (isJobSyscall(req)) return this.jobSyscall(req);
24042
25054
  if (isSocketSyscall(req)) return await this.socketSyscall(req, cancelled);
24043
25055
  if (isSemSyscall(req)) return await this.semSyscall(req, cancelled);
@@ -24101,7 +25113,7 @@ var WasmProcess = class {
24101
25113
  return owner;
24102
25114
  }
24103
25115
  pathKeys = /* @__PURE__ */ new Map();
24104
- async lockPath(target, want) {
25116
+ async lockPath(target, want2) {
24105
25117
  if (target.fd !== void 0) {
24106
25118
  const st = await this.fds.get(target.fd).file.stat?.();
24107
25119
  if (!st) throw new KernelError("EBADF");
@@ -24111,9 +25123,9 @@ var WasmProcess = class {
24111
25123
  if (!target.path.startsWith("/")) throw new KernelError("EINVAL");
24112
25124
  const path = target.path;
24113
25125
  const g = this.guarded();
24114
- const checked = `${want}:${path}`;
24115
- if (g && want !== 0 && !this.lockChecked.has(checked)) {
24116
- await openable(g, "lock", path, want, false);
25126
+ const checked = `${want2}:${path}`;
25127
+ if (g && want2 !== 0 && !this.lockChecked.has(checked)) {
25128
+ await openable(g, "lock", path, want2, false);
24117
25129
  this.lockChecked.add(checked);
24118
25130
  }
24119
25131
  return path;
@@ -24126,8 +25138,8 @@ var WasmProcess = class {
24126
25138
  const known = file ? fileKeys.get(file) : this.pathKeys.get(target.path ?? "");
24127
25139
  if (kind === "close" && !file && target.path !== void 0) this.pathKeys.delete(target.path);
24128
25140
  if (access2 && !access2.stat) return objectKey(access2);
24129
- const want = kind === "read" ? R_OK : kind === "write" ? W_OK : 0;
24130
- const path = await this.lockPath(target, want);
25141
+ const want2 = kind === "read" ? R_OK : kind === "write" ? W_OK : 0;
25142
+ const path = await this.lockPath(target, want2);
24131
25143
  if (known !== void 0) return known;
24132
25144
  const st = await this.options.fs.stat?.(path).catch((err) => {
24133
25145
  if (!file) throw err;
@@ -24213,7 +25225,7 @@ var WasmProcess = class {
24213
25225
  const file = this.fds.get(req.fd).file;
24214
25226
  const read = req.peek ? file.peek : file.read;
24215
25227
  if (!read) throw new KernelError(req.peek && file.read ? "EOPNOTSUPP" : "EBADF");
24216
- if (file.tty) this.checkForeground(file.tty);
25228
+ if (file.tty && !file.device) this.checkForeground(file.tty);
24217
25229
  if (file.clipboard) return this.readClipboard(file.clipboard, req, cancelled);
24218
25230
  if (file.embedder) return this.readEmbedder(file, req, cancelled);
24219
25231
  if (file instanceof EventFd && req.max < EventFd.SIZE) throw new KernelError("EINVAL");
@@ -24310,6 +25322,46 @@ var WasmProcess = class {
24310
25322
  throw err;
24311
25323
  });
24312
25324
  }
25325
+ async openDev(path, flags2, cancelled) {
25326
+ const devices = this.options.devices;
25327
+ if (!devices) throw new KernelError("ENOENT");
25328
+ const shown2 = await this.options.identity?.();
25329
+ const tags = this.options.embedder?.tagsOf(this.pid);
25330
+ const stamp = {
25331
+ pid: shown2?.pid ?? this.pid,
25332
+ uid: this.cred.euid,
25333
+ gid: this.cred.egid,
25334
+ groups: [...this.cred.groups],
25335
+ users: devices.users,
25336
+ ...tags?.length ? { tag: tags[0], tags: [...tags] } : {}
25337
+ };
25338
+ const guard2 = this.guarded();
25339
+ const file = await devices.open(path, flags2, {
25340
+ stamp,
25341
+ ...guard2 ? { guard: guard2 } : {},
25342
+ signal: this.blockingSignal(cancelled)
25343
+ });
25344
+ return this.fds.install(this.options.outputFilter?.(file) ?? file, 3);
25345
+ }
25346
+ devMeta(path, change) {
25347
+ const devices = this.options.devices;
25348
+ if (!devices) throw new KernelError("ENOENT");
25349
+ devices.meta(path, change, this.guarded());
25350
+ return { ok: true, kind: "void" };
25351
+ }
25352
+ async devIoctl(req, cancelled) {
25353
+ const file = this.fds.get(req.fd).file;
25354
+ if (!file.device) {
25355
+ if (req.request >>> 0 === 21531) return pollFile(file).nread ?? 0;
25356
+ throw new KernelError("ENOTTY");
25357
+ }
25358
+ const result = await file.device.ioctl(
25359
+ req.request >>> 0,
25360
+ req.arg,
25361
+ this.blockingSignal(cancelled)
25362
+ );
25363
+ return result ?? null;
25364
+ }
24313
25365
  async openEmbedder(flags2) {
24314
25366
  const broker = this.options.embedder;
24315
25367
  if (!broker) throw new KernelError("ENOENT");
@@ -24397,11 +25449,11 @@ var WasmProcess = class {
24397
25449
  } else if (req.create) {
24398
25450
  await nodes.onEntry(path, async () => {
24399
25451
  const marks = this.options.owners || plain3 !== void 0;
24400
- const fresh3 = marks && !await present3(fs, path, true);
25452
+ const fresh4 = marks && !await present3(fs, path, true);
24401
25453
  this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path), fs));
24402
25454
  await this.fds.get(fd).file.stat?.();
24403
- if (fresh3 && plain3 !== void 0) await fs.chmod?.(path, plain3);
24404
- else if (fresh3) {
25455
+ if (fresh4 && plain3 !== void 0) await fs.chmod?.(path, plain3);
25456
+ else if (fresh4) {
24405
25457
  await fs.chown?.(path, { ...this.newOwner(req.mode), kind: "new" });
24406
25458
  }
24407
25459
  });
@@ -24500,8 +25552,8 @@ var WasmProcess = class {
24500
25552
  if (req.exclusive) return inParent(g, "open", req.path);
24501
25553
  if (req.created && guard2.created?.take(req.path)) return;
24502
25554
  const access2 = req.flags & WRITE_ACCESS;
24503
- const want = (access2 === 1 ? 0 : R_OK) | (access2 !== 0 || req.truncate ? W_OK : 0);
24504
- await openable(g, "open", req.path, want, req.create === true);
25555
+ const want2 = (access2 === 1 ? 0 : R_OK) | (access2 !== 0 || req.truncate ? W_OK : 0);
25556
+ await openable(g, "open", req.path, want2, req.create === true);
24505
25557
  }
24506
25558
  openTty(name2) {
24507
25559
  const tty = name2 === void 0 ? this.controllingTerminal() : this.options.jobs?.terminalNamed(name2);
@@ -24565,7 +25617,7 @@ var WasmProcess = class {
24565
25617
  }
24566
25618
  terminalByName(name2) {
24567
25619
  const n = ptsNumber(name2);
24568
- const tty = n === void 0 ? this.options.jobs?.terminalNamed(name2) : this.options.ptys?.get(n).slave;
25620
+ const tty = n === void 0 ? this.options.jobs?.terminalNamed(name2) ?? this.options.devices?.openTty(name2) : this.options.ptys?.get(n).slave;
24569
25621
  if (!tty) throw new KernelError("ENOENT");
24570
25622
  return tty;
24571
25623
  }
@@ -24687,6 +25739,16 @@ var WasmProcess = class {
24687
25739
  return { ok: true, kind: "json", json: await this.openClipboard(req.flags) };
24688
25740
  case "fd-open-embedder":
24689
25741
  return { ok: true, kind: "json", json: await this.openEmbedder(req.flags) };
25742
+ case "fd-dev-nodes":
25743
+ return { ok: true, kind: "json", json: this.options.devices?.list() ?? [] };
25744
+ case "fd-open-dev":
25745
+ return { ok: true, kind: "json", json: await this.openDev(req.path, req.flags, cancelled) };
25746
+ case "fd-dev-ioctl":
25747
+ return { ok: true, kind: "json", json: await this.devIoctl(req, cancelled) };
25748
+ case "fd-dev-meta": {
25749
+ const { op: _, path, ...change } = req;
25750
+ return this.devMeta(path, change);
25751
+ }
24690
25752
  case "fd-clipboard-abort": {
24691
25753
  const file = this.fds.get(req.fd);
24692
25754
  file.file.clipboard?.abort();
@@ -24731,6 +25793,11 @@ var WasmProcess = class {
24731
25793
  return { ok: true, kind: "void" };
24732
25794
  }
24733
25795
  const file = this.fds.get(req.fd).file;
25796
+ const device = file.device;
25797
+ if (device && (req.op === "fd-chmod" || req.op === "fd-chown" || req.op === "fd-utimes")) {
25798
+ const { op: _, fd: __, ...change } = req;
25799
+ return this.devMeta(device.path, change);
25800
+ }
24734
25801
  if (!file.pread || !file.pwrite || !file.resize || !file.stat) throw new KernelError("ESPIPE");
24735
25802
  switch (req.op) {
24736
25803
  case "fd-pread":
@@ -24900,14 +25967,19 @@ var WasmProcess = class {
24900
25967
  ...file.fifo !== void 0 ? { fifo: file.fifo } : {}
24901
25968
  };
24902
25969
  }
24903
- ttySyscall(req) {
25970
+ async ttySyscall(req, cancelled) {
24904
25971
  const tty = this.tty(req.fd);
24905
25972
  const jobs = this.options.jobs;
25973
+ const device = this.fds.get(req.fd).file.device;
25974
+ if (device && req.op.startsWith("tty-pgrp")) throw new KernelError("ENOTTY");
24906
25975
  switch (req.op) {
24907
25976
  case "tty-get":
24908
25977
  return { ok: true, kind: "json", json: tty.tcgets() };
24909
25978
  case "tty-set":
24910
- tty.tcsets(req.termios, req.flush === true);
25979
+ if (device) {
25980
+ const how = { flush: req.flush === true, drain: req.drain === true };
25981
+ await device.setTermios(req.termios, how, this.blockingSignal(cancelled));
25982
+ } else tty.tcsets(req.termios, req.flush === true);
24911
25983
  return { ok: true, kind: "void" };
24912
25984
  case "tty-winsz": {
24913
25985
  const size = tty.winsize();
@@ -25788,6 +26860,7 @@ function processOptions(opts, header5, hooks, created4) {
25788
26860
  ...opts.outputFilter ? { outputFilter: opts.outputFilter } : {},
25789
26861
  ...opts.clipboard ? { clipboard: opts.clipboard, ...clipboardName(opts) } : {},
25790
26862
  ...embedderOptions(opts),
26863
+ devices: opts.devices,
25791
26864
  ...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
25792
26865
  ...opts.source ? { source: opts.source } : {},
25793
26866
  noLinks: opts.noLinks,
@@ -26542,15 +27615,15 @@ var RemoteWatch = class {
26542
27615
  this.channel.postMessage({ ack: seq2, from: this.tab, to: from });
26543
27616
  }
26544
27617
  hold(add, drop, wait2) {
26545
- const fresh3 = add.filter(
27618
+ const fresh4 = add.filter(
26546
27619
  ([path, fid]) => !this.myHolds.has(path) || this.myHolds.get(path) !== fid
26547
27620
  );
26548
27621
  const gone = drop.filter((path) => this.myHolds.has(path));
26549
27622
  for (const path of gone) this.myHolds.delete(path);
26550
- for (const [path, fid] of fresh3) this.myHolds.set(path, fid);
26551
- if (fresh3.length === 0 && gone.length === 0 || this.peers.size === 0)
27623
+ for (const [path, fid] of fresh4) this.myHolds.set(path, fid);
27624
+ if (fresh4.length === 0 && gone.length === 0 || this.peers.size === 0)
26552
27625
  return Promise.resolve();
26553
- const seq2 = this.sendHolds(fresh3, gone);
27626
+ const seq2 = this.sendHolds(fresh4, gone);
26554
27627
  if (!wait2) return Promise.resolve();
26555
27628
  const heard = new Set(
26556
27629
  [...this.peers].filter((peer) => this.holders.has(peer) && !this.silent.has(peer))
@@ -26974,14 +28047,14 @@ var LockService = class {
26974
28047
  );
26975
28048
  });
26976
28049
  }
26977
- async holder(key, owner, want) {
28050
+ async holder(key, owner, want2) {
26978
28051
  return await this.ask({
26979
28052
  op: "holder",
26980
28053
  key,
26981
28054
  owner,
26982
- kind: want.kind,
26983
- start: want.start,
26984
- end: want.end
28055
+ kind: want2.kind,
28056
+ start: want2.start,
28057
+ end: want2.end
26985
28058
  }) ?? void 0;
26986
28059
  }
26987
28060
  async set(key, owner, kind, start2, end) {
@@ -27260,9 +28333,9 @@ function local(name2) {
27260
28333
  function literal2(name2) {
27261
28334
  return ipv4Bytes(name2) !== void 0 || ipv6Groups(name2) !== void 0;
27262
28335
  }
27263
- function addressData(address, type) {
27264
- if (type === TYPE_A) return ipv4Bytes(address);
27265
- const groups = type === TYPE_AAAA ? ipv6Groups(address) : void 0;
28336
+ function addressData(address2, type) {
28337
+ if (type === TYPE_A) return ipv4Bytes(address2);
28338
+ const groups = type === TYPE_AAAA ? ipv6Groups(address2) : void 0;
27266
28339
  return groups?.flatMap((g) => [g >> 8, g & 255]);
27267
28340
  }
27268
28341
  var DnsStub = class {
@@ -27275,10 +28348,10 @@ var DnsStub = class {
27275
28348
  this.timeoutMs = options.timeoutMs ?? DNS_FORWARD_TIMEOUT_MS;
27276
28349
  }
27277
28350
  async answer(query, udp) {
27278
- const parsed2 = parseQuery(query);
27279
- if (!parsed2) return void 0;
27280
- if (!parsed2.ok) return errorReply2(parsed2.id, parsed2.flags, parsed2.rcode);
27281
- const q = parsed2.question;
28351
+ const parsed3 = parseQuery(query);
28352
+ if (!parsed3) return void 0;
28353
+ if (!parsed3.ok) return errorReply2(parsed3.id, parsed3.flags, parsed3.rcode);
28354
+ const q = parsed3.question;
27282
28355
  const reply = await this.reply(query, q).catch(
27283
28356
  () => answerReply(query, q, SERVFAIL, [], false)
27284
28357
  );
@@ -27287,8 +28360,8 @@ var DnsStub = class {
27287
28360
  async reply(query, q) {
27288
28361
  if (q.klass !== CLASS_IN || literal2(q.name)) return this.forward(query, q);
27289
28362
  if (q.type === TYPE_PTR) {
27290
- const address = reverseAddress(q.name);
27291
- const name2 = address !== void 0 ? await this.reverse(address) : void 0;
28363
+ const address2 = reverseAddress(q.name);
28364
+ const name2 = address2 !== void 0 ? await this.reverse(address2) : void 0;
27292
28365
  if (name2 !== void 0)
27293
28366
  return answerReply(query, q, NOERROR, [record2(TYPE_PTR, encodeName(name2))], true);
27294
28367
  return this.forward(query, q);
@@ -27318,8 +28391,8 @@ var DnsStub = class {
27318
28391
  known(name2) {
27319
28392
  return this.resolver.known(name2).catch(() => false);
27320
28393
  }
27321
- reverse(address) {
27322
- return this.resolver.reverse(address).catch(() => void 0);
28394
+ reverse(address2) {
28395
+ return this.resolver.reverse(address2).catch(() => void 0);
27323
28396
  }
27324
28397
  async forward(query, q) {
27325
28398
  if (!this.forwarder) return answerReply(query, q, SERVFAIL, [], false);
@@ -27498,7 +28571,7 @@ function honors(traits, redirect) {
27498
28571
  const raw2 = traits.manualRedirects === true && (hops === void 0 || hops === false);
27499
28572
  return redirect === "manual" && raw2;
27500
28573
  }
27501
- function refused3(message3) {
28574
+ function refused4(message3) {
27502
28575
  return new TransportError(message3, 502, void 0, "transport");
27503
28576
  }
27504
28577
  var RemoteTransport = class {
@@ -27589,7 +28662,7 @@ var RemoteTransport = class {
27589
28662
  const { signal, redirect } = request;
27590
28663
  signal.throwIfAborted();
27591
28664
  if (unfollowed2(redirect) && !honors(this.traits, redirect)) {
27592
- throw refused3(`this transport cannot honor redirect: ${redirect}`);
28665
+ throw refused4(`this transport cannot honor redirect: ${redirect}`);
27593
28666
  }
27594
28667
  let finished = false;
27595
28668
  const abort = () => {
@@ -27640,7 +28713,7 @@ var RemoteTransport = class {
27640
28713
  };
27641
28714
  if (unfollowed2(redirect) && (redirected2 || redirects?.length)) {
27642
28715
  await cancel();
27643
- throw refused3(`this transport followed a redirect despite redirect: ${redirect}`);
28716
+ throw refused4(`this transport followed a redirect despite redirect: ${redirect}`);
27644
28717
  }
27645
28718
  return {
27646
28719
  ...url !== void 0 ? { url } : {},
@@ -28160,6 +29233,7 @@ var SETUP_OPS = /* @__PURE__ */ new Set([
28160
29233
  "proc-list",
28161
29234
  "proc-identity",
28162
29235
  "fd-info",
29236
+ "fd-dev-nodes",
28163
29237
  "sig-mask",
28164
29238
  "sig-inherited",
28165
29239
  "proc-setpgid",
@@ -28256,8 +29330,8 @@ var WasmMemories = class {
28256
29330
  const learned = this.learned;
28257
29331
  if (!learned) return this.base;
28258
29332
  const steps = Math.floor((this.now() - learned.at) / DECAY_MS);
28259
- const raised = learned.ceiling + steps * DECAY_STEP;
28260
- if (raised < (this.base ?? DEFAULT_CEILING)) return raised;
29333
+ const raised2 = learned.ceiling + steps * DECAY_STEP;
29334
+ if (raised2 < (this.base ?? DEFAULT_CEILING)) return raised2;
28261
29335
  this.learned = void 0;
28262
29336
  return this.base;
28263
29337
  }
@@ -28400,9 +29474,9 @@ function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
28400
29474
  let open = false;
28401
29475
  const reply = (message3, transfer = []) => port.postMessage(message3, transfer);
28402
29476
  async function hello(message3) {
28403
- const refused4 = driverVersionError(message3.protocol);
28404
- if (refused4) {
28405
- reply({ hello: { protocol: DRIVER_PROTOCOL, error: refused4, errno: "EPROTO" } });
29477
+ const refused5 = driverVersionError(message3.protocol);
29478
+ if (refused5) {
29479
+ reply({ hello: { protocol: DRIVER_PROTOCOL, error: refused5, errno: "EPROTO" } });
28406
29480
  return;
28407
29481
  }
28408
29482
  try {
@@ -28455,8 +29529,8 @@ function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
28455
29529
  function noticeOf(data) {
28456
29530
  if (Array.isArray(data)) return { paths: data.filter((p) => typeof p === "string"), trees: [] };
28457
29531
  const notice = data ?? {};
28458
- const strings2 = (list) => Array.isArray(list) ? list.filter((p) => typeof p === "string") : [];
28459
- return { paths: strings2(notice.paths), trees: strings2(notice.trees), outside: true };
29532
+ const strings3 = (list) => Array.isArray(list) ? list.filter((p) => typeof p === "string") : [];
29533
+ return { paths: strings3(notice.paths), trees: strings3(notice.trees), outside: true };
28460
29534
  }
28461
29535
 
28462
29536
  // src/fs/opfs.ts
@@ -28950,7 +30024,7 @@ var OpfsFs = class {
28950
30024
  const parent = entries.at(-1);
28951
30025
  const setgid = ((parent?.mode ?? 0) & S_ISGID4) !== 0;
28952
30026
  const parentGid = (ownOf(parent) ?? inherit(entries.slice(0, -1))).gid;
28953
- const want = { uid: owner.uid, gid: setgid ? parentGid : owner.gid };
30027
+ const want2 = { uid: owner.uid, gid: setgid ? parentGid : owner.gid };
28954
30028
  let mode = owner.mode === void 0 ? void 0 : owner.mode & PERM_MASK;
28955
30029
  if (directory && setgid) mode = (mode ?? 493) | S_ISGID4;
28956
30030
  const fields = {};
@@ -28958,7 +30032,7 @@ var OpfsFs = class {
28958
30032
  fields.mode = mode;
28959
30033
  }
28960
30034
  if (self) fields.heir = void 0;
28961
- if (ownOf(self) || !sameOwner(want, inherit(entries))) Object.assign(fields, want);
30035
+ if (ownOf(self) || !sameOwner(want2, inherit(entries))) Object.assign(fields, want2);
28962
30036
  return Object.keys(fields).length > 0 ? fields : void 0;
28963
30037
  }
28964
30038
  async stampNew(path, owner, directory) {
@@ -29195,9 +30269,9 @@ var OpfsFs = class {
29195
30269
  this.lookups++;
29196
30270
  if (split2(path).length === 0) return this.root;
29197
30271
  const cached = this.files.get(path);
29198
- const fresh3 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
29199
- if (cached && !fresh3) this.files.delete(path);
29200
- if (cached && fresh3) {
30272
+ const fresh4 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
30273
+ if (cached && !fresh4) this.files.delete(path);
30274
+ if (cached && fresh4) {
29201
30275
  if (this.glanced(cached)) return this.remember(path, cached);
29202
30276
  try {
29203
30277
  await this.glance(cached);
@@ -29415,8 +30489,8 @@ var OpfsFs = class {
29415
30489
  glanced(handle) {
29416
30490
  if (this.exact > 0) return void 0;
29417
30491
  const seen = this.glances.get(handle);
29418
- const fresh3 = seen && seen.changes === this.changes && this.recent(seen, GLANCE_LOOKUPS);
29419
- return fresh3 ? seen.file : void 0;
30492
+ const fresh4 = seen && seen.changes === this.changes && this.recent(seen, GLANCE_LOOKUPS);
30493
+ return fresh4 ? seen.file : void 0;
29420
30494
  }
29421
30495
  async glance(handle) {
29422
30496
  const at = performance.now();
@@ -29618,13 +30692,13 @@ var OpfsFs = class {
29618
30692
  async keptFor(path) {
29619
30693
  const at = normalizePath(path);
29620
30694
  const cached = this.files.get(at);
29621
- const fresh3 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
29622
- const [target, existed] = fresh3 ? [at, true] : await this.writeTarget(path);
30695
+ const fresh4 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
30696
+ const [target, existed] = fresh4 ? [at, true] : await this.writeTarget(path);
29623
30697
  if (!existed) return [void 0, target, false];
29624
30698
  this.refuse(target);
29625
30699
  const [entry] = await this.meta.get([target]);
29626
30700
  if (entry?.fifo || entry?.socket) throw fsError("ENXIO", path);
29627
- const kept = fresh3 ? cached : this.files.get(target);
30701
+ const kept = fresh4 ? cached : this.files.get(target);
29628
30702
  const usable3 = kept && performance.now() - (this.found.get(kept) ?? -CHECK_MS) < CHECK_MS;
29629
30703
  return [usable3 ? kept : void 0, target, true];
29630
30704
  }
@@ -31178,7 +32252,7 @@ function unescape(field4) {
31178
32252
  );
31179
32253
  }
31180
32254
  function parseFstab(text) {
31181
- const lines2 = [];
32255
+ const lines3 = [];
31182
32256
  text.split("\n").forEach((raw2, index) => {
31183
32257
  const content = raw2.replace(/#.*$/, "").trim();
31184
32258
  if (!content) return;
@@ -31187,12 +32261,12 @@ function parseFstab(text) {
31187
32261
  if (options.split(",").includes("noauto")) return;
31188
32262
  const spec = { source, target, type, options: {} };
31189
32263
  try {
31190
- lines2.push({ line: index + 1, spec: { ...spec, options: parseMountOptions(options) } });
32264
+ lines3.push({ line: index + 1, spec: { ...spec, options: parseMountOptions(options) } });
31191
32265
  } catch (err) {
31192
- lines2.push({ line: index + 1, spec, invalid: err });
32266
+ lines3.push({ line: index + 1, spec, invalid: err });
31193
32267
  }
31194
32268
  });
31195
- return lines2;
32269
+ return lines3;
31196
32270
  }
31197
32271
  function codeOf6(err) {
31198
32272
  const code = err?.code;
@@ -31210,20 +32284,20 @@ function wait(ms, signal) {
31210
32284
  signal.addEventListener("abort", done4, { once: true });
31211
32285
  });
31212
32286
  }
31213
- async function mountFstab(lines2, mount, signal, retries = FSTAB_RETRIES, discard = () => {
32287
+ async function mountFstab(lines3, mount, signal, retries = FSTAB_RETRIES, discard = () => {
31214
32288
  }, settled = () => {
31215
32289
  }) {
31216
32290
  return Promise.all(
31217
- lines2.map(async (each) => {
32291
+ lines3.map(async (each) => {
31218
32292
  const result = await mountLine(each, mount, signal, retries, discard);
31219
32293
  settled(each, result);
31220
32294
  return result;
31221
32295
  })
31222
32296
  );
31223
32297
  }
31224
- async function mountLine({ line, spec, invalid }, mount, signal, retries, discard) {
31225
- let failed5 = invalid;
31226
- for (let attempt = 0; !invalid && attempt <= retries.length && !signal.aborted; attempt++) {
32298
+ async function mountLine({ line, spec, invalid: invalid2 }, mount, signal, retries, discard) {
32299
+ let failed5 = invalid2;
32300
+ for (let attempt = 0; !invalid2 && attempt <= retries.length && !signal.aborted; attempt++) {
31227
32301
  try {
31228
32302
  const entry = await mount(spec);
31229
32303
  if (!signal.aborted) return { line, target: spec.target, entry };
@@ -31391,14 +32465,14 @@ var RESTORE_AGAIN_MS = 3e4;
31391
32465
  function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
31392
32466
  const handles = /* @__PURE__ */ new Map();
31393
32467
  let nextFh = 0;
31394
- const fresh3 = (remote) => {
32468
+ const fresh4 = (remote) => {
31395
32469
  if (remote.client.token === remote.token && !remote.client.gone) return remote;
31396
32470
  throw fsError2("ESTALE", "the folder was granted again, so this open file is gone");
31397
32471
  };
31398
32472
  const live2 = (fh) => {
31399
32473
  const remote = handles.get(fh);
31400
32474
  if (!remote) throw fsError2("EBADF", `handle ${fh}`);
31401
- return fresh3(remote);
32475
+ return fresh4(remote);
31402
32476
  };
31403
32477
  const call = (op, body = {}) => current().call(op, body);
31404
32478
  const same2 = (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);
@@ -31480,7 +32554,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
31480
32554
  if (!remote) throw fsError2("EBADF", `handle ${fh}`);
31481
32555
  handles.delete(fh);
31482
32556
  try {
31483
- await fresh3(remote).client.call("release", { fh: remote.fh }, true);
32557
+ await fresh4(remote).client.call("release", { fh: remote.fh }, true);
31484
32558
  } finally {
31485
32559
  if (remote.restore) await settle(current(), remote.restore);
31486
32560
  }
@@ -31530,7 +32604,7 @@ async function resize(h, path, size) {
31530
32604
  await h.release(fh);
31531
32605
  }
31532
32606
  }
31533
- function sleep(ms, signal) {
32607
+ function sleep2(ms, signal) {
31534
32608
  return new Promise((resolve2) => {
31535
32609
  const done4 = () => {
31536
32610
  clearTimeout(timer);
@@ -31558,22 +32632,22 @@ function keepWatching(loop, signal) {
31558
32632
  const stop = () => session2.abort();
31559
32633
  signal.addEventListener("abort", stop);
31560
32634
  let timer;
31561
- const quiet3 = () => {
32635
+ const quiet5 = () => {
31562
32636
  clearTimeout(timer);
31563
32637
  timer = setTimeout(stop, silenceMs);
31564
32638
  };
31565
32639
  let opened2 = false;
31566
32640
  try {
31567
32641
  const client = await loop.connect();
31568
- quiet3();
32642
+ quiet5();
31569
32643
  await client.watch(
31570
32644
  session2.signal,
31571
32645
  (event) => {
31572
- quiet3();
32646
+ quiet5();
31573
32647
  if (!event.ping) loop.event(event);
31574
32648
  },
31575
32649
  () => {
31576
- quiet3();
32650
+ quiet5();
31577
32651
  opened2 = true;
31578
32652
  up = true;
31579
32653
  loop.up(client);
@@ -31602,7 +32676,7 @@ function keepWatching(loop, signal) {
31602
32676
  loop.down();
31603
32677
  }
31604
32678
  ready2.resolve();
31605
- await sleep(wait2, signal);
32679
+ await sleep2(wait2, signal);
31606
32680
  wait2 = Math.min(wait2 * 2, most);
31607
32681
  }
31608
32682
  })();
@@ -31844,7 +32918,7 @@ var PortTransport = class {
31844
32918
  }).finally(() => this.fail("the folder's watch ended"));
31845
32919
  }
31846
32920
  };
31847
- var errorOf2 = (err) => ({
32921
+ var errorOf3 = (err) => ({
31848
32922
  code: typeof codeOf7(err) === "string" ? codeOf7(err) : "EIO",
31849
32923
  message: err?.message ?? String(err)
31850
32924
  });
@@ -31894,7 +32968,7 @@ function serveHostfsPort(port, transport) {
31894
32968
  );
31895
32969
  send2({ id: id2, end: true });
31896
32970
  } catch (err) {
31897
- send2({ id: id2, error: errorOf2(err) });
32971
+ send2({ id: id2, error: errorOf3(err) });
31898
32972
  } finally {
31899
32973
  watches.delete(id2);
31900
32974
  }
@@ -31922,7 +32996,7 @@ function serveHostfsPort(port, transport) {
31922
32996
  if (op !== "call") throw fsError2("EINVAL", `unknown hostfs port op ${op}`);
31923
32997
  send2({ id: id2, result: await answer2(data.name, data.body) });
31924
32998
  } catch (err) {
31925
- send2({ id: id2, error: errorOf2(err) });
32999
+ send2({ id: id2, error: errorOf3(err) });
31926
33000
  }
31927
33001
  };
31928
33002
  const stop = () => {
@@ -33144,6 +34218,7 @@ var Launcher = class {
33144
34218
  scrubs = /* @__PURE__ */ new Map();
33145
34219
  clipboard;
33146
34220
  embedder;
34221
+ devices;
33147
34222
  rootFs;
33148
34223
  fileWatches;
33149
34224
  nextPid = 1e3;
@@ -33168,6 +34243,7 @@ var Launcher = class {
33168
34243
  );
33169
34244
  this.embedder = this.embedderBroker(options.embedder);
33170
34245
  this.embedder.denied = options.onDenial;
34246
+ this.devices = this.deviceNodes(options.devices);
33171
34247
  this.sealed = new SealedBroker(
33172
34248
  options.sealed,
33173
34249
  this.usersMode,
@@ -33383,16 +34459,16 @@ var Launcher = class {
33383
34459
  if (!found) {
33384
34460
  found = this.swaps.settled().then(() => this.trustedAppended()).then(async (appended2) => {
33385
34461
  const tiers = await this.tiers(scope, appended2);
33386
- const refused4 = [];
34462
+ const refused5 = [];
33387
34463
  const commands = await scanCommands(
33388
34464
  this.scanned,
33389
34465
  tiers,
33390
34466
  tiers.length - appended2.length,
33391
34467
  void 0,
33392
- refused4,
34468
+ refused5,
33393
34469
  await this.globalRoots(scope)
33394
34470
  );
33395
- this.refusedOf.set(commands, refused4.map(refusing));
34471
+ this.refusedOf.set(commands, refused5.map(refusing));
33396
34472
  return commands;
33397
34473
  }).then(
33398
34474
  (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)
@@ -33599,14 +34675,14 @@ var Launcher = class {
33599
34675
  return await this.commandAt(path, scope) ?? await this.refusedAt(path, name2, scope);
33600
34676
  }
33601
34677
  async refusedAt(path, name2, scope) {
33602
- const refused4 = this.refusedOf.get(await this.commands(scope)) ?? [];
33603
- if (refused4.length === 0) return void 0;
34678
+ const refused5 = this.refusedOf.get(await this.commands(scope)) ?? [];
34679
+ if (refused5.length === 0) return void 0;
33604
34680
  const st = await this.base.stat(path).catch(() => void 0);
33605
34681
  if (!st?.isFile) return void 0;
33606
34682
  const real = await physicalPathAsync(this.base, path).catch(() => path);
33607
34683
  const same2 = async (own) => own === path || await physicalPathAsync(this.base, own).catch(() => own) === real;
33608
34684
  const owners = [];
33609
- for (const c of refused4) {
34685
+ for (const c of refused5) {
33610
34686
  const origin = c.origin;
33611
34687
  for (const own of [origin.glue, origin.wasm, ...origin.variants ?? []]) {
33612
34688
  if (!await same2(own)) continue;
@@ -33616,11 +34692,11 @@ var Launcher = class {
33616
34692
  }
33617
34693
  const stem = name2.replace(/\.wasm$/, "");
33618
34694
  const named = owners.find((c) => c.name === name2) ?? owners.find((c) => c.name === stem);
33619
- return named ?? owners[0] ?? await this.refusedCopy(real, name2, refused4);
34695
+ return named ?? owners[0] ?? await this.refusedCopy(real, name2, refused5);
33620
34696
  }
33621
- async refusedCopy(real, name2, refused4) {
34697
+ async refusedCopy(real, name2, refused5) {
33622
34698
  const dir = real.slice(0, real.lastIndexOf("/"));
33623
- for (const c of refused4) {
34699
+ for (const c of refused5) {
33624
34700
  const origin = c.origin;
33625
34701
  if (origin.abi !== "emscripten" || origin.script || c.name !== name2) continue;
33626
34702
  const own = await physicalPathAsync(this.base, origin.glue).catch(() => origin.glue);
@@ -33787,8 +34863,8 @@ var Launcher = class {
33787
34863
  const via = planned?.via ?? [];
33788
34864
  for (const [i, file] of via.entries()) {
33789
34865
  if (!byPath(file)) continue;
33790
- const want = i < via.length - 1 ? R_OK | X_OK : X_OK;
33791
- await need(g, "exec", this.fs.resolvePath(req.cwd, file), want);
34866
+ const want2 = i < via.length - 1 ? R_OK | X_OK : X_OK;
34867
+ await need(g, "exec", this.fs.resolvePath(req.cwd, file), want2);
33792
34868
  }
33793
34869
  for (const bin of planned?.bins ?? []) await need(g, "exec", bin, R_OK | X_OK);
33794
34870
  if (planned?.target.copy) await need(g, "exec", planned.target.copy, R_OK | X_OK);
@@ -33828,12 +34904,12 @@ var Launcher = class {
33828
34904
  return void 0;
33829
34905
  }
33830
34906
  async planOne(req, { ppid, cred, scope }, catalogOnly = false) {
33831
- const refused4 = (err) => {
34907
+ const refused5 = (err) => {
33832
34908
  throw codeOf8(err) === "EACCES" ? new SpawnError("EACCES") : err;
33833
34909
  };
33834
- const file = await this.execFile(req.file, req.cwd, this.execGuard(cred, ppid)).catch(refused4);
34910
+ const file = await this.execFile(req.file, req.cwd, this.execGuard(cred, ppid)).catch(refused5);
33835
34911
  const planned = await this.plan(file, req.argv, req.cwd, scope, req.argv0, catalogOnly);
33836
- await this.mayExec(cred, { ...req, file, pid: ppid }, planned).catch(refused4);
34912
+ await this.mayExec(cred, { ...req, file, pid: ppid }, planned).catch(refused5);
33837
34913
  if (planned) return planned;
33838
34914
  throw new SpawnError(catalogOnly ? "ENOENT" : await this.unrunnable(file, req.cwd, scope));
33839
34915
  }
@@ -34469,6 +35545,7 @@ var Launcher = class {
34469
35545
  ...scrubs ? { outputFilter: (file) => scrubbedAll(file, scrubs) } : {},
34470
35546
  clipboard: this.clipboard,
34471
35547
  embedder: { broker: this.embedder, session: () => this.sessionOf(pid) },
35548
+ devices: this.devices,
34472
35549
  ...this.dac ? { dac: this.dac } : {},
34473
35550
  program: req.program,
34474
35551
  changes: this.changes.sab,
@@ -34652,8 +35729,8 @@ var Launcher = class {
34652
35729
  this.sampledAt = Math.max(this.sampledAt, now);
34653
35730
  this.load = this.load.map((avg, i) => {
34654
35731
  const window = LOAD_WINDOWS_MS[i];
34655
- const fresh3 = 1 - Math.exp(-LOAD_SAMPLE_MS / window);
34656
- return avg * Math.exp(-elapsed / window) + running * fresh3;
35732
+ const fresh4 = 1 - Math.exp(-LOAD_SAMPLE_MS / window);
35733
+ return avg * Math.exp(-elapsed / window) + running * fresh4;
34657
35734
  });
34658
35735
  }
34659
35736
  idleSince;
@@ -35000,7 +36077,7 @@ var Launcher = class {
35000
36077
  forker(ppid, parent) {
35001
36078
  const { exec: _exec, pgid: _pgid, group: _group, pid: _pid, scrub: _scrub, ...image } = parent;
35002
36079
  return async (state, given) => {
35003
- const fresh3 = (fds, quietOom) => this.start({
36080
+ const fresh4 = (fds, quietOom) => this.start({
35004
36081
  ...image,
35005
36082
  quietOom,
35006
36083
  cwd: state.cwd ?? parent.cwd,
@@ -35016,7 +36093,7 @@ var Launcher = class {
35016
36093
  tagBase: this.tagBases.get(ppid),
35017
36094
  links: this.links.get(ppid)
35018
36095
  });
35019
- return childHandle(await this.admitted(parent.program.abi !== "js", given, fresh3));
36096
+ return childHandle(await this.admitted(parent.program.abi !== "js", given, fresh4));
35020
36097
  };
35021
36098
  }
35022
36099
  keySignal(tty, fallback, sig, send2) {
@@ -35057,6 +36134,14 @@ var Launcher = class {
35057
36134
  this.sealed.userOf = async (uid) => (await this.users.lookup(uid))?.name;
35058
36135
  this.sealed.factsOf = (pid) => this.processFacts(pid);
35059
36136
  }
36137
+ deviceNodes(hook) {
36138
+ const nodes = new DeviceNodes(this.usersMode, {
36139
+ groupId: async (name2) => (await this.users.groups()).find((g) => g.name === name2)?.gid,
36140
+ userId: async (name2) => (await this.users.lookup(name2))?.uid
36141
+ });
36142
+ if (hook) nodes.register(0, hook);
36143
+ return nodes;
36144
+ }
35060
36145
  embedderBroker(hook) {
35061
36146
  const broker = new EmbedderBroker(
35062
36147
  this.usersMode,
@@ -35346,8 +36431,8 @@ var Launcher = class {
35346
36431
  for (const [path, mode] of layout) {
35347
36432
  if (isProtected(this.prefixes, normalizePath(path))) continue;
35348
36433
  const real = await this.realLayoutPath(path);
35349
- const fresh3 = real !== void 0 && !await this.base.hasOwner?.(real);
35350
- if (fresh3) await this.base.chown?.(real, { uid: 0, gid: 0, mode, follow: false });
36434
+ const fresh4 = real !== void 0 && !await this.base.hasOwner?.(real);
36435
+ if (fresh4) await this.base.chown?.(real, { uid: 0, gid: 0, mode, follow: false });
35351
36436
  }
35352
36437
  }
35353
36438
  async realLayoutPath(given) {
@@ -35481,10 +36566,10 @@ var Launcher = class {
35481
36566
  }
35482
36567
  async mountFstab() {
35483
36568
  const text = await this.base.readFile(FSTAB_PATH).catch(() => "");
35484
- const lines2 = parseFstab(text);
35485
- for (const { spec } of lines2) this.mounts.note(spec, "pending");
36569
+ const lines3 = parseFstab(text);
36570
+ for (const { spec } of lines3) this.mounts.note(spec, "pending");
35486
36571
  return mountFstab(
35487
- lines2,
36572
+ lines3,
35488
36573
  (spec) => this.mount(spec).catch((err) => {
35489
36574
  this.mounts.note(spec, "pending", err?.message ?? String(err));
35490
36575
  throw err;
@@ -36190,13 +37275,13 @@ function unsendableHeaders(url, names2, hint) {
36190
37275
  function refuseUnsendable(url, out, hint) {
36191
37276
  if (!platformFilters()) return;
36192
37277
  const kept = new Request(PROBE, { headers: out }).headers;
36193
- const refused4 = [];
37278
+ const refused5 = [];
36194
37279
  for (const name2 of new Set(out.keys())) {
36195
37280
  if (kept.has(name2)) continue;
36196
37281
  if (TRANSPORT_OWN.has(name2)) out.delete(name2);
36197
- else refused4.push(name2);
37282
+ else refused5.push(name2);
36198
37283
  }
36199
- if (refused4.length > 0) throw unsendableHeaders(url, refused4, hint);
37284
+ if (refused5.length > 0) throw unsendableHeaders(url, refused5, hint);
36200
37285
  }
36201
37286
  function streamOf(body) {
36202
37287
  const it = body[Symbol.asyncIterator]();
@@ -36508,7 +37593,7 @@ function serveTransport(peer, transport, {
36508
37593
  }
36509
37594
 
36510
37595
  // src/client/protocol.ts
36511
- var PROTOCOL = [1, 16];
37596
+ var PROTOCOL = [1, 18];
36512
37597
  var FS_METHODS = [
36513
37598
  "readFile",
36514
37599
  "writeFile",
@@ -36768,8 +37853,10 @@ function hostRegistries(send2, gone, port) {
36768
37853
  const cdps = cdpRegistry(send2, gone);
36769
37854
  const facades = facadeRegistry(send2, gone);
36770
37855
  const requests = requestBinding(send2, port);
37856
+ const devices = deviceBinding(send2, port);
36771
37857
  return {
36772
37858
  requests,
37859
+ devices,
36773
37860
  answer(l, req) {
36774
37861
  if (req.op === "serve-cdp") return cdps.serve(l, req);
36775
37862
  if (req.op === "unserve-cdp") return cdps.drop(req.registration);
@@ -36778,6 +37865,7 @@ function hostRegistries(send2, gone, port) {
36778
37865
  },
36779
37866
  close() {
36780
37867
  requests.close();
37868
+ devices.close();
36781
37869
  cdps.close();
36782
37870
  facades.close();
36783
37871
  }
@@ -36891,6 +37979,28 @@ function answered(pending, req, reply, detach) {
36891
37979
  }
36892
37980
  );
36893
37981
  }
37982
+ async function helloRefused(host, hosts, message3) {
37983
+ return versionError(message3.protocol) ?? await hosts.requests.accept(host, message3) ?? await hosts.devices.accept(host, message3);
37984
+ }
37985
+ function deviceBinding(send2, port) {
37986
+ const asker = devicesAsker({ postMessage: (m, t) => send2(m, t) });
37987
+ let unbind;
37988
+ port.addEventListener("message", (event) => {
37989
+ const reply = devicesReplyOf(event.data);
37990
+ if (reply) asker.answer(reply);
37991
+ });
37992
+ return {
37993
+ async accept(host, hello) {
37994
+ if (!hello.devices) return void 0;
37995
+ if (!host.attachDevices || host.session === void 0)
37996
+ return "EPERM: this port may not answer device opens";
37997
+ const launcher = await host.launcher();
37998
+ unbind = launcher.devices.register(host.session, asker.hookFor(host.session));
37999
+ return void 0;
38000
+ },
38001
+ close: () => unbind?.()
38002
+ };
38003
+ }
36894
38004
  function serveClient(port, host) {
36895
38005
  let state = "new";
36896
38006
  let self;
@@ -36924,9 +38034,9 @@ function serveClient(port, host) {
36924
38034
  ended.resolve();
36925
38035
  }
36926
38036
  async function hello(message3) {
36927
- const refused4 = versionError(message3.protocol) ?? await hosts.requests.accept(host, message3);
36928
- if (refused4) {
36929
- send2({ hello: { protocol: PROTOCOL, error: refused4 } });
38037
+ const refused5 = await helloRefused(host, hosts, message3);
38038
+ if (refused5) {
38039
+ send2({ hello: { protocol: PROTOCOL, error: refused5 } });
36930
38040
  detach();
36931
38041
  return;
36932
38042
  }
@@ -37121,14 +38231,14 @@ async function followRedirects(send2, request) {
37121
38231
  var DEVTOOLS_PORTS = [9222, 9223, 9224, 9225, 9226, 9227, 9228, 9229];
37122
38232
  var METADATA_V6 = [64768, 3778, 0, 0, 0, 0, 0, 596].join(":");
37123
38233
  var resolveHost = async (host) => (await lookup2(host, { all: true, verbatim: true })).map((a) => a.address);
37124
- function ipv4Place(address) {
37125
- const [a, b] = address.split(".").map(Number);
38234
+ function ipv4Place(address2) {
38235
+ const [a, b] = address2.split(".").map(Number);
37126
38236
  if (a === 127 || a === 0) return "loopback";
37127
38237
  if (a === 169 && b === 254) return "link-local";
37128
38238
  return void 0;
37129
38239
  }
37130
- function placeOf(address) {
37131
- const plain3 = address.replace(/^\[(.*)\]$/, "$1").toLowerCase();
38240
+ function placeOf(address2) {
38241
+ const plain3 = address2.replace(/^\[(.*)\]$/, "$1").toLowerCase();
37132
38242
  const kind = isIP3(plain3);
37133
38243
  if (kind === 4) return ipv4Place(plain3);
37134
38244
  if (kind !== 6) return void 0;
@@ -37144,12 +38254,12 @@ function ipv4Of(words3) {
37144
38254
  const [high = 0, low = 0] = words3.slice(6);
37145
38255
  return [high >> 8, high & 255, low >> 8, low & 255].join(".");
37146
38256
  }
37147
- function hextets2(address) {
37148
- const dotted = /(\d+\.\d+\.\d+\.\d+)$/.exec(address)?.[1];
37149
- let text = address;
38257
+ function hextets2(address2) {
38258
+ const dotted = /(\d+\.\d+\.\d+\.\d+)$/.exec(address2)?.[1];
38259
+ let text = address2;
37150
38260
  if (dotted) {
37151
38261
  const [a, b, c, d] = dotted.split(".").map(Number);
37152
- text = `${address.slice(0, -dotted.length)}${(a << 8 | b).toString(16)}:${(c << 8 | d).toString(16)}`;
38262
+ text = `${address2.slice(0, -dotted.length)}${(a << 8 | b).toString(16)}:${(c << 8 | d).toString(16)}`;
37153
38263
  }
37154
38264
  const [head = "", tail2] = text.split("::");
37155
38265
  const part = (side) => side ? side.split(":").map((w) => Number.parseInt(w, 16)) : [];
@@ -37170,13 +38280,13 @@ function allows(policy, port) {
37170
38280
  if (Array.isArray(policy)) return policy.includes(port);
37171
38281
  return policy === true && !DEVTOOLS_PORTS.includes(port);
37172
38282
  }
37173
- function refusal2(url, address, place) {
37174
- const where = `${address}:${portOf(url)}`;
38283
+ function refusal2(url, address2, place) {
38284
+ const where = `${address2}:${portOf(url)}`;
37175
38285
  const why = place === "link-local" ? `nodeTransport refuses link-local ${where}` : `nodeTransport refuses the host's loopback ${where} (see nodeTransport({ hostLoopback }))`;
37176
38286
  return Object.assign(new Error(why), { status: 403, code: "EACCES" });
37177
38287
  }
37178
- function connectRefusal(address, port, place) {
37179
- const where = `${address}:${port}`;
38288
+ function connectRefusal(address2, port, place) {
38289
+ const where = `${address2}:${port}`;
37180
38290
  const why = place === "link-local" ? `nodeTransport refuses link-local ${where}` : `nodeTransport refuses the host's loopback ${where} (see nodeTransport({ hostLoopback }))`;
37181
38291
  return Object.assign(new Error(why), { status: 403, code: "EACCES" });
37182
38292
  }
@@ -37188,11 +38298,11 @@ async function addressesOf(host, resolve2) {
37188
38298
  }
37189
38299
  async function checkDestination(target, policy, resolve2 = resolveHost) {
37190
38300
  const url = new URL(target);
37191
- for (const address of await addressesOf(url.hostname, resolve2)) {
37192
- const place = placeOf(address);
38301
+ for (const address2 of await addressesOf(url.hostname, resolve2)) {
38302
+ const place = placeOf(address2);
37193
38303
  if (!place) continue;
37194
38304
  if (place === "loopback" && allows(policy, portOf(url))) continue;
37195
- throw refusal2(url, address, place);
38305
+ throw refusal2(url, address2, place);
37196
38306
  }
37197
38307
  }
37198
38308
  function guarded3(check, signal) {
@@ -37216,9 +38326,9 @@ function envProxyEnabled() {
37216
38326
  }
37217
38327
  function normaliseProxyUrl(url) {
37218
38328
  try {
37219
- const parsed2 = new URL(url);
37220
- const host = parsed2.hostname.replace(/^\[(.*)\]$/, "$1");
37221
- const port = parsed2.port || (parsed2.protocol === "https:" ? "443" : "80");
38329
+ const parsed3 = new URL(url);
38330
+ const host = parsed3.hostname.replace(/^\[(.*)\]$/, "$1");
38331
+ const port = parsed3.port || (parsed3.protocol === "https:" ? "443" : "80");
37222
38332
  return `${host}:${port}`;
37223
38333
  } catch {
37224
38334
  return void 0;
@@ -37254,8 +38364,8 @@ function ensureSubscribed() {
37254
38364
  const entry = loopbackStore.getStore();
37255
38365
  if (!entry) return;
37256
38366
  const { socket, connectParams } = message3;
37257
- const address = socket.remoteAddress;
37258
- if (!address) {
38367
+ const address2 = socket.remoteAddress;
38368
+ if (!address2) {
37259
38369
  const err2 = connectRefusal("unknown", 0, "loopback");
37260
38370
  entry.refused = err2;
37261
38371
  socket.destroy(err2);
@@ -37270,11 +38380,11 @@ function ensureSubscribed() {
37270
38380
  }
37271
38381
  return;
37272
38382
  }
37273
- const place = placeOf(address);
38383
+ const place = placeOf(address2);
37274
38384
  if (!place) return;
37275
38385
  const port = socket.remotePort ?? 0;
37276
38386
  if (place === "loopback" && allows(entry.policy, port)) return;
37277
- const err = connectRefusal(address, port, place);
38387
+ const err = connectRefusal(address2, port, place);
37278
38388
  entry.refused = err;
37279
38389
  socket.destroy(err);
37280
38390
  });
@@ -37810,11 +38920,11 @@ function indexOf(haystack, needle) {
37810
38920
  return -1;
37811
38921
  }
37812
38922
  function parseHead2(bytes) {
37813
- const [statusLine = "", ...lines2] = latin13.decode(bytes).split("\r\n");
38923
+ const [statusLine = "", ...lines3] = latin13.decode(bytes).split("\r\n");
37814
38924
  const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
37815
38925
  if (!match) throw errorWithCode(`EPROTO: not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);
37816
38926
  const headers = [];
37817
- for (const line of lines2) {
38927
+ for (const line of lines3) {
37818
38928
  const colon = line.indexOf(":");
37819
38929
  if (colon > 0) headers.push([line.slice(0, colon).trim(), line.slice(colon + 1).trim()]);
37820
38930
  }
@@ -37883,13 +38993,13 @@ function stream(chunks2, socket, signal) {
37883
38993
  async function requestBytes(request) {
37884
38994
  const url = new URL(request.url);
37885
38995
  const body = request.body ? new Uint8Array(await request.arrayBuffer()) : void 0;
37886
- const lines2 = [`${request.method} ${url.pathname}${url.search} HTTP/1.1`, `Host: ${url.host}`];
38996
+ const lines3 = [`${request.method} ${url.pathname}${url.search} HTTP/1.1`, `Host: ${url.host}`];
37887
38997
  request.headers.forEach((value2, name2) => {
37888
- if (!OWN_HEADERS2.has(name2)) lines2.push(`${name2}: ${value2}`);
38998
+ if (!OWN_HEADERS2.has(name2)) lines3.push(`${name2}: ${value2}`);
37889
38999
  });
37890
- if (body) lines2.push(`Content-Length: ${body.length}`);
37891
- lines2.push("Connection: close", "", "");
37892
- const head = encoder11.encode(lines2.join("\r\n"));
39000
+ if (body) lines3.push(`Content-Length: ${body.length}`);
39001
+ lines3.push("Connection: close", "", "");
39002
+ const head = encoder11.encode(lines3.join("\r\n"));
37893
39003
  const out = new Uint8Array(head.length + (body?.length ?? 0));
37894
39004
  out.set(head);
37895
39005
  if (body) out.set(body, head.length);
@@ -38030,8 +39140,10 @@ var Channel = class {
38030
39140
  return this.request(call).done;
38031
39141
  }
38032
39142
  requests;
39143
+ devices;
38033
39144
  receive(data) {
38034
39145
  if (this.requests?.handle(data)) return;
39146
+ if (this.devices?.handle(data)) return;
38035
39147
  if (data.traits && data.id === void 0) {
38036
39148
  if (this.remote) this.remote.traits = data.traits;
38037
39149
  for (const listener of [...this.traitsListeners]) listener(data.traits);
@@ -38236,7 +39348,7 @@ async function sudoRunOn(channel, replay, { stdout, stderr, tag }) {
38236
39348
  return { pid, pgid: pid, ended: endedOf(done4, () => endedBy) };
38237
39349
  }
38238
39350
  function endedOf(done4, signal) {
38239
- return quiet(
39351
+ return quiet2(
38240
39352
  done4.then((status) => {
38241
39353
  const by = signal();
38242
39354
  return { status, ...by ? { signal: by } : {} };
@@ -38366,9 +39478,14 @@ function since(channel, minor, what, run2) {
38366
39478
  }
38367
39479
  return run2();
38368
39480
  }
38369
- async function greet(channel, lock, timeoutMs, requests) {
39481
+ async function greet(channel, lock, timeoutMs, requests, devices) {
38370
39482
  channel.post({
38371
- hello: { protocol: PROTOCOL, ...lock ? { lock } : {}, ...requests ? { requests } : {} }
39483
+ hello: {
39484
+ protocol: PROTOCOL,
39485
+ ...lock ? { lock } : {},
39486
+ ...requests ? { requests } : {},
39487
+ ...devices ? { devices: true } : {}
39488
+ }
38372
39489
  });
38373
39490
  const timer = setTimeout(
38374
39491
  () => channel.end(
@@ -38382,9 +39499,9 @@ async function greet(channel, lock, timeoutMs, requests) {
38382
39499
  } finally {
38383
39500
  clearTimeout(timer);
38384
39501
  }
38385
- const refused4 = hello.error ?? versionError(hello.protocol);
38386
- if (refused4) {
38387
- const error = new Error(refused4);
39502
+ const refused5 = hello.error ?? versionError(hello.protocol);
39503
+ if (refused5) {
39504
+ const error = new Error(refused5);
38388
39505
  channel.end(error);
38389
39506
  throw error;
38390
39507
  }
@@ -38396,11 +39513,14 @@ async function attachKernel(port, options = {}) {
38396
39513
  const channel = new Channel(port, own.release);
38397
39514
  const requests = options.requests;
38398
39515
  if (requests) channel.requests = embedderAnswerer(port, () => requests);
39516
+ const devices = options.devices ? devicesHook(options.devices) : void 0;
39517
+ if (devices) channel.devices = devicesAnswerer(port, () => devices);
38399
39518
  const hello = await greet(
38400
39519
  channel,
38401
39520
  own.name,
38402
39521
  options.timeoutMs ?? 1e4,
38403
- requests ? kindsOf(requests) : void 0
39522
+ requests ? kindsOf(requests) : void 0,
39523
+ devices !== void 0
38404
39524
  );
38405
39525
  channel.protocol = hello.protocol;
38406
39526
  if (hello.lock && locks) void locks.request(hello.lock, () => channel.end(new KernelGoneError()));
@@ -38727,7 +39847,8 @@ function pidAlive(pid) {
38727
39847
  }
38728
39848
  }
38729
39849
  var nodeSessions = 0;
38730
- function nodeClient(launcher, clients, { user, tag, requests }) {
39850
+ var portSessions = /* @__PURE__ */ new WeakMap();
39851
+ function nodeClient(launcher, clients, { user, tag, requests, devices }) {
38731
39852
  tagOption(tag);
38732
39853
  const { port1, port2 } = new MessageChannel3();
38733
39854
  const session = ++nodeSessions;
@@ -38736,17 +39857,27 @@ function nodeClient(launcher, clients, { user, tag, requests }) {
38736
39857
  signal: signalNumber,
38737
39858
  session,
38738
39859
  ...requests === "attach" ? { attachRequests: true } : {},
39860
+ ...devices === "attach" ? { attachDevices: true } : {},
38739
39861
  ...user !== void 0 ? { user } : {},
38740
39862
  ...tag !== void 0 ? { tag } : {}
38741
39863
  });
38742
39864
  clients.add(served2);
38743
39865
  const unbind = requests && requests !== "attach" ? launcher.embedder.register(session, checkedRequests(requests)) : void 0;
39866
+ const unbindDevices = devices && devices !== "attach" ? launcher.devices.register(session, devicesHook(devices)) : void 0;
38744
39867
  void served2.closed.then(() => {
38745
39868
  clients.delete(served2);
38746
39869
  unbind?.();
39870
+ unbindDevices?.();
38747
39871
  });
39872
+ portSessions.set(port2, session);
38748
39873
  return port2;
38749
39874
  }
39875
+ function sessionOfPort(client) {
39876
+ if (client === void 0) return 0;
39877
+ const session = portSessions.get(client);
39878
+ if (session === void 0) throw fsError("EINVAL", "client is not a port from connect()");
39879
+ return session;
39880
+ }
38750
39881
  function checkedRequests(hook) {
38751
39882
  const kinds = kindsOf(hook);
38752
39883
  return { kinds, handle: (request) => hook.handle(request) };
@@ -38773,7 +39904,23 @@ async function nodeSudoRun(launcher, guard2, replay, options) {
38773
39904
  const { status, signal } = await running.ended;
38774
39905
  return { status, ...signalField(signal) };
38775
39906
  });
38776
- return { pid: running.pid, pgid: running.pid, ended: quiet(ended) };
39907
+ return { pid: running.pid, pgid: running.pid, ended: quiet2(ended) };
39908
+ }
39909
+ function pageHooks(options) {
39910
+ return {
39911
+ ...options.clipboard ? { clipboard: options.clipboard } : {},
39912
+ ...options.approve ? { approve: options.approve } : {},
39913
+ ...options.requests ? { embedder: checkedRequests(options.requests) } : {},
39914
+ ...options.devices ? { devices: devicesHook(options.devices) } : {}
39915
+ };
39916
+ }
39917
+ function nodeDevices(launcher, guard2) {
39918
+ return {
39919
+ add: ({ client, ...spec }) => guard2(async () => {
39920
+ const id2 = await launcher.devices.add(spec, sessionOfPort(client));
39921
+ return { remove: async () => void launcher.devices.remove(id2) };
39922
+ })
39923
+ };
38777
39924
  }
38778
39925
  async function createNodeKernel(options = {}) {
38779
39926
  const hooks = sealedHooks(options.sealed);
@@ -38819,9 +39966,7 @@ async function createNodeKernel(options = {}) {
38819
39966
  ...options.protect ? { protect: options.protect } : {},
38820
39967
  ...hooks.request ? { sealed: hooks.request } : {},
38821
39968
  ...hooks.fetch ? { sealedFetch: hooks.fetch } : {},
38822
- ...options.clipboard ? { clipboard: options.clipboard } : {},
38823
- ...options.requests ? { embedder: checkedRequests(options.requests) } : {},
38824
- ...options.approve ? { approve: options.approve } : {},
39969
+ ...pageHooks(options),
38825
39970
  ...options.sudo?.clock ? { sudoClock: options.sudo.clock } : {},
38826
39971
  ...options.wasmMemories ? { wasmMemories: options.wasmMemories } : {},
38827
39972
  cron: { lock: cronLock(root), ...options.cron?.clock ? { clock: options.cron.clock } : {} }
@@ -38866,8 +40011,8 @@ async function createNodeKernel(options = {}) {
38866
40011
  );
38867
40012
  return {
38868
40013
  pid: session.pid,
38869
- exited: quiet(guard2(() => session.exited)),
38870
- ended: quiet(
40014
+ exited: quiet2(guard2(() => session.exited)),
40015
+ ended: quiet2(
38871
40016
  guard2(async () => ({ status: await session.exited, ...signalField(session.termsig()) }))
38872
40017
  ),
38873
40018
  get onData() {
@@ -38905,6 +40050,7 @@ async function createNodeKernel(options = {}) {
38905
40050
  updateTransportTraits: () => guard2(async () => launcher.updateTransportTraits()),
38906
40051
  ...nodeAccounts(launcher, guard2),
38907
40052
  sudoRun: (replay, sudoOptions = {}) => nodeSudoRun(launcher, guard2, replay, sudoOptions),
40053
+ devices: nodeDevices(launcher, guard2),
38908
40054
  async connect(connectOptions = {}) {
38909
40055
  if (terminated) throw new Error(TERMINATED);
38910
40056
  const { user } = connectOptions;