@ai-ecoverse/slicc-kernel 1.182.0 → 1.183.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +54 -5
  2. package/dist/driver-worker.js +290 -90
  3. package/dist/driver-worker.js.map +4 -4
  4. package/dist/index.js +328 -102
  5. package/dist/index.js.map +4 -4
  6. package/dist/kernel-worker.js +1705 -695
  7. package/dist/kernel-worker.js.map +4 -4
  8. package/dist/node-driver-worker.js +298 -98
  9. package/dist/node-driver-worker.js.map +4 -4
  10. package/dist/node-process-worker.cjs +849 -193
  11. package/dist/node-process-worker.cjs.map +4 -4
  12. package/dist/node-process-worker.js +849 -193
  13. package/dist/node-process-worker.js.map +4 -4
  14. package/dist/node.js +1875 -743
  15. package/dist/node.js.map +4 -4
  16. package/dist/process-worker.js +805 -149
  17. package/dist/process-worker.js.map +4 -4
  18. package/dist/types/client/attach.d.ts +2 -0
  19. package/dist/types/client/protocol.d.ts +1 -0
  20. package/dist/types/client/serve-client.d.ts +1 -0
  21. package/dist/types/devices-relay.d.ts +74 -0
  22. package/dist/types/index.d.ts +14 -0
  23. package/dist/types/kernel/devices.d.ts +155 -0
  24. package/dist/types/kernel/embedder.d.ts +2 -0
  25. package/dist/types/kernel/fd-table.d.ts +3 -1
  26. package/dist/types/kernel/host.d.ts +2 -0
  27. package/dist/types/kernel/process.d.ts +20 -0
  28. package/dist/types/kernel/serial-ioctl.d.ts +73 -0
  29. package/dist/types/launcher.d.ts +4 -0
  30. package/dist/types/node.d.ts +6 -2
  31. package/dist/types/process/js/js-dev.d.ts +24 -0
  32. package/dist/types/process/kernel-streams.d.ts +11 -1
  33. package/dist/types/process/process-pty.d.ts +1 -0
  34. package/dist/types/process/wasi/wasi-fds.d.ts +1 -0
  35. package/dist/types/process/wasi/wasi-termios.d.ts +1 -0
  36. package/dist/types/serve.d.ts +14 -1
  37. package/package.json +2 -2
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");
@@ -2731,7 +2951,7 @@ function signalName(sig) {
2731
2951
  const name2 = Object.entries(SIG).find(([, n]) => n === sig)?.[0];
2732
2952
  return name2 === void 0 ? void 0 : `SIG${name2}`;
2733
2953
  }
2734
- function quiet(p) {
2954
+ function quiet2(p) {
2735
2955
  p.catch(() => void 0);
2736
2956
  return p;
2737
2957
  }
@@ -2965,21 +3185,21 @@ function latin1Bytes(text) {
2965
3185
  return out;
2966
3186
  }
2967
3187
  function parseRequestHead(bytes) {
2968
- const lines2 = latin1(bytes).split(/\r?\n/);
2969
- while (lines2.length > 0 && lines2[0] === "") lines2.shift();
2970
- 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() ?? "");
2971
3191
  if (!match) throw new HttpError(400, "malformed request line");
2972
3192
  if (match[3] !== "1") throw new HttpError(505, `HTTP/${match[3]}.${match[4]} is not supported`);
2973
3193
  return {
2974
3194
  method: match[1],
2975
3195
  target: match[2],
2976
3196
  minor: Number(match[4]),
2977
- headers: parseFields(lines2)
3197
+ headers: parseFields(lines3)
2978
3198
  };
2979
3199
  }
2980
- function parseFields(lines2) {
3200
+ function parseFields(lines3) {
2981
3201
  const headers = [];
2982
- for (const line of lines2) {
3202
+ for (const line of lines3) {
2983
3203
  if (line === "") continue;
2984
3204
  if (line.startsWith(" ") || line.startsWith(" ")) {
2985
3205
  throw new HttpError(400, "obsolete line folding");
@@ -3464,10 +3684,10 @@ var CdpFacade = class {
3464
3684
  reject(new HttpError(502, `the CDP host closed: ${reason ?? "no reason"}`));
3465
3685
  };
3466
3686
  conn.onmessage = (message3) => {
3467
- const parsed2 = JSON.parse(message3);
3468
- if (parsed2.id !== 1) return;
3687
+ const parsed3 = JSON.parse(message3);
3688
+ if (parsed3.id !== 1) return;
3469
3689
  clearTimeout(timer);
3470
- resolve2(parsed2);
3690
+ resolve2(parsed3);
3471
3691
  };
3472
3692
  conn.send(JSON.stringify({ id: 1, method: "Target.getTargets" }));
3473
3693
  }
@@ -3801,7 +4021,7 @@ function satisfies(version, range2) {
3801
4021
  }
3802
4022
 
3803
4023
  // src/version.ts
3804
- var KERNEL_VERSION = true ? "1.182.0" : "0.0.0-dev";
4024
+ var KERNEL_VERSION = true ? "1.183.0" : "0.0.0-dev";
3805
4025
  var OS_RELEASE = "6.0.0-slicc";
3806
4026
  function procVersion() {
3807
4027
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -4093,10 +4313,10 @@ async function tierPackages(fs, physical, roots, bins, kernel, global) {
4093
4313
  const own = commandsOf(pkg, manifest);
4094
4314
  const named = new Set(own.map((command) => command.name));
4095
4315
  const extra = bins ? await binCommandsOf(physical, pkg, manifest, named) : [];
4096
- const refused4 = engineRefusal(manifest, kernel);
4316
+ const refused5 = engineRefusal(manifest, kernel);
4097
4317
  const name2 = pkg.slice(root.length + 1);
4098
4318
  const dependency = direct?.has(name2) === false;
4099
- const marks = { ...refused4 ? { refused: refused4 } : {}, ...dependency ? { dependency } : {} };
4319
+ const marks = { ...refused5 ? { refused: refused5 } : {}, ...dependency ? { dependency } : {} };
4100
4320
  out.push({
4101
4321
  name: name2,
4102
4322
  pkg,
@@ -4128,7 +4348,7 @@ function winners(tier) {
4128
4348
  }
4129
4349
  return out;
4130
4350
  }
4131
- 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()) {
4132
4352
  const found = /* @__PURE__ */ new Map();
4133
4353
  const physical = physicalResolver(fs);
4134
4354
  const tiers = (typeof modules === "string" ? [modules] : modules).map(
@@ -4142,7 +4362,7 @@ async function scanCommands(fs, modules, binsFrom = Number.POSITIVE_INFINITY, ke
4142
4362
  const dependencies = [];
4143
4363
  for (const [index, roots] of tiers.entries()) {
4144
4364
  const tier = await tierPackages(fs, physical, roots, index >= binsFrom, kernel, global);
4145
- 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)));
4146
4366
  await take(tier.filter((entry) => !entry.dependency));
4147
4367
  dependencies.push(tier.filter((entry) => entry.dependency));
4148
4368
  }
@@ -4746,9 +4966,9 @@ var TAGS_VERSION = 1;
4746
4966
  async function readTags(fs) {
4747
4967
  const text = await fs.readFile(TAGS_PATH).catch(() => "");
4748
4968
  try {
4749
- const parsed2 = JSON.parse(text);
4750
- if (!parsed2 || typeof parsed2 !== "object") return {};
4751
- const { v: _, ...tables } = parsed2;
4969
+ const parsed3 = JSON.parse(text);
4970
+ if (!parsed3 || typeof parsed3 !== "object") return {};
4971
+ const { v: _, ...tables } = parsed3;
4752
4972
  return tables;
4753
4973
  } catch {
4754
4974
  return {};
@@ -4818,27 +5038,27 @@ var Crontabs = class {
4818
5038
  const json5 = JSON.stringify({ v: TAGS_VERSION, ...kept });
4819
5039
  if (json5 === await fs.readFile(TAGS_PATH).catch(() => "")) return;
4820
5040
  const owner = owners ? { uid: 0, gid: 0, mode: 384 } : void 0;
4821
- const fresh3 = `${TAGS_PATH}.${crypto.randomUUID()}.new`;
4822
- await fs.writeFile(fresh3, json5, owner);
4823
- 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);
4824
5044
  }
4825
5045
  async install(path, target, text, caller) {
4826
5046
  if (text.length > CRONTAB_MAX) return { error: "crontab: the table is too long" };
4827
5047
  const { errors } = parseCrontab(text, path);
4828
5048
  if (errors.length > 0) {
4829
- const lines2 = errors.map(({ line, message: message3 }) => `"-":${line}: ${message3}`);
4830
- 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") };
4831
5051
  }
4832
5052
  const { fs, owners } = this.o;
4833
5053
  if (!await fs.exists(SPOOL_DIR)) {
4834
5054
  await fs.mkdir(SPOOL_DIR, { recursive: true });
4835
5055
  if (owners) await fs.chown?.(SPOOL_DIR, { uid: 0, gid: 0, mode: 448, follow: false });
4836
5056
  }
4837
- const fresh3 = `${SPOOL_DIR}/.${target.name}.${crypto.randomUUID()}.new`;
5057
+ const fresh4 = `${SPOOL_DIR}/.${target.name}.${crypto.randomUUID()}.new`;
4838
5058
  const owner = owners ? { uid: target.uid, gid: target.gid, mode: 384 } : void 0;
4839
5059
  await this.retag(path, text, caller);
4840
- await fs.writeFile(fresh3, text, owner);
4841
- await fs.rename(fresh3, path);
5060
+ await fs.writeFile(fresh4, text, owner);
5061
+ await fs.rename(fresh4, path);
4842
5062
  return { text: "" };
4843
5063
  }
4844
5064
  };
@@ -5172,15 +5392,15 @@ var UnlinkedKeeper = class {
5172
5392
  const releases = [];
5173
5393
  const take = async () => {
5174
5394
  for (; ; ) {
5175
- const fresh3 = [...this.heldWithin(target).keys()].filter((path) => !paths.includes(path));
5176
- if (fresh3.length === 0) return;
5177
- fresh3.sort();
5178
- 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])) {
5179
5399
  for (const release of releases.splice(0)) release();
5180
- fresh3.push(...paths.splice(0));
5181
- fresh3.sort();
5400
+ fresh4.push(...paths.splice(0));
5401
+ fresh4.sort();
5182
5402
  }
5183
- for (const path of fresh3) {
5403
+ for (const path of fresh4) {
5184
5404
  paths.push(path);
5185
5405
  releases.push(await this.gate.acquire(path));
5186
5406
  }
@@ -5576,10 +5796,10 @@ var Output = class {
5576
5796
  };
5577
5797
  function readStamp(text) {
5578
5798
  try {
5579
- const parsed2 = JSON.parse(text ?? "");
5580
- const jobs = parsed2.jobs && typeof parsed2.jobs === "object" ? parsed2.jobs : {};
5581
- const reboot = parsed2.reboot && typeof parsed2.reboot === "object" ? parsed2.reboot : {};
5582
- 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 : {};
5583
5803
  return { jobs: { ...jobs }, reboot: { ...reboot }, when: { ...when } };
5584
5804
  } catch {
5585
5805
  return { jobs: {}, reboot: {}, when: {} };
@@ -5699,10 +5919,10 @@ var CronService = class {
5699
5919
  const text = JSON.stringify(stamp);
5700
5920
  const old = await fs.readFile(STAMP_PATH).catch(() => void 0);
5701
5921
  if (old === text || old === void 0 && text === EMPTY_STAMP) return;
5702
- const fresh3 = `${STAMP_PATH}.new`;
5922
+ const fresh4 = `${STAMP_PATH}.new`;
5703
5923
  const owner = owners ? { uid: 0, gid: 0, mode: 384 } : void 0;
5704
- await fs.writeFile(fresh3, text, owner);
5705
- await fs.rename(fresh3, STAMP_PATH);
5924
+ await fs.writeFile(fresh4, text, owner);
5925
+ await fs.rename(fresh4, STAMP_PATH);
5706
5926
  }
5707
5927
  async load() {
5708
5928
  const { fs } = this.host;
@@ -5755,10 +5975,10 @@ var CronService = class {
5755
5975
  await this.once(path, text, errors);
5756
5976
  return table;
5757
5977
  }
5758
- async once(path, version, lines2) {
5978
+ async once(path, version, lines3) {
5759
5979
  if (this.reported.get(path) === version) return;
5760
5980
  this.reported.set(path, version);
5761
- for (const line of lines2) await this.error(line);
5981
+ for (const line of lines3) await this.error(line);
5762
5982
  }
5763
5983
  async error(line) {
5764
5984
  if (this.stopping.signal.aborted) return;
@@ -6060,8 +6280,8 @@ function canonicalHost2(host) {
6060
6280
  function localV4(a, b) {
6061
6281
  return a === 127 || a === 0 || a === 169 && b === 254;
6062
6282
  }
6063
- function hextets(address) {
6064
- const [head, tail2, extra] = address.split("::");
6283
+ function hextets(address2) {
6284
+ const [head, tail2, extra] = address2.split("::");
6065
6285
  if (extra !== void 0) return void 0;
6066
6286
  const part = (text) => text ? text.split(":").map((h) => Number.parseInt(h, 16)) : [];
6067
6287
  const front = part(head);
@@ -6069,8 +6289,8 @@ function hextets(address) {
6069
6289
  const groups = tail2 === void 0 ? front : [...front, ...new Array(8 - front.length - back.length).fill(0), ...back];
6070
6290
  return groups.length === 8 && groups.every((g) => g >= 0 && g <= 65535) ? groups : void 0;
6071
6291
  }
6072
- function localV6(address) {
6073
- const g = hextets(address);
6292
+ function localV6(address2) {
6293
+ const g = hextets(address2);
6074
6294
  if (!g) return true;
6075
6295
  if ((g[0] & 65472) === 65152) return true;
6076
6296
  const zeroPrefix = g.slice(0, 5).every((x) => x === 0);
@@ -6443,8 +6663,8 @@ var RealmProxy = class {
6443
6663
  }
6444
6664
  async connect(conn, incoming, req, idle2) {
6445
6665
  const target = tunnelTarget(req);
6446
- const refused4 = target.port === 443 ? await this.tunnelReady(target) : void 0;
6447
- if (refused4) throw refused4;
6666
+ const refused5 = target.port === 443 ? await this.tunnelReady(target) : void 0;
6667
+ if (refused5) throw refused5;
6448
6668
  await conn.write(latin1Bytes("HTTP/1.1 200 Connection Established\r\n\r\n"), this.stop.signal);
6449
6669
  try {
6450
6670
  const first = await incoming.peek(this.stop.signal);
@@ -7000,15 +7220,15 @@ async function followLinks(fs, path) {
7000
7220
  }
7001
7221
  var NOBODY = { ruid: -1, euid: -1, suid: -1, rgid: -1, egid: -1, sgid: -1, groups: [] };
7002
7222
  var EMPTY2 = new Uint8Array(0);
7003
- function quiet2(promise) {
7223
+ function quiet3(promise) {
7004
7224
  return promise.catch(() => void 0);
7005
7225
  }
7006
7226
  async function readsAs2(fs, cred, target) {
7007
- 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 }));
7008
7228
  return walked !== void 0 && walked.denied === void 0 && !walked.missing;
7009
7229
  }
7010
7230
  async function shownTarget(fs, target) {
7011
- const st = await quiet2(fs.stat(target));
7231
+ const st = await quiet3(fs.stat(target));
7012
7232
  return st?.isFile && await readsAs2(fs, NOBODY, target) ? st : void 0;
7013
7233
  }
7014
7234
  async function madeFor(owner, virtual) {
@@ -7331,10 +7551,10 @@ var FreshModules = class {
7331
7551
  this.fresh.set(key, { umask: mask, pid, added: 0, setup: mask !== DEFAULT_UMASK });
7332
7552
  }
7333
7553
  forget(pid) {
7334
- 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);
7335
7555
  }
7336
- drop(fresh3) {
7337
- 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);
7338
7558
  }
7339
7559
  moved(from, to) {
7340
7560
  const source = normalizePath(from);
@@ -7343,10 +7563,10 @@ var FreshModules = class {
7343
7563
  for (const key of [...this.fresh.keys()]) {
7344
7564
  if (key === target || within3(key, target)) this.fresh.delete(key);
7345
7565
  }
7346
- for (const [key, fresh3] of [...this.fresh]) {
7566
+ for (const [key, fresh4] of [...this.fresh]) {
7347
7567
  if (key !== source && !within3(key, source)) continue;
7348
7568
  this.fresh.delete(key);
7349
- this.fresh.set(target + key.slice(source.length), fresh3);
7569
+ this.fresh.set(target + key.slice(source.length), fresh4);
7350
7570
  }
7351
7571
  }
7352
7572
  wrap(fs) {
@@ -7356,15 +7576,15 @@ var FreshModules = class {
7356
7576
  };
7357
7577
  const decide = async (given, known) => {
7358
7578
  const path = normalizePath(given);
7359
- const fresh3 = this.fresh.get(path);
7360
- if (!fresh3) return;
7579
+ const fresh4 = this.fresh.get(path);
7580
+ if (!fresh4) return;
7361
7581
  const bytes = known && known.length >= MAGIC.length ? known : await head(path);
7362
- const want = isModule(bytes) ? 73 & ~fresh3.umask : 0;
7363
- fresh3.setup = false;
7364
- if (want === fresh3.added) return;
7582
+ const want2 = isModule(bytes) ? 73 & ~fresh4.umask : 0;
7583
+ fresh4.setup = false;
7584
+ if (want2 === fresh4.added) return;
7365
7585
  const mode = (await fs.stat(path)).mode & 4095;
7366
- await fs.chmod(path, mode & ~fresh3.added | want);
7367
- fresh3.added = want;
7586
+ await fs.chmod(path, mode & ~fresh4.added | want2);
7587
+ fresh4.added = want2;
7368
7588
  };
7369
7589
  const overrides = {
7370
7590
  writeFile: async (path, content, owner) => {
@@ -7377,9 +7597,9 @@ var FreshModules = class {
7377
7597
  }
7378
7598
  },
7379
7599
  chmod: async (path, mode) => {
7380
- const fresh3 = this.fresh.get(normalizePath(path));
7381
- if (fresh3?.setup && (mode & 4095) === (438 & ~fresh3.umask)) fresh3.setup = false;
7382
- 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);
7383
7603
  await fs.chmod(path, mode);
7384
7604
  },
7385
7605
  rm: async (path, options) => {
@@ -7396,16 +7616,16 @@ var FreshModules = class {
7396
7616
  link: async (from, to, follow2) => {
7397
7617
  await fs.link(from, to, follow2);
7398
7618
  const source = follow2 && fs.realpath ? await fs.realpath(from, true).catch(() => from) : from;
7399
- const fresh3 = this.fresh.get(normalizePath(source));
7400
- 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);
7401
7621
  }
7402
7622
  };
7403
7623
  const { append } = fs;
7404
7624
  if (append) {
7405
7625
  overrides.append = async (path, bytes, options) => {
7406
- const fresh3 = this.fresh.has(normalizePath(path));
7626
+ const fresh4 = this.fresh.has(normalizePath(path));
7407
7627
  const end = await append.call(fs, path, bytes, options);
7408
- if (fresh3 && end - bytes.length < MAGIC.length) await decide(path);
7628
+ if (fresh4 && end - bytes.length < MAGIC.length) await decide(path);
7409
7629
  return end;
7410
7630
  };
7411
7631
  }
@@ -8260,7 +8480,7 @@ var FsWatchers = class {
8260
8480
  };
8261
8481
  const content = (run2, types) => async (...args) => {
8262
8482
  const at = normalizePath(args[0]);
8263
- const fresh3 = this.covers(at) && !await fs.exists(at).catch(() => true);
8483
+ const fresh4 = this.covers(at) && !await fs.exists(at).catch(() => true);
8264
8484
  let done4 = false;
8265
8485
  try {
8266
8486
  await run2(...args);
@@ -8269,11 +8489,11 @@ var FsWatchers = class {
8269
8489
  const real = await realOf(at);
8270
8490
  const paths = real === at ? [at] : [at, real];
8271
8491
  this.report(paths);
8272
- 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);
8273
8493
  }
8274
8494
  };
8275
- const written2 = (fresh3) => fresh3 ? ["rename", "change"] : ["change"];
8276
- 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"];
8277
8497
  const reshaping = (run2) => async (...args) => {
8278
8498
  this.reshaped();
8279
8499
  try {
@@ -8306,14 +8526,14 @@ var FsWatchers = class {
8306
8526
  if (created4.length > 0 || !await fs.exists(ancestor)) created4.push(ancestor);
8307
8527
  }
8308
8528
  }
8309
- 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);
8310
8530
  let done4 = false;
8311
8531
  try {
8312
8532
  await fs.mkdir(path, options);
8313
8533
  done4 = true;
8314
8534
  } finally {
8315
8535
  this.report([...created4, path]);
8316
- if (done4) this.emit("rename", fresh3 ? [...created4, path] : created4);
8536
+ if (done4) this.emit("rename", fresh4 ? [...created4, path] : created4);
8317
8537
  }
8318
8538
  },
8319
8539
  rm: reshaping(async (path, options) => {
@@ -8823,15 +9043,15 @@ function embedderFailure(err) {
8823
9043
  return new KernelError(ERRNOS[String(name2)] ?? "EIO", lineOf2(err));
8824
9044
  }
8825
9045
  function headerOf(bytes) {
8826
- let parsed2;
9046
+ let parsed3;
8827
9047
  try {
8828
- parsed2 = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
9048
+ parsed3 = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
8829
9049
  } catch {
8830
9050
  throw new KernelError("EINVAL", "the request header is not JSON");
8831
9051
  }
8832
- if (typeof parsed2 !== "object" || parsed2 === null || Array.isArray(parsed2))
9052
+ if (typeof parsed3 !== "object" || parsed3 === null || Array.isArray(parsed3))
8833
9053
  throw new KernelError("EINVAL", "the request header is not an object");
8834
- const { kind, ...options } = parsed2;
9054
+ const { kind, ...options } = parsed3;
8835
9055
  if (typeof kind !== "string" || kind.length > 64 || !KIND.test(kind))
8836
9056
  throw new KernelError("EINVAL", "the request has no valid kind");
8837
9057
  return { kind, options };
@@ -9272,6 +9492,970 @@ function kindsOf(hook) {
9272
9492
  );
9273
9493
  }
9274
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
+
9275
10459
  // src/kernel/exec-ticket.ts
9276
10460
  var TICKET_ENV = "SLICC_SEALED_TICKET";
9277
10461
  var TICKET_TTL_MS = 3e4;
@@ -9661,8 +10845,8 @@ var DriverConnection = class _DriverConnection {
9661
10845
  } finally {
9662
10846
  clearTimeout(timer);
9663
10847
  }
9664
- const refused4 = driverVersionError(hello.protocol);
9665
- if (refused4) throw errnoError("EPROTO", refused4);
10848
+ const refused5 = driverVersionError(hello.protocol);
10849
+ if (refused5) throw errnoError("EPROTO", refused5);
9666
10850
  if (hello.error) throw errnoError(hello.errno ?? "EIO", hello.error);
9667
10851
  return new _DriverConnection(port, hello.capabilities ?? {}, waiting, timeoutMs, options);
9668
10852
  }
@@ -9708,7 +10892,7 @@ var DriverConnection = class _DriverConnection {
9708
10892
  var S_ISGID3 = 1024;
9709
10893
  var ID = /^\d{1,10}$/;
9710
10894
  var MASK = /^0*[0-7]{1,4}$/;
9711
- var MAX_ID2 = 4294967294;
10895
+ var MAX_ID3 = 4294967294;
9712
10896
  function idOption(value2) {
9713
10897
  return value2 === void 0 ? 0 : Number(value2);
9714
10898
  }
@@ -9720,7 +10904,7 @@ function checkIds(options) {
9720
10904
  for (const key of ["uid", "gid"]) {
9721
10905
  const value2 = options[key];
9722
10906
  if (value2 === void 0) continue;
9723
- 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}`);
9724
10908
  }
9725
10909
  }
9726
10910
  function checkMasks(options) {
@@ -9732,7 +10916,7 @@ function checkMasks(options) {
9732
10916
  }
9733
10917
  }
9734
10918
  }
9735
- function modeOf(attr2, mount) {
10919
+ function modeOf2(attr2, mount) {
9736
10920
  const type = attr2.kind === "directory" ? 16384 : attr2.kind === "symlink" ? 40960 : attr2.kind === "fifo" ? 4096 : 32768;
9737
10921
  const fallback = type | (attr2.kind === "directory" ? 493 : attr2.kind === "symlink" ? 511 : 420);
9738
10922
  if (mount.caps.owners) return attr2.mode ?? fallback;
@@ -9893,9 +11077,9 @@ var HeldPaths = class extends Map {
9893
11077
  this.unlinked.set(path, file);
9894
11078
  }
9895
11079
  keep(path, file) {
9896
- const fresh3 = this.handlesAt(path).filter((id2) => !this.keptHandles.has(id2));
9897
- for (const id2 of fresh3) this.keptHandles.set(id2, file);
9898
- 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));
9899
11083
  this.unlinked.set(path, file);
9900
11084
  }
9901
11085
  unkeep(path, file) {
@@ -10016,7 +11200,7 @@ function statOf2(attr2, path, mount) {
10016
11200
  isSymbolicLink: attr2.kind === "symlink",
10017
11201
  ...attr2.kind === "fifo" ? { isFifo: true } : {},
10018
11202
  size: attr2.size,
10019
- mode: modeOf(attr2, mount),
11203
+ mode: modeOf2(attr2, mount),
10020
11204
  mtime,
10021
11205
  atime: attr2.atime === void 0 ? mtime : new Date(attr2.atime),
10022
11206
  ctime: mtime,
@@ -11107,8 +12291,8 @@ var MountFs = class _MountFs {
11107
12291
  }
11108
12292
  this.writable(mount, real);
11109
12293
  const { uid, gid, mode } = change;
11110
- const fresh3 = change.kind === "new" && { uid, gid, mode };
11111
- 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;
11112
12296
  this.table.forget(mount, real);
11113
12297
  const attrs = Object.entries({ uid: set3.uid, gid: set3.gid, mode: set3.mode });
11114
12298
  await mount.conn.call({
@@ -11385,10 +12569,10 @@ async function runMkdir(entry, path, req) {
11385
12569
  const plain3 = plainDirMode(entry, req.mode);
11386
12570
  const parentsMode = !req.exclusive ? plainParentsMode(entry) : void 0;
11387
12571
  const fixParents = parentsMode !== void 0 ? await missingParents(fs, path, parentsMode) : void 0;
11388
- const fresh3 = plain3 !== void 0 && !await fs.exists(path);
12572
+ const fresh4 = plain3 !== void 0 && !await fs.exists(path);
11389
12573
  await (req.exclusive ? mkdirExclusive(entry, path, req.mode) : fs.mkdir(path, mkdirOptions(entry, req.mode)));
11390
12574
  await fixParents?.();
11391
- if (fresh3) await fs.chmod(path, plain3);
12575
+ if (fresh4) await fs.chmod(path, plain3);
11392
12576
  return done;
11393
12577
  }
11394
12578
  async function shownAs(entry, path, st) {
@@ -11600,10 +12784,10 @@ async function guard(g, path, req, cwd) {
11600
12784
  }
11601
12785
  async function opening(entry, path, mode) {
11602
12786
  const g = guardOf(entry);
11603
- 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);
11604
12788
  const created4 = (mode & HOLD_CREATED) !== 0 && entry.created?.take(path) === true;
11605
- if (g && want !== 0 && !created4) {
11606
- await need(g, "open", path, want, { directory: false });
12789
+ if (g && want2 !== 0 && !created4) {
12790
+ await need(g, "open", path, want2, { directory: false });
11607
12791
  }
11608
12792
  }
11609
12793
  async function stillThere(entry, path, req) {
@@ -11627,7 +12811,7 @@ async function holdOp(entry, path, req) {
11627
12811
  await entry.source?.open(path, real);
11628
12812
  await stillThere(entry, path, req);
11629
12813
  if (req.nofollow && !await ownEntry(entry, path, real ?? path)) continue;
11630
- 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);
11631
12815
  if (!r || !r.missed(path) && !r.changedSince(path, gen) && !r.inFlight?.(path))
11632
12816
  return json2(await heldOpen(entry, path, real, req, fid));
11633
12817
  }
@@ -11641,7 +12825,7 @@ async function heldOpen(entry, path, real, req, fid) {
11641
12825
  await entry.announceHold?.(real ?? path, fid, ((req.mode ?? 0) & HOLD_CREATED) !== 0);
11642
12826
  return real !== void 0 && real !== path ? { handle, real } : handle;
11643
12827
  }
11644
- async function idOf(entry, path) {
12828
+ async function idOf2(entry, path) {
11645
12829
  return await entry.fileId?.(path)?.catch(() => void 0) ?? void 0;
11646
12830
  }
11647
12831
  async function ownEntry(entry, path, real) {
@@ -11713,8 +12897,8 @@ async function guarded(entry, path, req) {
11713
12897
  return { paths };
11714
12898
  } catch (err) {
11715
12899
  const refusal3 = err.code === "EACCES";
11716
- const refused4 = req.op === "exists" && refusal3 ? json2(false) : toErrno(err);
11717
- return { refused: refused4 };
12900
+ const refused5 = req.op === "exists" && refusal3 ? json2(false) : toErrno(err);
12901
+ return { refused: refused5 };
11718
12902
  }
11719
12903
  }
11720
12904
  async function copyOp(entry, path, req) {
@@ -11815,7 +12999,7 @@ async function bound(entry, path, handle) {
11815
12999
  const now = await entry.fs.stat(path).catch(() => void 0);
11816
13000
  if (now === void 0 && bound3.fid !== void 0 && !await entry.fs.lstat(path).catch(() => void 0))
11817
13001
  throw syncError("ENOENT", path);
11818
- 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;
11819
13003
  if (same2) {
11820
13004
  entry.trust?.(handle, bound3.doubt);
11821
13005
  return;
@@ -11910,13 +13094,13 @@ async function created(entry, req) {
11910
13094
  return seen === void 0 ? looked : json2({ ...looked.json, seen });
11911
13095
  }
11912
13096
  var DENIED = /* @__PURE__ */ new Set(["EACCES", "EPERM"]);
11913
- async function rmPermitted(entry, path, req, refused4) {
13097
+ async function rmPermitted(entry, path, req, refused5) {
11914
13098
  const st = await entry.fs.lstat(path).catch(() => void 0);
11915
- if (!st?.isDirectory) return refused4;
13099
+ if (!st?.isDirectory) return refused5;
11916
13100
  const unlisted = await guarded(entry, path, { ...req, op: "readdir" });
11917
13101
  if ("refused" in unlisted) return unlisted.refused;
11918
13102
  const names2 = await entry.fs.readdir(path).catch(() => void 0);
11919
- if (!names2) return refused4;
13103
+ if (!names2) return refused5;
11920
13104
  let first;
11921
13105
  for (const name2 of names2) {
11922
13106
  const child = await dispatchOne(entry, {
@@ -11967,14 +13151,14 @@ async function newDevName(entry, path, req) {
11967
13151
  const named = NAMING.has(req.op);
11968
13152
  if (!named && !CREATIONS.has(req.op)) return void 0;
11969
13153
  const target = named ? entry.fs.resolvePath(entry.cwd, req.arg2 ?? "") : path;
11970
- const refused4 = devCreation(target, entry.cred?.());
11971
- if (!refused4) return void 0;
13154
+ const refused5 = devCreation(target, entry.cred?.());
13155
+ if (!refused5) return void 0;
11972
13156
  const there = await entry.fs.lstat(target).then(
11973
13157
  () => true,
11974
13158
  () => false
11975
13159
  );
11976
13160
  if (there) return void 0;
11977
- 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` };
11978
13162
  }
11979
13163
  var MISS_OPS = /* @__PURE__ */ new Set(["stat", "lstat"]);
11980
13164
  async function linkFree(fs, path) {
@@ -12085,13 +13269,13 @@ var SAB_DEFAULT_WINDOW_BYTES = 1024 * 1024;
12085
13269
  var SAB_MIN_BYTES = SAB_HEADER_BYTES + 4096;
12086
13270
  var SYNC_SAB_REQ_MSG = "sync-sab-req";
12087
13271
  var SYNC_SAB_NEXT_MSG = "sync-sab-next";
12088
- var encoder3 = new TextEncoder();
13272
+ var encoder4 = new TextEncoder();
12089
13273
  var decoder2 = new TextDecoder();
12090
13274
  function encodeSabResult(result) {
12091
13275
  if (!result.ok) {
12092
13276
  return {
12093
13277
  status: SAB_STATUS_ERRNO,
12094
- payload: encoder3.encode(
13278
+ payload: encoder4.encode(
12095
13279
  JSON.stringify({
12096
13280
  errno: result.errno,
12097
13281
  message: result.message,
@@ -12106,7 +13290,7 @@ function encodeSabResult(result) {
12106
13290
  case "bytes":
12107
13291
  return { status: SAB_STATUS_BYTES, payload: result.bytes };
12108
13292
  case "json":
12109
- 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)) };
12110
13294
  default:
12111
13295
  return { status: SAB_STATUS_VOID, payload: new Uint8Array(0) };
12112
13296
  }
@@ -12433,9 +13617,9 @@ var Pages = class {
12433
13617
  const page = (start2 + at) / PAGE;
12434
13618
  if (this.pages.has(page)) continue;
12435
13619
  const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
12436
- const fresh3 = this.take(part.length);
12437
- fresh3.set(part);
12438
- this.pages.set(page, fresh3);
13620
+ const fresh4 = this.take(part.length);
13621
+ fresh4.set(part);
13622
+ this.pages.set(page, fresh4);
12439
13623
  }
12440
13624
  }
12441
13625
  read(at, length2) {
@@ -12646,16 +13830,16 @@ var pinnedRoot;
12646
13830
  var rootKind = () => globalThis.navigator?.storage ? "opfs" : "memory";
12647
13831
  function mountTable(mounts = []) {
12648
13832
  const root = pinnedRoot ?? rootKind();
12649
- 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"];
12650
13834
  for (const m of mounts) {
12651
13835
  const options = Object.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
12652
13836
  if (m.state !== "ok") options.push(m.state);
12653
13837
  const access2 = options.includes("ro") ? [] : ["rw"];
12654
- lines2.push(
13838
+ lines3.push(
12655
13839
  `${field3(m.source)} ${field3(m.target)} ${m.type} ${[...access2, ...options].join(",")} 0 0`
12656
13840
  );
12657
13841
  }
12658
- return `${lines2.join("\n")}
13842
+ return `${lines3.join("\n")}
12659
13843
  `;
12660
13844
  }
12661
13845
 
@@ -13690,7 +14874,7 @@ function recordRefusal(opening2) {
13690
14874
  const found = headerClass(opening2);
13691
14875
  return found && new KernelError("EPROTO", `${found[0]}: ${found[1]}`);
13692
14876
  }
13693
- function refused2(engine, session, err, opening2) {
14877
+ function refused3(engine, session, err, opening2) {
13694
14878
  const flags2 = session.verifyResult;
13695
14879
  const code = verifyCode(flags2);
13696
14880
  if (code && flags2 !== -1)
@@ -13711,7 +14895,7 @@ async function handshake(conn, o, session, inbox) {
13711
14895
  done4 = session.handshake();
13712
14896
  } catch (err) {
13713
14897
  await send(conn, session).catch(() => void 0);
13714
- throw refused2(o.engine, session, err, inbox.opening);
14898
+ throw refused3(o.engine, session, err, inbox.opening);
13715
14899
  }
13716
14900
  await send(conn, session);
13717
14901
  if (done4) return;
@@ -14162,10 +15346,10 @@ async function anchorFor(engine, chain2, host, found) {
14162
15346
  var FRAGMENT = 16;
14163
15347
  var MIN_VALUE = 8;
14164
15348
  var MIN_BASE64 = 8;
14165
- var encoder4 = new TextEncoder();
15349
+ var encoder5 = new TextEncoder();
14166
15350
  var STD = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
14167
15351
  function utf8(text) {
14168
- return encoder4.encode(text);
15352
+ return encoder5.encode(text);
14169
15353
  }
14170
15354
  function base64(bytes, padded = true) {
14171
15355
  let out = "";
@@ -14239,14 +15423,73 @@ function encodings(bytes) {
14239
15423
  }
14240
15424
  return [...standard, ...standard.map(urlSafe)];
14241
15425
  }
14242
- 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)) {
14243
15479
  const raw2 = utf8(value2);
14244
15480
  const seen = /* @__PURE__ */ new Set([value2]);
14245
15481
  const out = [];
14246
- if (raw2.length < FRAGMENT) out.push({ bytes: raw2, form: "raw" });
14247
- for (let offset = 0; raw2.length >= FRAGMENT && offset + FRAGMENT <= raw2.length; offset++) {
14248
- out.push({ bytes: raw2.subarray(offset, offset + FRAGMENT), form: "fragment", offset });
14249
- }
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
+ });
14250
15493
  const add = (text, form) => {
14251
15494
  if (seen.has(text)) return;
14252
15495
  seen.add(text);
@@ -14379,12 +15622,19 @@ function prepare(secrets, sameLength) {
14379
15622
  const ids = /* @__PURE__ */ new Map();
14380
15623
  const wrapIds = /* @__PURE__ */ new Map();
14381
15624
  const values = secrets.map((secret) => utf8(secret.value));
15625
+ const splits = [];
14382
15626
  secrets.forEach((secret, index) => {
14383
15627
  if (typeof secret.value !== "string" || values[index].length < MIN_VALUE) {
14384
15628
  throw new RangeError(`the value of ${secret.name} is shorter than ${MIN_VALUE} bytes`);
14385
15629
  }
14386
- for (const shape of shapes(secret.value)) {
14387
- 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
+ };
14388
15638
  register(main, sources, ids, shape.bytes, source);
14389
15639
  if (wrappable(shape.form)) register(wrap, wrapSources, wrapIds, shape.bytes, source);
14390
15640
  }
@@ -14395,7 +15645,16 @@ function prepare(secrets, sameLength) {
14395
15645
  utf8(secret.placeholder),
14396
15646
  utf8(`[redacted:${secret.name}]`)
14397
15647
  ]);
14398
- 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
+ };
14399
15658
  }
14400
15659
  function filled(input, start2, end) {
14401
15660
  const out = new Uint8Array(end - start2);
@@ -14426,14 +15685,16 @@ function resolve(c, input, start2, end, source) {
14426
15685
  growing: false
14427
15686
  };
14428
15687
  }
14429
- const value2 = c.values[source.secret];
15688
+ const split3 = c.splits[source.secret];
15689
+ const value2 = split3.pieces[source.piece];
14430
15690
  let stop = end;
14431
15691
  let at = source.offset + FRAGMENT;
14432
15692
  while (stop < input.length && at < value2.length && input[stop] === value2[at]) {
14433
15693
  stop++;
14434
15694
  at++;
14435
15695
  }
14436
- 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";
14437
15698
  const growing = stop === input.length && at < value2.length;
14438
15699
  return { start: start2, end: stop, secret: source.secret, form, rank: RANK[form], growing };
14439
15700
  }
@@ -14587,9 +15848,9 @@ function compile(secrets, options = {}) {
14587
15848
  // src/kernel/scrub-file.ts
14588
15849
  var SCRUB_IDLE_MS = 50;
14589
15850
  var SCRUB_NONBLOCK_MAX = 64 * 1024;
14590
- var encoder5 = new TextEncoder();
15851
+ var encoder6 = new TextEncoder();
14591
15852
  function scrubberOf(secrets, sameLength = false) {
14592
- 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);
14593
15854
  return usable3.length > 0 ? compile(usable3, { idleMs: SCRUB_IDLE_MS, sameLength }) : void 0;
14594
15855
  }
14595
15856
  async function writeAll(file, bytes) {
@@ -14613,7 +15874,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
14613
15874
  let timer;
14614
15875
  let failed5;
14615
15876
  const waiters = /* @__PURE__ */ new Set();
14616
- const quiet3 = () => !busy && pending === 0;
15877
+ const quiet5 = () => !busy && pending === 0;
14617
15878
  const wake = () => {
14618
15879
  for (const waiter of [...waiters]) waiter();
14619
15880
  };
@@ -14630,7 +15891,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
14630
15891
  if (failed5 === void 0) failed5 = err;
14631
15892
  }).finally(() => {
14632
15893
  pending -= 1;
14633
- if (quiet3()) wake();
15894
+ if (quiet5()) wake();
14634
15895
  });
14635
15896
  };
14636
15897
  const settling = (signal) => new Promise((resolve2, reject) => {
@@ -14648,7 +15909,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
14648
15909
  signal?.addEventListener("abort", stop, { once: true });
14649
15910
  });
14650
15911
  const settled = async () => {
14651
- while (!quiet3()) await settling();
15912
+ while (!quiet5()) await settling();
14652
15913
  };
14653
15914
  const changed = async (signal) => {
14654
15915
  const round = new AbortController();
@@ -14664,7 +15925,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
14664
15925
  };
14665
15926
  const turn = async (signal) => {
14666
15927
  for (let waited = false; ; waited = true) {
14667
- if (quiet3() && (pollFile(file).writable || !file.changed)) {
15928
+ if (quiet5() && (pollFile(file).writable || !file.changed)) {
14668
15929
  busy = true;
14669
15930
  return;
14670
15931
  }
@@ -14693,13 +15954,14 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
14693
15954
  ...bound2(file, "stat"),
14694
15955
  poll: () => {
14695
15956
  const state = pollFile(file);
14696
- return quiet3() ? state : { ...state, writable: false };
15957
+ return quiet5() ? state : { ...state, writable: false };
14697
15958
  },
14698
15959
  changed,
14699
15960
  ...file.tty ? { tty: file.tty } : {},
14700
15961
  identity: file.identity ?? file,
14701
15962
  ...file.clipboard ? { clipboard: file.clipboard } : {},
14702
15963
  ...file.embedder ? { embedder: file.embedder } : {},
15964
+ ...file.device ? { device: file.device } : {},
14703
15965
  ...file.write ? {
14704
15966
  write: async (bytes, signal) => {
14705
15967
  await turn(signal);
@@ -14711,7 +15973,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
14711
15973
  await send2(stream2.push(taken), signal);
14712
15974
  } finally {
14713
15975
  busy = false;
14714
- if (quiet3()) wake();
15976
+ if (quiet5()) wake();
14715
15977
  }
14716
15978
  timer = setTimeout(release, idleMs);
14717
15979
  return taken.length;
@@ -14770,9 +16032,9 @@ function scrubStdio(fds, scrubber) {
14770
16032
  // src/kernel/net/sealed-scrubs.ts
14771
16033
  function addTo(tree2, host, secrets) {
14772
16034
  const entry = tree2.get(host) ?? { secrets: [], version: 0, scrubber: void 0 };
14773
- const fresh3 = secrets.filter((s) => !entry.secrets.some((e) => e.value === s.value));
14774
- if (fresh3.length === 0) return;
14775
- 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];
14776
16038
  entry.version += 1;
14777
16039
  entry.scrubber = void 0;
14778
16040
  tree2.set(host, entry);
@@ -14935,7 +16197,7 @@ var HEADER = new RegExp(`^header:${TOKEN3}$`);
14935
16197
  var METHOD = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
14936
16198
  var TARGET = /^[\x21-\x7e]+$/;
14937
16199
  var SESSION_EXIT = /^(tailscale|device:[A-Za-z0-9._:-]+)$/;
14938
- var HOST = /^[a-z0-9.:-]+$/;
16200
+ var HOST2 = /^[a-z0-9.:-]+$/;
14939
16201
  var UNSAFE = /[\r\n\0]/;
14940
16202
  var ANCHOR_ID = /^[A-Za-z0-9+/]{43}=$/;
14941
16203
  function verifiedByOk(f) {
@@ -14989,7 +16251,7 @@ function scramOk(f) {
14989
16251
  return texts.every((t) => typeof t === "string" && SCRAM_TEXT.test(t));
14990
16252
  }
14991
16253
  function hostOk(host) {
14992
- return typeof host === "string" && HOST.test(host) && boundHost(host) === host;
16254
+ return typeof host === "string" && HOST2.test(host) && boundHost(host) === host;
14993
16255
  }
14994
16256
  function sessionAskable(f, now) {
14995
16257
  if (!Object.hasOwn(POSITIONS, f.protocol) || typeof f.position !== "string") return false;
@@ -15079,20 +16341,20 @@ var ASK_FIELDS = /* @__PURE__ */ new Set([
15079
16341
  "caller",
15080
16342
  "requester"
15081
16343
  ]);
15082
- var encoder6 = new TextEncoder();
16344
+ var encoder7 = new TextEncoder();
15083
16345
  var decoder3 = new TextDecoder("utf-8", { fatal: true });
15084
16346
  function parseSealed(bytes) {
15085
16347
  try {
15086
16348
  if (bytes.length > SEALED_MAX_BYTES) throw new KernelError("EMSGSIZE");
15087
- let parsed2;
16349
+ let parsed3;
15088
16350
  try {
15089
- parsed2 = JSON.parse(decoder3.decode(bytes));
16351
+ parsed3 = JSON.parse(decoder3.decode(bytes));
15090
16352
  } catch {
15091
16353
  throw new KernelError("EINVAL");
15092
16354
  }
15093
- if (typeof parsed2 !== "object" || parsed2 === null || Array.isArray(parsed2))
16355
+ if (typeof parsed3 !== "object" || parsed3 === null || Array.isArray(parsed3))
15094
16356
  throw new KernelError("EINVAL");
15095
- const req = parsed2;
16357
+ const req = parsed3;
15096
16358
  if (req.v !== 1 || typeof req.op !== "string" || !SEALED_OPS.has(req.op))
15097
16359
  throw new KernelError("EINVAL");
15098
16360
  if (req.op === "materialize" && !materializable(req)) throw new KernelError("EINVAL");
@@ -15188,7 +16450,7 @@ var SealedChannel = class {
15188
16450
  await this.broker.tickets.record(req, reply, caller.pid);
15189
16451
  if (this.inflight !== flight) return;
15190
16452
  this.inflight = void 0;
15191
- if (reply) this.reply = encoder6.encode(serialized(reply));
16453
+ if (reply) this.reply = encoder7.encode(serialized(reply));
15192
16454
  });
15193
16455
  this.inflight = flight;
15194
16456
  return length2;
@@ -15464,7 +16726,7 @@ function shown(text) {
15464
16726
  const chars = [...safe];
15465
16727
  return `'${chars.length > SHOWN_MAX ? `${chars.slice(0, SHOWN_MAX - 1).join("")}\u2026` : safe}'`;
15466
16728
  }
15467
- function unescaped(double) {
16729
+ function unescaped2(double) {
15468
16730
  return double.replace(/\\(["\\$`])/g, "$1");
15469
16731
  }
15470
16732
  function splitWords(text) {
@@ -15480,7 +16742,7 @@ function splitWords(text) {
15480
16742
  word = void 0;
15481
16743
  continue;
15482
16744
  }
15483
- word = (word ?? "") + (single ?? (double !== void 0 ? unescaped(double) : escaped ?? plain3));
16745
+ word = (word ?? "") + (single ?? (double !== void 0 ? unescaped2(double) : escaped ?? plain3));
15484
16746
  }
15485
16747
  if (word !== void 0) words3.push(word);
15486
16748
  return words3;
@@ -15517,7 +16779,7 @@ function splitPatterns(text) {
15517
16779
  }
15518
16780
  word ??= [];
15519
16781
  if (plain3 !== void 0) addPlain(word, plain3);
15520
- else addPart(word, single ?? (double !== void 0 ? unescaped(double) : escaped));
16782
+ else addPart(word, single ?? (double !== void 0 ? unescaped2(double) : escaped));
15521
16783
  }
15522
16784
  if (word !== void 0) words3.push(word);
15523
16785
  return words3;
@@ -15568,13 +16830,13 @@ function commandMatches(cmnd, argv) {
15568
16830
  if (cmnd === "*") return true;
15569
16831
  const [path, ...args] = argv;
15570
16832
  if (path !== cmnd.path) return false;
15571
- const want = cmnd.args;
15572
- if (want === void 0) return true;
16833
+ const want2 = cmnd.args;
16834
+ if (want2 === void 0) return true;
15573
16835
  return globMatch(
15574
- want.length,
16836
+ want2.length,
15575
16837
  args.length,
15576
- (i) => isAny(want[i]),
15577
- (i, j) => wordMatch(want[i], args[j])
16838
+ (i) => isAny(want2[i]),
16839
+ (i, j) => wordMatch(want2[i], args[j])
15578
16840
  );
15579
16841
  }
15580
16842
  function commandText(cmnd) {
@@ -15871,11 +17133,11 @@ function parsePolicy(text, file) {
15871
17133
  const defaults2 = [];
15872
17134
  const errors = [];
15873
17135
  for (const [n, line] of text.split("\n").entries()) {
15874
- const parsed2 = parseLine(line, file);
15875
- if (parsed2 === null) continue;
15876
- if ("error" in parsed2) errors.push({ line: n + 1, message: parsed2.error });
15877
- else if ("defaults" in parsed2) defaults2.push(parsed2.defaults);
15878
- 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);
15879
17141
  }
15880
17142
  return { rules, defaults: defaults2, errors };
15881
17143
  }
@@ -15895,18 +17157,18 @@ var HOME_DIR = /^\/home\/[^/]+$/;
15895
17157
  var SYSTEM_PNPM_HOME = "/usr/local/share/pnpm";
15896
17158
  var PNPM_CONFIG = "minimumReleaseAge: 1440\n";
15897
17159
  var isId = (text) => text !== void 0 && /^\d+$/.test(text);
15898
- function lines(text) {
17160
+ function lines2(text) {
15899
17161
  return text.split("\n").filter((line, i, all) => line !== "" || i < all.length - 1);
15900
17162
  }
15901
17163
  function parsePasswd(text) {
15902
- return lines(text).map((line) => {
17164
+ return lines2(text).map((line) => {
15903
17165
  const [name2, , uid, gid, gecos = "", home = "/", shell = "/bin/sh"] = line.split(":");
15904
17166
  if (!name2 || !isId(uid) || !isId(gid)) return line;
15905
17167
  return { name: name2, uid: Number(uid), gid: Number(gid), gecos, home, shell };
15906
17168
  });
15907
17169
  }
15908
17170
  function parseGroup(text) {
15909
- return lines(text).map((line) => {
17171
+ return lines2(text).map((line) => {
15910
17172
  const [name2, , gid, members = ""] = line.split(":");
15911
17173
  if (!name2 || !isId(gid)) return line;
15912
17174
  return { name: name2, gid: Number(gid), members: members.split(",").filter(Boolean) };
@@ -15922,7 +17184,7 @@ function formatGroup(rows) {
15922
17184
  return `${rows.map((r) => typeof r === "string" ? r : line(r)).join("\n")}
15923
17185
  `;
15924
17186
  }
15925
- var parsed = (rows) => rows.filter((r) => typeof r !== "string");
17187
+ var parsed2 = (rows) => rows.filter((r) => typeof r !== "string");
15926
17188
  function homeOf(home) {
15927
17189
  if (typeof home !== "string" || !home.startsWith("/") || /[:\r\n]/.test(home)) {
15928
17190
  throw fsError("EINVAL", `not a home directory: ${String(home)}`);
@@ -16115,20 +17377,20 @@ var UserDb = class {
16115
17377
  return (await this.lookupTerminal(user))?.account;
16116
17378
  }
16117
17379
  async ttyGroup() {
16118
- 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;
16119
17381
  }
16120
17382
  async lookupTerminal(user) {
16121
17383
  const { passwd, group } = await this.tables();
16122
17384
  const key = typeof user === "number" ? "uid" : "name";
16123
- const row = parsed(passwd).find((r) => r[key] === user);
16124
- const groups = parsed(group);
17385
+ const row = parsed2(passwd).find((r) => r[key] === user);
17386
+ const groups = parsed2(group);
16125
17387
  const tty = groups.find((g) => g.name === "tty")?.gid;
16126
17388
  return row && { account: this.account(row, groups), tty };
16127
17389
  }
16128
17390
  async list() {
16129
17391
  const { passwd, group } = await this.tables();
16130
- const groups = parsed(group);
16131
- return parsed(passwd).map((row) => this.account(row, groups));
17392
+ const groups = parsed2(group);
17393
+ return parsed2(passwd).map((row) => this.account(row, groups));
16132
17394
  }
16133
17395
  add(options) {
16134
17396
  return this.serial(async () => {
@@ -16140,8 +17402,8 @@ var UserDb = class {
16140
17402
  if (options.umask !== void 0) this.umasks.set(name2, options.umask & 511);
16141
17403
  };
16142
17404
  const { passwd, group } = await this.tables();
16143
- const users = parsed(passwd);
16144
- const groups = parsed(group);
17405
+ const users = parsed2(passwd);
17406
+ const groups = parsed2(group);
16145
17407
  const found = users.find((r) => r.name === name2);
16146
17408
  const shared2 = homeGroupOf(groups, options.homeGroup);
16147
17409
  const homeMode = homeModeOf(options.homeMode);
@@ -16166,7 +17428,7 @@ var UserDb = class {
16166
17428
  joined3(name2, extra);
16167
17429
  group.push({ name: name2, gid: uid, members: [] });
16168
17430
  passwd.push(row);
16169
- const fresh3 = !await this.fs.exists(home);
17431
+ const fresh4 = !await this.fs.exists(home);
16170
17432
  await this.fs.mkdir(home, { recursive: true });
16171
17433
  try {
16172
17434
  await this.provision(row, this.umaskFor(options), shared2);
@@ -16174,16 +17436,16 @@ var UserDb = class {
16174
17436
  await this.fs.mkdir("/etc", { recursive: true });
16175
17437
  await this.commit(formatPasswd(passwd), formatGroup(group));
16176
17438
  } catch (err) {
16177
- if (fresh3) await this.fs.rm(home, { recursive: true, force: true });
17439
+ if (fresh4) await this.fs.rm(home, { recursive: true, force: true });
16178
17440
  throw err;
16179
17441
  }
16180
17442
  remember();
16181
- return this.account(row, parsed(group));
17443
+ return this.account(row, parsed2(group));
16182
17444
  });
16183
17445
  }
16184
17446
  async groups() {
16185
17447
  const { group } = await this.tables();
16186
- 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] }));
16187
17449
  }
16188
17450
  addGroup(name2, gid) {
16189
17451
  return this.serial(async () => {
@@ -16194,7 +17456,7 @@ var UserDb = class {
16194
17456
  throw fsError("EINVAL", `not a group id: ${String(gid)}`);
16195
17457
  }
16196
17458
  const { group } = await this.tables();
16197
- const rows = parsed(group);
17459
+ const rows = parsed2(group);
16198
17460
  const found = rows.find((g) => g.name === name2);
16199
17461
  if (found) return { name: found.name, gid: found.gid, members: [...found.members] };
16200
17462
  const taken = rows.find((g) => g.gid === gid);
@@ -16214,9 +17476,9 @@ var UserDb = class {
16214
17476
  member(name2, user, on) {
16215
17477
  return this.serial(async () => {
16216
17478
  const { passwd, group } = await this.tables();
16217
- const row = parsed(group).find((g) => g.name === name2);
17479
+ const row = parsed2(group).find((g) => g.name === name2);
16218
17480
  if (!row) throw fsError("ENOENT", `no group ${String(name2)}`);
16219
- const who = parsed(passwd).find((u) => u.name === user);
17481
+ const who = parsed2(passwd).find((u) => u.name === user);
16220
17482
  if (!who) throw fsError("ENOENT", `no user ${String(user)}`);
16221
17483
  if (who.gid === row.gid) throw fsError("EINVAL", `${name2} is the own group of ${user}`);
16222
17484
  if (row.members.includes(user) !== on) {
@@ -16229,7 +17491,7 @@ var UserDb = class {
16229
17491
  remove(name2, options = {}) {
16230
17492
  return this.serial(async () => {
16231
17493
  const { passwd, group } = await this.tables();
16232
- const row = parsed(passwd).find((r) => r.name === name2);
17494
+ const row = parsed2(passwd).find((r) => r.name === name2);
16233
17495
  if (!row) return false;
16234
17496
  if (row.uid < FIRST_UID || row.uid > LAST_UID) {
16235
17497
  throw fsError("EPERM", `${name2} is a system account`);
@@ -16237,9 +17499,9 @@ var UserDb = class {
16237
17499
  const keptGroups = group.filter(
16238
17500
  (r) => typeof r === "string" || !(r.name === name2 && r.gid === row.gid)
16239
17501
  );
16240
- 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);
16241
17503
  const home = this.fs.resolvePath("/", row.home);
16242
- const shared2 = parsed(passwd).some(
17504
+ const shared2 = parsed2(passwd).some(
16243
17505
  (r) => r !== row && this.fs.resolvePath("/", r.home) === home
16244
17506
  );
16245
17507
  const removeHome = !options.keepHome && !shared2 && HOME_DIR.test(home) && await this.fs.exists(home);
@@ -16305,11 +17567,11 @@ var REPLAY_TAGS_MAX = 64;
16305
17567
  var REPLAY_ARGV_MAX = 4096;
16306
17568
  var REPLAY_BYTES_MAX = 1024 * 1024;
16307
17569
  var NOT_A_REQUEST = "sudo: that isn't a sudo request this kernel can run";
16308
- function strings(value2, max) {
17570
+ function strings2(value2, max) {
16309
17571
  return Array.isArray(value2) && value2.length <= max && value2.every((s) => typeof s === "string");
16310
17572
  }
16311
17573
  function words(value2) {
16312
- if (!strings(value2, REPLAY_ARGV_MAX) || value2.length === 0) return false;
17574
+ if (!strings2(value2, REPLAY_ARGV_MAX) || value2.length === 0) return false;
16313
17575
  const bytes = value2.reduce((n, s) => n + s.length + 1, 0);
16314
17576
  return bytes <= REPLAY_BYTES_MAX && !value2.some((s) => s.includes("\0"));
16315
17577
  }
@@ -16344,7 +17606,7 @@ function replayOf(value2) {
16344
17606
  const bound3 = filesOf(r.userWritable);
16345
17607
  const named = (s) => typeof s === "string" && s !== "";
16346
17608
  const absolute2 = (s) => typeof s === "string" && s.startsWith("/") && s.length <= PATH_MAX && !s.includes("\0");
16347
- 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;
16348
17610
  if (!valid2) return failed2("EINVAL", NOT_A_REQUEST);
16349
17611
  const all = tags ?? (tag === void 0 ? [] : [tag]);
16350
17612
  return {
@@ -16436,28 +17698,28 @@ function recordedCommand(argv) {
16436
17698
  const [path = "", ...args] = argv;
16437
17699
  return commandText({ path, args: args.map((a) => [a]) });
16438
17700
  }
16439
- function flags(parsed2, arg, next3) {
17701
+ function flags(parsed3, arg, next3) {
16440
17702
  for (let j = 1; j < arg.length; j++) {
16441
17703
  const flag = arg[j];
16442
- if (flag === "n") parsed2.nonInteractive = true;
16443
- else if (flag === "l") parsed2.list = true;
16444
- else if (flag === "v") parsed2.validate = true;
16445
- else if (flag === "k") parsed2.reset = true;
16446
- 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;
16447
17709
  else if (flag !== "u") return `sudo: invalid option -- '${flag}'
16448
17710
  ${USAGE}`;
16449
17711
  else {
16450
17712
  const user = arg.slice(j + 1) || next3;
16451
17713
  if (!user) return `sudo: option requires an argument -- 'u'
16452
17714
  ${USAGE}`;
16453
- parsed2.user = user;
17715
+ parsed3.user = user;
16454
17716
  return arg.slice(j + 1) ? 0 : 1;
16455
17717
  }
16456
17718
  }
16457
17719
  return 0;
16458
17720
  }
16459
17721
  function parseArgs(args) {
16460
- const parsed2 = {
17722
+ const parsed3 = {
16461
17723
  list: false,
16462
17724
  nonInteractive: false,
16463
17725
  validate: false,
@@ -16474,19 +17736,19 @@ function parseArgs(args) {
16474
17736
  break;
16475
17737
  }
16476
17738
  if (!arg.startsWith("-") || arg === "-") break;
16477
- const used = flags(parsed2, arg, args[i + 1]);
17739
+ const used = flags(parsed3, arg, args[i + 1]);
16478
17740
  if (typeof used === "string") return used;
16479
17741
  i += used;
16480
17742
  }
16481
- parsed2.command = args.slice(i);
16482
- return usable(parsed2) ? parsed2 : USAGE;
17743
+ parsed3.command = args.slice(i);
17744
+ return usable(parsed3) ? parsed3 : USAGE;
16483
17745
  }
16484
- function usable(parsed2) {
16485
- const command = parsed2.command.length > 0;
16486
- const modes = [parsed2.list, parsed2.validate, parsed2.removeAll].filter(Boolean).length;
16487
- 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;
16488
17750
  if (modes === 1) return !command;
16489
- return command || parsed2.reset;
17751
+ return command || parsed3.reset;
16490
17752
  }
16491
17753
  function policyOf(settings) {
16492
17754
  return { type: settings.timestampType, minutes: settings.timestampTimeout };
@@ -16630,28 +17892,28 @@ var Sudo = class {
16630
17892
  };
16631
17893
  }
16632
17894
  async check(cred, call) {
16633
- const lines2 = [];
17895
+ const lines3 = [];
16634
17896
  let ok4 = true;
16635
17897
  const report = ({ path, problems, ok: read }) => {
16636
17898
  for (const problem of problems) {
16637
17899
  const where = problem.line === void 0 ? `${path}: sudo skips it:` : `${path}:${problem.line}:`;
16638
- lines2.push([2, `visudo: ${where} ${problem.message}`]);
17900
+ lines3.push([2, `visudo: ${where} ${problem.message}`]);
16639
17901
  }
16640
- if (read && problems.length === 0) lines2.push([1, `${path}: parsed OK`]);
17902
+ if (read && problems.length === 0) lines3.push([1, `${path}: parsed OK`]);
16641
17903
  if (problems.length > 0) ok4 = false;
16642
17904
  };
16643
17905
  const values = (defaults2) => {
16644
- for (const line of defaultsText(defaults2)) lines2.push([1, line]);
17906
+ for (const line of defaultsText(defaults2)) lines3.push([1, line]);
16645
17907
  };
16646
17908
  if (call.files !== void 0) {
16647
17909
  const defaults2 = [];
16648
17910
  for (const { name: name2, text } of call.files) {
16649
- const parsed2 = parsePolicy(text);
16650
- defaults2.push(...parsed2.defaults);
16651
- 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 });
16652
17914
  }
16653
17915
  if (defaults2.length > 0) values(defaults2);
16654
- return { lines: lines2, ok: ok4 };
17916
+ return { lines: lines3, ok: ok4 };
16655
17917
  }
16656
17918
  if (this.o.mode === "enforce" && cred.euid !== 0) {
16657
17919
  const hint = "check the policy as root (sudo visudo -c), or a file with visudo -c -f <file>";
@@ -16660,7 +17922,7 @@ var Sudo = class {
16660
17922
  const files = await this.policy();
16661
17923
  for (const file of files) report(file);
16662
17924
  values(files.flatMap((f) => f.defaults ?? []));
16663
- return { lines: lines2, ok: ok4 };
17925
+ return { lines: lines3, ok: ok4 };
16664
17926
  }
16665
17927
  async groupIds() {
16666
17928
  return new Map((await this.o.users.groups()).map((g) => [g.name, g.gid]));
@@ -16725,13 +17987,13 @@ var Sudo = class {
16725
17987
  return void 0;
16726
17988
  }
16727
17989
  async request(caller, call, signal) {
16728
- const parsed2 = parseArgs(call.args);
16729
- if (typeof parsed2 === "string") return failed2("EINVAL", parsed2);
17990
+ const parsed3 = parseArgs(call.args);
17991
+ if (typeof parsed3 === "string") return failed2("EINVAL", parsed3);
16730
17992
  if (this.o.mode !== "enforce") {
16731
17993
  const mode = this.o.mode;
16732
- 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
16733
17995
  ` };
16734
- return parsed2.command.length > 0 ? { run: parsed2.command } : DONE;
17996
+ return parsed3.command.length > 0 ? { run: parsed3.command } : DONE;
16735
17997
  }
16736
17998
  const { cred } = caller;
16737
17999
  const policy = await this.policyRules(caller);
@@ -16744,22 +18006,22 @@ var Sudo = class {
16744
18006
  };
16745
18007
  const applies = await this.applies(self, cred);
16746
18008
  const settings = resolveDefaults(policy.defaults, applies);
16747
- if (parsed2.removeAll) {
18009
+ if (parsed3.removeAll) {
16748
18010
  this.timestamps.remove(owner.uid);
16749
18011
  return DONE;
16750
18012
  }
16751
- if (parsed2.reset && !parsed2.validate && !parsed2.list && parsed2.command.length === 0) {
18013
+ if (parsed3.reset && !parsed3.validate && !parsed3.list && parsed3.command.length === 0) {
16752
18014
  this.timestamps.invalidate(owner, settings.timestampType);
16753
18015
  return DONE;
16754
18016
  }
16755
- const target = await this.lookup(parsed2.user);
16756
- 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}`);
16757
18019
  const root = cred.euid === 0;
16758
18020
  const rules = root ? [] : policy.rules;
16759
18021
  const mine = rules.filter((rule) => applies(rule.who));
16760
- if (parsed2.list) return await this.listed(self, root, mine, settings.matching);
16761
- const session = { caller, self, target, owner, policy: policyOf(settings), parsed: parsed2, signal };
16762
- 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);
16763
18025
  return this.command(session, root, mine, call);
16764
18026
  }
16765
18027
  async replay(value2) {
@@ -16818,9 +18080,9 @@ var Sudo = class {
16818
18080
  );
16819
18081
  }
16820
18082
  async command(s, root, mine, call) {
16821
- const { caller, self, target, parsed: parsed2 } = s;
18083
+ const { caller, self, target, parsed: parsed3 } = s;
16822
18084
  const { cred } = caller;
16823
- const [word = "", ...args] = parsed2.command;
18085
+ const [word = "", ...args] = parsed3.command;
16824
18086
  const cwd = await this.canonical(call.cwd);
16825
18087
  if (cwd === void 0 || !await this.reachable(cred, cwd)) {
16826
18088
  return failed2(
@@ -16835,7 +18097,7 @@ var Sudo = class {
16835
18097
  const matching = mine.filter(
16836
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)
16837
18099
  );
16838
- 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);
16839
18101
  if (root || matching.some((rule) => rule.nopasswd)) return grant();
16840
18102
  const quoted = argv.map(quoteWord).join(" ");
16841
18103
  if (matching.length === 0) {
@@ -16844,38 +18106,38 @@ var Sudo = class {
16844
18106
  `sudo: ${self.name} is not allowed to run ${quoted} as ${target.name}`
16845
18107
  );
16846
18108
  }
16847
- if (!parsed2.reset && this.timestamps.current(s.owner, s.policy, bound3)) {
18109
+ if (!parsed3.reset && this.timestamps.current(s.owner, s.policy, bound3)) {
16848
18110
  this.timestamps.stamp(s.owner, s.policy, bound3);
16849
18111
  this.logged(s, cwd, argv);
16850
18112
  return grant();
16851
18113
  }
16852
- if (parsed2.nonInteractive) return failed2("EPERM", "sudo: a password is required");
18114
+ if (parsed3.nonInteractive) return failed2("EPERM", "sudo: a password is required");
16853
18115
  return this.pendingFor(cred.ruid, async () => {
16854
- 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 });
16855
18117
  if ("error" in answer2) return answer2;
16856
18118
  if (await this.canonical(call.cwd) !== cwd || !await this.reachable(cred, cwd)) {
16857
18119
  return failed2("EACCES", `sudo: ${call.cwd} changed while the request waited`);
16858
18120
  }
16859
18121
  const now = await this.bound(path, cwd);
16860
18122
  if (!now) return failed2("ENOENT", `sudo: ${word}: command not found`);
16861
- const shown2 = recordable(cwd, [...parsed2.command, ...Object.keys(bound3)]);
18123
+ const shown2 = recordable(cwd, [...parsed3.command, ...Object.keys(bound3)]);
16862
18124
  const result = answer2.answer === "once" ? grant() : await this.always(self, target, { cwd, argv, bound: bound3 }, now, shown2, grant);
16863
18125
  if (!("error" in result)) this.stamped(s, answer2, bound3);
16864
18126
  return result;
16865
18127
  });
16866
18128
  }
16867
18129
  async validate(s, root, mine, dir) {
16868
- const { self, parsed: parsed2 } = s;
18130
+ const { self, parsed: parsed3 } = s;
16869
18131
  if (root) return DONE;
16870
18132
  if (mine.length === 0) {
16871
18133
  return failed2("EPERM", `Sorry, user ${self.name} may not run sudo on ${this.o.hostname}.`);
16872
18134
  }
16873
18135
  if (mine.every((rule) => rule.nopasswd)) return DONE;
16874
- if (!parsed2.reset && this.timestamps.current(s.owner, s.policy)) {
18136
+ if (!parsed3.reset && this.timestamps.current(s.owner, s.policy)) {
16875
18137
  this.timestamps.stamp(s.owner, s.policy);
16876
18138
  return DONE;
16877
18139
  }
16878
- if (parsed2.nonInteractive) return failed2("EPERM", "sudo: a password is required");
18140
+ if (parsed3.nonInteractive) return failed2("EPERM", "sudo: a password is required");
16879
18141
  const cwd = await this.canonical(dir) ?? normalizePath(dir);
16880
18142
  return this.pendingFor(s.caller.cred.ruid, async () => {
16881
18143
  const answer2 = await this.ask(s, { argv: [], path: "", cwd, bound: {}, validate: true });
@@ -16930,7 +18192,7 @@ var Sudo = class {
16930
18192
  }
16931
18193
  const shown2 = [];
16932
18194
  for (const rule of rules) shown2.push(described(rule, await this.writable(rule)));
16933
- const lines2 = shown2.join("\n");
18195
+ const lines3 = shown2.join("\n");
16934
18196
  const params = defaults2.flatMap((d) => d.params).join(", ");
16935
18197
  const matching = params === "" ? "" : `Matching Defaults entries for ${self.name} on ${host}:
16936
18198
  ${params}
@@ -16938,7 +18200,7 @@ var Sudo = class {
16938
18200
  `;
16939
18201
  return {
16940
18202
  list: `${matching}User ${self.name} may run the following commands on ${host}:
16941
- ${lines2}
18203
+ ${lines3}
16942
18204
  `
16943
18205
  };
16944
18206
  }
@@ -17355,7 +18617,7 @@ async function epollSyscall(req, proc) {
17355
18617
 
17356
18618
  // src/kernel/locks.ts
17357
18619
  var overlaps = (a, b) => a.start < b.end && b.start < a.end;
17358
- 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");
17359
18621
  function lockRange(start2, len) {
17360
18622
  if (!Number.isSafeInteger(start2) || !Number.isSafeInteger(len)) throw new KernelError("EINVAL");
17361
18623
  const from = len < 0 ? start2 + len : start2;
@@ -17441,8 +18703,8 @@ var LockTable = class {
17441
18703
  blocked = /* @__PURE__ */ new Map();
17442
18704
  wakers = /* @__PURE__ */ new Map();
17443
18705
  held = /* @__PURE__ */ new Map();
17444
- holder(key, owner, want) {
17445
- 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));
17446
18708
  }
17447
18709
  set(key, owner, kind, start2, end) {
17448
18710
  if (kind !== "unlock" && this.holder(key, owner, { key, kind, start: start2, end })) return false;
@@ -17461,10 +18723,10 @@ var LockTable = class {
17461
18723
  return true;
17462
18724
  }
17463
18725
  async wait(key, owner, kind, range2, blocking) {
17464
- const want = { key, kind, ...range2 };
18726
+ const want2 = { key, kind, ...range2 };
17465
18727
  let signal;
17466
18728
  for (; ; ) {
17467
- const holder = this.holder(key, owner, want);
18729
+ const holder = this.holder(key, owner, want2);
17468
18730
  if (!holder) {
17469
18731
  this.set(key, owner, kind, range2.start, range2.end);
17470
18732
  return;
@@ -17472,7 +18734,7 @@ var LockTable = class {
17472
18734
  if (this.waitsOn(holder.owner, owner)) throw new KernelError("EDEADLK");
17473
18735
  signal ??= blocking();
17474
18736
  if (signal.aborted) throw new KernelError("EINTR");
17475
- this.blocked.set(owner, want);
18737
+ this.blocked.set(owner, want2);
17476
18738
  try {
17477
18739
  await this.changed(key, signal);
17478
18740
  } finally {
@@ -17484,9 +18746,9 @@ var LockTable = class {
17484
18746
  const seen = /* @__PURE__ */ new Set();
17485
18747
  for (let at = from; at !== void 0 && !seen.has(at); ) {
17486
18748
  seen.add(at);
17487
- const want = this.blocked.get(at);
17488
- if (!want) return false;
17489
- 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);
17490
18752
  if (!holder) return false;
17491
18753
  if (holder.owner === target) return true;
17492
18754
  at = holder.owner;
@@ -17851,24 +19113,24 @@ function errno2(code, message3) {
17851
19113
  }
17852
19114
  function requestHead(method, url, headers, extra, first = []) {
17853
19115
  if (!TOKEN4.test(method)) throw errno2("EINVAL", `method ${method}`);
17854
- const lines2 = [`${method} ${url.pathname}${url.search} HTTP/1.1`, `host: ${url.host}`];
17855
- 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}`);
17856
19118
  for (const [name2, value2] of headers) {
17857
19119
  if (OWN_HEADERS.has(name2.toLowerCase())) continue;
17858
19120
  if (!TOKEN4.test(name2) || !SAFE_VALUE.test(value2)) throw errno2("EINVAL", `header ${name2}`);
17859
- lines2.push(`${name2}: ${value2}`);
19121
+ lines3.push(`${name2}: ${value2}`);
17860
19122
  }
17861
- for (const [name2, value2] of extra) lines2.push(`${name2}: ${value2}`);
17862
- 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
17863
19125
  \r
17864
19126
  `);
17865
19127
  }
17866
19128
  function parseHead(bytes) {
17867
- const [statusLine = "", ...lines2] = latin1(bytes).split(/\r?\n/);
19129
+ const [statusLine = "", ...lines3] = latin1(bytes).split(/\r?\n/);
17868
19130
  const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
17869
19131
  if (!match) throw errno2("EPROTO", `not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);
17870
19132
  const headers = [];
17871
- for (const line of lines2) {
19133
+ for (const line of lines3) {
17872
19134
  const colon = line.indexOf(":");
17873
19135
  if (colon > 0) headers.push([line.slice(0, colon).trim(), line.slice(colon + 1).trim()]);
17874
19136
  }
@@ -17958,8 +19220,8 @@ var KernelLoopback = class {
17958
19220
  );
17959
19221
  } catch (err) {
17960
19222
  socket.close();
17961
- const refused4 = err instanceof KernelError && err.code === "ECONNREFUSED";
17962
- 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;
17963
19225
  }
17964
19226
  signal.addEventListener("abort", () => socket.close(), { once: true });
17965
19227
  return socket;
@@ -18348,9 +19610,9 @@ function pem(der2) {
18348
19610
  for (let i = 0; i < der2.length; i += 32768) {
18349
19611
  b642 += String.fromCharCode(...der2.subarray(i, i + 32768));
18350
19612
  }
18351
- const lines2 = btoa(b642).match(/.{1,64}/g) ?? [];
19613
+ const lines3 = btoa(b642).match(/.{1,64}/g) ?? [];
18352
19614
  return `-----BEGIN CERTIFICATE-----
18353
- ${lines2.join("\n")}
19615
+ ${lines3.join("\n")}
18354
19616
  -----END CERTIFICATE-----
18355
19617
  `;
18356
19618
  }
@@ -19089,13 +20351,13 @@ var HOSTS_PATH = "/etc/hosts";
19089
20351
  function parseHosts(text) {
19090
20352
  const table = /* @__PURE__ */ new Map();
19091
20353
  for (const raw2 of text.split("\n")) {
19092
- const [address, ...names2] = raw2.replace(/#.*/, "").trim().split(/\s+/);
19093
- if (!address || names2.length === 0) continue;
19094
- 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;
19095
20357
  for (const name2 of names2) {
19096
20358
  const key = name2.toLowerCase().replace(/\.$/, "");
19097
20359
  const known = table.get(key) ?? [];
19098
- if (!known.includes(address)) known.push(address);
20360
+ if (!known.includes(address2)) known.push(address2);
19099
20361
  table.set(key, known);
19100
20362
  }
19101
20363
  }
@@ -19183,14 +20445,14 @@ var Resolver = class {
19183
20445
  if (hostsEntry(key, this.own) !== void 0) return true;
19184
20446
  return ((await this.hosts?.().catch(() => void 0))?.get(key)?.length ?? 0) > 0;
19185
20447
  }
19186
- async reverse(address) {
19187
- const v6 = ipv6Groups(address)?.join(":");
19188
- 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;
19189
20451
  const file = await this.hosts?.().catch(() => void 0);
19190
20452
  for (const table of [file, this.builtin]) {
19191
20453
  for (const [name2, addresses] of table ?? []) if (addresses.some(same2)) return name2;
19192
20454
  }
19193
- if (ipv4Bytes(address)?.[0] === 127) return "localhost";
20455
+ if (ipv4Bytes(address2)?.[0] === 127) return "localhost";
19194
20456
  return void 0;
19195
20457
  }
19196
20458
  async resolve(name2, family, blocking) {
@@ -19244,13 +20506,13 @@ var Resolver = class {
19244
20506
  if (addresses.length > 0) this.remember(id2, addresses, answer2.ttl);
19245
20507
  return addresses;
19246
20508
  }
19247
- usable(name2, address, family) {
19248
- const v4 = ipv4Bytes(address) !== void 0;
19249
- 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;
19250
20512
  if (v4 && family === 6) return false;
19251
- const kind = this.routes.classify(address);
20513
+ const kind = this.routes.classify(address2);
19252
20514
  if (kind !== "loopback" && kind !== "host" && kind !== "reserved") return true;
19253
- 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}`);
19254
20516
  return false;
19255
20517
  }
19256
20518
  remember(id2, addresses, ttl) {
@@ -19284,275 +20546,6 @@ function waitFor(lookup4, signal) {
19284
20546
  });
19285
20547
  }
19286
20548
 
19287
- // src/kernel/tty.ts
19288
- var ICRNL = 256;
19289
- var IUTF8 = 16384;
19290
- var OPOST = 1;
19291
- var ONLCR = 4;
19292
- var ISIG = 1;
19293
- var ICANON = 2;
19294
- var ECHO = 8;
19295
- var ECHOE = 16;
19296
- var ECHOCTL = 512;
19297
- var NOFLSH = 128;
19298
- var IEXTEN = 32768;
19299
- var VINTR = 0;
19300
- var VQUIT = 1;
19301
- var VERASE = 2;
19302
- var VKILL = 3;
19303
- var VEOF = 4;
19304
- var VSUSP = 10;
19305
- var VWERASE = 14;
19306
- function defaultTermios() {
19307
- const cCc = new Array(32).fill(0);
19308
- Object.assign(cCc, { [VINTR]: 3, [VQUIT]: 28, [VERASE]: 127, [VKILL]: 21 });
19309
- Object.assign(cCc, { [VEOF]: 4, 6: 1, 8: 17, 9: 19, [VSUSP]: 26 });
19310
- Object.assign(cCc, { 12: 18, 13: 15, [VWERASE]: 23, 15: 22 });
19311
- return {
19312
- c_iflag: ICRNL | 1024 | 8192 | IUTF8,
19313
- c_oflag: OPOST | ONLCR,
19314
- c_cflag: 15 | 48 | 128,
19315
- c_lflag: ISIG | ICANON | ECHO | ECHOE | 32 | ECHOCTL | 2048 | IEXTEN,
19316
- c_cc: cCc
19317
- };
19318
- }
19319
- var encoder7 = new TextEncoder();
19320
- function isContinuation(byte) {
19321
- return (byte & 192) === 128;
19322
- }
19323
- var KernelTty = class {
19324
- termios = defaultTermios();
19325
- cols = 80;
19326
- rows = 24;
19327
- readable = [];
19328
- line = [];
19329
- waiters = [];
19330
- hungUp = false;
19331
- name;
19332
- owner;
19333
- onRead;
19334
- readers = 0;
19335
- screen;
19336
- signal;
19337
- constructor(screen, signal) {
19338
- this.screen = screen;
19339
- this.signal = signal;
19340
- }
19341
- receive(bytes) {
19342
- for (const byte of bytes) this.key(byte);
19343
- this.wake();
19344
- }
19345
- resize(cols, rows) {
19346
- if (cols === this.cols && rows === this.rows) return;
19347
- this.setSize(cols, rows);
19348
- this.signal(SIG.WINCH);
19349
- }
19350
- hangup() {
19351
- this.hungUp = true;
19352
- this.wake();
19353
- }
19354
- signalHangup() {
19355
- this.signal(SIG.HUP);
19356
- }
19357
- setSize(cols, rows) {
19358
- this.cols = cols;
19359
- this.rows = rows;
19360
- }
19361
- tcgets() {
19362
- return { ...this.termios, c_cc: [...this.termios.c_cc] };
19363
- }
19364
- tcsets(termios, flush = false) {
19365
- const wasCanonical = this.canonical;
19366
- this.termios = { ...termios, c_cc: [...termios.c_cc] };
19367
- if (flush) {
19368
- this.line = [];
19369
- this.readable = [];
19370
- }
19371
- if (wasCanonical && !this.canonical && this.line.length > 0) {
19372
- this.readable.push(Uint8Array.from(this.line));
19373
- this.line = [];
19374
- this.wake();
19375
- }
19376
- }
19377
- winsize() {
19378
- return [this.rows, this.cols];
19379
- }
19380
- file() {
19381
- return new OpenFile({
19382
- read: (max, signal) => this.read(max, signal),
19383
- write: async (bytes) => {
19384
- if (this.hungUp) throw new KernelError("EIO");
19385
- this.output(bytes);
19386
- return bytes.length;
19387
- },
19388
- poll: () => ({
19389
- readable: this.readable.length > 0 || this.hungUp,
19390
- writable: true,
19391
- hangup: this.hungUp,
19392
- nread: this.readable.reduce((n, chunk2) => n + (chunk2?.length ?? 0), 0)
19393
- }),
19394
- changed: (signal) => this.changed(signal),
19395
- close: () => {
19396
- },
19397
- tty: this
19398
- });
19399
- }
19400
- get canonical() {
19401
- return (this.termios.c_lflag & ICANON) !== 0;
19402
- }
19403
- cc(slot2) {
19404
- return this.termios.c_cc[slot2] ?? 0;
19405
- }
19406
- key(input) {
19407
- const { c_iflag, c_lflag } = this.termios;
19408
- const byte = input === 13 && c_iflag & ICRNL ? 10 : input;
19409
- if (c_lflag & ISIG && this.signalKey(byte)) return;
19410
- if (!this.canonical) {
19411
- this.readable.push(Uint8Array.of(byte));
19412
- if (c_lflag & ECHO) this.echo(byte);
19413
- return;
19414
- }
19415
- this.edit(byte);
19416
- }
19417
- signalKey(byte) {
19418
- const sig = byte === this.cc(VINTR) ? SIG.INT : byte === this.cc(VQUIT) ? SIG.QUIT : byte === this.cc(VSUSP) ? SIG.TSTP : 0;
19419
- if (!sig || byte === 0) return false;
19420
- if (this.termios.c_lflag & ECHO) this.echo(byte);
19421
- if (!(this.termios.c_lflag & NOFLSH)) {
19422
- this.line = [];
19423
- this.readable = [];
19424
- }
19425
- this.signal(sig);
19426
- return true;
19427
- }
19428
- edit(byte) {
19429
- const echo = (this.termios.c_lflag & ECHO) !== 0;
19430
- if (byte === this.cc(VERASE) || byte === 8) {
19431
- this.rubOut(this.lastCharLength());
19432
- } else if (byte === this.cc(VKILL)) {
19433
- this.rubOut(this.line.length);
19434
- } else if (byte === this.cc(VWERASE)) {
19435
- this.rubOut(this.lastWordLength());
19436
- } else if (byte === this.cc(VEOF)) {
19437
- this.readable.push(this.line.length > 0 ? Uint8Array.from(this.line) : null);
19438
- this.line = [];
19439
- } else if (byte === 10) {
19440
- this.line.push(byte);
19441
- this.readable.push(Uint8Array.from(this.line));
19442
- this.line = [];
19443
- if (echo) this.output(encoder7.encode("\n"));
19444
- } else {
19445
- this.line.push(byte);
19446
- if (echo) this.echo(byte);
19447
- }
19448
- }
19449
- lastWordLength() {
19450
- let end = this.line.length;
19451
- while (end > 0 && this.line[end - 1] === 32) end--;
19452
- while (end > 0 && this.line[end - 1] !== 32) end--;
19453
- return this.line.length - end;
19454
- }
19455
- lastCharLength() {
19456
- const { line } = this;
19457
- let n = Math.min(1, line.length);
19458
- if (this.termios.c_iflag & IUTF8) {
19459
- while (n < line.length && isContinuation(line[line.length - n] ?? 0)) n++;
19460
- }
19461
- return n;
19462
- }
19463
- rubOut(n) {
19464
- const dropped2 = this.line.splice(this.line.length - n, n);
19465
- const utf83 = (this.termios.c_iflag & IUTF8) !== 0;
19466
- const cells = utf83 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
19467
- if (cells > 0 && this.termios.c_lflag & ECHO) {
19468
- this.output(encoder7.encode("\b \b".repeat(cells)), false);
19469
- }
19470
- }
19471
- echo(byte) {
19472
- if (byte < 32 && byte !== 10 && byte !== 9 && this.termios.c_lflag & ECHOCTL) {
19473
- this.output(Uint8Array.of(94, byte + 64), false);
19474
- return;
19475
- }
19476
- this.output(Uint8Array.of(byte));
19477
- }
19478
- output(bytes, translate2 = true) {
19479
- const { c_oflag } = this.termios;
19480
- if (!translate2 || !(c_oflag & OPOST && c_oflag & ONLCR) || !bytes.includes(10)) {
19481
- this.screen.write(bytes);
19482
- return;
19483
- }
19484
- const out = [];
19485
- for (const byte of bytes) {
19486
- if (byte === 10) out.push(13);
19487
- out.push(byte);
19488
- }
19489
- this.screen.write(Uint8Array.from(out));
19490
- }
19491
- get reading() {
19492
- return this.readers > 0;
19493
- }
19494
- async read(max, signal) {
19495
- this.onRead?.();
19496
- this.readers++;
19497
- try {
19498
- return await this.take(max, signal);
19499
- } finally {
19500
- this.readers--;
19501
- }
19502
- }
19503
- async take(max, signal) {
19504
- while (this.readable.length === 0) {
19505
- if (this.hungUp) return new Uint8Array(0);
19506
- await this.changed(signal);
19507
- }
19508
- const head = this.readable[0];
19509
- if (head === null) {
19510
- this.readable.shift();
19511
- return new Uint8Array(0);
19512
- }
19513
- if (head === void 0) return new Uint8Array(0);
19514
- const chunks2 = [];
19515
- let n = 0;
19516
- while (this.readable.length > 0 && n < max) {
19517
- const next3 = this.readable[0];
19518
- if (next3 === null || next3 === void 0) break;
19519
- const take = next3.subarray(0, max - n);
19520
- chunks2.push(take);
19521
- n += take.length;
19522
- if (take.length < next3.length) this.readable[0] = next3.subarray(take.length);
19523
- else this.readable.shift();
19524
- if (this.canonical) break;
19525
- }
19526
- const out = new Uint8Array(n);
19527
- let at = 0;
19528
- for (const chunk2 of chunks2) {
19529
- out.set(chunk2, at);
19530
- at += chunk2.length;
19531
- }
19532
- return out;
19533
- }
19534
- changed(signal) {
19535
- if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
19536
- return new Promise((resolve2, reject) => {
19537
- const onAbort = () => {
19538
- this.waiters = this.waiters.filter((w) => w !== waiter);
19539
- reject(new KernelError("EINTR"));
19540
- };
19541
- const waiter = () => {
19542
- signal?.removeEventListener("abort", onAbort);
19543
- resolve2();
19544
- };
19545
- this.waiters.push(waiter);
19546
- signal?.addEventListener("abort", onAbort, { once: true });
19547
- });
19548
- }
19549
- wake() {
19550
- const waiters = this.waiters;
19551
- this.waiters = [];
19552
- for (const resolve2 of waiters) resolve2();
19553
- }
19554
- };
19555
-
19556
20549
  // src/kernel/pty.ts
19557
20550
  var PtyPair = class {
19558
20551
  slave;
@@ -19745,6 +20738,7 @@ function ptySyscall(req, ctx) {
19745
20738
  return done3;
19746
20739
  case "pty-ctty": {
19747
20740
  const tty = terminalOf(ctx, req.fd);
20741
+ if (ctx.fds.get(req.fd).file.device) throw new KernelError("EPERM");
19748
20742
  const leader = ctx.jobs?.getsid(ctx.pid, 0) === ctx.pid;
19749
20743
  if (leader && ctx.jobs?.controllingTerminal(ctx.pid) === tty) return done3;
19750
20744
  if (!ctx.jobs?.acquireTerminal(ctx.pid, tty)) throw new KernelError("EPERM");
@@ -20526,10 +21520,10 @@ function basicOf(value2) {
20526
21520
  function placeholdersIn(text) {
20527
21521
  return [...new Set(text.match(ANY_PLACEHOLDER) ?? [])];
20528
21522
  }
20529
- function scan(lines2) {
21523
+ function scan(lines3) {
20530
21524
  const found = [];
20531
- for (let i = 1; i < lines2.length; i++) {
20532
- const m = HEADER_LINE.exec(lines2[i]);
21525
+ for (let i = 1; i < lines3.length; i++) {
21526
+ const m = HEADER_LINE.exec(lines3[i]);
20533
21527
  if (!m) return void 0;
20534
21528
  const value2 = m[2];
20535
21529
  const basic = basicOf(value2);
@@ -20552,10 +21546,10 @@ function usable2(values) {
20552
21546
  }
20553
21547
  async function substituteHead(head, ask) {
20554
21548
  if (!head.endsWith("\r\n\r\n")) return void 0;
20555
- const lines2 = head.slice(0, -4).split("\r\n");
20556
- if (lines2.some((l) => l.includes("\n") || l.includes("\r"))) return void 0;
20557
- const request = REQUEST_LINE2.exec(lines2[0]);
20558
- 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;
20559
21553
  if (!request || !found || found.length !== 1) return void 0;
20560
21554
  const at = found[0];
20561
21555
  if (at.placeholders.length > SESSION_PLACEHOLDERS) return void 0;
@@ -20567,8 +21561,8 @@ async function substituteHead(head, ask) {
20567
21561
  );
20568
21562
  if (Object.keys(values).length === 0) return void 0;
20569
21563
  const value2 = at.basic ? `${at.basic.scheme}${btoa(replaced(at.basic.decoded, values))}` : replaced(at.value, values);
20570
- const out = [...lines2];
20571
- out[at.line] = `${lines2[at.line].split(":")[0]}: ${value2}`;
21564
+ const out = [...lines3];
21565
+ out[at.line] = `${lines3[at.line].split(":")[0]}: ${value2}`;
20572
21566
  return { head: `${out.join("\r\n")}\r
20573
21567
  \r
20574
21568
  `, values };
@@ -20891,9 +21885,9 @@ function serverHeader(header5, server) {
20891
21885
  return server.plus && server.binding ? END_POINT : "n,,";
20892
21886
  }
20893
21887
  function rewritten(first, header5) {
20894
- const parsed2 = clientFirstOf(first.data);
20895
- if (!parsed2 || parsed2.header === header5) return first;
20896
- 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}` };
20897
21891
  }
20898
21892
  var PgClient = class {
20899
21893
  clientFirst;
@@ -20913,10 +21907,10 @@ var PgClient = class {
20913
21907
  }
20914
21908
  if (server.sasl && this.clientFirst === void 0) {
20915
21909
  const first = initial(message3);
20916
- const parsed2 = first && clientFirstOf(first.data);
20917
- if (!first || !parsed2) return { bytes: message3, done: true };
21910
+ const parsed3 = first && clientFirstOf(first.data);
21911
+ if (!first || !parsed3) return { bytes: message3, done: true };
20918
21912
  this.clientFirst = first.data;
20919
- this.header = serverHeader(parsed2.header, server);
21913
+ this.header = serverHeader(parsed3.header, server);
20920
21914
  const sent = rewritten(first, this.header);
20921
21915
  if (sent === first) return { bytes: message3, done: false };
20922
21916
  return {
@@ -21081,7 +22075,7 @@ function headEnd2(bytes) {
21081
22075
  }
21082
22076
  return -1;
21083
22077
  }
21084
- function modeOf2(start2) {
22078
+ function modeOf3(start2) {
21085
22079
  const sniffs = [
21086
22080
  ["http1", sniffHttp1(start2)],
21087
22081
  ["smtp", sniffSmtp(start2)],
@@ -21190,7 +22184,7 @@ var SessionWriter = class {
21190
22184
  joined5.set(bytes, this.buffer.length);
21191
22185
  this.buffer.fill(0);
21192
22186
  this.buffer = joined5;
21193
- 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";
21194
22188
  if (this.mode === "pg" && !this.sawPg) {
21195
22189
  this.sawPg = true;
21196
22190
  if (!this.s.verified) void this.proof("pg");
@@ -21677,12 +22671,12 @@ function secure(prepared, seal) {
21677
22671
  prepared.own?.release();
21678
22672
  }
21679
22673
  };
21680
- const abandon = () => {
22674
+ const abandon2 = () => {
21681
22675
  if (ran) return;
21682
22676
  prepared.options.session.free();
21683
22677
  prepared.own?.release();
21684
22678
  };
21685
- return { make, abandon };
22679
+ return { make, abandon: abandon2 };
21686
22680
  }
21687
22681
  function supportOf(net) {
21688
22682
  const support = net.tlsClient;
@@ -21699,19 +22693,19 @@ async function uplinkAddress(proc, host) {
21699
22693
  async function connectSecure(proc, socket, host, port, req) {
21700
22694
  const servername = serverName(req.servername || host);
21701
22695
  if (isLoopbackHost(servername) || isLoopbackHost(host)) throw new KernelError("ENOTSUP");
21702
- const address = await uplinkAddress(proc, host);
21703
- const route2 = proc.net.uplinkFor(address);
22696
+ const address2 = await uplinkAddress(proc, host);
22697
+ const route2 = proc.net.uplinkFor(address2);
21704
22698
  if (!route2)
21705
22699
  throw new KernelError("ENOTSUP", "TLS in the kernel needs a destination the uplink routes");
21706
22700
  const support = supportOf(proc.net);
21707
22701
  let info;
21708
- const { make, abandon } = secure(await prepare2(support, req, servername), { proc, port });
22702
+ const { make, abandon: abandon2 } = secure(await prepare2(support, req, servername), { proc, port });
21709
22703
  try {
21710
- await socket.connectSecure({ family: "inet", host: address, port }, route2, async (conn) => {
22704
+ await socket.connectSecure({ family: "inet", host: address2, port }, route2, async (conn) => {
21711
22705
  info = await conn.upgrade(make);
21712
22706
  });
21713
22707
  } finally {
21714
- abandon();
22708
+ abandon2();
21715
22709
  }
21716
22710
  return sessionOf(info, servername);
21717
22711
  }
@@ -21744,14 +22738,14 @@ async function upgradeSecure(proc, socket, req) {
21744
22738
  const host = req.servername || (peer?.family === "inet" ? peer.host : "");
21745
22739
  const servername = serverName(host);
21746
22740
  const port = peer?.family === "inet" ? peer.port : 0;
21747
- const { make, abandon } = secure(await prepare2(supportOf(proc.net), req, servername), {
22741
+ const { make, abandon: abandon2 } = secure(await prepare2(supportOf(proc.net), req, servername), {
21748
22742
  proc,
21749
22743
  port
21750
22744
  });
21751
22745
  try {
21752
22746
  return sessionOf(await socket.upgradeSecure(make), servername);
21753
22747
  } finally {
21754
- abandon();
22748
+ abandon2();
21755
22749
  }
21756
22750
  }
21757
22751
 
@@ -23675,6 +24669,10 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
23675
24669
  "fd-open-clipboard",
23676
24670
  "fd-clipboard-abort",
23677
24671
  "fd-open-embedder",
24672
+ "fd-dev-nodes",
24673
+ "fd-open-dev",
24674
+ "fd-dev-ioctl",
24675
+ "fd-dev-meta",
23678
24676
  "tty-get",
23679
24677
  "tty-set",
23680
24678
  "tty-winsz",
@@ -24051,7 +25049,7 @@ var WasmProcess = class {
24051
25049
  async dispatch(req, cancelled) {
24052
25050
  try {
24053
25051
  if (isFdSyscall(req)) return await this.fdSyscall(req, cancelled);
24054
- if (isTtySyscall(req)) return this.ttySyscall(req);
25052
+ if (isTtySyscall(req)) return await this.ttySyscall(req, cancelled);
24055
25053
  if (isJobSyscall(req)) return this.jobSyscall(req);
24056
25054
  if (isSocketSyscall(req)) return await this.socketSyscall(req, cancelled);
24057
25055
  if (isSemSyscall(req)) return await this.semSyscall(req, cancelled);
@@ -24115,7 +25113,7 @@ var WasmProcess = class {
24115
25113
  return owner;
24116
25114
  }
24117
25115
  pathKeys = /* @__PURE__ */ new Map();
24118
- async lockPath(target, want) {
25116
+ async lockPath(target, want2) {
24119
25117
  if (target.fd !== void 0) {
24120
25118
  const st = await this.fds.get(target.fd).file.stat?.();
24121
25119
  if (!st) throw new KernelError("EBADF");
@@ -24125,9 +25123,9 @@ var WasmProcess = class {
24125
25123
  if (!target.path.startsWith("/")) throw new KernelError("EINVAL");
24126
25124
  const path = target.path;
24127
25125
  const g = this.guarded();
24128
- const checked = `${want}:${path}`;
24129
- if (g && want !== 0 && !this.lockChecked.has(checked)) {
24130
- 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);
24131
25129
  this.lockChecked.add(checked);
24132
25130
  }
24133
25131
  return path;
@@ -24140,8 +25138,8 @@ var WasmProcess = class {
24140
25138
  const known = file ? fileKeys.get(file) : this.pathKeys.get(target.path ?? "");
24141
25139
  if (kind === "close" && !file && target.path !== void 0) this.pathKeys.delete(target.path);
24142
25140
  if (access2 && !access2.stat) return objectKey(access2);
24143
- const want = kind === "read" ? R_OK : kind === "write" ? W_OK : 0;
24144
- 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);
24145
25143
  if (known !== void 0) return known;
24146
25144
  const st = await this.options.fs.stat?.(path).catch((err) => {
24147
25145
  if (!file) throw err;
@@ -24227,7 +25225,7 @@ var WasmProcess = class {
24227
25225
  const file = this.fds.get(req.fd).file;
24228
25226
  const read = req.peek ? file.peek : file.read;
24229
25227
  if (!read) throw new KernelError(req.peek && file.read ? "EOPNOTSUPP" : "EBADF");
24230
- if (file.tty) this.checkForeground(file.tty);
25228
+ if (file.tty && !file.device) this.checkForeground(file.tty);
24231
25229
  if (file.clipboard) return this.readClipboard(file.clipboard, req, cancelled);
24232
25230
  if (file.embedder) return this.readEmbedder(file, req, cancelled);
24233
25231
  if (file instanceof EventFd && req.max < EventFd.SIZE) throw new KernelError("EINVAL");
@@ -24324,6 +25322,46 @@ var WasmProcess = class {
24324
25322
  throw err;
24325
25323
  });
24326
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
+ }
24327
25365
  async openEmbedder(flags2) {
24328
25366
  const broker = this.options.embedder;
24329
25367
  if (!broker) throw new KernelError("ENOENT");
@@ -24411,11 +25449,11 @@ var WasmProcess = class {
24411
25449
  } else if (req.create) {
24412
25450
  await nodes.onEntry(path, async () => {
24413
25451
  const marks = this.options.owners || plain3 !== void 0;
24414
- const fresh3 = marks && !await present3(fs, path, true);
25452
+ const fresh4 = marks && !await present3(fs, path, true);
24415
25453
  this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path), fs));
24416
25454
  await this.fds.get(fd).file.stat?.();
24417
- if (fresh3 && plain3 !== void 0) await fs.chmod?.(path, plain3);
24418
- else if (fresh3) {
25455
+ if (fresh4 && plain3 !== void 0) await fs.chmod?.(path, plain3);
25456
+ else if (fresh4) {
24419
25457
  await fs.chown?.(path, { ...this.newOwner(req.mode), kind: "new" });
24420
25458
  }
24421
25459
  });
@@ -24514,8 +25552,8 @@ var WasmProcess = class {
24514
25552
  if (req.exclusive) return inParent(g, "open", req.path);
24515
25553
  if (req.created && guard2.created?.take(req.path)) return;
24516
25554
  const access2 = req.flags & WRITE_ACCESS;
24517
- const want = (access2 === 1 ? 0 : R_OK) | (access2 !== 0 || req.truncate ? W_OK : 0);
24518
- 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);
24519
25557
  }
24520
25558
  openTty(name2) {
24521
25559
  const tty = name2 === void 0 ? this.controllingTerminal() : this.options.jobs?.terminalNamed(name2);
@@ -24579,7 +25617,7 @@ var WasmProcess = class {
24579
25617
  }
24580
25618
  terminalByName(name2) {
24581
25619
  const n = ptsNumber(name2);
24582
- 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;
24583
25621
  if (!tty) throw new KernelError("ENOENT");
24584
25622
  return tty;
24585
25623
  }
@@ -24701,6 +25739,16 @@ var WasmProcess = class {
24701
25739
  return { ok: true, kind: "json", json: await this.openClipboard(req.flags) };
24702
25740
  case "fd-open-embedder":
24703
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
+ }
24704
25752
  case "fd-clipboard-abort": {
24705
25753
  const file = this.fds.get(req.fd);
24706
25754
  file.file.clipboard?.abort();
@@ -24745,6 +25793,11 @@ var WasmProcess = class {
24745
25793
  return { ok: true, kind: "void" };
24746
25794
  }
24747
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
+ }
24748
25801
  if (!file.pread || !file.pwrite || !file.resize || !file.stat) throw new KernelError("ESPIPE");
24749
25802
  switch (req.op) {
24750
25803
  case "fd-pread":
@@ -24914,14 +25967,19 @@ var WasmProcess = class {
24914
25967
  ...file.fifo !== void 0 ? { fifo: file.fifo } : {}
24915
25968
  };
24916
25969
  }
24917
- ttySyscall(req) {
25970
+ async ttySyscall(req, cancelled) {
24918
25971
  const tty = this.tty(req.fd);
24919
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");
24920
25975
  switch (req.op) {
24921
25976
  case "tty-get":
24922
25977
  return { ok: true, kind: "json", json: tty.tcgets() };
24923
25978
  case "tty-set":
24924
- 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);
24925
25983
  return { ok: true, kind: "void" };
24926
25984
  case "tty-winsz": {
24927
25985
  const size = tty.winsize();
@@ -25802,6 +26860,7 @@ function processOptions(opts, header5, hooks, created4) {
25802
26860
  ...opts.outputFilter ? { outputFilter: opts.outputFilter } : {},
25803
26861
  ...opts.clipboard ? { clipboard: opts.clipboard, ...clipboardName(opts) } : {},
25804
26862
  ...embedderOptions(opts),
26863
+ devices: opts.devices,
25805
26864
  ...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
25806
26865
  ...opts.source ? { source: opts.source } : {},
25807
26866
  noLinks: opts.noLinks,
@@ -26556,15 +27615,15 @@ var RemoteWatch = class {
26556
27615
  this.channel.postMessage({ ack: seq2, from: this.tab, to: from });
26557
27616
  }
26558
27617
  hold(add, drop, wait2) {
26559
- const fresh3 = add.filter(
27618
+ const fresh4 = add.filter(
26560
27619
  ([path, fid]) => !this.myHolds.has(path) || this.myHolds.get(path) !== fid
26561
27620
  );
26562
27621
  const gone = drop.filter((path) => this.myHolds.has(path));
26563
27622
  for (const path of gone) this.myHolds.delete(path);
26564
- for (const [path, fid] of fresh3) this.myHolds.set(path, fid);
26565
- 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)
26566
27625
  return Promise.resolve();
26567
- const seq2 = this.sendHolds(fresh3, gone);
27626
+ const seq2 = this.sendHolds(fresh4, gone);
26568
27627
  if (!wait2) return Promise.resolve();
26569
27628
  const heard = new Set(
26570
27629
  [...this.peers].filter((peer) => this.holders.has(peer) && !this.silent.has(peer))
@@ -26988,14 +28047,14 @@ var LockService = class {
26988
28047
  );
26989
28048
  });
26990
28049
  }
26991
- async holder(key, owner, want) {
28050
+ async holder(key, owner, want2) {
26992
28051
  return await this.ask({
26993
28052
  op: "holder",
26994
28053
  key,
26995
28054
  owner,
26996
- kind: want.kind,
26997
- start: want.start,
26998
- end: want.end
28055
+ kind: want2.kind,
28056
+ start: want2.start,
28057
+ end: want2.end
26999
28058
  }) ?? void 0;
27000
28059
  }
27001
28060
  async set(key, owner, kind, start2, end) {
@@ -27274,9 +28333,9 @@ function local(name2) {
27274
28333
  function literal2(name2) {
27275
28334
  return ipv4Bytes(name2) !== void 0 || ipv6Groups(name2) !== void 0;
27276
28335
  }
27277
- function addressData(address, type) {
27278
- if (type === TYPE_A) return ipv4Bytes(address);
27279
- 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;
27280
28339
  return groups?.flatMap((g) => [g >> 8, g & 255]);
27281
28340
  }
27282
28341
  var DnsStub = class {
@@ -27289,10 +28348,10 @@ var DnsStub = class {
27289
28348
  this.timeoutMs = options.timeoutMs ?? DNS_FORWARD_TIMEOUT_MS;
27290
28349
  }
27291
28350
  async answer(query, udp) {
27292
- const parsed2 = parseQuery(query);
27293
- if (!parsed2) return void 0;
27294
- if (!parsed2.ok) return errorReply2(parsed2.id, parsed2.flags, parsed2.rcode);
27295
- 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;
27296
28355
  const reply = await this.reply(query, q).catch(
27297
28356
  () => answerReply(query, q, SERVFAIL, [], false)
27298
28357
  );
@@ -27301,8 +28360,8 @@ var DnsStub = class {
27301
28360
  async reply(query, q) {
27302
28361
  if (q.klass !== CLASS_IN || literal2(q.name)) return this.forward(query, q);
27303
28362
  if (q.type === TYPE_PTR) {
27304
- const address = reverseAddress(q.name);
27305
- 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;
27306
28365
  if (name2 !== void 0)
27307
28366
  return answerReply(query, q, NOERROR, [record2(TYPE_PTR, encodeName(name2))], true);
27308
28367
  return this.forward(query, q);
@@ -27332,8 +28391,8 @@ var DnsStub = class {
27332
28391
  known(name2) {
27333
28392
  return this.resolver.known(name2).catch(() => false);
27334
28393
  }
27335
- reverse(address) {
27336
- return this.resolver.reverse(address).catch(() => void 0);
28394
+ reverse(address2) {
28395
+ return this.resolver.reverse(address2).catch(() => void 0);
27337
28396
  }
27338
28397
  async forward(query, q) {
27339
28398
  if (!this.forwarder) return answerReply(query, q, SERVFAIL, [], false);
@@ -27512,7 +28571,7 @@ function honors(traits, redirect) {
27512
28571
  const raw2 = traits.manualRedirects === true && (hops === void 0 || hops === false);
27513
28572
  return redirect === "manual" && raw2;
27514
28573
  }
27515
- function refused3(message3) {
28574
+ function refused4(message3) {
27516
28575
  return new TransportError(message3, 502, void 0, "transport");
27517
28576
  }
27518
28577
  var RemoteTransport = class {
@@ -27603,7 +28662,7 @@ var RemoteTransport = class {
27603
28662
  const { signal, redirect } = request;
27604
28663
  signal.throwIfAborted();
27605
28664
  if (unfollowed2(redirect) && !honors(this.traits, redirect)) {
27606
- throw refused3(`this transport cannot honor redirect: ${redirect}`);
28665
+ throw refused4(`this transport cannot honor redirect: ${redirect}`);
27607
28666
  }
27608
28667
  let finished = false;
27609
28668
  const abort = () => {
@@ -27654,7 +28713,7 @@ var RemoteTransport = class {
27654
28713
  };
27655
28714
  if (unfollowed2(redirect) && (redirected2 || redirects?.length)) {
27656
28715
  await cancel();
27657
- throw refused3(`this transport followed a redirect despite redirect: ${redirect}`);
28716
+ throw refused4(`this transport followed a redirect despite redirect: ${redirect}`);
27658
28717
  }
27659
28718
  return {
27660
28719
  ...url !== void 0 ? { url } : {},
@@ -28174,6 +29233,7 @@ var SETUP_OPS = /* @__PURE__ */ new Set([
28174
29233
  "proc-list",
28175
29234
  "proc-identity",
28176
29235
  "fd-info",
29236
+ "fd-dev-nodes",
28177
29237
  "sig-mask",
28178
29238
  "sig-inherited",
28179
29239
  "proc-setpgid",
@@ -28270,8 +29330,8 @@ var WasmMemories = class {
28270
29330
  const learned = this.learned;
28271
29331
  if (!learned) return this.base;
28272
29332
  const steps = Math.floor((this.now() - learned.at) / DECAY_MS);
28273
- const raised = learned.ceiling + steps * DECAY_STEP;
28274
- 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;
28275
29335
  this.learned = void 0;
28276
29336
  return this.base;
28277
29337
  }
@@ -28414,9 +29474,9 @@ function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
28414
29474
  let open = false;
28415
29475
  const reply = (message3, transfer = []) => port.postMessage(message3, transfer);
28416
29476
  async function hello(message3) {
28417
- const refused4 = driverVersionError(message3.protocol);
28418
- if (refused4) {
28419
- 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" } });
28420
29480
  return;
28421
29481
  }
28422
29482
  try {
@@ -28469,8 +29529,8 @@ function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
28469
29529
  function noticeOf(data) {
28470
29530
  if (Array.isArray(data)) return { paths: data.filter((p) => typeof p === "string"), trees: [] };
28471
29531
  const notice = data ?? {};
28472
- const strings2 = (list) => Array.isArray(list) ? list.filter((p) => typeof p === "string") : [];
28473
- 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 };
28474
29534
  }
28475
29535
 
28476
29536
  // src/fs/opfs.ts
@@ -28964,7 +30024,7 @@ var OpfsFs = class {
28964
30024
  const parent = entries.at(-1);
28965
30025
  const setgid = ((parent?.mode ?? 0) & S_ISGID4) !== 0;
28966
30026
  const parentGid = (ownOf(parent) ?? inherit(entries.slice(0, -1))).gid;
28967
- const want = { uid: owner.uid, gid: setgid ? parentGid : owner.gid };
30027
+ const want2 = { uid: owner.uid, gid: setgid ? parentGid : owner.gid };
28968
30028
  let mode = owner.mode === void 0 ? void 0 : owner.mode & PERM_MASK;
28969
30029
  if (directory && setgid) mode = (mode ?? 493) | S_ISGID4;
28970
30030
  const fields = {};
@@ -28972,7 +30032,7 @@ var OpfsFs = class {
28972
30032
  fields.mode = mode;
28973
30033
  }
28974
30034
  if (self) fields.heir = void 0;
28975
- if (ownOf(self) || !sameOwner(want, inherit(entries))) Object.assign(fields, want);
30035
+ if (ownOf(self) || !sameOwner(want2, inherit(entries))) Object.assign(fields, want2);
28976
30036
  return Object.keys(fields).length > 0 ? fields : void 0;
28977
30037
  }
28978
30038
  async stampNew(path, owner, directory) {
@@ -29209,9 +30269,9 @@ var OpfsFs = class {
29209
30269
  this.lookups++;
29210
30270
  if (split2(path).length === 0) return this.root;
29211
30271
  const cached = this.files.get(path);
29212
- const fresh3 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
29213
- if (cached && !fresh3) this.files.delete(path);
29214
- 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) {
29215
30275
  if (this.glanced(cached)) return this.remember(path, cached);
29216
30276
  try {
29217
30277
  await this.glance(cached);
@@ -29429,8 +30489,8 @@ var OpfsFs = class {
29429
30489
  glanced(handle) {
29430
30490
  if (this.exact > 0) return void 0;
29431
30491
  const seen = this.glances.get(handle);
29432
- const fresh3 = seen && seen.changes === this.changes && this.recent(seen, GLANCE_LOOKUPS);
29433
- 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;
29434
30494
  }
29435
30495
  async glance(handle) {
29436
30496
  const at = performance.now();
@@ -29632,13 +30692,13 @@ var OpfsFs = class {
29632
30692
  async keptFor(path) {
29633
30693
  const at = normalizePath(path);
29634
30694
  const cached = this.files.get(at);
29635
- const fresh3 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
29636
- 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);
29637
30697
  if (!existed) return [void 0, target, false];
29638
30698
  this.refuse(target);
29639
30699
  const [entry] = await this.meta.get([target]);
29640
30700
  if (entry?.fifo || entry?.socket) throw fsError("ENXIO", path);
29641
- const kept = fresh3 ? cached : this.files.get(target);
30701
+ const kept = fresh4 ? cached : this.files.get(target);
29642
30702
  const usable3 = kept && performance.now() - (this.found.get(kept) ?? -CHECK_MS) < CHECK_MS;
29643
30703
  return [usable3 ? kept : void 0, target, true];
29644
30704
  }
@@ -31192,7 +32252,7 @@ function unescape(field4) {
31192
32252
  );
31193
32253
  }
31194
32254
  function parseFstab(text) {
31195
- const lines2 = [];
32255
+ const lines3 = [];
31196
32256
  text.split("\n").forEach((raw2, index) => {
31197
32257
  const content = raw2.replace(/#.*$/, "").trim();
31198
32258
  if (!content) return;
@@ -31201,12 +32261,12 @@ function parseFstab(text) {
31201
32261
  if (options.split(",").includes("noauto")) return;
31202
32262
  const spec = { source, target, type, options: {} };
31203
32263
  try {
31204
- lines2.push({ line: index + 1, spec: { ...spec, options: parseMountOptions(options) } });
32264
+ lines3.push({ line: index + 1, spec: { ...spec, options: parseMountOptions(options) } });
31205
32265
  } catch (err) {
31206
- lines2.push({ line: index + 1, spec, invalid: err });
32266
+ lines3.push({ line: index + 1, spec, invalid: err });
31207
32267
  }
31208
32268
  });
31209
- return lines2;
32269
+ return lines3;
31210
32270
  }
31211
32271
  function codeOf6(err) {
31212
32272
  const code = err?.code;
@@ -31224,20 +32284,20 @@ function wait(ms, signal) {
31224
32284
  signal.addEventListener("abort", done4, { once: true });
31225
32285
  });
31226
32286
  }
31227
- async function mountFstab(lines2, mount, signal, retries = FSTAB_RETRIES, discard = () => {
32287
+ async function mountFstab(lines3, mount, signal, retries = FSTAB_RETRIES, discard = () => {
31228
32288
  }, settled = () => {
31229
32289
  }) {
31230
32290
  return Promise.all(
31231
- lines2.map(async (each) => {
32291
+ lines3.map(async (each) => {
31232
32292
  const result = await mountLine(each, mount, signal, retries, discard);
31233
32293
  settled(each, result);
31234
32294
  return result;
31235
32295
  })
31236
32296
  );
31237
32297
  }
31238
- async function mountLine({ line, spec, invalid }, mount, signal, retries, discard) {
31239
- let failed5 = invalid;
31240
- 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++) {
31241
32301
  try {
31242
32302
  const entry = await mount(spec);
31243
32303
  if (!signal.aborted) return { line, target: spec.target, entry };
@@ -31405,14 +32465,14 @@ var RESTORE_AGAIN_MS = 3e4;
31405
32465
  function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
31406
32466
  const handles = /* @__PURE__ */ new Map();
31407
32467
  let nextFh = 0;
31408
- const fresh3 = (remote) => {
32468
+ const fresh4 = (remote) => {
31409
32469
  if (remote.client.token === remote.token && !remote.client.gone) return remote;
31410
32470
  throw fsError2("ESTALE", "the folder was granted again, so this open file is gone");
31411
32471
  };
31412
32472
  const live2 = (fh) => {
31413
32473
  const remote = handles.get(fh);
31414
32474
  if (!remote) throw fsError2("EBADF", `handle ${fh}`);
31415
- return fresh3(remote);
32475
+ return fresh4(remote);
31416
32476
  };
31417
32477
  const call = (op, body = {}) => current().call(op, body);
31418
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);
@@ -31494,7 +32554,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
31494
32554
  if (!remote) throw fsError2("EBADF", `handle ${fh}`);
31495
32555
  handles.delete(fh);
31496
32556
  try {
31497
- await fresh3(remote).client.call("release", { fh: remote.fh }, true);
32557
+ await fresh4(remote).client.call("release", { fh: remote.fh }, true);
31498
32558
  } finally {
31499
32559
  if (remote.restore) await settle(current(), remote.restore);
31500
32560
  }
@@ -31544,7 +32604,7 @@ async function resize(h, path, size) {
31544
32604
  await h.release(fh);
31545
32605
  }
31546
32606
  }
31547
- function sleep(ms, signal) {
32607
+ function sleep2(ms, signal) {
31548
32608
  return new Promise((resolve2) => {
31549
32609
  const done4 = () => {
31550
32610
  clearTimeout(timer);
@@ -31572,22 +32632,22 @@ function keepWatching(loop, signal) {
31572
32632
  const stop = () => session2.abort();
31573
32633
  signal.addEventListener("abort", stop);
31574
32634
  let timer;
31575
- const quiet3 = () => {
32635
+ const quiet5 = () => {
31576
32636
  clearTimeout(timer);
31577
32637
  timer = setTimeout(stop, silenceMs);
31578
32638
  };
31579
32639
  let opened2 = false;
31580
32640
  try {
31581
32641
  const client = await loop.connect();
31582
- quiet3();
32642
+ quiet5();
31583
32643
  await client.watch(
31584
32644
  session2.signal,
31585
32645
  (event) => {
31586
- quiet3();
32646
+ quiet5();
31587
32647
  if (!event.ping) loop.event(event);
31588
32648
  },
31589
32649
  () => {
31590
- quiet3();
32650
+ quiet5();
31591
32651
  opened2 = true;
31592
32652
  up = true;
31593
32653
  loop.up(client);
@@ -31616,7 +32676,7 @@ function keepWatching(loop, signal) {
31616
32676
  loop.down();
31617
32677
  }
31618
32678
  ready2.resolve();
31619
- await sleep(wait2, signal);
32679
+ await sleep2(wait2, signal);
31620
32680
  wait2 = Math.min(wait2 * 2, most);
31621
32681
  }
31622
32682
  })();
@@ -31858,7 +32918,7 @@ var PortTransport = class {
31858
32918
  }).finally(() => this.fail("the folder's watch ended"));
31859
32919
  }
31860
32920
  };
31861
- var errorOf2 = (err) => ({
32921
+ var errorOf3 = (err) => ({
31862
32922
  code: typeof codeOf7(err) === "string" ? codeOf7(err) : "EIO",
31863
32923
  message: err?.message ?? String(err)
31864
32924
  });
@@ -31908,7 +32968,7 @@ function serveHostfsPort(port, transport) {
31908
32968
  );
31909
32969
  send2({ id: id2, end: true });
31910
32970
  } catch (err) {
31911
- send2({ id: id2, error: errorOf2(err) });
32971
+ send2({ id: id2, error: errorOf3(err) });
31912
32972
  } finally {
31913
32973
  watches.delete(id2);
31914
32974
  }
@@ -31936,7 +32996,7 @@ function serveHostfsPort(port, transport) {
31936
32996
  if (op !== "call") throw fsError2("EINVAL", `unknown hostfs port op ${op}`);
31937
32997
  send2({ id: id2, result: await answer2(data.name, data.body) });
31938
32998
  } catch (err) {
31939
- send2({ id: id2, error: errorOf2(err) });
32999
+ send2({ id: id2, error: errorOf3(err) });
31940
33000
  }
31941
33001
  };
31942
33002
  const stop = () => {
@@ -33158,6 +34218,7 @@ var Launcher = class {
33158
34218
  scrubs = /* @__PURE__ */ new Map();
33159
34219
  clipboard;
33160
34220
  embedder;
34221
+ devices;
33161
34222
  rootFs;
33162
34223
  fileWatches;
33163
34224
  nextPid = 1e3;
@@ -33182,6 +34243,7 @@ var Launcher = class {
33182
34243
  );
33183
34244
  this.embedder = this.embedderBroker(options.embedder);
33184
34245
  this.embedder.denied = options.onDenial;
34246
+ this.devices = this.deviceNodes(options.devices);
33185
34247
  this.sealed = new SealedBroker(
33186
34248
  options.sealed,
33187
34249
  this.usersMode,
@@ -33397,16 +34459,16 @@ var Launcher = class {
33397
34459
  if (!found) {
33398
34460
  found = this.swaps.settled().then(() => this.trustedAppended()).then(async (appended2) => {
33399
34461
  const tiers = await this.tiers(scope, appended2);
33400
- const refused4 = [];
34462
+ const refused5 = [];
33401
34463
  const commands = await scanCommands(
33402
34464
  this.scanned,
33403
34465
  tiers,
33404
34466
  tiers.length - appended2.length,
33405
34467
  void 0,
33406
- refused4,
34468
+ refused5,
33407
34469
  await this.globalRoots(scope)
33408
34470
  );
33409
- this.refusedOf.set(commands, refused4.map(refusing));
34471
+ this.refusedOf.set(commands, refused5.map(refusing));
33410
34472
  return commands;
33411
34473
  }).then(
33412
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)
@@ -33613,14 +34675,14 @@ var Launcher = class {
33613
34675
  return await this.commandAt(path, scope) ?? await this.refusedAt(path, name2, scope);
33614
34676
  }
33615
34677
  async refusedAt(path, name2, scope) {
33616
- const refused4 = this.refusedOf.get(await this.commands(scope)) ?? [];
33617
- 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;
33618
34680
  const st = await this.base.stat(path).catch(() => void 0);
33619
34681
  if (!st?.isFile) return void 0;
33620
34682
  const real = await physicalPathAsync(this.base, path).catch(() => path);
33621
34683
  const same2 = async (own) => own === path || await physicalPathAsync(this.base, own).catch(() => own) === real;
33622
34684
  const owners = [];
33623
- for (const c of refused4) {
34685
+ for (const c of refused5) {
33624
34686
  const origin = c.origin;
33625
34687
  for (const own of [origin.glue, origin.wasm, ...origin.variants ?? []]) {
33626
34688
  if (!await same2(own)) continue;
@@ -33630,11 +34692,11 @@ var Launcher = class {
33630
34692
  }
33631
34693
  const stem = name2.replace(/\.wasm$/, "");
33632
34694
  const named = owners.find((c) => c.name === name2) ?? owners.find((c) => c.name === stem);
33633
- return named ?? owners[0] ?? await this.refusedCopy(real, name2, refused4);
34695
+ return named ?? owners[0] ?? await this.refusedCopy(real, name2, refused5);
33634
34696
  }
33635
- async refusedCopy(real, name2, refused4) {
34697
+ async refusedCopy(real, name2, refused5) {
33636
34698
  const dir = real.slice(0, real.lastIndexOf("/"));
33637
- for (const c of refused4) {
34699
+ for (const c of refused5) {
33638
34700
  const origin = c.origin;
33639
34701
  if (origin.abi !== "emscripten" || origin.script || c.name !== name2) continue;
33640
34702
  const own = await physicalPathAsync(this.base, origin.glue).catch(() => origin.glue);
@@ -33801,8 +34863,8 @@ var Launcher = class {
33801
34863
  const via = planned?.via ?? [];
33802
34864
  for (const [i, file] of via.entries()) {
33803
34865
  if (!byPath(file)) continue;
33804
- const want = i < via.length - 1 ? R_OK | X_OK : X_OK;
33805
- 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);
33806
34868
  }
33807
34869
  for (const bin of planned?.bins ?? []) await need(g, "exec", bin, R_OK | X_OK);
33808
34870
  if (planned?.target.copy) await need(g, "exec", planned.target.copy, R_OK | X_OK);
@@ -33842,12 +34904,12 @@ var Launcher = class {
33842
34904
  return void 0;
33843
34905
  }
33844
34906
  async planOne(req, { ppid, cred, scope }, catalogOnly = false) {
33845
- const refused4 = (err) => {
34907
+ const refused5 = (err) => {
33846
34908
  throw codeOf8(err) === "EACCES" ? new SpawnError("EACCES") : err;
33847
34909
  };
33848
- 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);
33849
34911
  const planned = await this.plan(file, req.argv, req.cwd, scope, req.argv0, catalogOnly);
33850
- await this.mayExec(cred, { ...req, file, pid: ppid }, planned).catch(refused4);
34912
+ await this.mayExec(cred, { ...req, file, pid: ppid }, planned).catch(refused5);
33851
34913
  if (planned) return planned;
33852
34914
  throw new SpawnError(catalogOnly ? "ENOENT" : await this.unrunnable(file, req.cwd, scope));
33853
34915
  }
@@ -34483,6 +35545,7 @@ var Launcher = class {
34483
35545
  ...scrubs ? { outputFilter: (file) => scrubbedAll(file, scrubs) } : {},
34484
35546
  clipboard: this.clipboard,
34485
35547
  embedder: { broker: this.embedder, session: () => this.sessionOf(pid) },
35548
+ devices: this.devices,
34486
35549
  ...this.dac ? { dac: this.dac } : {},
34487
35550
  program: req.program,
34488
35551
  changes: this.changes.sab,
@@ -34666,8 +35729,8 @@ var Launcher = class {
34666
35729
  this.sampledAt = Math.max(this.sampledAt, now);
34667
35730
  this.load = this.load.map((avg, i) => {
34668
35731
  const window = LOAD_WINDOWS_MS[i];
34669
- const fresh3 = 1 - Math.exp(-LOAD_SAMPLE_MS / window);
34670
- 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;
34671
35734
  });
34672
35735
  }
34673
35736
  idleSince;
@@ -35014,7 +36077,7 @@ var Launcher = class {
35014
36077
  forker(ppid, parent) {
35015
36078
  const { exec: _exec, pgid: _pgid, group: _group, pid: _pid, scrub: _scrub, ...image } = parent;
35016
36079
  return async (state, given) => {
35017
- const fresh3 = (fds, quietOom) => this.start({
36080
+ const fresh4 = (fds, quietOom) => this.start({
35018
36081
  ...image,
35019
36082
  quietOom,
35020
36083
  cwd: state.cwd ?? parent.cwd,
@@ -35030,7 +36093,7 @@ var Launcher = class {
35030
36093
  tagBase: this.tagBases.get(ppid),
35031
36094
  links: this.links.get(ppid)
35032
36095
  });
35033
- return childHandle(await this.admitted(parent.program.abi !== "js", given, fresh3));
36096
+ return childHandle(await this.admitted(parent.program.abi !== "js", given, fresh4));
35034
36097
  };
35035
36098
  }
35036
36099
  keySignal(tty, fallback, sig, send2) {
@@ -35071,6 +36134,14 @@ var Launcher = class {
35071
36134
  this.sealed.userOf = async (uid) => (await this.users.lookup(uid))?.name;
35072
36135
  this.sealed.factsOf = (pid) => this.processFacts(pid);
35073
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
+ }
35074
36145
  embedderBroker(hook) {
35075
36146
  const broker = new EmbedderBroker(
35076
36147
  this.usersMode,
@@ -35360,8 +36431,8 @@ var Launcher = class {
35360
36431
  for (const [path, mode] of layout) {
35361
36432
  if (isProtected(this.prefixes, normalizePath(path))) continue;
35362
36433
  const real = await this.realLayoutPath(path);
35363
- const fresh3 = real !== void 0 && !await this.base.hasOwner?.(real);
35364
- 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 });
35365
36436
  }
35366
36437
  }
35367
36438
  async realLayoutPath(given) {
@@ -35495,10 +36566,10 @@ var Launcher = class {
35495
36566
  }
35496
36567
  async mountFstab() {
35497
36568
  const text = await this.base.readFile(FSTAB_PATH).catch(() => "");
35498
- const lines2 = parseFstab(text);
35499
- 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");
35500
36571
  return mountFstab(
35501
- lines2,
36572
+ lines3,
35502
36573
  (spec) => this.mount(spec).catch((err) => {
35503
36574
  this.mounts.note(spec, "pending", err?.message ?? String(err));
35504
36575
  throw err;
@@ -36204,13 +37275,13 @@ function unsendableHeaders(url, names2, hint) {
36204
37275
  function refuseUnsendable(url, out, hint) {
36205
37276
  if (!platformFilters()) return;
36206
37277
  const kept = new Request(PROBE, { headers: out }).headers;
36207
- const refused4 = [];
37278
+ const refused5 = [];
36208
37279
  for (const name2 of new Set(out.keys())) {
36209
37280
  if (kept.has(name2)) continue;
36210
37281
  if (TRANSPORT_OWN.has(name2)) out.delete(name2);
36211
- else refused4.push(name2);
37282
+ else refused5.push(name2);
36212
37283
  }
36213
- if (refused4.length > 0) throw unsendableHeaders(url, refused4, hint);
37284
+ if (refused5.length > 0) throw unsendableHeaders(url, refused5, hint);
36214
37285
  }
36215
37286
  function streamOf(body) {
36216
37287
  const it = body[Symbol.asyncIterator]();
@@ -36522,7 +37593,7 @@ function serveTransport(peer, transport, {
36522
37593
  }
36523
37594
 
36524
37595
  // src/client/protocol.ts
36525
- var PROTOCOL = [1, 17];
37596
+ var PROTOCOL = [1, 18];
36526
37597
  var FS_METHODS = [
36527
37598
  "readFile",
36528
37599
  "writeFile",
@@ -36782,8 +37853,10 @@ function hostRegistries(send2, gone, port) {
36782
37853
  const cdps = cdpRegistry(send2, gone);
36783
37854
  const facades = facadeRegistry(send2, gone);
36784
37855
  const requests = requestBinding(send2, port);
37856
+ const devices = deviceBinding(send2, port);
36785
37857
  return {
36786
37858
  requests,
37859
+ devices,
36787
37860
  answer(l, req) {
36788
37861
  if (req.op === "serve-cdp") return cdps.serve(l, req);
36789
37862
  if (req.op === "unserve-cdp") return cdps.drop(req.registration);
@@ -36792,6 +37865,7 @@ function hostRegistries(send2, gone, port) {
36792
37865
  },
36793
37866
  close() {
36794
37867
  requests.close();
37868
+ devices.close();
36795
37869
  cdps.close();
36796
37870
  facades.close();
36797
37871
  }
@@ -36905,6 +37979,28 @@ function answered(pending, req, reply, detach) {
36905
37979
  }
36906
37980
  );
36907
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
+ }
36908
38004
  function serveClient(port, host) {
36909
38005
  let state = "new";
36910
38006
  let self;
@@ -36938,9 +38034,9 @@ function serveClient(port, host) {
36938
38034
  ended.resolve();
36939
38035
  }
36940
38036
  async function hello(message3) {
36941
- const refused4 = versionError(message3.protocol) ?? await hosts.requests.accept(host, message3);
36942
- if (refused4) {
36943
- send2({ hello: { protocol: PROTOCOL, error: refused4 } });
38037
+ const refused5 = await helloRefused(host, hosts, message3);
38038
+ if (refused5) {
38039
+ send2({ hello: { protocol: PROTOCOL, error: refused5 } });
36944
38040
  detach();
36945
38041
  return;
36946
38042
  }
@@ -37135,14 +38231,14 @@ async function followRedirects(send2, request) {
37135
38231
  var DEVTOOLS_PORTS = [9222, 9223, 9224, 9225, 9226, 9227, 9228, 9229];
37136
38232
  var METADATA_V6 = [64768, 3778, 0, 0, 0, 0, 0, 596].join(":");
37137
38233
  var resolveHost = async (host) => (await lookup2(host, { all: true, verbatim: true })).map((a) => a.address);
37138
- function ipv4Place(address) {
37139
- const [a, b] = address.split(".").map(Number);
38234
+ function ipv4Place(address2) {
38235
+ const [a, b] = address2.split(".").map(Number);
37140
38236
  if (a === 127 || a === 0) return "loopback";
37141
38237
  if (a === 169 && b === 254) return "link-local";
37142
38238
  return void 0;
37143
38239
  }
37144
- function placeOf(address) {
37145
- const plain3 = address.replace(/^\[(.*)\]$/, "$1").toLowerCase();
38240
+ function placeOf(address2) {
38241
+ const plain3 = address2.replace(/^\[(.*)\]$/, "$1").toLowerCase();
37146
38242
  const kind = isIP3(plain3);
37147
38243
  if (kind === 4) return ipv4Place(plain3);
37148
38244
  if (kind !== 6) return void 0;
@@ -37158,12 +38254,12 @@ function ipv4Of(words3) {
37158
38254
  const [high = 0, low = 0] = words3.slice(6);
37159
38255
  return [high >> 8, high & 255, low >> 8, low & 255].join(".");
37160
38256
  }
37161
- function hextets2(address) {
37162
- const dotted = /(\d+\.\d+\.\d+\.\d+)$/.exec(address)?.[1];
37163
- let text = address;
38257
+ function hextets2(address2) {
38258
+ const dotted = /(\d+\.\d+\.\d+\.\d+)$/.exec(address2)?.[1];
38259
+ let text = address2;
37164
38260
  if (dotted) {
37165
38261
  const [a, b, c, d] = dotted.split(".").map(Number);
37166
- 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)}`;
37167
38263
  }
37168
38264
  const [head = "", tail2] = text.split("::");
37169
38265
  const part = (side) => side ? side.split(":").map((w) => Number.parseInt(w, 16)) : [];
@@ -37184,13 +38280,13 @@ function allows(policy, port) {
37184
38280
  if (Array.isArray(policy)) return policy.includes(port);
37185
38281
  return policy === true && !DEVTOOLS_PORTS.includes(port);
37186
38282
  }
37187
- function refusal2(url, address, place) {
37188
- const where = `${address}:${portOf(url)}`;
38283
+ function refusal2(url, address2, place) {
38284
+ const where = `${address2}:${portOf(url)}`;
37189
38285
  const why = place === "link-local" ? `nodeTransport refuses link-local ${where}` : `nodeTransport refuses the host's loopback ${where} (see nodeTransport({ hostLoopback }))`;
37190
38286
  return Object.assign(new Error(why), { status: 403, code: "EACCES" });
37191
38287
  }
37192
- function connectRefusal(address, port, place) {
37193
- const where = `${address}:${port}`;
38288
+ function connectRefusal(address2, port, place) {
38289
+ const where = `${address2}:${port}`;
37194
38290
  const why = place === "link-local" ? `nodeTransport refuses link-local ${where}` : `nodeTransport refuses the host's loopback ${where} (see nodeTransport({ hostLoopback }))`;
37195
38291
  return Object.assign(new Error(why), { status: 403, code: "EACCES" });
37196
38292
  }
@@ -37202,11 +38298,11 @@ async function addressesOf(host, resolve2) {
37202
38298
  }
37203
38299
  async function checkDestination(target, policy, resolve2 = resolveHost) {
37204
38300
  const url = new URL(target);
37205
- for (const address of await addressesOf(url.hostname, resolve2)) {
37206
- const place = placeOf(address);
38301
+ for (const address2 of await addressesOf(url.hostname, resolve2)) {
38302
+ const place = placeOf(address2);
37207
38303
  if (!place) continue;
37208
38304
  if (place === "loopback" && allows(policy, portOf(url))) continue;
37209
- throw refusal2(url, address, place);
38305
+ throw refusal2(url, address2, place);
37210
38306
  }
37211
38307
  }
37212
38308
  function guarded3(check, signal) {
@@ -37230,9 +38326,9 @@ function envProxyEnabled() {
37230
38326
  }
37231
38327
  function normaliseProxyUrl(url) {
37232
38328
  try {
37233
- const parsed2 = new URL(url);
37234
- const host = parsed2.hostname.replace(/^\[(.*)\]$/, "$1");
37235
- 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");
37236
38332
  return `${host}:${port}`;
37237
38333
  } catch {
37238
38334
  return void 0;
@@ -37268,8 +38364,8 @@ function ensureSubscribed() {
37268
38364
  const entry = loopbackStore.getStore();
37269
38365
  if (!entry) return;
37270
38366
  const { socket, connectParams } = message3;
37271
- const address = socket.remoteAddress;
37272
- if (!address) {
38367
+ const address2 = socket.remoteAddress;
38368
+ if (!address2) {
37273
38369
  const err2 = connectRefusal("unknown", 0, "loopback");
37274
38370
  entry.refused = err2;
37275
38371
  socket.destroy(err2);
@@ -37284,11 +38380,11 @@ function ensureSubscribed() {
37284
38380
  }
37285
38381
  return;
37286
38382
  }
37287
- const place = placeOf(address);
38383
+ const place = placeOf(address2);
37288
38384
  if (!place) return;
37289
38385
  const port = socket.remotePort ?? 0;
37290
38386
  if (place === "loopback" && allows(entry.policy, port)) return;
37291
- const err = connectRefusal(address, port, place);
38387
+ const err = connectRefusal(address2, port, place);
37292
38388
  entry.refused = err;
37293
38389
  socket.destroy(err);
37294
38390
  });
@@ -37824,11 +38920,11 @@ function indexOf(haystack, needle) {
37824
38920
  return -1;
37825
38921
  }
37826
38922
  function parseHead2(bytes) {
37827
- const [statusLine = "", ...lines2] = latin13.decode(bytes).split("\r\n");
38923
+ const [statusLine = "", ...lines3] = latin13.decode(bytes).split("\r\n");
37828
38924
  const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
37829
38925
  if (!match) throw errorWithCode(`EPROTO: not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);
37830
38926
  const headers = [];
37831
- for (const line of lines2) {
38927
+ for (const line of lines3) {
37832
38928
  const colon = line.indexOf(":");
37833
38929
  if (colon > 0) headers.push([line.slice(0, colon).trim(), line.slice(colon + 1).trim()]);
37834
38930
  }
@@ -37897,13 +38993,13 @@ function stream(chunks2, socket, signal) {
37897
38993
  async function requestBytes(request) {
37898
38994
  const url = new URL(request.url);
37899
38995
  const body = request.body ? new Uint8Array(await request.arrayBuffer()) : void 0;
37900
- 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}`];
37901
38997
  request.headers.forEach((value2, name2) => {
37902
- if (!OWN_HEADERS2.has(name2)) lines2.push(`${name2}: ${value2}`);
38998
+ if (!OWN_HEADERS2.has(name2)) lines3.push(`${name2}: ${value2}`);
37903
38999
  });
37904
- if (body) lines2.push(`Content-Length: ${body.length}`);
37905
- lines2.push("Connection: close", "", "");
37906
- 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"));
37907
39003
  const out = new Uint8Array(head.length + (body?.length ?? 0));
37908
39004
  out.set(head);
37909
39005
  if (body) out.set(body, head.length);
@@ -38044,8 +39140,10 @@ var Channel = class {
38044
39140
  return this.request(call).done;
38045
39141
  }
38046
39142
  requests;
39143
+ devices;
38047
39144
  receive(data) {
38048
39145
  if (this.requests?.handle(data)) return;
39146
+ if (this.devices?.handle(data)) return;
38049
39147
  if (data.traits && data.id === void 0) {
38050
39148
  if (this.remote) this.remote.traits = data.traits;
38051
39149
  for (const listener of [...this.traitsListeners]) listener(data.traits);
@@ -38250,7 +39348,7 @@ async function sudoRunOn(channel, replay, { stdout, stderr, tag }) {
38250
39348
  return { pid, pgid: pid, ended: endedOf(done4, () => endedBy) };
38251
39349
  }
38252
39350
  function endedOf(done4, signal) {
38253
- return quiet(
39351
+ return quiet2(
38254
39352
  done4.then((status) => {
38255
39353
  const by = signal();
38256
39354
  return { status, ...by ? { signal: by } : {} };
@@ -38380,9 +39478,14 @@ function since(channel, minor, what, run2) {
38380
39478
  }
38381
39479
  return run2();
38382
39480
  }
38383
- async function greet(channel, lock, timeoutMs, requests) {
39481
+ async function greet(channel, lock, timeoutMs, requests, devices) {
38384
39482
  channel.post({
38385
- hello: { protocol: PROTOCOL, ...lock ? { lock } : {}, ...requests ? { requests } : {} }
39483
+ hello: {
39484
+ protocol: PROTOCOL,
39485
+ ...lock ? { lock } : {},
39486
+ ...requests ? { requests } : {},
39487
+ ...devices ? { devices: true } : {}
39488
+ }
38386
39489
  });
38387
39490
  const timer = setTimeout(
38388
39491
  () => channel.end(
@@ -38396,9 +39499,9 @@ async function greet(channel, lock, timeoutMs, requests) {
38396
39499
  } finally {
38397
39500
  clearTimeout(timer);
38398
39501
  }
38399
- const refused4 = hello.error ?? versionError(hello.protocol);
38400
- if (refused4) {
38401
- const error = new Error(refused4);
39502
+ const refused5 = hello.error ?? versionError(hello.protocol);
39503
+ if (refused5) {
39504
+ const error = new Error(refused5);
38402
39505
  channel.end(error);
38403
39506
  throw error;
38404
39507
  }
@@ -38410,11 +39513,14 @@ async function attachKernel(port, options = {}) {
38410
39513
  const channel = new Channel(port, own.release);
38411
39514
  const requests = options.requests;
38412
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);
38413
39518
  const hello = await greet(
38414
39519
  channel,
38415
39520
  own.name,
38416
39521
  options.timeoutMs ?? 1e4,
38417
- requests ? kindsOf(requests) : void 0
39522
+ requests ? kindsOf(requests) : void 0,
39523
+ devices !== void 0
38418
39524
  );
38419
39525
  channel.protocol = hello.protocol;
38420
39526
  if (hello.lock && locks) void locks.request(hello.lock, () => channel.end(new KernelGoneError()));
@@ -38741,7 +39847,8 @@ function pidAlive(pid) {
38741
39847
  }
38742
39848
  }
38743
39849
  var nodeSessions = 0;
38744
- function nodeClient(launcher, clients, { user, tag, requests }) {
39850
+ var portSessions = /* @__PURE__ */ new WeakMap();
39851
+ function nodeClient(launcher, clients, { user, tag, requests, devices }) {
38745
39852
  tagOption(tag);
38746
39853
  const { port1, port2 } = new MessageChannel3();
38747
39854
  const session = ++nodeSessions;
@@ -38750,17 +39857,27 @@ function nodeClient(launcher, clients, { user, tag, requests }) {
38750
39857
  signal: signalNumber,
38751
39858
  session,
38752
39859
  ...requests === "attach" ? { attachRequests: true } : {},
39860
+ ...devices === "attach" ? { attachDevices: true } : {},
38753
39861
  ...user !== void 0 ? { user } : {},
38754
39862
  ...tag !== void 0 ? { tag } : {}
38755
39863
  });
38756
39864
  clients.add(served2);
38757
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;
38758
39867
  void served2.closed.then(() => {
38759
39868
  clients.delete(served2);
38760
39869
  unbind?.();
39870
+ unbindDevices?.();
38761
39871
  });
39872
+ portSessions.set(port2, session);
38762
39873
  return port2;
38763
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
+ }
38764
39881
  function checkedRequests(hook) {
38765
39882
  const kinds = kindsOf(hook);
38766
39883
  return { kinds, handle: (request) => hook.handle(request) };
@@ -38787,7 +39904,23 @@ async function nodeSudoRun(launcher, guard2, replay, options) {
38787
39904
  const { status, signal } = await running.ended;
38788
39905
  return { status, ...signalField(signal) };
38789
39906
  });
38790
- 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
+ };
38791
39924
  }
38792
39925
  async function createNodeKernel(options = {}) {
38793
39926
  const hooks = sealedHooks(options.sealed);
@@ -38833,9 +39966,7 @@ async function createNodeKernel(options = {}) {
38833
39966
  ...options.protect ? { protect: options.protect } : {},
38834
39967
  ...hooks.request ? { sealed: hooks.request } : {},
38835
39968
  ...hooks.fetch ? { sealedFetch: hooks.fetch } : {},
38836
- ...options.clipboard ? { clipboard: options.clipboard } : {},
38837
- ...options.requests ? { embedder: checkedRequests(options.requests) } : {},
38838
- ...options.approve ? { approve: options.approve } : {},
39969
+ ...pageHooks(options),
38839
39970
  ...options.sudo?.clock ? { sudoClock: options.sudo.clock } : {},
38840
39971
  ...options.wasmMemories ? { wasmMemories: options.wasmMemories } : {},
38841
39972
  cron: { lock: cronLock(root), ...options.cron?.clock ? { clock: options.cron.clock } : {} }
@@ -38880,8 +40011,8 @@ async function createNodeKernel(options = {}) {
38880
40011
  );
38881
40012
  return {
38882
40013
  pid: session.pid,
38883
- exited: quiet(guard2(() => session.exited)),
38884
- ended: quiet(
40014
+ exited: quiet2(guard2(() => session.exited)),
40015
+ ended: quiet2(
38885
40016
  guard2(async () => ({ status: await session.exited, ...signalField(session.termsig()) }))
38886
40017
  ),
38887
40018
  get onData() {
@@ -38919,6 +40050,7 @@ async function createNodeKernel(options = {}) {
38919
40050
  updateTransportTraits: () => guard2(async () => launcher.updateTransportTraits()),
38920
40051
  ...nodeAccounts(launcher, guard2),
38921
40052
  sudoRun: (replay, sudoOptions = {}) => nodeSudoRun(launcher, guard2, replay, sudoOptions),
40053
+ devices: nodeDevices(launcher, guard2),
38922
40054
  async connect(connectOptions = {}) {
38923
40055
  if (terminated) throw new Error(TERMINATED);
38924
40056
  const { user } = connectOptions;