@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
@@ -2994,8 +2994,8 @@ function linkLive(Fs, from, to, follow3) {
2994
2994
  try {
2995
2995
  bridge.link(path, base === "/" ? `/${name2}` : `${base}/${name2}`);
2996
2996
  } catch (err) {
2997
- const refused2 = toErrno(Fs, err);
2998
- if (refused2.errno !== wasiErrno("EPERM") || linkable(bridge, path)) throw refused2;
2997
+ const refused3 = toErrno(Fs, err);
2998
+ if (refused3.errno !== wasiErrno("EPERM") || linkable(bridge, path)) throw refused3;
2999
2999
  throw new Fs.ErrnoError(wasiErrno("EMLINK"));
3000
3000
  }
3001
3001
  source.live.stat = void 0;
@@ -3180,7 +3180,7 @@ var init_version = __esm({
3180
3180
  "src/version.ts"() {
3181
3181
  "use strict";
3182
3182
  init_captured_globals();
3183
- KERNEL_VERSION = true ? "1.182.0" : "0.0.0-dev";
3183
+ KERNEL_VERSION = true ? "1.183.0" : "0.0.0-dev";
3184
3184
  OS_RELEASE = "6.0.0-slicc";
3185
3185
  }
3186
3186
  });
@@ -3226,8 +3226,8 @@ function writeHolder(view, at, found) {
3226
3226
  view.setInt32(at + 24, found.pid, true);
3227
3227
  }
3228
3228
  function refused(kind, flags) {
3229
- const access = flags & O_ACCMODE2;
3230
- return kind === "write" && access === O_RDONLY || kind === "read" && access === O_WRONLY;
3229
+ const access2 = flags & O_ACCMODE2;
3230
+ return kind === "write" && access2 === O_RDONLY || kind === "read" && access2 === O_WRONLY;
3231
3231
  }
3232
3232
  function baseOf(Fs, stream) {
3233
3233
  return (whence) => whence === SEEK_CUR2 ? stream.position : whence === SEEK_END2 ? sizeOf(Fs, stream) : 0;
@@ -3323,15 +3323,221 @@ var init_process_locks = __esm({
3323
3323
  }
3324
3324
  });
3325
3325
 
3326
+ // src/kernel/serial-ioctl.ts
3327
+ function codeOf(speed) {
3328
+ const low = SPEEDS.indexOf(speed);
3329
+ if (low >= 0) return low;
3330
+ const high = FAST.indexOf(speed);
3331
+ return high > 0 ? BOTHER | high : BOTHER;
3332
+ }
3333
+ function settingsOf(cflag, baudRate) {
3334
+ return {
3335
+ baudRate,
3336
+ dataBits: 5 + ((cflag & CSIZE) >> 4),
3337
+ parity: cflag & PARENB ? cflag & PARODD ? "odd" : "even" : "none",
3338
+ stopBits: cflag & CSTOPB ? 2 : 1,
3339
+ flowControl: cflag & CRTSCTS ? "hardware" : "none"
3340
+ };
3341
+ }
3342
+ function readFlags(view, at, ncc) {
3343
+ const cCc = new capturedArray(NCCS).fill(0);
3344
+ for (let i = 0; i < ncc; i++) cCc[i] = view.getUint8(at + C_CC + i);
3345
+ return {
3346
+ c_iflag: view.getUint32(at, true),
3347
+ c_oflag: view.getUint32(at + 4, true),
3348
+ c_cflag: view.getUint32(at + 8, true),
3349
+ c_lflag: view.getUint32(at + 12, true),
3350
+ c_cc: cCc
3351
+ };
3352
+ }
3353
+ function writeFlags(view, at, t, ncc) {
3354
+ view.setUint32(at, t.c_iflag >>> 0, true);
3355
+ view.setUint32(at + 4, t.c_oflag >>> 0, true);
3356
+ view.setUint32(at + 8, t.c_cflag >>> 0, true);
3357
+ view.setUint32(at + 12, t.c_lflag >>> 0, true);
3358
+ view.setUint8(at + C_LINE, 0);
3359
+ for (let i = 0; i < ncc; i++) view.setUint8(at + C_CC + i, t.c_cc[i] ?? 0);
3360
+ }
3361
+ function ioctlArg(view, request, arg) {
3362
+ if (TERMIOS_SETS.has(request)) {
3363
+ if (request > 65535) {
3364
+ const t = readFlags(view, arg, NCCS2);
3365
+ return {
3366
+ ...t,
3367
+ c_ispeed: view.getUint32(arg + 36, true),
3368
+ c_ospeed: view.getUint32(arg + 40, true)
3369
+ };
3370
+ }
3371
+ return readFlags(view, arg, NCCS);
3372
+ }
3373
+ if (INT_IN.has(request)) return view.getInt32(arg, true);
3374
+ if (INT_ARG.has(request)) return arg | 0;
3375
+ if (request === IOCTL.TIOCSWINSZ)
3376
+ return { rows: view.getUint16(arg, true), cols: view.getUint16(arg + 2, true) };
3377
+ return void 0;
3378
+ }
3379
+ function ioctlResult(view, request, arg, result) {
3380
+ if (TERMIOS_GETS.has(request)) {
3381
+ const t = result;
3382
+ if (request === IOCTL.TCGETS2) {
3383
+ writeFlags(view, arg, t, NCCS2);
3384
+ view.setUint32(arg + 36, (t.c_ispeed ?? 0) >>> 0, true);
3385
+ view.setUint32(arg + 40, (t.c_ospeed ?? 0) >>> 0, true);
3386
+ } else {
3387
+ writeFlags(view, arg, t, NCCS);
3388
+ view.setUint32(arg + 52, t.c_cflag & CBAUD, true);
3389
+ view.setUint32(arg + 56, t.c_cflag & CBAUD, true);
3390
+ }
3391
+ } else if (INT_OUT.has(request)) view.setInt32(arg, capturedNumber(result) | 0, true);
3392
+ else if (request === IOCTL.TIOCGWINSZ) {
3393
+ const size = result;
3394
+ view.setUint16(arg, size.rows, true);
3395
+ view.setUint16(arg + 2, size.cols, true);
3396
+ view.setUint32(arg + 4, 0, true);
3397
+ }
3398
+ }
3399
+ var IOCTL, FILE_IOCTLS, TERMIOS_GETS, TERMIOS_SETS, INT_OUT, INT_IN, INT_ARG, TIOCM, CBAUD, BOTHER, CSIZE, CSTOPB, PARENB, PARODD, CRTSCTS, SPEEDS, FAST, SERIAL_CFLAG, SERIAL_LFLAG, NCCS, NCCS2, C_LINE, C_CC;
3400
+ var init_serial_ioctl = __esm({
3401
+ "src/kernel/serial-ioctl.ts"() {
3402
+ "use strict";
3403
+ init_captured_globals();
3404
+ IOCTL = {
3405
+ TCGETS: 21505,
3406
+ TCSETS: 21506,
3407
+ TCSETSW: 21507,
3408
+ TCSETSF: 21508,
3409
+ TCSBRK: 21513,
3410
+ TCFLSH: 21515,
3411
+ TIOCEXCL: 21516,
3412
+ TIOCNXCL: 21517,
3413
+ TIOCSCTTY: 21518,
3414
+ TIOCGPGRP: 21519,
3415
+ TIOCSPGRP: 21520,
3416
+ TIOCOUTQ: 21521,
3417
+ TIOCGWINSZ: 21523,
3418
+ TIOCSWINSZ: 21524,
3419
+ TIOCMGET: 21525,
3420
+ TIOCMBIS: 21526,
3421
+ TIOCMBIC: 21527,
3422
+ TIOCMSET: 21528,
3423
+ FIONREAD: 21531,
3424
+ TIOCNOTTY: 21538,
3425
+ TCSBRKP: 21541,
3426
+ TIOCSBRK: 21543,
3427
+ TIOCCBRK: 21544,
3428
+ TCGETS2: 2150388778,
3429
+ TCSETS2: 1076646955,
3430
+ TCSETSW2: 1076646956,
3431
+ TCSETSF2: 1076646957
3432
+ };
3433
+ FILE_IOCTLS = new capturedSet([21537, 21584, 21585, 21586]);
3434
+ TERMIOS_GETS = new capturedSet([IOCTL.TCGETS, IOCTL.TCGETS2]);
3435
+ TERMIOS_SETS = new capturedSet([
3436
+ IOCTL.TCSETS,
3437
+ IOCTL.TCSETSW,
3438
+ IOCTL.TCSETSF,
3439
+ IOCTL.TCSETS2,
3440
+ IOCTL.TCSETSW2,
3441
+ IOCTL.TCSETSF2
3442
+ ]);
3443
+ INT_OUT = new capturedSet([
3444
+ IOCTL.TIOCMGET,
3445
+ IOCTL.FIONREAD,
3446
+ IOCTL.TIOCOUTQ
3447
+ ]);
3448
+ INT_IN = new capturedSet([
3449
+ IOCTL.TIOCMBIS,
3450
+ IOCTL.TIOCMBIC,
3451
+ IOCTL.TIOCMSET
3452
+ ]);
3453
+ INT_ARG = new capturedSet([IOCTL.TCSBRK, IOCTL.TCSBRKP, IOCTL.TCFLSH]);
3454
+ TIOCM = {
3455
+ DTR: 2,
3456
+ RTS: 4,
3457
+ CTS: 32,
3458
+ CD: 64,
3459
+ RI: 128,
3460
+ DSR: 256
3461
+ };
3462
+ CBAUD = 4111;
3463
+ BOTHER = 4096;
3464
+ CSIZE = 48;
3465
+ CSTOPB = 64;
3466
+ PARENB = 256;
3467
+ PARODD = 512;
3468
+ CRTSCTS = 2147483648;
3469
+ SPEEDS = [
3470
+ 0,
3471
+ 50,
3472
+ 75,
3473
+ 110,
3474
+ 134,
3475
+ 150,
3476
+ 200,
3477
+ 300,
3478
+ 600,
3479
+ 1200,
3480
+ 1800,
3481
+ 2400,
3482
+ 4800,
3483
+ 9600,
3484
+ 19200,
3485
+ 38400
3486
+ ];
3487
+ FAST = [
3488
+ 0,
3489
+ 57600,
3490
+ 115200,
3491
+ 230400,
3492
+ 460800,
3493
+ 5e5,
3494
+ 576e3,
3495
+ 921600,
3496
+ 1e6,
3497
+ 1152e3,
3498
+ 15e5,
3499
+ 2e6,
3500
+ 25e5,
3501
+ 3e6,
3502
+ 35e5,
3503
+ 4e6
3504
+ ];
3505
+ SERIAL_CFLAG = 13 | 48 | 128 | 1024 | 2048;
3506
+ SERIAL_LFLAG = 16 | 32 | 512 | 2048;
3507
+ NCCS = 32;
3508
+ NCCS2 = 19;
3509
+ C_LINE = 16;
3510
+ C_CC = 17;
3511
+ }
3512
+ });
3513
+
3326
3514
  // src/process/process-pty.ts
3327
3515
  function failed(e) {
3328
3516
  const code2 = e?.code;
3329
3517
  if (typeof code2 === "string") return -wasiErrno(code2);
3330
3518
  throw e;
3331
3519
  }
3520
+ function deviceIoctl(stream, request, varargs, deps) {
3521
+ const heap = deps.heap();
3522
+ if (!heap) return -wasiErrno("EFAULT");
3523
+ const view = new capturedDataView(heap.buffer);
3524
+ const argp = (heap[varargs >> 2] ?? 0) >>> 0;
3525
+ try {
3526
+ const arg = ioctlArg(view, request, argp);
3527
+ const kernel = deps.kernel;
3528
+ const result = kernel.devIoctl(stream.sliccKernelFd, request, arg);
3529
+ ioctlResult(new capturedDataView(heap.buffer), request, argp, result);
3530
+ return 0;
3531
+ } catch (e) {
3532
+ return failed(e);
3533
+ }
3534
+ }
3332
3535
  function ptyIoctl(ioctl, deps) {
3333
3536
  return (fd, op, varargs) => {
3334
3537
  const request = op >>> 0;
3538
+ const device2 = deps.fs()?.getStream(fd);
3539
+ if (device2?.sliccDevice && deps.kernel.devIoctl && !FILE_IOCTLS.has(request))
3540
+ return deviceIoctl(device2, request, varargs ?? 0, deps);
3335
3541
  if (request !== TIOCGPTN && request !== TIOCSPTLCK && request !== TIOCSCTTY && request !== TIOCSWINSZ && request !== TIOCPKT) {
3336
3542
  return ioctl(fd, op, varargs);
3337
3543
  }
@@ -3374,6 +3580,7 @@ var init_process_pty = __esm({
3374
3580
  "src/process/process-pty.ts"() {
3375
3581
  "use strict";
3376
3582
  init_captured_globals();
3583
+ init_serial_ioctl();
3377
3584
  init_wasi_errno();
3378
3585
  TIOCGPTN = 2147767344;
3379
3586
  TIOCSPTLCK = 1074025521;
@@ -3869,8 +4076,8 @@ function useDevRemoval(Fs) {
3869
4076
  const remove = fs[name2];
3870
4077
  if (typeof remove !== "function") continue;
3871
4078
  fs[name2] = (path) => {
3872
- const refused2 = devRemoval(absoluteOf(path, Fs.cwd()), fs.sliccCred);
3873
- if (refused2) throw new Fs.ErrnoError(wasiErrno(refused2));
4079
+ const refused3 = devRemoval(absoluteOf(path, Fs.cwd()), fs.sliccCred);
4080
+ if (refused3) throw new Fs.ErrnoError(wasiErrno(refused3));
3874
4081
  remove.call(Fs, path);
3875
4082
  };
3876
4083
  }
@@ -3925,16 +4132,16 @@ function pinRootKind() {
3925
4132
  }
3926
4133
  function mountTable(mounts = []) {
3927
4134
  const root = pinnedRoot ?? rootKind();
3928
- const lines = [`${root} / ${root} rw 0 0`, "devfs /dev devfs rw 0 0", "proc /proc proc rw 0 0"];
4135
+ const lines2 = [`${root} / ${root} rw 0 0`, "devfs /dev devfs rw 0 0", "proc /proc proc rw 0 0"];
3929
4136
  for (const m of mounts) {
3930
4137
  const options = capturedObject.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
3931
4138
  if (m.state !== "ok") options.push(m.state);
3932
- const access = options.includes("ro") ? [] : ["rw"];
3933
- lines.push(
3934
- `${field(m.source)} ${field(m.target)} ${m.type} ${[...access, ...options].join(",")} 0 0`
4139
+ const access2 = options.includes("ro") ? [] : ["rw"];
4140
+ lines2.push(
4141
+ `${field(m.source)} ${field(m.target)} ${m.type} ${[...access2, ...options].join(",")} 0 0`
3935
4142
  );
3936
4143
  }
3937
- return `${lines.join("\n")}
4144
+ return `${lines2.join("\n")}
3938
4145
  `;
3939
4146
  }
3940
4147
  function accounts(env = {}) {
@@ -4054,8 +4261,8 @@ function kernelAccess(imports, fs, memory, glue) {
4054
4261
  const Fs = fs();
4055
4262
  const heap = memory();
4056
4263
  const call2 = { dirfd, path, amode: amode & 7, flags };
4057
- const refused2 = Fs?.lookupPath && heap ? readonlyRefusal(Fs, heap, call2) : 0;
4058
- if (refused2 !== 0) return refused2;
4264
+ const refused3 = Fs?.lookupPath && heap ? readonlyRefusal(Fs, heap, call2) : 0;
4265
+ if (refused3 !== 0) return refused3;
4059
4266
  const rc = own(dirfd, path, amode, flags);
4060
4267
  if (rc !== 0 || !Fs?.lookupPath || !heap) return rc;
4061
4268
  try {
@@ -4094,13 +4301,13 @@ function kernelLink(imports, fs, memory, glue) {
4094
4301
  function refusedCreation(Fs, heap, dirfd, ptr) {
4095
4302
  const name2 = cString(heap, ptr);
4096
4303
  const path = absoluteOf(name2, name2.startsWith("/") ? "/" : baseOf2(Fs, dirfd));
4097
- const refused2 = devCreation(path, Fs.sliccCred);
4098
- if (!refused2 || !Fs.lookupPath) return 0;
4304
+ const refused3 = devCreation(path, Fs.sliccCred);
4305
+ if (!refused3 || !Fs.lookupPath) return 0;
4099
4306
  try {
4100
4307
  Fs.lookupPath(path, { follow: false });
4101
4308
  return 0;
4102
4309
  } catch {
4103
- return -wasiErrno(refused2);
4310
+ return -wasiErrno(refused3);
4104
4311
  }
4105
4312
  }
4106
4313
  function devCreations(imports, fs, memory, glue) {
@@ -4366,6 +4573,29 @@ var init_fd_metadata = __esm({
4366
4573
  });
4367
4574
 
4368
4575
  // src/process/kernel-streams.ts
4576
+ function deviceStat(node) {
4577
+ return {
4578
+ dev: 22,
4579
+ ino: 4096 + (node.major << 8) + node.minor,
4580
+ mode: 8192 | node.mode,
4581
+ nlink: 1,
4582
+ uid: node.uid,
4583
+ gid: node.gid,
4584
+ rdev: node.major << 8 | node.minor,
4585
+ size: 0,
4586
+ blksize: 4096,
4587
+ blocks: 0,
4588
+ atime: new capturedDate(node.atimeMs),
4589
+ mtime: new capturedDate(node.mtimeMs),
4590
+ ctime: new capturedDate(node.ctimeMs)
4591
+ };
4592
+ }
4593
+ function byIdNames(nodes) {
4594
+ const names = new capturedMap();
4595
+ for (const node of nodes)
4596
+ for (const link of node.links) names.set(link.slice(BY_ID_DIR.length + 1), node.path);
4597
+ return names;
4598
+ }
4369
4599
  function isPtyPath(path) {
4370
4600
  return PTY_PATH.test(path);
4371
4601
  }
@@ -4416,7 +4646,7 @@ function datesOf(real) {
4416
4646
  dates[key] = new capturedDate(value2);
4417
4647
  return dates;
4418
4648
  }
4419
- var POLLIN, POLLOUT, POLLERR, POLLHUP, POLLRDNORM, POLLWRNORM, TCSETSF, O_NONBLOCK, SOCKET_MODE, PTY_PATH, TTY_PATH, PTS_MAJOR, TTY_NAME, SEALED_PATH, STRERROR, CLIPBOARD_PATH, EMBEDDER_PATH, DEVICE_INO, O_CREAT3, O_EXCL2, O_NOCTTY, O_TRUNC3, O_DIRECTORY, KILLED_BY_SIGPIPE, ProcessExit, SyscallError, KernelStreams;
4649
+ var POLLIN, POLLOUT, POLLERR, POLLHUP, POLLRDNORM, POLLWRNORM, TCSETSF, O_NONBLOCK, SOCKET_MODE, PTY_PATH, TTY_PATH, PTS_MAJOR, TTY_NAME, DEVICE_PATH, DEVICE_LEAF, SERIAL_DIR, BY_ID_DIR, SEALED_PATH, STRERROR, CLIPBOARD_PATH, EMBEDDER_PATH, DEVICE_INO, O_CREAT3, O_EXCL2, O_NOCTTY, O_TRUNC3, O_DIRECTORY, KILLED_BY_SIGPIPE, ProcessExit, SyscallError, KernelStreams;
4420
4650
  var init_kernel_streams = __esm({
4421
4651
  "src/process/kernel-streams.ts"() {
4422
4652
  "use strict";
@@ -4436,6 +4666,10 @@ var init_kernel_streams = __esm({
4436
4666
  TTY_PATH = /^\/dev\/tty\d+$/;
4437
4667
  PTS_MAJOR = 136;
4438
4668
  TTY_NAME = /^tty\d+$/;
4669
+ DEVICE_PATH = /^\/dev\/tty(?:USB|ACM)\d+$/;
4670
+ DEVICE_LEAF = /^tty(?:USB|ACM)\d+$/;
4671
+ SERIAL_DIR = "/dev/serial";
4672
+ BY_ID_DIR = "/dev/serial/by-id";
4439
4673
  SEALED_PATH = "/dev/sealed";
4440
4674
  STRERROR = {
4441
4675
  EACCES: "Permission denied",
@@ -4522,6 +4756,7 @@ var init_kernel_streams = __esm({
4522
4756
  const kfd = stream.sliccKernelFd;
4523
4757
  if (!stream.tty || kfd === void 0) return;
4524
4758
  const name2 = this.sys.ttyName?.(kfd);
4759
+ if (name2 && DEVICE_PATH.test(name2)) stream.sliccDevice = true;
4525
4760
  if (name2) this.nameStream(stream, name2);
4526
4761
  }
4527
4762
  nameStream(stream, name2) {
@@ -4690,6 +4925,121 @@ var init_kernel_streams = __esm({
4690
4925
  return [...names, ...extra.filter((name2) => !names.includes(name2))];
4691
4926
  };
4692
4927
  }
4928
+ useDeviceNodes() {
4929
+ const fs = this.Fs;
4930
+ const { lookupNode, readdir, getPath, mkdir, mkdev, makedev, registerDevice } = fs;
4931
+ const nodes = this.sys.devNodes?.bind(this.sys);
4932
+ if (!lookupNode || !readdir || !getPath || !mkdir || !mkdev || !makedev || !nodes) return;
4933
+ const serial = (dir, name2) => dir === "/dev" && (DEVICE_LEAF.test(name2) || name2 === "serial") || dir === SERIAL_DIR && name2 === "by-id" || dir === BY_ID_DIR;
4934
+ const make = (dir, name2) => {
4935
+ if (!serial(dir, name2)) return false;
4936
+ const known = nodes();
4937
+ const links = byIdNames(known);
4938
+ if (dir === "/dev" && DEVICE_LEAF.test(name2)) {
4939
+ const node = known.find((n) => n.path === `/dev/${name2}`);
4940
+ if (!node) return false;
4941
+ const dev = makedev.call(fs, node.major, node.minor);
4942
+ registerDevice?.call(fs, dev, {
4943
+ open: (stream) => this.openDeviceNode(stream, node.path)
4944
+ });
4945
+ const made = mkdev.call(fs, node.path, dev);
4946
+ made.node_ops = { ...made.node_ops, getattr: () => this.liveDeviceStat(node.path) };
4947
+ } else if (dir === "/dev" && name2 === "serial" && links.size > 0)
4948
+ mkdir.call(fs, SERIAL_DIR, 493);
4949
+ else if (dir === SERIAL_DIR && name2 === "by-id" && links.size > 0)
4950
+ mkdir.call(fs, BY_ID_DIR, 493);
4951
+ else if (dir === BY_ID_DIR && links.has(name2))
4952
+ fs.symlink?.call(
4953
+ fs,
4954
+ `../..${links.get(name2).slice(4)}`,
4955
+ `${BY_ID_DIR}/${name2}`
4956
+ );
4957
+ else return false;
4958
+ return true;
4959
+ };
4960
+ let making = false;
4961
+ fs.lookupNode = (parent, name2) => {
4962
+ try {
4963
+ return lookupNode.call(fs, parent, name2);
4964
+ } catch (e) {
4965
+ if (making) throw e;
4966
+ making = true;
4967
+ let made;
4968
+ try {
4969
+ made = make(getPath.call(fs, parent), name2);
4970
+ } finally {
4971
+ making = false;
4972
+ }
4973
+ if (!made) throw e;
4974
+ return lookupNode.call(fs, parent, name2);
4975
+ }
4976
+ };
4977
+ this.useDeviceMeta();
4978
+ fs.readdir = (path) => {
4979
+ const at = path.replace(/\/+$/, "");
4980
+ const names = readdir.call(fs, path);
4981
+ if (at !== "/dev" && at !== SERIAL_DIR && at !== BY_ID_DIR) return names;
4982
+ const known = nodes();
4983
+ const links = byIdNames(known);
4984
+ const wanted = at === "/dev" ? [...known.map((n) => n.path.slice(5)), ...links.size > 0 ? ["serial"] : []] : at === SERIAL_DIR ? links.size > 0 ? ["by-id"] : [] : [...links.keys()];
4985
+ const stale2 = (name2) => at === "/dev" ? (DEVICE_LEAF.test(name2) || name2 === "serial") && !wanted.includes(name2) : name2 !== "." && name2 !== ".." && !wanted.includes(name2);
4986
+ const kept = names.filter((name2) => !stale2(name2));
4987
+ return [...kept, ...wanted.filter((name2) => !kept.includes(name2))];
4988
+ };
4989
+ }
4990
+ useDeviceMeta() {
4991
+ const fs = this.Fs;
4992
+ const { lookupPath, getPath } = fs;
4993
+ const devMeta = this.sys.devMeta?.bind(this.sys);
4994
+ if (!lookupPath || !getPath || !devMeta) return;
4995
+ const deviceOf2 = (path, follow3) => {
4996
+ try {
4997
+ const at = typeof path === "string" ? lookupPath.call(fs, path, { follow: follow3 }).path : getPath.call(fs, path);
4998
+ return DEVICE_PATH.test(at) ? at : void 0;
4999
+ } catch {
5000
+ return void 0;
5001
+ }
5002
+ };
5003
+ const set = (path, change) => this.call(() => devMeta(path, change));
5004
+ const { chmod, chown, utime } = fs;
5005
+ if (chmod)
5006
+ fs.chmod = (path, mode, dontFollow) => {
5007
+ const device2 = deviceOf2(path, !dontFollow);
5008
+ if (device2 === void 0) return chmod.call(fs, path, mode, dontFollow);
5009
+ set(device2, { mode: mode & 4095 });
5010
+ };
5011
+ if (chown)
5012
+ fs.chown = (path, uid, gid, dontFollow) => {
5013
+ const device2 = deviceOf2(path, !dontFollow);
5014
+ if (device2 === void 0) return chown.call(fs, path, uid, gid, dontFollow);
5015
+ set(device2, { uid, gid });
5016
+ };
5017
+ if (utime)
5018
+ fs.utime = (path, atime, mtime) => {
5019
+ const device2 = deviceOf2(path, true);
5020
+ if (device2 === void 0) return utime.call(fs, path, atime, mtime);
5021
+ set(device2, {
5022
+ atimeMs: capturedNumber(atime),
5023
+ mtimeMs: capturedNumber(mtime),
5024
+ now: fs.sliccTouch === true
5025
+ });
5026
+ };
5027
+ }
5028
+ liveDeviceStat(path) {
5029
+ const node = this.call(() => this.sys.devNodes?.() ?? []).find((n) => n.path === path);
5030
+ if (!node) throw new this.Fs.ErrnoError(wasiErrno("ENOENT"));
5031
+ return deviceStat(node);
5032
+ }
5033
+ openDeviceNode(stream, path) {
5034
+ const { openDev: openDev2 } = this.sys;
5035
+ stream.sliccDevice = true;
5036
+ this.openNode(
5037
+ stream,
5038
+ path,
5039
+ (flags) => openDev2.call(this.sys, path, flags | stream.flags & O_NONBLOCK),
5040
+ true
5041
+ );
5042
+ }
4693
5043
  ptyNode(path, dev) {
4694
5044
  const fs = this.Fs;
4695
5045
  fs.registerDevice?.call(fs, dev, {
@@ -4784,6 +5134,7 @@ var init_kernel_streams = __esm({
4784
5134
  this.usePtyPaths();
4785
5135
  this.useTerminalNodes();
4786
5136
  this.usePtsNodes();
5137
+ this.useDeviceNodes();
4787
5138
  this.listSealed();
4788
5139
  const open = this.Fs.open.bind(this.Fs);
4789
5140
  this.Fs.open = (path, flags, mode) => {
@@ -4795,7 +5146,7 @@ var init_kernel_streams = __esm({
4795
5146
  if (pty) return pty;
4796
5147
  this.refuseExclusiveLink(path, flags);
4797
5148
  const stream = this.openRacing(open, path, flags, mode);
4798
- if (!stream.tty) return stream;
5149
+ if (!stream.tty || stream.sliccDevice) return stream;
4799
5150
  if (stream.path === "/dev/tty" && this.sys.openTty) {
4800
5151
  let kfd;
4801
5152
  try {
@@ -5039,6 +5390,7 @@ var init_kernel_streams = __esm({
5039
5390
  if (state.readable) mask |= POLLIN | POLLRDNORM;
5040
5391
  if (state.writable) mask |= POLLOUT | POLLWRNORM;
5041
5392
  if (state.hangup) mask |= state.readable ? POLLHUP : POLLERR;
5393
+ if (state.error) mask |= POLLERR;
5042
5394
  return mask;
5043
5395
  },
5044
5396
  dup: (stream) => {
@@ -5140,9 +5492,9 @@ function kernelEntry(stream, kernel) {
5140
5492
  }
5141
5493
  const device2 = stream.path === void 0 ? void 0 : NAMED_DEVICES[stream.path];
5142
5494
  const kind = stream.sliccKernelFile ? "file" : stream.tty ? "tty" : device2 ?? "stream";
5143
- const access = kind === "tty" ? O_RDWR : (stream.flags ?? O_RDWR) & O_ACCMODE4;
5495
+ const access2 = kind === "tty" ? O_RDWR : (stream.flags ?? O_RDWR) & O_ACCMODE4;
5144
5496
  const fifo = stream.sliccKernelFifo ? { fifo: stream.sliccKernelFifo } : {};
5145
- return { fd: stream.fd, kernel, kind, flags: access, ...cloexec, ...fifo };
5497
+ return { fd: stream.fd, kernel, kind, flags: access2, ...cloexec, ...fifo };
5146
5498
  }
5147
5499
  function describeInherited(Fs, sys, streams2, livePath, actions = []) {
5148
5500
  const table = new capturedMap();
@@ -5171,8 +5523,8 @@ function describeInherited(Fs, sys, streams2, livePath, actions = []) {
5171
5523
  function deviceOfStream(stream) {
5172
5524
  const device2 = stream.path === void 0 ? void 0 : DEVICES[stream.path];
5173
5525
  if (!device2 || stream.sliccKernelFd !== void 0) return void 0;
5174
- const access = ACCESS[stream.flags & O_ACCMODE4];
5175
- return { device: device2, ...access ? { access } : {} };
5526
+ const access2 = ACCESS[stream.flags & O_ACCMODE4];
5527
+ return { device: device2, ...access2 ? { access: access2 } : {} };
5176
5528
  }
5177
5529
  function inheritedSlot(fd, stream) {
5178
5530
  if (stream.sliccKernelFd === void 0) {
@@ -5189,12 +5541,12 @@ function place(Fs, stream, fd) {
5189
5541
  return moved;
5190
5542
  }
5191
5543
  function placeholder(Fs, streams2, entry) {
5192
- const access = entry.flags ?? O_RDWR;
5193
- if (entry.kind === "socket") return streams2.socketStream(access);
5544
+ const access2 = entry.flags ?? O_RDWR;
5545
+ if (entry.kind === "socket") return streams2.socketStream(access2);
5194
5546
  if (entry.kind === "tty") return Fs.open("/dev/tty", O_RDWR);
5195
- if (entry.kind === "stream" || DEVICE_KINDS.has(entry.kind)) return Fs.open("/dev/null", access);
5547
+ if (entry.kind === "stream" || DEVICE_KINDS.has(entry.kind)) return Fs.open("/dev/null", access2);
5196
5548
  Fs.mkdirTree(PLACEHOLDER_DIR);
5197
- return Fs.open(`${PLACEHOLDER_DIR}/${entry.fd}`, access | O_CREAT4);
5549
+ return Fs.open(`${PLACEHOLDER_DIR}/${entry.fd}`, access2 | O_CREAT4);
5198
5550
  }
5199
5551
  function asFifo(stream, streams2, identity) {
5200
5552
  const inos = streamInos.get(streams2) ?? new capturedMap();
@@ -6418,6 +6770,29 @@ function ownBytes(bytes) {
6418
6770
  function ownedBuffer(body, given) {
6419
6771
  return body === given ? void 0 : [body.buffer];
6420
6772
  }
6773
+ function deviceSys(call2) {
6774
+ const json2 = (r) => r.ok && r.kind === "json" ? r.json : void 0;
6775
+ return {
6776
+ devNodes() {
6777
+ return json2(call2({ op: "fd-dev-nodes" }, "fd-dev-nodes"));
6778
+ },
6779
+ openDev(path, flags) {
6780
+ return json2(call2({ op: "fd-open-dev", path, flags }, `fd-open-dev ${path}`));
6781
+ },
6782
+ devIoctl(fd, request, arg) {
6783
+ const req = {
6784
+ op: "fd-dev-ioctl",
6785
+ fd,
6786
+ request,
6787
+ ...arg !== void 0 ? { arg } : {}
6788
+ };
6789
+ return json2(call2(req, `fd-dev-ioctl ${fd}`));
6790
+ },
6791
+ devMeta(path, change) {
6792
+ call2({ op: "fd-dev-meta", path, ...change }, `fd-dev-meta ${path}`);
6793
+ }
6794
+ };
6795
+ }
6421
6796
  function kernelSys(transport) {
6422
6797
  const call2 = (req, label, transfer) => {
6423
6798
  const r = transport.call(req, capturedNumber.POSITIVE_INFINITY, label, transfer);
@@ -6426,6 +6801,7 @@ function kernelSys(transport) {
6426
6801
  };
6427
6802
  const json2 = (r) => r.ok && r.kind === "json" ? r.json : void 0;
6428
6803
  return {
6804
+ ...deviceSys(call2),
6429
6805
  read(fd, max, opts) {
6430
6806
  const flags = {
6431
6807
  ...opts?.nonblock ? { nonblock: true } : {},
@@ -6538,8 +6914,9 @@ function kernelSys(transport) {
6538
6914
  tcgets(fd) {
6539
6915
  return json2(call2({ op: "tty-get", fd }, `tty-get ${fd}`));
6540
6916
  },
6541
- tcsets(fd, termios, flush) {
6542
- call2({ op: "tty-set", fd, termios, ...flush ? { flush } : {} }, `tty-set ${fd}`);
6917
+ tcsets(fd, termios, flush, drain2) {
6918
+ const how = { ...flush ? { flush } : {}, ...drain2 ? { drain: drain2 } : {} };
6919
+ call2({ op: "tty-set", fd, termios, ...how }, `tty-set ${fd}`);
6543
6920
  },
6544
6921
  winsize(fd) {
6545
6922
  return json2(call2({ op: "tty-winsz", fd }, `tty-winsz ${fd}`));
@@ -6795,8 +7172,8 @@ async function runWasmProcess(init, port2, deps = {}) {
6795
7172
  };
6796
7173
  const transport = credSyncTransport(gated, () => early(), init.cred);
6797
7174
  const sys = kernelSys(transport);
6798
- const encoder6 = new capturedTextEncoder();
6799
- const say = (fd) => (text) => sys.write(fd, encoder6.encode(`${text}
7175
+ const encoder7 = new capturedTextEncoder();
7176
+ const say = (fd) => (text) => sys.write(fd, encoder7.encode(`${text}
6800
7177
  `));
6801
7178
  let ready;
6802
7179
  let failed5;
@@ -7057,7 +7434,7 @@ var init_process_runtime = __esm({
7057
7434
  O_RDWR3 = 2;
7058
7435
  O_ACCMODE5 = 3;
7059
7436
  O_DIRECTORY2 = 65536;
7060
- accessMode = (access) => access === "read" ? O_RDONLY2 : access === "write" ? O_WRONLY3 : O_RDWR3;
7437
+ accessMode = (access2) => access2 === "read" ? O_RDONLY2 : access2 === "write" ? O_WRONLY3 : O_RDWR3;
7061
7438
  PLACED = new capturedSet([
7062
7439
  "stream",
7063
7440
  "file",
@@ -8130,6 +8507,92 @@ var init_wasi_files = __esm({
8130
8507
  }
8131
8508
  });
8132
8509
 
8510
+ // src/fs/dac.ts
8511
+ var init_dac = __esm({
8512
+ "src/fs/dac.ts"() {
8513
+ "use strict";
8514
+ init_captured_globals();
8515
+ init_access();
8516
+ init_owners();
8517
+ init_types();
8518
+ init_walk();
8519
+ }
8520
+ });
8521
+
8522
+ // src/kernel/pipe.ts
8523
+ var PIPE_CAPACITY;
8524
+ var init_pipe = __esm({
8525
+ "src/kernel/pipe.ts"() {
8526
+ "use strict";
8527
+ init_captured_globals();
8528
+ PIPE_CAPACITY = 64 * 1024;
8529
+ }
8530
+ });
8531
+
8532
+ // src/kernel/fd-table.ts
8533
+ function deviceAt(path) {
8534
+ return DEVICE_PATHS.get(path);
8535
+ }
8536
+ var DEVICE_PATHS;
8537
+ var init_fd_table = __esm({
8538
+ "src/kernel/fd-table.ts"() {
8539
+ "use strict";
8540
+ init_captured_globals();
8541
+ init_pipe();
8542
+ DEVICE_PATHS = new capturedMap([
8543
+ ["/dev/null", "null"],
8544
+ ["/dev/zero", "zero"],
8545
+ ["/dev/full", "full"],
8546
+ ["/dev/urandom", "urandom"],
8547
+ ["/dev/random", "urandom"]
8548
+ ]);
8549
+ }
8550
+ });
8551
+
8552
+ // src/kernel/embedder.ts
8553
+ var EMBEDDER_HEADER_MAX, EMBEDDER_WINDOW, EMBEDDER_BUDGET;
8554
+ var init_embedder = __esm({
8555
+ "src/kernel/embedder.ts"() {
8556
+ "use strict";
8557
+ init_captured_globals();
8558
+ init_fd_table();
8559
+ EMBEDDER_HEADER_MAX = 64 * 1024;
8560
+ EMBEDDER_WINDOW = 1024 * 1024;
8561
+ EMBEDDER_BUDGET = 256 * 1024 * 1024;
8562
+ }
8563
+ });
8564
+
8565
+ // src/kernel/tty.ts
8566
+ var encoder2;
8567
+ var init_tty = __esm({
8568
+ "src/kernel/tty.ts"() {
8569
+ "use strict";
8570
+ init_captured_globals();
8571
+ init_fd_table();
8572
+ init_signals();
8573
+ encoder2 = new capturedTextEncoder();
8574
+ }
8575
+ });
8576
+
8577
+ // src/kernel/devices.ts
8578
+ var DEVICE_NAME, BY_ID, HIGH_WATER, DEFAULT_SETTINGS;
8579
+ var init_devices = __esm({
8580
+ "src/kernel/devices.ts"() {
8581
+ "use strict";
8582
+ init_captured_globals();
8583
+ init_access();
8584
+ init_dac();
8585
+ init_embedder();
8586
+ init_fd_table();
8587
+ init_serial_ioctl();
8588
+ init_tty();
8589
+ DEVICE_NAME = /^tty(USB|ACM)(\d{1,3})$/;
8590
+ BY_ID = "/dev/serial/by-id";
8591
+ HIGH_WATER = 64 * 1024;
8592
+ DEFAULT_SETTINGS = settingsOf(SERIAL_CFLAG, 9600);
8593
+ }
8594
+ });
8595
+
8133
8596
  // src/process/js/async-sab.ts
8134
8597
  async function changed(header, index3, seen, waitAsync = defaultWaitAsync) {
8135
8598
  const waited = waitAsync(header, index3, seen);
@@ -8272,6 +8735,8 @@ var init_js_kernel = __esm({
8272
8735
  "fd-close",
8273
8736
  "fd-open-clipboard",
8274
8737
  "fd-open-embedder",
8738
+ "fd-open-dev",
8739
+ "fd-dev-ioctl",
8275
8740
  "net-ws-ready",
8276
8741
  "sock-tls",
8277
8742
  "sock-tls-upgrade",
@@ -8545,8 +9010,8 @@ function checkOpen(abs, o, existing) {
8545
9010
  }
8546
9011
  function flagsOf(o) {
8547
9012
  const write2 = o.write || o.append || o.truncate;
8548
- const access = write2 ? o.read ? O_RDWR4 : O_WRONLY4 : 0;
8549
- return access | (o.append ? O_APPEND2 : 0) | (o.nonblock ? O_NONBLOCK_OPEN : 0);
9013
+ const access2 = write2 ? o.read ? O_RDWR4 : O_WRONLY4 : 0;
9014
+ return access2 | (o.append ? O_APPEND2 : 0) | (o.nonblock ? O_NONBLOCK_OPEN : 0);
8550
9015
  }
8551
9016
  function openRequest(abs, o) {
8552
9017
  return {
@@ -8602,9 +9067,28 @@ function nodeStat(mode, size = 0, owner) {
8602
9067
  gid: owner?.gid ?? 0
8603
9068
  };
8604
9069
  }
9070
+ function serialNamed(name2) {
9071
+ return DEVICE_NAME.test(name2) || name2 === "serial" || name2.startsWith("serial/");
9072
+ }
9073
+ function serialNode(abs, nodes) {
9074
+ const node = nodes.find((n) => n.path === abs);
9075
+ if (node) return { kind: "serial", node };
9076
+ const links = nodes.some((n) => n.links.length > 0);
9077
+ if (links && (abs === SERIAL || abs === BY_ID)) return { kind: "dir", mode: DIR | 493 };
9078
+ const owner = nodes.find((n) => n.links.includes(abs));
9079
+ if (owner) return { kind: "devlink", target: `../..${owner.path.slice(4)}`, node: owner };
9080
+ return void 0;
9081
+ }
9082
+ function* deviceNodes() {
9083
+ return yield { op: "fd-dev-nodes" };
9084
+ }
8605
9085
  function* lookup(abs) {
8606
9086
  if (abs === "/dev") return { kind: "dir", mode: DIR | 493 };
8607
9087
  const name2 = abs.slice("/dev/".length);
9088
+ if (serialNamed(name2)) {
9089
+ const found = serialNode(abs, yield* deviceNodes());
9090
+ if (found) return found;
9091
+ }
8608
9092
  if (DEVICES2.includes(name2)) return { kind: "device" };
8609
9093
  if (name2 === "tty") return { kind: "tty" };
8610
9094
  if (name2 === "clipboard") return { kind: "clipboard" };
@@ -8672,6 +9156,14 @@ function* fdTarget(fd, path) {
8672
9156
  function devFdStat(fd) {
8673
9157
  return fdStat(fd, `/dev/fd/${fd}`);
8674
9158
  }
9159
+ function serialStat(node) {
9160
+ return {
9161
+ ...nodeStat(CHAR | node.mode, 0, node),
9162
+ atimeMs: node.atimeMs,
9163
+ mtimeMs: node.mtimeMs,
9164
+ ctimeMs: node.ctimeMs
9165
+ };
9166
+ }
8675
9167
  function linkStat(target) {
8676
9168
  return nodeStat(LINK | 511, target.length);
8677
9169
  }
@@ -8688,6 +9180,10 @@ function* devStat(abs, follow3) {
8688
9180
  return nodeStat(CHAR | 438);
8689
9181
  case "terminal":
8690
9182
  return yield* terminalStat(node.name);
9183
+ case "serial":
9184
+ return serialStat(node.node);
9185
+ case "devlink":
9186
+ return follow3 ? serialStat(node.node) : linkStat(node.target);
8691
9187
  case "link":
8692
9188
  if (!follow3) return linkStat(node.target);
8693
9189
  return node.fd === void 0 ? nodeStat(DIR | 365) : yield* fdStat(node.fd, abs);
@@ -8706,7 +9202,7 @@ function* devExists(abs) {
8706
9202
  }
8707
9203
  function* devReadlink(abs) {
8708
9204
  const node = yield* lookup(abs);
8709
- if (node.kind === "link") return node.target;
9205
+ if (node.kind === "link" || node.kind === "devlink") return node.target;
8710
9206
  if (node.kind === "fd") return yield* fdTarget(node.fd, abs);
8711
9207
  throw new JsCallError("EINVAL", abs);
8712
9208
  }
@@ -8727,9 +9223,18 @@ function* devReaddir(abs) {
8727
9223
  }
8728
9224
  const node = yield* lookup(abs);
8729
9225
  if (node.kind !== "dir") throw new JsCallError("ENOTDIR", abs);
9226
+ if (abs === SERIAL || abs === BY_ID) {
9227
+ const nodes2 = yield* deviceNodes();
9228
+ if (abs === SERIAL) return ["by-id"];
9229
+ return nodes2.flatMap((n) => n.links.map((l) => l.slice(BY_ID.length + 1))).sort();
9230
+ }
8730
9231
  const names = yield { op: "fd-tty-names" };
8731
9232
  const ttys = names.filter((n) => /^\/dev\/tty\d+$/.test(n)).map((n) => n.slice(5));
9233
+ const nodes = yield* deviceNodes();
9234
+ const serial = nodes.map((n) => n.path.slice(5));
9235
+ if (nodes.some((n) => n.links.length > 0)) serial.push("serial");
8732
9236
  return [
9237
+ ...serial,
8733
9238
  ...DEVICES2,
8734
9239
  "clipboard",
8735
9240
  "embedder",
@@ -8788,6 +9293,13 @@ function* opened(abs, o) {
8788
9293
  };
8789
9294
  case "sealed":
8790
9295
  return { fd: yield { op: "fd-open-sealed", flags: accessFlags(o) }, file: false };
9296
+ case "serial":
9297
+ case "devlink":
9298
+ return {
9299
+ fd: yield { op: "fd-open-dev", path: node.node.path, flags: accessFlags(o) },
9300
+ file: false,
9301
+ device: true
9302
+ };
8791
9303
  case "terminal": {
8792
9304
  const req = node.pts === void 0 ? { op: "fd-open-tty", name: node.name } : { op: "pty-slave-open", n: node.pts, noctty: true };
8793
9305
  return { fd: yield req, file: false };
@@ -8834,6 +9346,85 @@ function devSync(kernel, step) {
8834
9346
  function tooLarge(got) {
8835
9347
  if (got > DEV_READ_MAX) throw new JsCallError("EFBIG", "readFile");
8836
9348
  }
9349
+ function ioctlRequest(fd, request, arg) {
9350
+ return { op: "fd-dev-ioctl", fd, request, ...arg !== void 0 ? { arg } : {} };
9351
+ }
9352
+ function jsTermios(t) {
9353
+ const { c_iflag, c_oflag, c_cflag, c_lflag, c_cc } = t;
9354
+ return {
9355
+ c_iflag,
9356
+ c_oflag,
9357
+ c_cflag,
9358
+ c_lflag,
9359
+ c_cc,
9360
+ ispeed: t.c_ispeed,
9361
+ ospeed: t.c_ospeed
9362
+ };
9363
+ }
9364
+ function kernelTermios(t) {
9365
+ const ospeed = speedArg(t?.ospeed);
9366
+ const ispeed = t.ispeed === void 0 ? ospeed : speedArg(t.ispeed);
9367
+ const out = codeOf(ospeed);
9368
+ const cflag = capturedNumber(t.c_cflag) >>> 0 & ~(CBAUD | CBAUD << 16) | out;
9369
+ return {
9370
+ c_iflag: capturedNumber(t.c_iflag) >>> 0,
9371
+ c_oflag: capturedNumber(t.c_oflag) >>> 0,
9372
+ c_cflag: (ispeed === ospeed ? cflag : cflag | codeOf(ispeed) << 16) >>> 0,
9373
+ c_lflag: capturedNumber(t.c_lflag) >>> 0,
9374
+ c_cc: capturedArray.from({ length: 32 }, (_, i) => capturedNumber(t.c_cc?.[i] ?? 0) & 255),
9375
+ c_ispeed: ispeed,
9376
+ c_ospeed: ospeed
9377
+ };
9378
+ }
9379
+ function speedArg(speed) {
9380
+ if (!capturedNumber.isInteger(speed) || speed < 0 || speed > 4294967295)
9381
+ throw new JsCallError("EINVAL", "setTermios");
9382
+ return speed;
9383
+ }
9384
+ function modemOf(bits) {
9385
+ const b = capturedNumber(bits);
9386
+ return {
9387
+ dtr: (b & TIOCM.DTR) !== 0,
9388
+ rts: (b & TIOCM.RTS) !== 0,
9389
+ cts: (b & TIOCM.CTS) !== 0,
9390
+ dsr: (b & TIOCM.DSR) !== 0,
9391
+ cd: (b & TIOCM.CD) !== 0,
9392
+ ri: (b & TIOCM.RI) !== 0
9393
+ };
9394
+ }
9395
+ function lines(m, on) {
9396
+ return (m?.dtr === on ? TIOCM.DTR : 0) | (m?.rts === on ? TIOCM.RTS : 0);
9397
+ }
9398
+ function serialControls(fd, live, ioctl, then) {
9399
+ const ask = (request, arg) => {
9400
+ live();
9401
+ return ioctl(fd, request, arg);
9402
+ };
9403
+ const none = () => void 0;
9404
+ return {
9405
+ termios: () => then(ask(IOCTL.TCGETS2), (t) => jsTermios(t)),
9406
+ setTermios: (termios, when = "now") => {
9407
+ const request = WHEN[when];
9408
+ if (request === void 0) throw new JsCallError("EINVAL", "setTermios");
9409
+ return then(ask(request, kernelTermios(termios)), none);
9410
+ },
9411
+ modem: () => then(ask(IOCTL.TIOCMGET), modemOf),
9412
+ setModem: (modem) => {
9413
+ const on = lines(modem, true);
9414
+ const off = lines(modem, false);
9415
+ const clear = () => off ? then(ask(IOCTL.TIOCMBIC, off), none) : void 0;
9416
+ return on ? then(ask(IOCTL.TIOCMBIS, on), clear) : then(clear(), none);
9417
+ },
9418
+ setBreak: (on) => then(ask(on ? IOCTL.TIOCSBRK : IOCTL.TIOCCBRK), none),
9419
+ sendBreak: () => then(ask(IOCTL.TCSBRK, 0), none),
9420
+ drain: () => then(ask(IOCTL.TCSBRK, 1), none),
9421
+ flush: (queue = "both") => {
9422
+ const n = QUEUE[queue];
9423
+ if (n === void 0) throw new JsCallError("EINVAL", "flush");
9424
+ return then(ask(IOCTL.TCFLSH, n), none);
9425
+ }
9426
+ };
9427
+ }
8837
9428
  function liveness(path) {
8838
9429
  let open = true;
8839
9430
  return {
@@ -8848,13 +9439,13 @@ function liveness(path) {
8848
9439
  function allowed(may, what) {
8849
9440
  if (!may) throw new JsCallError("EBADF", what);
8850
9441
  }
8851
- function streamFile(fd, path, io, access) {
9442
+ function streamFile(fd, path, io, access2) {
8852
9443
  const { live, end } = liveness(path);
8853
9444
  const readAsync = async (position, length) => {
8854
9445
  index2(position, "read");
8855
9446
  index2(length, "read");
8856
9447
  live();
8857
- allowed(access.read, "read");
9448
+ allowed(access2.read, "read");
8858
9449
  return length === 0 ? new capturedUint8Array(0) : io.read(fd, capturedMath.min(length, MAX_IO));
8859
9450
  };
8860
9451
  const file = {
@@ -8869,7 +9460,7 @@ function streamFile(fd, path, io, access) {
8869
9460
  async write(position, data) {
8870
9461
  index2(position, "write");
8871
9462
  live();
8872
- allowed(access.write, "write");
9463
+ allowed(access2.write, "write");
8873
9464
  await io.write(fd, data);
8874
9465
  return data.length;
8875
9466
  },
@@ -8885,15 +9476,22 @@ function streamFile(fd, path, io, access) {
8885
9476
  await io.close(fd);
8886
9477
  }
8887
9478
  };
9479
+ if (access2.device && io.ioctl) {
9480
+ const ioctl = io.ioctl;
9481
+ capturedObject.assign(
9482
+ file,
9483
+ serialControls(fd, live, ioctl, (v, next2) => capturedPromise.resolve(v).then(next2))
9484
+ );
9485
+ }
8888
9486
  return { file, invalidate: end };
8889
9487
  }
8890
- function streamSyncFile(fd, path, io, access) {
9488
+ function streamSyncFile(fd, path, io, access2) {
8891
9489
  const { live, end } = liveness(path);
8892
9490
  const readSync = (position, length) => {
8893
9491
  index2(position, "read");
8894
9492
  index2(length, "read");
8895
9493
  live();
8896
- allowed(access.read, "read");
9494
+ allowed(access2.read, "read");
8897
9495
  return length === 0 ? new capturedUint8Array(0) : io.read(fd, capturedMath.min(length, MAX_IO));
8898
9496
  };
8899
9497
  const file = {
@@ -8908,7 +9506,7 @@ function streamSyncFile(fd, path, io, access) {
8908
9506
  write(position, data) {
8909
9507
  index2(position, "write");
8910
9508
  live();
8911
- allowed(access.write, "write");
9509
+ allowed(access2.write, "write");
8912
9510
  io.write(fd, data);
8913
9511
  return data.length;
8914
9512
  },
@@ -8924,6 +9522,11 @@ function streamSyncFile(fd, path, io, access) {
8924
9522
  io.close(fd);
8925
9523
  }
8926
9524
  };
9525
+ if (access2.device && io.ioctl)
9526
+ capturedObject.assign(
9527
+ file,
9528
+ serialControls(fd, live, io.ioctl, (v, next2) => next2(v))
9529
+ );
8927
9530
  return { file, invalidate: end };
8928
9531
  }
8929
9532
  async function readWhole(file) {
@@ -8956,12 +9559,14 @@ function readWholeSync(file) {
8956
9559
  file.close();
8957
9560
  }
8958
9561
  }
8959
- var DEV_READ_MAX, DEVICES2, STDIO, SELF_FD, S_IFMT3, DIR, LINK, CHAR, BORN, CHAR_KINDS;
9562
+ var DEV_READ_MAX, DEVICES2, STDIO, SELF_FD, S_IFMT3, DIR, LINK, CHAR, BORN, CHAR_KINDS, SERIAL, WHEN, QUEUE;
8960
9563
  var init_js_dev = __esm({
8961
9564
  "src/process/js/js-dev.ts"() {
8962
9565
  "use strict";
8963
9566
  init_captured_globals();
8964
9567
  init_dev_removal();
9568
+ init_devices();
9569
+ init_serial_ioctl();
8965
9570
  init_sync_fs_wire();
8966
9571
  init_js_io();
8967
9572
  init_js_kernel();
@@ -8981,6 +9586,13 @@ var init_js_dev = __esm({
8981
9586
  "embedder",
8982
9587
  "sealed"
8983
9588
  ]);
9589
+ SERIAL = "/dev/serial";
9590
+ WHEN = {
9591
+ now: IOCTL.TCSETS2,
9592
+ drain: IOCTL.TCSETSW2,
9593
+ flush: IOCTL.TCSETSF2
9594
+ };
9595
+ QUEUE = { input: 0, output: 1, both: 2 };
8984
9596
  }
8985
9597
  });
8986
9598
 
@@ -9218,36 +9830,6 @@ var init_wasi_cred = __esm({
9218
9830
  }
9219
9831
  });
9220
9832
 
9221
- // src/kernel/pipe.ts
9222
- var PIPE_CAPACITY;
9223
- var init_pipe = __esm({
9224
- "src/kernel/pipe.ts"() {
9225
- "use strict";
9226
- init_captured_globals();
9227
- PIPE_CAPACITY = 64 * 1024;
9228
- }
9229
- });
9230
-
9231
- // src/kernel/fd-table.ts
9232
- function deviceAt(path) {
9233
- return DEVICE_PATHS.get(path);
9234
- }
9235
- var DEVICE_PATHS;
9236
- var init_fd_table = __esm({
9237
- "src/kernel/fd-table.ts"() {
9238
- "use strict";
9239
- init_captured_globals();
9240
- init_pipe();
9241
- DEVICE_PATHS = new capturedMap([
9242
- ["/dev/null", "null"],
9243
- ["/dev/zero", "zero"],
9244
- ["/dev/full", "full"],
9245
- ["/dev/urandom", "urandom"],
9246
- ["/dev/random", "urandom"]
9247
- ]);
9248
- }
9249
- });
9250
-
9251
9833
  // src/process/wasi/wasi-fds.ts
9252
9834
  function stdioAlias(path) {
9253
9835
  const m = /^\/dev\/(?:(stdin)|(stdout)|(stderr)|fd\/(\d+))$/.exec(path);
@@ -9332,13 +9914,14 @@ function promoteRequest(fd, f, handed) {
9332
9914
  ...f.buffer.handle !== void 0 && f.ino !== void 0 ? { ino: f.ino } : {}
9333
9915
  };
9334
9916
  }
9335
- var SYNTHETIC_DIRS2, O_NONBLOCK2, O_WRONLY5, O_RDWR5, O_APPEND3, SEALED_PATH2, CLIPBOARD_PATH2, EMBEDDER_PATH2, NAMED_PATHS, WasiFds, GEN2;
9917
+ var SYNTHETIC_DIRS2, O_NONBLOCK2, O_WRONLY5, O_RDWR5, O_APPEND3, DEVICE_PATH2, SEALED_PATH2, CLIPBOARD_PATH2, EMBEDDER_PATH2, NAMED_PATHS, WasiFds, GEN2;
9336
9918
  var init_wasi_fds = __esm({
9337
9919
  "src/process/wasi/wasi-fds.ts"() {
9338
9920
  "use strict";
9339
9921
  init_captured_globals();
9340
9922
  init_changes();
9341
9923
  init_ranged();
9924
+ init_devices();
9342
9925
  init_fd_table();
9343
9926
  init_sync_fs_wire();
9344
9927
  init_wasi_abi();
@@ -9348,6 +9931,7 @@ var init_wasi_fds = __esm({
9348
9931
  O_WRONLY5 = 1;
9349
9932
  O_RDWR5 = 2;
9350
9933
  O_APPEND3 = 1024;
9934
+ DEVICE_PATH2 = /^\/dev\/tty(?:USB|ACM)\d+$/;
9351
9935
  SEALED_PATH2 = "/dev/sealed";
9352
9936
  CLIPBOARD_PATH2 = "/dev/clipboard";
9353
9937
  EMBEDDER_PATH2 = "/dev/embedder";
@@ -10013,6 +10597,19 @@ var init_wasi_fds = __esm({
10013
10597
  this.adopt(fd, kind, nonblock);
10014
10598
  return fd;
10015
10599
  }
10600
+ openSerial(path, oflags, fdflags, rights) {
10601
+ const { openDev: openDev2 } = this.kernel.sys;
10602
+ const linked = path.startsWith(`${BY_ID}/`);
10603
+ if (!openDev2 || !(linked || DEVICE_PATH2.test(path))) return void 0;
10604
+ if (this.nofollow && linked) throw new WasiError("ELOOP");
10605
+ const fd = this.openCharDevice(
10606
+ (flags) => openDev2.call(this.kernel.sys, path, flags),
10607
+ oflags,
10608
+ rights
10609
+ );
10610
+ this.adopt(fd, "tty", (fdflags & FDFLAGS.NONBLOCK) !== 0);
10611
+ return fd;
10612
+ }
10016
10613
  openCharDevice(openDevice2, oflags, rights) {
10017
10614
  if (oflags & OFLAGS.DIRECTORY) throw new WasiError("ENOTDIR");
10018
10615
  if (oflags & OFLAGS.CREAT && oflags & OFLAGS.EXCL) throw new WasiError("EEXIST");
@@ -10062,6 +10659,8 @@ var init_wasi_fds = __esm({
10062
10659
  if (exclusive && namedDevice(path)) throw new WasiError("EEXIST");
10063
10660
  const device2 = deviceOf(path);
10064
10661
  if (device2) return this.install({ type: "device", device: device2 });
10662
+ const serial = this.openSerial(path, oflags, fdflags, rights);
10663
+ if (serial !== void 0) return serial;
10065
10664
  const kernelDevice = this.openKernelDevice(path, oflags, fdflags, rights);
10066
10665
  if (kernelDevice !== void 0) return kernelDevice;
10067
10666
  const alias = stdioAlias(path);
@@ -10190,9 +10789,9 @@ var init_wasi_fds = __esm({
10190
10789
  kernelFifo(path, existing, oflags, rights, fdflags) {
10191
10790
  const { readable: readable3, writable: writable2 } = openAccess(existing, oflags, rights);
10192
10791
  const nonblock = (fdflags & FDFLAGS.NONBLOCK) !== 0;
10193
- const access = writable2 ? readable3 ? O_RDWR5 : O_WRONLY5 : 0;
10792
+ const access2 = writable2 ? readable3 ? O_RDWR5 : O_WRONLY5 : 0;
10194
10793
  const fd = this.restarting(
10195
- () => this.kernel.sys.openVfs(path, access | (nonblock ? O_NONBLOCK2 : 0), 0, { fifo: true })
10794
+ () => this.kernel.sys.openVfs(path, access2 | (nonblock ? O_NONBLOCK2 : 0), 0, { fifo: true })
10196
10795
  );
10197
10796
  this.table.set(fd, { type: "kernel", kind: "stream", nonblock, append: false });
10198
10797
  if (nonblock) this.publishFlags(fd, { nonblock, append: false });
@@ -10346,11 +10945,11 @@ var init_wasi_memory = __esm({
10346
10945
  return at;
10347
10946
  }
10348
10947
  putStrings(list, ptrs, buf) {
10349
- const encoder6 = new capturedTextEncoder();
10948
+ const encoder7 = new capturedTextEncoder();
10350
10949
  const v = this.view();
10351
10950
  let at = buf;
10352
10951
  list.forEach((s, i) => {
10353
- const b = encoder6.encode(s);
10952
+ const b = encoder7.encode(s);
10354
10953
  v.setUint32(ptrs + i * 4, at, true);
10355
10954
  this.bytes(at, b.length).set(b);
10356
10955
  v.setUint8(at + b.length, 0);
@@ -10358,12 +10957,12 @@ var init_wasi_memory = __esm({
10358
10957
  });
10359
10958
  }
10360
10959
  putSizes(list, countPtr, sizePtr) {
10361
- const encoder6 = new capturedTextEncoder();
10960
+ const encoder7 = new capturedTextEncoder();
10362
10961
  const v = this.view();
10363
10962
  v.setUint32(countPtr, list.length, true);
10364
10963
  v.setUint32(
10365
10964
  sizePtr,
10366
- list.reduce((n, s) => n + encoder6.encode(s).length + 1, 0),
10965
+ list.reduce((n, s) => n + encoder7.encode(s).length + 1, 0),
10367
10966
  true
10368
10967
  );
10369
10968
  }
@@ -10638,10 +11237,10 @@ function writeTermios(mem, ptr, termios) {
10638
11237
  v.setUint32(ptr + 4, termios.c_oflag, true);
10639
11238
  v.setUint32(ptr + 8, termios.c_cflag, true);
10640
11239
  v.setUint32(ptr + 12, termios.c_lflag, true);
10641
- for (let i = 0; i < NCCS; i++) v.setUint8(ptr + C_CC + i, termios.c_cc[i] ?? 0);
10642
- v.setUint8(ptr + C_LINE, 0);
10643
- v.setUint32(ptr + ISPEED, B38400, true);
10644
- v.setUint32(ptr + OSPEED, B38400, true);
11240
+ for (let i = 0; i < NCCS3; i++) v.setUint8(ptr + C_CC2 + i, termios.c_cc[i] ?? 0);
11241
+ v.setUint8(ptr + C_LINE2, 0);
11242
+ v.setUint32(ptr + ISPEED, termios.c_cflag & CBAUD2, true);
11243
+ v.setUint32(ptr + OSPEED, termios.c_cflag & CBAUD2, true);
10645
11244
  }
10646
11245
  function readTermios(mem, ptr) {
10647
11246
  const v = mem.view();
@@ -10650,7 +11249,7 @@ function readTermios(mem, ptr) {
10650
11249
  c_oflag: v.getUint32(ptr + 4, true),
10651
11250
  c_cflag: v.getUint32(ptr + 8, true),
10652
11251
  c_lflag: v.getUint32(ptr + 12, true),
10653
- c_cc: capturedArray.from({ length: NCCS }, (_, i) => v.getUint8(ptr + C_CC + i))
11252
+ c_cc: capturedArray.from({ length: NCCS3 }, (_, i) => v.getUint8(ptr + C_CC2 + i))
10654
11253
  };
10655
11254
  }
10656
11255
  function sliccTty(mem, fds, kernel, interrupted = () => void 0) {
@@ -10674,7 +11273,17 @@ function sliccTty(mem, fds, kernel, interrupted = () => void 0) {
10674
11273
  if (actions < 0 || actions > TCSAFLUSH) throw new WasiError("EINVAL");
10675
11274
  const at = terminal(fd);
10676
11275
  needs(sys.tcsets);
10677
- sys.tcsets?.(at, readTermios(mem, ptr), actions === TCSAFLUSH);
11276
+ sys.tcsets?.(at, readTermios(mem, ptr), actions === TCSAFLUSH, actions !== 0);
11277
+ };
11278
+ const control = (fd, request, arg) => {
11279
+ interrupted();
11280
+ const e = fds.find(fd);
11281
+ if (!e) throw new WasiError("EBADF");
11282
+ if (e.type !== "kernel") throw new WasiError("ENOTTY");
11283
+ const asked = request >>> 0;
11284
+ const value2 = ioctlArg(mem.view(), asked, arg >>> 0);
11285
+ const result = needs(sys.devIoctl).call(sys, fd, asked, value2);
11286
+ ioctlResult(mem.view(), asked, arg >>> 0, result);
10678
11287
  };
10679
11288
  const size = (fd, ptr) => {
10680
11289
  interrupted();
@@ -10687,21 +11296,23 @@ function sliccTty(mem, fds, kernel, interrupted = () => void 0) {
10687
11296
  return {
10688
11297
  tcgetattr: (fd, ptr) => void getAttr(fd, ptr),
10689
11298
  tcsetattr: (fd, actions, ptr) => void setAttr(fd, actions, ptr),
10690
- winsize: (fd, ptr) => void size(fd, ptr)
11299
+ winsize: (fd, ptr) => void size(fd, ptr),
11300
+ ioctl: (fd, request, arg) => void control(fd, request, arg)
10691
11301
  };
10692
11302
  }
10693
- var NCCS, C_LINE, C_CC, ISPEED, OSPEED, B38400, TCSAFLUSH;
11303
+ var NCCS3, C_LINE2, C_CC2, ISPEED, OSPEED, CBAUD2, TCSAFLUSH;
10694
11304
  var init_wasi_termios = __esm({
10695
11305
  "src/process/wasi/wasi-termios.ts"() {
10696
11306
  "use strict";
10697
11307
  init_captured_globals();
11308
+ init_serial_ioctl();
10698
11309
  init_wasi_files();
10699
- NCCS = 32;
10700
- C_LINE = 16;
10701
- C_CC = 17;
11310
+ NCCS3 = 32;
11311
+ C_LINE2 = 16;
11312
+ C_CC2 = 17;
10702
11313
  ISPEED = 52;
10703
11314
  OSPEED = 56;
10704
- B38400 = 15;
11315
+ CBAUD2 = 4111;
10705
11316
  TCSAFLUSH = 2;
10706
11317
  }
10707
11318
  });
@@ -12044,21 +12655,21 @@ var init_wasi_host = __esm({
12044
12655
  }
12045
12656
  return out;
12046
12657
  }
12047
- file(fd, access) {
12658
+ file(fd, access2) {
12048
12659
  const e = this.fds.get(fd);
12049
12660
  if (e.type !== "file") {
12050
12661
  throw new WasiError(e.type === "dir" ? "EISDIR" : e.type === "kernel" ? "ESPIPE" : "EINVAL");
12051
12662
  }
12052
- if (access === "read" && !e.file.readable || access === "write" && !e.file.writable) {
12663
+ if (access2 === "read" && !e.file.readable || access2 === "write" && !e.file.writable) {
12053
12664
  throw new WasiError("EBADF");
12054
12665
  }
12055
12666
  return e.file;
12056
12667
  }
12057
- positional(fd, access) {
12668
+ positional(fd, access2) {
12058
12669
  const e = this.fds.get(fd);
12059
12670
  const kind = e.type === "kernel" ? this.fds.kind(fd, e) : e.type;
12060
12671
  if (kind === "device") this.interrupted?.();
12061
- if (kind !== "file" || e.type !== "kernel") return this.file(fd, access);
12672
+ if (kind !== "file" || e.type !== "kernel") return this.file(fd, access2);
12062
12673
  const kernel = this.o.kernel;
12063
12674
  return {
12064
12675
  pread: (max, at) => {
@@ -12208,9 +12819,9 @@ var init_wasi_host = __esm({
12208
12819
  v.setUint16(out + 2, flags, true);
12209
12820
  let rights = seeks ? RIGHTS.ALL : RIGHTS.ALL & ~(RIGHTS.FD_SEEK | RIGHTS.FD_TELL);
12210
12821
  if (e.type === "kernel") {
12211
- const access = this.o.kernel.sys.access?.(fd);
12212
- if (access === "read") rights &= ~RIGHTS.FD_WRITE;
12213
- else if (access === "write") rights &= ~RIGHTS.FD_READ;
12822
+ const access2 = this.o.kernel.sys.access?.(fd);
12823
+ if (access2 === "read") rights &= ~RIGHTS.FD_WRITE;
12824
+ else if (access2 === "write") rights &= ~RIGHTS.FD_READ;
12214
12825
  }
12215
12826
  v.setBigUint64(out + 8, rights, true);
12216
12827
  v.setBigUint64(out + 16, RIGHTS.ALL, true);
@@ -13596,32 +14207,61 @@ function ptsBridge(fs, call2) {
13596
14207
  }
13597
14208
  };
13598
14209
  const dir = () => devSync(kernel, devStat(PTS, true));
13599
- const statOf = (p, real) => {
14210
+ const statOf = (p, real, follow3 = true) => {
13600
14211
  if (p === PTS) return dir();
14212
+ if (SERIAL_AREA.test(p)) return devSync(kernel, devStat(p, follow3));
13601
14213
  return PTS_ENTRY.test(p) ? entry(p) : real();
13602
14214
  };
14215
+ const serialNames = () => devSync(kernel, devReaddir("/dev")).filter((n) => SERIAL_AREA.test(`/dev/${n}`));
14216
+ const withSerial = (listed2) => [
14217
+ ...listed2,
14218
+ ...serialNames().filter((n) => !listed2.includes(n))
14219
+ ];
13603
14220
  const names = (known = open()) => known.map(capturedString).sort();
14221
+ const serialEntry = (p, name2) => {
14222
+ try {
14223
+ return [name2, devSync(kernel, devStat(p, false))];
14224
+ } catch {
14225
+ return [name2, null];
14226
+ }
14227
+ };
13604
14228
  const over = {
13605
14229
  stat: (p) => statOf(p, () => fs.stat(p)),
13606
- lstat: (p) => statOf(p, () => fs.lstat(p)),
14230
+ lstat: (p) => statOf(p, () => fs.lstat(p), false),
14231
+ readlink: (p) => SERIAL_AREA.test(p) ? devSync(kernel, devReadlink(p)) : fs.readlink(p),
13607
14232
  exists: (p) => {
13608
14233
  if (p === PTS) return true;
14234
+ if (SERIAL_AREA.test(p)) {
14235
+ try {
14236
+ devSync(kernel, devStat(p, true));
14237
+ return true;
14238
+ } catch {
14239
+ return false;
14240
+ }
14241
+ }
13609
14242
  if (!PTS_ENTRY.test(p)) return fs.exists(p);
13610
14243
  return open().includes(capturedNumber(PTS_ENTRY.exec(p)?.[1]));
13611
14244
  },
13612
14245
  readdir: (p) => {
13613
14246
  if (p === PTS) return names();
14247
+ if (SERIAL_AREA.test(p)) return devSync(kernel, devReaddir(p));
13614
14248
  const listed2 = fs.readdir(p);
13615
- return p === "/dev" && !listed2.includes("pts") ? [...listed2, "pts"] : listed2;
14249
+ if (p !== "/dev") return listed2;
14250
+ return withSerial(listed2.includes("pts") ? listed2 : [...listed2, "pts"]);
13616
14251
  },
13617
14252
  readdirStat: (p) => {
13618
14253
  if (p === PTS) {
13619
14254
  const known = open();
13620
14255
  return names(known).map((name2) => listedEntry(name2, known));
13621
14256
  }
14257
+ if (SERIAL_AREA.test(p))
14258
+ return devSync(kernel, devReaddir(p)).map((name2) => serialEntry(`${p}/${name2}`, name2));
13622
14259
  const listed2 = fs.readdirStat(p);
13623
- if (p !== "/dev" || listed2.some(([name2]) => name2 === "pts")) return listed2;
13624
- return [...listed2, ["pts", dir()]];
14260
+ if (p !== "/dev") return listed2;
14261
+ const all = listed2.some(([name2]) => name2 === "pts") ? listed2 : [...listed2, ["pts", dir()]];
14262
+ const have = all.map(([name2]) => name2);
14263
+ const extra = serialNames().filter((n) => !have.includes(n));
14264
+ return [...all, ...extra.map((name2) => serialEntry(`/dev/${name2}`, name2))];
13625
14265
  }
13626
14266
  };
13627
14267
  return new capturedProxy(fs, {
@@ -13632,7 +14272,7 @@ function ptsBridge(fs, call2) {
13632
14272
  }
13633
14273
  });
13634
14274
  }
13635
- var PTS, PTS_ENTRY;
14275
+ var PTS, PTS_ENTRY, SERIAL_AREA;
13636
14276
  var init_wasi_ptsfs = __esm({
13637
14277
  "src/process/wasi/wasi-ptsfs.ts"() {
13638
14278
  "use strict";
@@ -13641,6 +14281,7 @@ var init_wasi_ptsfs = __esm({
13641
14281
  init_js_dev();
13642
14282
  PTS = "/dev/pts";
13643
14283
  PTS_ENTRY = /^\/dev\/pts\/(\d+)$/;
14284
+ SERIAL_AREA = /^\/dev\/(?:tty(?:USB|ACM)\d+|serial(?:\/.*)?)$/;
13644
14285
  }
13645
14286
  });
13646
14287
 
@@ -13854,13 +14495,13 @@ var init_wasi_stats = __esm({
13854
14495
  }
13855
14496
  report() {
13856
14497
  const ms = (v) => v.toFixed(1).padStart(9);
13857
- const lines = this.phases.map(([name2, t]) => `${ms(t)} ms ${name2}`);
13858
- lines.push(`${"total ms".padStart(12)} ${"calls".padStart(7)} ${"max ms".padStart(9)} call`);
14498
+ const lines2 = this.phases.map(([name2, t]) => `${ms(t)} ms ${name2}`);
14499
+ lines2.push(`${"total ms".padStart(12)} ${"calls".padStart(7)} ${"max ms".padStart(9)} call`);
13859
14500
  const sorted = [...this.rows].sort((a, b) => b[1].ms - a[1].ms);
13860
14501
  for (const [key, row] of sorted) {
13861
- lines.push(`${ms(row.ms)} ${capturedString(row.n).padStart(7)} ${ms(row.max)} ${key}`);
14502
+ lines2.push(`${ms(row.ms)} ${capturedString(row.n).padStart(7)} ${ms(row.max)} ${key}`);
13862
14503
  }
13863
- return `${lines.join("\n")}
14504
+ return `${lines2.join("\n")}
13864
14505
  `;
13865
14506
  }
13866
14507
  };
@@ -15406,11 +16047,11 @@ function parseFunctionNames(payload) {
15406
16047
  }
15407
16048
  return names;
15408
16049
  }
15409
- function mainModule(lines) {
15410
- for (let i = lines.length - 1; i >= 0; i--) {
15411
- const m = WASM_MODULE.exec(lines[i]);
16050
+ function mainModule(lines2) {
16051
+ for (let i = lines2.length - 1; i >= 0; i--) {
16052
+ const m = WASM_MODULE.exec(lines2[i]);
15412
16053
  if (!m) continue;
15413
- return lines.slice(i + 1).some((line) => /^\s+at /.test(line)) ? m[1] : void 0;
16054
+ return lines2.slice(i + 1).some((line) => /^\s+at /.test(line)) ? m[1] : void 0;
15414
16055
  }
15415
16056
  return void 0;
15416
16057
  }
@@ -15574,12 +16215,12 @@ function unsupportedImport(module, memory, extended = false, foreign = {}) {
15574
16215
  const wasix = imports.some((i) => i.module === WASIX);
15575
16216
  const wasi = wasix || imports.some((i) => i.module === PREVIEW1);
15576
16217
  const scope2 = { extended, wasi, wasix, pie: dylinkInfo(module) !== void 0, memory };
15577
- const refused2 = imports.find((imp) => !accepted(imp, scope2));
15578
- if (refused2) {
15579
- if (refused2.kind === "memory" || refused2.module === "wasi") {
15580
- return `imports ${refused2.module}.${refused2.name}: no WASI program this host runs`;
16218
+ const refused3 = imports.find((imp) => !accepted(imp, scope2));
16219
+ if (refused3) {
16220
+ if (refused3.kind === "memory" || refused3.module === "wasi") {
16221
+ return `imports ${refused3.module}.${refused3.name}: no WASI program this host runs`;
15581
16222
  }
15582
- return `imports ${refused2.module}.${refused2.name}: no WASI preview1 program (an Emscripten one runs with its glue)`;
16223
+ return `imports ${refused3.module}.${refused3.name}: no WASI preview1 program (an Emscripten one runs with its glue)`;
15583
16224
  }
15584
16225
  const unanswerable = extended ? void 0 : firstOther(foreign);
15585
16226
  if (unanswerable) return noErrno(unanswerable);
@@ -15874,14 +16515,14 @@ async function runWasiProcess(init, port2) {
15874
16515
  const stats = init.env.SLICC_WASI_STATS === "1" ? new WasiStats() : void 0;
15875
16516
  const call2 = stats ? timedCalls(stats, kernelCall) : kernelCall;
15876
16517
  const { module } = init.program;
15877
- const refused2 = unsupportedImport(
16518
+ const refused3 = unsupportedImport(
15878
16519
  module,
15879
16520
  init.program.memory,
15880
16521
  init.program.imports !== void 0,
15881
16522
  init.program.foreign
15882
16523
  );
15883
- if (refused2) {
15884
- say(refused2);
16524
+ if (refused3) {
16525
+ say(refused3);
15885
16526
  return 126;
15886
16527
  }
15887
16528
  const fork = init.fork?.wasi;
@@ -15969,9 +16610,9 @@ async function runWasiProcess(init, port2) {
15969
16610
  function trapMessage(e, env, where = "", name2) {
15970
16611
  const head = `wasm trap${where}: ${e.message}`;
15971
16612
  if (env.SLICC_WASM_BACKTRACE !== "1") return head;
15972
- const lines = (rawStack(e) ?? "").split("\n");
15973
- const main = name2 && mainModule(lines);
15974
- const frames = lines.filter((line) => /^\s+at .*wasm:\/\/wasm\//.test(line)).slice(0, BACKTRACE_FRAMES).map((line) => name2 && main ? nameFrame(line, main, name2) : line);
16613
+ const lines2 = (rawStack(e) ?? "").split("\n");
16614
+ const main = name2 && mainModule(lines2);
16615
+ const frames = lines2.filter((line) => /^\s+at .*wasm:\/\/wasm\//.test(line)).slice(0, BACKTRACE_FRAMES).map((line) => name2 && main ? nameFrame(line, main, name2) : line);
15975
16616
  return frames.length ? `${head}
15976
16617
  ${frames.join("\n")}` : head;
15977
16618
  }
@@ -17091,8 +17732,8 @@ function servedOf(r) {
17091
17732
  ...keylogOf(r.keylog)
17092
17733
  });
17093
17734
  }
17094
- function keylogOf(lines) {
17095
- return lines ? { keylog: capturedObject.freeze([...lines]) } : {};
17735
+ function keylogOf(lines2) {
17736
+ return lines2 ? { keylog: capturedObject.freeze([...lines2]) } : {};
17096
17737
  }
17097
17738
  function clientCert(tls, what = "connect") {
17098
17739
  if (tls.pfx !== void 0) {
@@ -17723,7 +18364,7 @@ function sendmsgRequest(fd, data, options = {}) {
17723
18364
  return {
17724
18365
  op: "sock-sendmsg",
17725
18366
  fd,
17726
- body: typeof data === "string" ? encoder2.encode(data) : data,
18367
+ body: typeof data === "string" ? encoder3.encode(data) : data,
17727
18368
  fds: options.fds ?? [],
17728
18369
  nonblock: options.nonblock === true
17729
18370
  };
@@ -17770,14 +18411,14 @@ function rightsAsync(kernel, forget, watch) {
17770
18411
  }
17771
18412
  };
17772
18413
  }
17773
- var jsonOf, bytesOf2, encoder2, RECV_DEFAULT;
18414
+ var jsonOf, bytesOf2, encoder3, RECV_DEFAULT;
17774
18415
  var init_js_rights = __esm({
17775
18416
  "src/process/js/js-rights.ts"() {
17776
18417
  "use strict";
17777
18418
  init_captured_globals();
17778
18419
  jsonOf = (r) => r.ok && r.kind === "json" ? r.json : void 0;
17779
18420
  bytesOf2 = (r) => r.ok && r.kind === "bytes" ? r.bytes : new capturedUint8Array(8);
17780
- encoder2 = new capturedTextEncoder();
18421
+ encoder3 = new capturedTextEncoder();
17781
18422
  RECV_DEFAULT = 64 * 1024;
17782
18423
  }
17783
18424
  });
@@ -17819,7 +18460,7 @@ function runSync(kernel, d, o) {
17819
18460
  const named = argv0Of(o.argv0, "run");
17820
18461
  const stdout = stdioOf(o.stdout);
17821
18462
  const stderr = stderrOf(o.stderr);
17822
- const input = typeof o.input === "string" ? encoder3.encode(o.input) : o.input;
18463
+ const input = typeof o.input === "string" ? encoder4.encode(o.input) : o.input;
17823
18464
  const maxBuffer = limit(o.maxBuffer);
17824
18465
  const timeout = limit(o.timeout);
17825
18466
  const bytes = kernel.bytes({
@@ -17842,14 +18483,14 @@ function runSync(kernel, d, o) {
17842
18483
  });
17843
18484
  return decodeRun(bytes, stdout, stderr);
17844
18485
  }
17845
- var encoder3, STDIO4;
18486
+ var encoder4, STDIO4;
17846
18487
  var init_js_run = __esm({
17847
18488
  "src/process/js/js-run.ts"() {
17848
18489
  "use strict";
17849
18490
  init_captured_globals();
17850
18491
  init_js_children();
17851
18492
  init_js_kernel();
17852
- encoder3 = new capturedTextEncoder();
18493
+ encoder4 = new capturedTextEncoder();
17853
18494
  STDIO4 = new capturedSet(["pipe", "ignore", "inherit"]);
17854
18495
  }
17855
18496
  });
@@ -17912,7 +18553,7 @@ function syncOps(kernel, shared) {
17912
18553
  return r.ok && r.kind === "bytes" ? r.bytes : new capturedUint8Array(0);
17913
18554
  };
17914
18555
  const write2 = (fd, data, options) => {
17915
- const bytes = typeof data === "string" ? encoder4.encode(data) : data;
18556
+ const bytes = typeof data === "string" ? encoder5.encode(data) : data;
17916
18557
  const i = info(fd);
17917
18558
  if (writeDevice(i)) return bytes.length;
17918
18559
  const nonblock = options?.nonblock === true;
@@ -17946,7 +18587,7 @@ function syncOps(kernel, shared) {
17946
18587
  }
17947
18588
  };
17948
18589
  const openDev2 = (abs, options) => {
17949
- const { fd, file, ...access } = devSync(kernel, devOpen(abs, options));
18590
+ const { fd, file, ...access2 } = devSync(kernel, devOpen(abs, options));
17950
18591
  const forget = () => {
17951
18592
  infos.delete(fd);
17952
18593
  handles.delete(fd);
@@ -17956,7 +18597,17 @@ function syncOps(kernel, shared) {
17956
18597
  forget();
17957
18598
  kernel.call({ op: "fd-close", fd: n });
17958
18599
  };
17959
- const handle = file ? syncFile(kernel, fd, abs, options.append === true, forget) : streamSyncFile(fd, abs, { read: (n, max) => read2(n, max), write: write2, close }, access);
18600
+ const handle = file ? syncFile(kernel, fd, abs, options.append === true, forget) : streamSyncFile(
18601
+ fd,
18602
+ abs,
18603
+ {
18604
+ read: (n, max) => read2(n, max),
18605
+ write: write2,
18606
+ close,
18607
+ ioctl: (n, request, arg) => kernel.json(ioctlRequest(n, request, arg))
18608
+ },
18609
+ access2
18610
+ );
17960
18611
  handles.set(fd, handle.invalidate);
17961
18612
  return handle.file;
17962
18613
  };
@@ -18163,14 +18814,14 @@ function syncPathOps(kernel, resolve, statOf, open, clean) {
18163
18814
  ...mode !== void 0 ? { mode } : {}
18164
18815
  });
18165
18816
  try {
18166
- file.write(0, typeof data === "string" ? encoder4.encode(data) : data);
18817
+ file.write(0, typeof data === "string" ? encoder5.encode(data) : data);
18167
18818
  } finally {
18168
18819
  file.close();
18169
18820
  }
18170
18821
  }
18171
18822
  };
18172
18823
  }
18173
- var encoder4, typed2, getter2, bufferOf2, offsetOf3, setInto, noLane, READ_ONLY, JsSyncKernel;
18824
+ var encoder5, typed2, getter2, bufferOf2, offsetOf3, setInto, noLane, READ_ONLY, JsSyncKernel;
18174
18825
  var init_js_sync = __esm({
18175
18826
  "src/process/js/js-sync.ts"() {
18176
18827
  "use strict";
@@ -18184,7 +18835,7 @@ var init_js_sync = __esm({
18184
18835
  init_js_kernel();
18185
18836
  init_js_proc();
18186
18837
  init_js_rights();
18187
- encoder4 = new capturedTextEncoder();
18838
+ encoder5 = new capturedTextEncoder();
18188
18839
  typed2 = capturedObject.getPrototypeOf(capturedUint8Array.prototype);
18189
18840
  getter2 = (name2) => capturedObject.getOwnPropertyDescriptor(typed2, name2)?.get;
18190
18841
  bufferOf2 = getter2("buffer");
@@ -19133,7 +19784,7 @@ function tracked2(pending, writeAll, writeSome, failed5) {
19133
19784
  return (fd, data, options) => {
19134
19785
  const status2 = failed5?.();
19135
19786
  if (status2 !== void 0) return capturedPromise.reject(new JsExit(status2));
19136
- const bytes = typeof data === "string" ? encoder5.encode(data) : data;
19787
+ const bytes = typeof data === "string" ? encoder6.encode(data) : data;
19137
19788
  let settle;
19138
19789
  const settled2 = new capturedPromise((resolve) => settle = resolve);
19139
19790
  pending.add(settled2);
@@ -19357,7 +20008,7 @@ function createContext(o) {
19357
20008
  return { ctx, drain: drain2 };
19358
20009
  }
19359
20010
  async function openDev(kernel, abs, options, state) {
19360
- const { fd, file, ...access } = await devAsync(kernel, devOpen(abs, options));
20011
+ const { fd, file, ...access2 } = await devAsync(kernel, devOpen(abs, options));
19361
20012
  const forget = () => {
19362
20013
  state.infos.delete(fd);
19363
20014
  state.handles.delete(fd);
@@ -19370,8 +20021,13 @@ async function openDev(kernel, abs, options, state) {
19370
20021
  const handle = file ? fileHandle(kernel, fd, abs, options.append === true, forget) : streamFile(
19371
20022
  fd,
19372
20023
  abs,
19373
- { read: (n, max) => state.read(n, max), write: state.write, close },
19374
- access
20024
+ {
20025
+ read: (n, max) => state.read(n, max),
20026
+ write: state.write,
20027
+ close,
20028
+ ioctl: (n, request, arg) => kernel.json(ioctlRequest(n, request, arg))
20029
+ },
20030
+ access2
19375
20031
  );
19376
20032
  state.handles.set(fd, handle.invalidate);
19377
20033
  return handle.file;
@@ -19540,14 +20196,14 @@ function pathOps(kernel, resolve, statOf, open, clean) {
19540
20196
  true
19541
20197
  );
19542
20198
  try {
19543
- await file.write(0, typeof data === "string" ? encoder5.encode(data) : data);
20199
+ await file.write(0, typeof data === "string" ? encoder6.encode(data) : data);
19544
20200
  } finally {
19545
20201
  await file.close();
19546
20202
  }
19547
20203
  }
19548
20204
  };
19549
20205
  }
19550
- var KERNEL_INFO, JsExit, encoder5, cryptoRandom, MEMORY_FIELDS;
20206
+ var KERNEL_INFO, JsExit, encoder6, cryptoRandom, MEMORY_FIELDS;
19551
20207
  var init_js_context = __esm({
19552
20208
  "src/process/js/js-context.ts"() {
19553
20209
  "use strict";
@@ -19583,7 +20239,7 @@ var init_js_context = __esm({
19583
20239
  this.status = status2;
19584
20240
  }
19585
20241
  };
19586
- encoder5 = new capturedTextEncoder();
20242
+ encoder6 = new capturedTextEncoder();
19587
20243
  cryptoRandom = (bytes) => void capturedcrypto.getRandomValues(bytes);
19588
20244
  MEMORY_FIELDS = [
19589
20245
  "heapUsed",
@@ -19870,17 +20526,17 @@ function processEntry(port2, run = runWasmProcess, wasi = loadWasi, js = loadJs,
19870
20526
  message: err instanceof capturedError ? rawStack(err) ?? err.message : capturedString(err),
19871
20527
  ...code2 === void 0 ? {} : { code: code2 }
19872
20528
  });
19873
- let refused2;
20529
+ let refused3;
19874
20530
  const sealOnce = () => {
19875
- if (refused2 === void 0) {
20531
+ if (refused3 === void 0) {
19876
20532
  try {
19877
20533
  seal();
19878
- refused2 = null;
20534
+ refused3 = null;
19879
20535
  } catch (error) {
19880
- refused2 = { error };
20536
+ refused3 = { error };
19881
20537
  }
19882
20538
  }
19883
- return refused2;
20539
+ return refused3;
19884
20540
  };
19885
20541
  return (data) => {
19886
20542
  const type = data?.type;