@ai-ecoverse/slicc-kernel 1.181.0 → 1.183.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +62 -11
  2. package/dist/driver-worker.js +290 -90
  3. package/dist/driver-worker.js.map +4 -4
  4. package/dist/index.js +328 -102
  5. package/dist/index.js.map +4 -4
  6. package/dist/kernel-worker.js +1727 -703
  7. package/dist/kernel-worker.js.map +4 -4
  8. package/dist/node-driver-worker.js +298 -98
  9. package/dist/node-driver-worker.js.map +4 -4
  10. package/dist/node-process-worker.cjs +849 -193
  11. package/dist/node-process-worker.cjs.map +4 -4
  12. package/dist/node-process-worker.js +849 -193
  13. package/dist/node-process-worker.js.map +4 -4
  14. package/dist/node.js +1897 -751
  15. package/dist/node.js.map +4 -4
  16. package/dist/process-worker.js +805 -149
  17. package/dist/process-worker.js.map +4 -4
  18. package/dist/types/cdp/types.d.ts +2 -0
  19. package/dist/types/client/attach.d.ts +2 -0
  20. package/dist/types/client/protocol.d.ts +1 -0
  21. package/dist/types/client/serve-client.d.ts +1 -0
  22. package/dist/types/devices-relay.d.ts +74 -0
  23. package/dist/types/index.d.ts +14 -0
  24. package/dist/types/kernel/devices.d.ts +155 -0
  25. package/dist/types/kernel/embedder.d.ts +2 -0
  26. package/dist/types/kernel/fd-table.d.ts +3 -1
  27. package/dist/types/kernel/host.d.ts +2 -0
  28. package/dist/types/kernel/process.d.ts +20 -0
  29. package/dist/types/kernel/serial-ioctl.d.ts +73 -0
  30. package/dist/types/kernel/socket.d.ts +2 -0
  31. package/dist/types/launcher.d.ts +4 -0
  32. package/dist/types/node.d.ts +6 -2
  33. package/dist/types/process/js/js-dev.d.ts +24 -0
  34. package/dist/types/process/kernel-streams.d.ts +11 -1
  35. package/dist/types/process/process-pty.d.ts +1 -0
  36. package/dist/types/process/wasi/wasi-fds.d.ts +1 -0
  37. package/dist/types/process/wasi/wasi-termios.d.ts +1 -0
  38. package/dist/types/serve.d.ts +14 -1
  39. package/package.json +3 -3
@@ -2185,6 +2185,322 @@ var init_signals = __esm({
2185
2185
  }
2186
2186
  });
2187
2187
 
2188
+ // src/fs/owners.ts
2189
+ function inGroup(cred, gid) {
2190
+ return cred.egid === gid || cred.groups.includes(gid);
2191
+ }
2192
+ var ROOT_OWNER;
2193
+ var init_owners = __esm({
2194
+ "src/fs/owners.ts"() {
2195
+ "use strict";
2196
+ init_captured_globals();
2197
+ init_types();
2198
+ ROOT_OWNER = capturedObject.freeze({ uid: 0, gid: 0 });
2199
+ }
2200
+ });
2201
+
2202
+ // src/fs/walk.ts
2203
+ var init_walk = __esm({
2204
+ "src/fs/walk.ts"() {
2205
+ "use strict";
2206
+ init_captured_globals();
2207
+ init_types();
2208
+ }
2209
+ });
2210
+
2211
+ // src/fs/access.ts
2212
+ function permits(cred, perm, want, directory = true) {
2213
+ const mode = perm.mode ?? (directory ? 493 : 420);
2214
+ if (cred.euid === 0) return (want & X_OK) === 0 || directory || (mode & 73) !== 0;
2215
+ const shift = cred.euid === perm.uid ? 6 : inGroup(cred, perm.gid) ? 3 : 0;
2216
+ return (mode >> shift & want) === want;
2217
+ }
2218
+ var X_OK;
2219
+ var init_access = __esm({
2220
+ "src/fs/access.ts"() {
2221
+ "use strict";
2222
+ init_captured_globals();
2223
+ init_owners();
2224
+ init_types();
2225
+ init_walk();
2226
+ X_OK = 1;
2227
+ }
2228
+ });
2229
+
2230
+ // src/fs/dac.ts
2231
+ var init_dac = __esm({
2232
+ "src/fs/dac.ts"() {
2233
+ "use strict";
2234
+ init_captured_globals();
2235
+ init_access();
2236
+ init_owners();
2237
+ init_types();
2238
+ init_walk();
2239
+ }
2240
+ });
2241
+
2242
+ // src/kernel/pipe.ts
2243
+ var PIPE_CAPACITY;
2244
+ var init_pipe = __esm({
2245
+ "src/kernel/pipe.ts"() {
2246
+ "use strict";
2247
+ init_captured_globals();
2248
+ PIPE_CAPACITY = 64 * 1024;
2249
+ }
2250
+ });
2251
+
2252
+ // src/kernel/fd-table.ts
2253
+ function deviceAt(path) {
2254
+ return DEVICE_PATHS.get(path);
2255
+ }
2256
+ var DEVICE_PATHS;
2257
+ var init_fd_table = __esm({
2258
+ "src/kernel/fd-table.ts"() {
2259
+ "use strict";
2260
+ init_captured_globals();
2261
+ init_pipe();
2262
+ DEVICE_PATHS = new capturedMap([
2263
+ ["/dev/null", "null"],
2264
+ ["/dev/zero", "zero"],
2265
+ ["/dev/full", "full"],
2266
+ ["/dev/urandom", "urandom"],
2267
+ ["/dev/random", "urandom"]
2268
+ ]);
2269
+ }
2270
+ });
2271
+
2272
+ // src/kernel/embedder.ts
2273
+ var EMBEDDER_HEADER_MAX, EMBEDDER_WINDOW, EMBEDDER_BUDGET;
2274
+ var init_embedder = __esm({
2275
+ "src/kernel/embedder.ts"() {
2276
+ "use strict";
2277
+ init_captured_globals();
2278
+ init_fd_table();
2279
+ EMBEDDER_HEADER_MAX = 64 * 1024;
2280
+ EMBEDDER_WINDOW = 1024 * 1024;
2281
+ EMBEDDER_BUDGET = 256 * 1024 * 1024;
2282
+ }
2283
+ });
2284
+
2285
+ // src/kernel/serial-ioctl.ts
2286
+ function codeOf(speed) {
2287
+ const low = SPEEDS.indexOf(speed);
2288
+ if (low >= 0) return low;
2289
+ const high = FAST.indexOf(speed);
2290
+ return high > 0 ? BOTHER | high : BOTHER;
2291
+ }
2292
+ function settingsOf(cflag, baudRate) {
2293
+ return {
2294
+ baudRate,
2295
+ dataBits: 5 + ((cflag & CSIZE) >> 4),
2296
+ parity: cflag & PARENB ? cflag & PARODD ? "odd" : "even" : "none",
2297
+ stopBits: cflag & CSTOPB ? 2 : 1,
2298
+ flowControl: cflag & CRTSCTS ? "hardware" : "none"
2299
+ };
2300
+ }
2301
+ function readFlags(view, at, ncc) {
2302
+ const cCc = new capturedArray(NCCS).fill(0);
2303
+ for (let i = 0; i < ncc; i++) cCc[i] = view.getUint8(at + C_CC + i);
2304
+ return {
2305
+ c_iflag: view.getUint32(at, true),
2306
+ c_oflag: view.getUint32(at + 4, true),
2307
+ c_cflag: view.getUint32(at + 8, true),
2308
+ c_lflag: view.getUint32(at + 12, true),
2309
+ c_cc: cCc
2310
+ };
2311
+ }
2312
+ function writeFlags(view, at, t, ncc) {
2313
+ view.setUint32(at, t.c_iflag >>> 0, true);
2314
+ view.setUint32(at + 4, t.c_oflag >>> 0, true);
2315
+ view.setUint32(at + 8, t.c_cflag >>> 0, true);
2316
+ view.setUint32(at + 12, t.c_lflag >>> 0, true);
2317
+ view.setUint8(at + C_LINE, 0);
2318
+ for (let i = 0; i < ncc; i++) view.setUint8(at + C_CC + i, t.c_cc[i] ?? 0);
2319
+ }
2320
+ function ioctlArg(view, request, arg) {
2321
+ if (TERMIOS_SETS.has(request)) {
2322
+ if (request > 65535) {
2323
+ const t = readFlags(view, arg, NCCS2);
2324
+ return {
2325
+ ...t,
2326
+ c_ispeed: view.getUint32(arg + 36, true),
2327
+ c_ospeed: view.getUint32(arg + 40, true)
2328
+ };
2329
+ }
2330
+ return readFlags(view, arg, NCCS);
2331
+ }
2332
+ if (INT_IN.has(request)) return view.getInt32(arg, true);
2333
+ if (INT_ARG.has(request)) return arg | 0;
2334
+ if (request === IOCTL.TIOCSWINSZ)
2335
+ return { rows: view.getUint16(arg, true), cols: view.getUint16(arg + 2, true) };
2336
+ return void 0;
2337
+ }
2338
+ function ioctlResult(view, request, arg, result) {
2339
+ if (TERMIOS_GETS.has(request)) {
2340
+ const t = result;
2341
+ if (request === IOCTL.TCGETS2) {
2342
+ writeFlags(view, arg, t, NCCS2);
2343
+ view.setUint32(arg + 36, (t.c_ispeed ?? 0) >>> 0, true);
2344
+ view.setUint32(arg + 40, (t.c_ospeed ?? 0) >>> 0, true);
2345
+ } else {
2346
+ writeFlags(view, arg, t, NCCS);
2347
+ view.setUint32(arg + 52, t.c_cflag & CBAUD, true);
2348
+ view.setUint32(arg + 56, t.c_cflag & CBAUD, true);
2349
+ }
2350
+ } else if (INT_OUT.has(request)) view.setInt32(arg, capturedNumber(result) | 0, true);
2351
+ else if (request === IOCTL.TIOCGWINSZ) {
2352
+ const size = result;
2353
+ view.setUint16(arg, size.rows, true);
2354
+ view.setUint16(arg + 2, size.cols, true);
2355
+ view.setUint32(arg + 4, 0, true);
2356
+ }
2357
+ }
2358
+ 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;
2359
+ var init_serial_ioctl = __esm({
2360
+ "src/kernel/serial-ioctl.ts"() {
2361
+ "use strict";
2362
+ init_captured_globals();
2363
+ IOCTL = {
2364
+ TCGETS: 21505,
2365
+ TCSETS: 21506,
2366
+ TCSETSW: 21507,
2367
+ TCSETSF: 21508,
2368
+ TCSBRK: 21513,
2369
+ TCFLSH: 21515,
2370
+ TIOCEXCL: 21516,
2371
+ TIOCNXCL: 21517,
2372
+ TIOCSCTTY: 21518,
2373
+ TIOCGPGRP: 21519,
2374
+ TIOCSPGRP: 21520,
2375
+ TIOCOUTQ: 21521,
2376
+ TIOCGWINSZ: 21523,
2377
+ TIOCSWINSZ: 21524,
2378
+ TIOCMGET: 21525,
2379
+ TIOCMBIS: 21526,
2380
+ TIOCMBIC: 21527,
2381
+ TIOCMSET: 21528,
2382
+ FIONREAD: 21531,
2383
+ TIOCNOTTY: 21538,
2384
+ TCSBRKP: 21541,
2385
+ TIOCSBRK: 21543,
2386
+ TIOCCBRK: 21544,
2387
+ TCGETS2: 2150388778,
2388
+ TCSETS2: 1076646955,
2389
+ TCSETSW2: 1076646956,
2390
+ TCSETSF2: 1076646957
2391
+ };
2392
+ FILE_IOCTLS = new capturedSet([21537, 21584, 21585, 21586]);
2393
+ TERMIOS_GETS = new capturedSet([IOCTL.TCGETS, IOCTL.TCGETS2]);
2394
+ TERMIOS_SETS = new capturedSet([
2395
+ IOCTL.TCSETS,
2396
+ IOCTL.TCSETSW,
2397
+ IOCTL.TCSETSF,
2398
+ IOCTL.TCSETS2,
2399
+ IOCTL.TCSETSW2,
2400
+ IOCTL.TCSETSF2
2401
+ ]);
2402
+ INT_OUT = new capturedSet([
2403
+ IOCTL.TIOCMGET,
2404
+ IOCTL.FIONREAD,
2405
+ IOCTL.TIOCOUTQ
2406
+ ]);
2407
+ INT_IN = new capturedSet([
2408
+ IOCTL.TIOCMBIS,
2409
+ IOCTL.TIOCMBIC,
2410
+ IOCTL.TIOCMSET
2411
+ ]);
2412
+ INT_ARG = new capturedSet([IOCTL.TCSBRK, IOCTL.TCSBRKP, IOCTL.TCFLSH]);
2413
+ TIOCM = {
2414
+ DTR: 2,
2415
+ RTS: 4,
2416
+ CTS: 32,
2417
+ CD: 64,
2418
+ RI: 128,
2419
+ DSR: 256
2420
+ };
2421
+ CBAUD = 4111;
2422
+ BOTHER = 4096;
2423
+ CSIZE = 48;
2424
+ CSTOPB = 64;
2425
+ PARENB = 256;
2426
+ PARODD = 512;
2427
+ CRTSCTS = 2147483648;
2428
+ SPEEDS = [
2429
+ 0,
2430
+ 50,
2431
+ 75,
2432
+ 110,
2433
+ 134,
2434
+ 150,
2435
+ 200,
2436
+ 300,
2437
+ 600,
2438
+ 1200,
2439
+ 1800,
2440
+ 2400,
2441
+ 4800,
2442
+ 9600,
2443
+ 19200,
2444
+ 38400
2445
+ ];
2446
+ FAST = [
2447
+ 0,
2448
+ 57600,
2449
+ 115200,
2450
+ 230400,
2451
+ 460800,
2452
+ 5e5,
2453
+ 576e3,
2454
+ 921600,
2455
+ 1e6,
2456
+ 1152e3,
2457
+ 15e5,
2458
+ 2e6,
2459
+ 25e5,
2460
+ 3e6,
2461
+ 35e5,
2462
+ 4e6
2463
+ ];
2464
+ SERIAL_CFLAG = 13 | 48 | 128 | 1024 | 2048;
2465
+ SERIAL_LFLAG = 16 | 32 | 512 | 2048;
2466
+ NCCS = 32;
2467
+ NCCS2 = 19;
2468
+ C_LINE = 16;
2469
+ C_CC = 17;
2470
+ }
2471
+ });
2472
+
2473
+ // src/kernel/tty.ts
2474
+ var encoder2;
2475
+ var init_tty = __esm({
2476
+ "src/kernel/tty.ts"() {
2477
+ "use strict";
2478
+ init_captured_globals();
2479
+ init_fd_table();
2480
+ init_signals();
2481
+ encoder2 = new capturedTextEncoder();
2482
+ }
2483
+ });
2484
+
2485
+ // src/kernel/devices.ts
2486
+ var DEVICE_NAME, BY_ID, HIGH_WATER, DEFAULT_SETTINGS;
2487
+ var init_devices = __esm({
2488
+ "src/kernel/devices.ts"() {
2489
+ "use strict";
2490
+ init_captured_globals();
2491
+ init_access();
2492
+ init_dac();
2493
+ init_embedder();
2494
+ init_fd_table();
2495
+ init_serial_ioctl();
2496
+ init_tty();
2497
+ DEVICE_NAME = /^tty(USB|ACM)(\d{1,3})$/;
2498
+ BY_ID = "/dev/serial/by-id";
2499
+ HIGH_WATER = 64 * 1024;
2500
+ DEFAULT_SETTINGS = settingsOf(SERIAL_CFLAG, 9600);
2501
+ }
2502
+ });
2503
+
2188
2504
  // src/realm/sync-sab-bridge.ts
2189
2505
  function exactBody(req, transfer = []) {
2190
2506
  const body = req.body;
@@ -2662,6 +2978,8 @@ var init_js_kernel = __esm({
2662
2978
  "fd-close",
2663
2979
  "fd-open-clipboard",
2664
2980
  "fd-open-embedder",
2981
+ "fd-open-dev",
2982
+ "fd-dev-ioctl",
2665
2983
  "net-ws-ready",
2666
2984
  "sock-tls",
2667
2985
  "sock-tls-upgrade",
@@ -2935,8 +3253,8 @@ function checkOpen(abs, o, existing) {
2935
3253
  }
2936
3254
  function flagsOf(o) {
2937
3255
  const write2 = o.write || o.append || o.truncate;
2938
- const access = write2 ? o.read ? O_RDWR : O_WRONLY : 0;
2939
- return access | (o.append ? O_APPEND : 0) | (o.nonblock ? O_NONBLOCK_OPEN : 0);
3256
+ const access2 = write2 ? o.read ? O_RDWR : O_WRONLY : 0;
3257
+ return access2 | (o.append ? O_APPEND : 0) | (o.nonblock ? O_NONBLOCK_OPEN : 0);
2940
3258
  }
2941
3259
  function openRequest(abs, o) {
2942
3260
  return {
@@ -2992,9 +3310,28 @@ function nodeStat(mode, size = 0, owner) {
2992
3310
  gid: owner?.gid ?? 0
2993
3311
  };
2994
3312
  }
3313
+ function serialNamed(name2) {
3314
+ return DEVICE_NAME.test(name2) || name2 === "serial" || name2.startsWith("serial/");
3315
+ }
3316
+ function serialNode(abs, nodes) {
3317
+ const node = nodes.find((n) => n.path === abs);
3318
+ if (node) return { kind: "serial", node };
3319
+ const links = nodes.some((n) => n.links.length > 0);
3320
+ if (links && (abs === SERIAL || abs === BY_ID)) return { kind: "dir", mode: DIR | 493 };
3321
+ const owner = nodes.find((n) => n.links.includes(abs));
3322
+ if (owner) return { kind: "devlink", target: `../..${owner.path.slice(4)}`, node: owner };
3323
+ return void 0;
3324
+ }
3325
+ function* deviceNodes() {
3326
+ return yield { op: "fd-dev-nodes" };
3327
+ }
2995
3328
  function* lookup(abs) {
2996
3329
  if (abs === "/dev") return { kind: "dir", mode: DIR | 493 };
2997
3330
  const name2 = abs.slice("/dev/".length);
3331
+ if (serialNamed(name2)) {
3332
+ const found2 = serialNode(abs, yield* deviceNodes());
3333
+ if (found2) return found2;
3334
+ }
2998
3335
  if (DEVICES.includes(name2)) return { kind: "device" };
2999
3336
  if (name2 === "tty") return { kind: "tty" };
3000
3337
  if (name2 === "clipboard") return { kind: "clipboard" };
@@ -3062,6 +3399,14 @@ function* fdTarget(fd, path) {
3062
3399
  function devFdStat(fd) {
3063
3400
  return fdStat(fd, `/dev/fd/${fd}`);
3064
3401
  }
3402
+ function serialStat(node) {
3403
+ return {
3404
+ ...nodeStat(CHAR | node.mode, 0, node),
3405
+ atimeMs: node.atimeMs,
3406
+ mtimeMs: node.mtimeMs,
3407
+ ctimeMs: node.ctimeMs
3408
+ };
3409
+ }
3065
3410
  function linkStat(target) {
3066
3411
  return nodeStat(LINK | 511, target.length);
3067
3412
  }
@@ -3078,6 +3423,10 @@ function* devStat(abs, follow3) {
3078
3423
  return nodeStat(CHAR | 438);
3079
3424
  case "terminal":
3080
3425
  return yield* terminalStat(node.name);
3426
+ case "serial":
3427
+ return serialStat(node.node);
3428
+ case "devlink":
3429
+ return follow3 ? serialStat(node.node) : linkStat(node.target);
3081
3430
  case "link":
3082
3431
  if (!follow3) return linkStat(node.target);
3083
3432
  return node.fd === void 0 ? nodeStat(DIR | 365) : yield* fdStat(node.fd, abs);
@@ -3096,7 +3445,7 @@ function* devExists(abs) {
3096
3445
  }
3097
3446
  function* devReadlink(abs) {
3098
3447
  const node = yield* lookup(abs);
3099
- if (node.kind === "link") return node.target;
3448
+ if (node.kind === "link" || node.kind === "devlink") return node.target;
3100
3449
  if (node.kind === "fd") return yield* fdTarget(node.fd, abs);
3101
3450
  throw new JsCallError("EINVAL", abs);
3102
3451
  }
@@ -3117,9 +3466,18 @@ function* devReaddir(abs) {
3117
3466
  }
3118
3467
  const node = yield* lookup(abs);
3119
3468
  if (node.kind !== "dir") throw new JsCallError("ENOTDIR", abs);
3469
+ if (abs === SERIAL || abs === BY_ID) {
3470
+ const nodes2 = yield* deviceNodes();
3471
+ if (abs === SERIAL) return ["by-id"];
3472
+ return nodes2.flatMap((n) => n.links.map((l) => l.slice(BY_ID.length + 1))).sort();
3473
+ }
3120
3474
  const names = yield { op: "fd-tty-names" };
3121
3475
  const ttys = names.filter((n) => /^\/dev\/tty\d+$/.test(n)).map((n) => n.slice(5));
3476
+ const nodes = yield* deviceNodes();
3477
+ const serial = nodes.map((n) => n.path.slice(5));
3478
+ if (nodes.some((n) => n.links.length > 0)) serial.push("serial");
3122
3479
  return [
3480
+ ...serial,
3123
3481
  ...DEVICES,
3124
3482
  "clipboard",
3125
3483
  "embedder",
@@ -3178,6 +3536,13 @@ function* opened(abs, o) {
3178
3536
  };
3179
3537
  case "sealed":
3180
3538
  return { fd: yield { op: "fd-open-sealed", flags: accessFlags(o) }, file: false };
3539
+ case "serial":
3540
+ case "devlink":
3541
+ return {
3542
+ fd: yield { op: "fd-open-dev", path: node.node.path, flags: accessFlags(o) },
3543
+ file: false,
3544
+ device: true
3545
+ };
3181
3546
  case "terminal": {
3182
3547
  const req = node.pts === void 0 ? { op: "fd-open-tty", name: node.name } : { op: "pty-slave-open", n: node.pts, noctty: true };
3183
3548
  return { fd: yield req, file: false };
@@ -3224,6 +3589,85 @@ function devSync(kernel, step) {
3224
3589
  function tooLarge(got) {
3225
3590
  if (got > DEV_READ_MAX) throw new JsCallError("EFBIG", "readFile");
3226
3591
  }
3592
+ function ioctlRequest(fd, request, arg) {
3593
+ return { op: "fd-dev-ioctl", fd, request, ...arg !== void 0 ? { arg } : {} };
3594
+ }
3595
+ function jsTermios(t) {
3596
+ const { c_iflag, c_oflag, c_cflag, c_lflag, c_cc } = t;
3597
+ return {
3598
+ c_iflag,
3599
+ c_oflag,
3600
+ c_cflag,
3601
+ c_lflag,
3602
+ c_cc,
3603
+ ispeed: t.c_ispeed,
3604
+ ospeed: t.c_ospeed
3605
+ };
3606
+ }
3607
+ function kernelTermios(t) {
3608
+ const ospeed = speedArg(t?.ospeed);
3609
+ const ispeed = t.ispeed === void 0 ? ospeed : speedArg(t.ispeed);
3610
+ const out = codeOf(ospeed);
3611
+ const cflag = capturedNumber(t.c_cflag) >>> 0 & ~(CBAUD | CBAUD << 16) | out;
3612
+ return {
3613
+ c_iflag: capturedNumber(t.c_iflag) >>> 0,
3614
+ c_oflag: capturedNumber(t.c_oflag) >>> 0,
3615
+ c_cflag: (ispeed === ospeed ? cflag : cflag | codeOf(ispeed) << 16) >>> 0,
3616
+ c_lflag: capturedNumber(t.c_lflag) >>> 0,
3617
+ c_cc: capturedArray.from({ length: 32 }, (_, i) => capturedNumber(t.c_cc?.[i] ?? 0) & 255),
3618
+ c_ispeed: ispeed,
3619
+ c_ospeed: ospeed
3620
+ };
3621
+ }
3622
+ function speedArg(speed) {
3623
+ if (!capturedNumber.isInteger(speed) || speed < 0 || speed > 4294967295)
3624
+ throw new JsCallError("EINVAL", "setTermios");
3625
+ return speed;
3626
+ }
3627
+ function modemOf(bits) {
3628
+ const b = capturedNumber(bits);
3629
+ return {
3630
+ dtr: (b & TIOCM.DTR) !== 0,
3631
+ rts: (b & TIOCM.RTS) !== 0,
3632
+ cts: (b & TIOCM.CTS) !== 0,
3633
+ dsr: (b & TIOCM.DSR) !== 0,
3634
+ cd: (b & TIOCM.CD) !== 0,
3635
+ ri: (b & TIOCM.RI) !== 0
3636
+ };
3637
+ }
3638
+ function lines(m, on) {
3639
+ return (m?.dtr === on ? TIOCM.DTR : 0) | (m?.rts === on ? TIOCM.RTS : 0);
3640
+ }
3641
+ function serialControls(fd, live, ioctl, then) {
3642
+ const ask = (request, arg) => {
3643
+ live();
3644
+ return ioctl(fd, request, arg);
3645
+ };
3646
+ const none = () => void 0;
3647
+ return {
3648
+ termios: () => then(ask(IOCTL.TCGETS2), (t) => jsTermios(t)),
3649
+ setTermios: (termios, when = "now") => {
3650
+ const request = WHEN[when];
3651
+ if (request === void 0) throw new JsCallError("EINVAL", "setTermios");
3652
+ return then(ask(request, kernelTermios(termios)), none);
3653
+ },
3654
+ modem: () => then(ask(IOCTL.TIOCMGET), modemOf),
3655
+ setModem: (modem) => {
3656
+ const on = lines(modem, true);
3657
+ const off = lines(modem, false);
3658
+ const clear = () => off ? then(ask(IOCTL.TIOCMBIC, off), none) : void 0;
3659
+ return on ? then(ask(IOCTL.TIOCMBIS, on), clear) : then(clear(), none);
3660
+ },
3661
+ setBreak: (on) => then(ask(on ? IOCTL.TIOCSBRK : IOCTL.TIOCCBRK), none),
3662
+ sendBreak: () => then(ask(IOCTL.TCSBRK, 0), none),
3663
+ drain: () => then(ask(IOCTL.TCSBRK, 1), none),
3664
+ flush: (queue = "both") => {
3665
+ const n = QUEUE[queue];
3666
+ if (n === void 0) throw new JsCallError("EINVAL", "flush");
3667
+ return then(ask(IOCTL.TCFLSH, n), none);
3668
+ }
3669
+ };
3670
+ }
3227
3671
  function liveness(path) {
3228
3672
  let open = true;
3229
3673
  return {
@@ -3238,13 +3682,13 @@ function liveness(path) {
3238
3682
  function allowed(may, what) {
3239
3683
  if (!may) throw new JsCallError("EBADF", what);
3240
3684
  }
3241
- function streamFile(fd, path, io, access) {
3685
+ function streamFile(fd, path, io, access2) {
3242
3686
  const { live, end } = liveness(path);
3243
3687
  const readAsync = async (position, length) => {
3244
3688
  index2(position, "read");
3245
3689
  index2(length, "read");
3246
3690
  live();
3247
- allowed(access.read, "read");
3691
+ allowed(access2.read, "read");
3248
3692
  return length === 0 ? new capturedUint8Array(0) : io.read(fd, capturedMath.min(length, MAX_IO));
3249
3693
  };
3250
3694
  const file = {
@@ -3259,7 +3703,7 @@ function streamFile(fd, path, io, access) {
3259
3703
  async write(position, data2) {
3260
3704
  index2(position, "write");
3261
3705
  live();
3262
- allowed(access.write, "write");
3706
+ allowed(access2.write, "write");
3263
3707
  await io.write(fd, data2);
3264
3708
  return data2.length;
3265
3709
  },
@@ -3275,15 +3719,22 @@ function streamFile(fd, path, io, access) {
3275
3719
  await io.close(fd);
3276
3720
  }
3277
3721
  };
3722
+ if (access2.device && io.ioctl) {
3723
+ const ioctl = io.ioctl;
3724
+ capturedObject.assign(
3725
+ file,
3726
+ serialControls(fd, live, ioctl, (v, next2) => capturedPromise.resolve(v).then(next2))
3727
+ );
3728
+ }
3278
3729
  return { file, invalidate: end };
3279
3730
  }
3280
- function streamSyncFile(fd, path, io, access) {
3731
+ function streamSyncFile(fd, path, io, access2) {
3281
3732
  const { live, end } = liveness(path);
3282
3733
  const readSync = (position, length) => {
3283
3734
  index2(position, "read");
3284
3735
  index2(length, "read");
3285
3736
  live();
3286
- allowed(access.read, "read");
3737
+ allowed(access2.read, "read");
3287
3738
  return length === 0 ? new capturedUint8Array(0) : io.read(fd, capturedMath.min(length, MAX_IO));
3288
3739
  };
3289
3740
  const file = {
@@ -3298,7 +3749,7 @@ function streamSyncFile(fd, path, io, access) {
3298
3749
  write(position, data2) {
3299
3750
  index2(position, "write");
3300
3751
  live();
3301
- allowed(access.write, "write");
3752
+ allowed(access2.write, "write");
3302
3753
  io.write(fd, data2);
3303
3754
  return data2.length;
3304
3755
  },
@@ -3314,6 +3765,11 @@ function streamSyncFile(fd, path, io, access) {
3314
3765
  io.close(fd);
3315
3766
  }
3316
3767
  };
3768
+ if (access2.device && io.ioctl)
3769
+ capturedObject.assign(
3770
+ file,
3771
+ serialControls(fd, live, io.ioctl, (v, next2) => next2(v))
3772
+ );
3317
3773
  return { file, invalidate: end };
3318
3774
  }
3319
3775
  async function readWhole(file) {
@@ -3346,12 +3802,14 @@ function readWholeSync(file) {
3346
3802
  file.close();
3347
3803
  }
3348
3804
  }
3349
- var DEV_READ_MAX, DEVICES, STDIO, SELF_FD, S_IFMT, DIR, LINK, CHAR, BORN, CHAR_KINDS;
3805
+ var DEV_READ_MAX, DEVICES, STDIO, SELF_FD, S_IFMT, DIR, LINK, CHAR, BORN, CHAR_KINDS, SERIAL, WHEN, QUEUE;
3350
3806
  var init_js_dev = __esm({
3351
3807
  "src/process/js/js-dev.ts"() {
3352
3808
  "use strict";
3353
3809
  init_captured_globals();
3354
3810
  init_dev_removal();
3811
+ init_devices();
3812
+ init_serial_ioctl();
3355
3813
  init_sync_fs_wire();
3356
3814
  init_js_io();
3357
3815
  init_js_kernel();
@@ -3371,6 +3829,13 @@ var init_js_dev = __esm({
3371
3829
  "embedder",
3372
3830
  "sealed"
3373
3831
  ]);
3832
+ SERIAL = "/dev/serial";
3833
+ WHEN = {
3834
+ now: IOCTL.TCSETS2,
3835
+ drain: IOCTL.TCSETSW2,
3836
+ flush: IOCTL.TCSETSF2
3837
+ };
3838
+ QUEUE = { input: 0, output: 1, both: 2 };
3374
3839
  }
3375
3840
  });
3376
3841
 
@@ -3608,36 +4073,6 @@ var init_wasi_cred = __esm({
3608
4073
  }
3609
4074
  });
3610
4075
 
3611
- // src/kernel/pipe.ts
3612
- var PIPE_CAPACITY;
3613
- var init_pipe = __esm({
3614
- "src/kernel/pipe.ts"() {
3615
- "use strict";
3616
- init_captured_globals();
3617
- PIPE_CAPACITY = 64 * 1024;
3618
- }
3619
- });
3620
-
3621
- // src/kernel/fd-table.ts
3622
- function deviceAt(path) {
3623
- return DEVICE_PATHS.get(path);
3624
- }
3625
- var DEVICE_PATHS;
3626
- var init_fd_table = __esm({
3627
- "src/kernel/fd-table.ts"() {
3628
- "use strict";
3629
- init_captured_globals();
3630
- init_pipe();
3631
- DEVICE_PATHS = new capturedMap([
3632
- ["/dev/null", "null"],
3633
- ["/dev/zero", "zero"],
3634
- ["/dev/full", "full"],
3635
- ["/dev/urandom", "urandom"],
3636
- ["/dev/random", "urandom"]
3637
- ]);
3638
- }
3639
- });
3640
-
3641
4076
  // src/process/wasi/wasi-fds.ts
3642
4077
  function stdioAlias(path) {
3643
4078
  const m = /^\/dev\/(?:(stdin)|(stdout)|(stderr)|fd\/(\d+))$/.exec(path);
@@ -3722,13 +4157,14 @@ function promoteRequest(fd, f, handed) {
3722
4157
  ...f.buffer.handle !== void 0 && f.ino !== void 0 ? { ino: f.ino } : {}
3723
4158
  };
3724
4159
  }
3725
- var SYNTHETIC_DIRS, O_NONBLOCK, O_WRONLY2, O_RDWR2, O_APPEND2, SEALED_PATH, CLIPBOARD_PATH, EMBEDDER_PATH, NAMED_PATHS, WasiFds, GEN2;
4160
+ var SYNTHETIC_DIRS, O_NONBLOCK, O_WRONLY2, O_RDWR2, O_APPEND2, DEVICE_PATH, SEALED_PATH, CLIPBOARD_PATH, EMBEDDER_PATH, NAMED_PATHS, WasiFds, GEN2;
3726
4161
  var init_wasi_fds = __esm({
3727
4162
  "src/process/wasi/wasi-fds.ts"() {
3728
4163
  "use strict";
3729
4164
  init_captured_globals();
3730
4165
  init_changes();
3731
4166
  init_ranged();
4167
+ init_devices();
3732
4168
  init_fd_table();
3733
4169
  init_sync_fs_wire();
3734
4170
  init_wasi_abi();
@@ -3738,6 +4174,7 @@ var init_wasi_fds = __esm({
3738
4174
  O_WRONLY2 = 1;
3739
4175
  O_RDWR2 = 2;
3740
4176
  O_APPEND2 = 1024;
4177
+ DEVICE_PATH = /^\/dev\/tty(?:USB|ACM)\d+$/;
3741
4178
  SEALED_PATH = "/dev/sealed";
3742
4179
  CLIPBOARD_PATH = "/dev/clipboard";
3743
4180
  EMBEDDER_PATH = "/dev/embedder";
@@ -4403,6 +4840,19 @@ var init_wasi_fds = __esm({
4403
4840
  this.adopt(fd, kind, nonblock);
4404
4841
  return fd;
4405
4842
  }
4843
+ openSerial(path, oflags, fdflags, rights) {
4844
+ const { openDev: openDev2 } = this.kernel.sys;
4845
+ const linked = path.startsWith(`${BY_ID}/`);
4846
+ if (!openDev2 || !(linked || DEVICE_PATH.test(path))) return void 0;
4847
+ if (this.nofollow && linked) throw new WasiError("ELOOP");
4848
+ const fd = this.openCharDevice(
4849
+ (flags) => openDev2.call(this.kernel.sys, path, flags),
4850
+ oflags,
4851
+ rights
4852
+ );
4853
+ this.adopt(fd, "tty", (fdflags & FDFLAGS.NONBLOCK) !== 0);
4854
+ return fd;
4855
+ }
4406
4856
  openCharDevice(openDevice2, oflags, rights) {
4407
4857
  if (oflags & OFLAGS.DIRECTORY) throw new WasiError("ENOTDIR");
4408
4858
  if (oflags & OFLAGS.CREAT && oflags & OFLAGS.EXCL) throw new WasiError("EEXIST");
@@ -4452,6 +4902,8 @@ var init_wasi_fds = __esm({
4452
4902
  if (exclusive && namedDevice(path)) throw new WasiError("EEXIST");
4453
4903
  const device2 = deviceOf(path);
4454
4904
  if (device2) return this.install({ type: "device", device: device2 });
4905
+ const serial = this.openSerial(path, oflags, fdflags, rights);
4906
+ if (serial !== void 0) return serial;
4455
4907
  const kernelDevice = this.openKernelDevice(path, oflags, fdflags, rights);
4456
4908
  if (kernelDevice !== void 0) return kernelDevice;
4457
4909
  const alias = stdioAlias(path);
@@ -4580,9 +5032,9 @@ var init_wasi_fds = __esm({
4580
5032
  kernelFifo(path, existing, oflags, rights, fdflags) {
4581
5033
  const { readable: readable3, writable: writable2 } = openAccess(existing, oflags, rights);
4582
5034
  const nonblock = (fdflags & FDFLAGS.NONBLOCK) !== 0;
4583
- const access = writable2 ? readable3 ? O_RDWR2 : O_WRONLY2 : 0;
5035
+ const access2 = writable2 ? readable3 ? O_RDWR2 : O_WRONLY2 : 0;
4584
5036
  const fd = this.restarting(
4585
- () => this.kernel.sys.openVfs(path, access | (nonblock ? O_NONBLOCK : 0), 0, { fifo: true })
5037
+ () => this.kernel.sys.openVfs(path, access2 | (nonblock ? O_NONBLOCK : 0), 0, { fifo: true })
4586
5038
  );
4587
5039
  this.table.set(fd, { type: "kernel", kind: "stream", nonblock, append: false });
4588
5040
  if (nonblock) this.publishFlags(fd, { nonblock, append: false });
@@ -4736,11 +5188,11 @@ var init_wasi_memory = __esm({
4736
5188
  return at;
4737
5189
  }
4738
5190
  putStrings(list, ptrs, buf) {
4739
- const encoder6 = new capturedTextEncoder();
5191
+ const encoder7 = new capturedTextEncoder();
4740
5192
  const v = this.view();
4741
5193
  let at = buf;
4742
5194
  list.forEach((s, i) => {
4743
- const b = encoder6.encode(s);
5195
+ const b = encoder7.encode(s);
4744
5196
  v.setUint32(ptrs + i * 4, at, true);
4745
5197
  this.bytes(at, b.length).set(b);
4746
5198
  v.setUint8(at + b.length, 0);
@@ -4748,12 +5200,12 @@ var init_wasi_memory = __esm({
4748
5200
  });
4749
5201
  }
4750
5202
  putSizes(list, countPtr, sizePtr) {
4751
- const encoder6 = new capturedTextEncoder();
5203
+ const encoder7 = new capturedTextEncoder();
4752
5204
  const v = this.view();
4753
5205
  v.setUint32(countPtr, list.length, true);
4754
5206
  v.setUint32(
4755
5207
  sizePtr,
4756
- list.reduce((n, s) => n + encoder6.encode(s).length + 1, 0),
5208
+ list.reduce((n, s) => n + encoder7.encode(s).length + 1, 0),
4757
5209
  true
4758
5210
  );
4759
5211
  }
@@ -5028,10 +5480,10 @@ function writeTermios(mem, ptr, termios) {
5028
5480
  v.setUint32(ptr + 4, termios.c_oflag, true);
5029
5481
  v.setUint32(ptr + 8, termios.c_cflag, true);
5030
5482
  v.setUint32(ptr + 12, termios.c_lflag, true);
5031
- for (let i = 0; i < NCCS; i++) v.setUint8(ptr + C_CC + i, termios.c_cc[i] ?? 0);
5032
- v.setUint8(ptr + C_LINE, 0);
5033
- v.setUint32(ptr + ISPEED, B38400, true);
5034
- v.setUint32(ptr + OSPEED, B38400, true);
5483
+ for (let i = 0; i < NCCS3; i++) v.setUint8(ptr + C_CC2 + i, termios.c_cc[i] ?? 0);
5484
+ v.setUint8(ptr + C_LINE2, 0);
5485
+ v.setUint32(ptr + ISPEED, termios.c_cflag & CBAUD2, true);
5486
+ v.setUint32(ptr + OSPEED, termios.c_cflag & CBAUD2, true);
5035
5487
  }
5036
5488
  function readTermios(mem, ptr) {
5037
5489
  const v = mem.view();
@@ -5040,7 +5492,7 @@ function readTermios(mem, ptr) {
5040
5492
  c_oflag: v.getUint32(ptr + 4, true),
5041
5493
  c_cflag: v.getUint32(ptr + 8, true),
5042
5494
  c_lflag: v.getUint32(ptr + 12, true),
5043
- c_cc: capturedArray.from({ length: NCCS }, (_, i) => v.getUint8(ptr + C_CC + i))
5495
+ c_cc: capturedArray.from({ length: NCCS3 }, (_, i) => v.getUint8(ptr + C_CC2 + i))
5044
5496
  };
5045
5497
  }
5046
5498
  function sliccTty(mem, fds, kernel, interrupted = () => void 0) {
@@ -5064,7 +5516,17 @@ function sliccTty(mem, fds, kernel, interrupted = () => void 0) {
5064
5516
  if (actions < 0 || actions > TCSAFLUSH) throw new WasiError("EINVAL");
5065
5517
  const at = terminal(fd);
5066
5518
  needs(sys.tcsets);
5067
- sys.tcsets?.(at, readTermios(mem, ptr), actions === TCSAFLUSH);
5519
+ sys.tcsets?.(at, readTermios(mem, ptr), actions === TCSAFLUSH, actions !== 0);
5520
+ };
5521
+ const control = (fd, request, arg) => {
5522
+ interrupted();
5523
+ const e = fds.find(fd);
5524
+ if (!e) throw new WasiError("EBADF");
5525
+ if (e.type !== "kernel") throw new WasiError("ENOTTY");
5526
+ const asked = request >>> 0;
5527
+ const value2 = ioctlArg(mem.view(), asked, arg >>> 0);
5528
+ const result = needs(sys.devIoctl).call(sys, fd, asked, value2);
5529
+ ioctlResult(mem.view(), asked, arg >>> 0, result);
5068
5530
  };
5069
5531
  const size = (fd, ptr) => {
5070
5532
  interrupted();
@@ -5077,21 +5539,23 @@ function sliccTty(mem, fds, kernel, interrupted = () => void 0) {
5077
5539
  return {
5078
5540
  tcgetattr: (fd, ptr) => void getAttr(fd, ptr),
5079
5541
  tcsetattr: (fd, actions, ptr) => void setAttr(fd, actions, ptr),
5080
- winsize: (fd, ptr) => void size(fd, ptr)
5542
+ winsize: (fd, ptr) => void size(fd, ptr),
5543
+ ioctl: (fd, request, arg) => void control(fd, request, arg)
5081
5544
  };
5082
5545
  }
5083
- var NCCS, C_LINE, C_CC, ISPEED, OSPEED, B38400, TCSAFLUSH;
5546
+ var NCCS3, C_LINE2, C_CC2, ISPEED, OSPEED, CBAUD2, TCSAFLUSH;
5084
5547
  var init_wasi_termios = __esm({
5085
5548
  "src/process/wasi/wasi-termios.ts"() {
5086
5549
  "use strict";
5087
5550
  init_captured_globals();
5551
+ init_serial_ioctl();
5088
5552
  init_wasi_files();
5089
- NCCS = 32;
5090
- C_LINE = 16;
5091
- C_CC = 17;
5553
+ NCCS3 = 32;
5554
+ C_LINE2 = 16;
5555
+ C_CC2 = 17;
5092
5556
  ISPEED = 52;
5093
5557
  OSPEED = 56;
5094
- B38400 = 15;
5558
+ CBAUD2 = 4111;
5095
5559
  TCSAFLUSH = 2;
5096
5560
  }
5097
5561
  });
@@ -6484,21 +6948,21 @@ var init_wasi_host = __esm({
6484
6948
  }
6485
6949
  return out;
6486
6950
  }
6487
- file(fd, access) {
6951
+ file(fd, access2) {
6488
6952
  const e = this.fds.get(fd);
6489
6953
  if (e.type !== "file") {
6490
6954
  throw new WasiError(e.type === "dir" ? "EISDIR" : e.type === "kernel" ? "ESPIPE" : "EINVAL");
6491
6955
  }
6492
- if (access === "read" && !e.file.readable || access === "write" && !e.file.writable) {
6956
+ if (access2 === "read" && !e.file.readable || access2 === "write" && !e.file.writable) {
6493
6957
  throw new WasiError("EBADF");
6494
6958
  }
6495
6959
  return e.file;
6496
6960
  }
6497
- positional(fd, access) {
6961
+ positional(fd, access2) {
6498
6962
  const e = this.fds.get(fd);
6499
6963
  const kind = e.type === "kernel" ? this.fds.kind(fd, e) : e.type;
6500
6964
  if (kind === "device") this.interrupted?.();
6501
- if (kind !== "file" || e.type !== "kernel") return this.file(fd, access);
6965
+ if (kind !== "file" || e.type !== "kernel") return this.file(fd, access2);
6502
6966
  const kernel = this.o.kernel;
6503
6967
  return {
6504
6968
  pread: (max, at) => {
@@ -6648,9 +7112,9 @@ var init_wasi_host = __esm({
6648
7112
  v.setUint16(out + 2, flags, true);
6649
7113
  let rights = seeks ? RIGHTS.ALL : RIGHTS.ALL & ~(RIGHTS.FD_SEEK | RIGHTS.FD_TELL);
6650
7114
  if (e.type === "kernel") {
6651
- const access = this.o.kernel.sys.access?.(fd);
6652
- if (access === "read") rights &= ~RIGHTS.FD_WRITE;
6653
- else if (access === "write") rights &= ~RIGHTS.FD_READ;
7115
+ const access2 = this.o.kernel.sys.access?.(fd);
7116
+ if (access2 === "read") rights &= ~RIGHTS.FD_WRITE;
7117
+ else if (access2 === "write") rights &= ~RIGHTS.FD_READ;
6654
7118
  }
6655
7119
  v.setBigUint64(out + 8, rights, true);
6656
7120
  v.setBigUint64(out + 16, RIGHTS.ALL, true);
@@ -7518,7 +7982,7 @@ var init_version = __esm({
7518
7982
  "src/version.ts"() {
7519
7983
  "use strict";
7520
7984
  init_captured_globals();
7521
- KERNEL_VERSION = true ? "1.181.0" : "0.0.0-dev";
7985
+ KERNEL_VERSION = true ? "1.183.0" : "0.0.0-dev";
7522
7986
  OS_RELEASE = "6.0.0-slicc";
7523
7987
  }
7524
7988
  });
@@ -8382,8 +8846,8 @@ function servedOf(r) {
8382
8846
  ...keylogOf(r.keylog)
8383
8847
  });
8384
8848
  }
8385
- function keylogOf(lines) {
8386
- return lines ? { keylog: capturedObject.freeze([...lines]) } : {};
8849
+ function keylogOf(lines2) {
8850
+ return lines2 ? { keylog: capturedObject.freeze([...lines2]) } : {};
8387
8851
  }
8388
8852
  function clientCert(tls, what = "connect") {
8389
8853
  if (tls.pfx !== void 0) {
@@ -8878,48 +9342,6 @@ var init_proc_info = __esm({
8878
9342
  }
8879
9343
  });
8880
9344
 
8881
- // src/fs/owners.ts
8882
- function inGroup(cred, gid) {
8883
- return cred.egid === gid || cred.groups.includes(gid);
8884
- }
8885
- var ROOT_OWNER;
8886
- var init_owners = __esm({
8887
- "src/fs/owners.ts"() {
8888
- "use strict";
8889
- init_captured_globals();
8890
- init_types();
8891
- ROOT_OWNER = capturedObject.freeze({ uid: 0, gid: 0 });
8892
- }
8893
- });
8894
-
8895
- // src/fs/walk.ts
8896
- var init_walk = __esm({
8897
- "src/fs/walk.ts"() {
8898
- "use strict";
8899
- init_captured_globals();
8900
- init_types();
8901
- }
8902
- });
8903
-
8904
- // src/fs/access.ts
8905
- function permits(cred, perm, want, directory = true) {
8906
- const mode = perm.mode ?? (directory ? 493 : 420);
8907
- if (cred.euid === 0) return (want & X_OK) === 0 || directory || (mode & 73) !== 0;
8908
- const shift = cred.euid === perm.uid ? 6 : inGroup(cred, perm.gid) ? 3 : 0;
8909
- return (mode >> shift & want) === want;
8910
- }
8911
- var X_OK;
8912
- var init_access = __esm({
8913
- "src/fs/access.ts"() {
8914
- "use strict";
8915
- init_captured_globals();
8916
- init_owners();
8917
- init_types();
8918
- init_walk();
8919
- X_OK = 1;
8920
- }
8921
- });
8922
-
8923
9345
  // src/process/wasi-errno.ts
8924
9346
  function wasiErrno(code2) {
8925
9347
  return WASI_ERRNO[code2] ?? E.IO;
@@ -10268,8 +10690,8 @@ function linkLive(Fs, from, to, follow3) {
10268
10690
  try {
10269
10691
  bridge.link(path, base === "/" ? `/${name2}` : `${base}/${name2}`);
10270
10692
  } catch (err) {
10271
- const refused3 = toErrno(Fs, err);
10272
- if (refused3.errno !== wasiErrno("EPERM") || linkable(bridge, path)) throw refused3;
10693
+ const refused4 = toErrno(Fs, err);
10694
+ if (refused4.errno !== wasiErrno("EPERM") || linkable(bridge, path)) throw refused4;
10273
10695
  throw new Fs.ErrnoError(wasiErrno("EMLINK"));
10274
10696
  }
10275
10697
  source.live.stat = void 0;
@@ -10374,9 +10796,9 @@ function writeHolder2(view, at, found2) {
10374
10796
  view.setBigInt64(at + 16, capturedBigInt(found2.len), true);
10375
10797
  view.setInt32(at + 24, found2.pid, true);
10376
10798
  }
10377
- function refused2(kind, flags) {
10378
- const access = flags & O_ACCMODE2;
10379
- return kind === "write" && access === O_RDONLY || kind === "read" && access === O_WRONLY4;
10799
+ function refused3(kind, flags) {
10800
+ const access2 = flags & O_ACCMODE2;
10801
+ return kind === "write" && access2 === O_RDONLY || kind === "read" && access2 === O_WRONLY4;
10380
10802
  }
10381
10803
  function baseOf2(Fs, stream) {
10382
10804
  return (whence) => whence === SEEK_CUR3 ? stream.position : whence === SEEK_END3 ? sizeOf2(Fs, stream) : 0;
@@ -10403,7 +10825,7 @@ function recordLock(fcntl, deps) {
10403
10825
  );
10404
10826
  return 0;
10405
10827
  }
10406
- if (refused2(lock.kind, stream.flags)) return -wasiErrno("EBADF");
10828
+ if (refused3(lock.kind, stream.flags)) return -wasiErrno("EBADF");
10407
10829
  if (lock.kind !== "write") deps.flush();
10408
10830
  deps.kernel.setLock(target, lock.kind, lock.start, lock.len, cmd === F_SETLKW2);
10409
10831
  if (lock.kind !== "unlock") held.set(Fs, true);
@@ -10478,9 +10900,27 @@ function failed2(e) {
10478
10900
  if (typeof code2 === "string") return -wasiErrno(code2);
10479
10901
  throw e;
10480
10902
  }
10903
+ function deviceIoctl(stream, request, varargs, deps) {
10904
+ const heap = deps.heap();
10905
+ if (!heap) return -wasiErrno("EFAULT");
10906
+ const view = new capturedDataView(heap.buffer);
10907
+ const argp = (heap[varargs >> 2] ?? 0) >>> 0;
10908
+ try {
10909
+ const arg = ioctlArg(view, request, argp);
10910
+ const kernel = deps.kernel;
10911
+ const result = kernel.devIoctl(stream.sliccKernelFd, request, arg);
10912
+ ioctlResult(new capturedDataView(heap.buffer), request, argp, result);
10913
+ return 0;
10914
+ } catch (e) {
10915
+ return failed2(e);
10916
+ }
10917
+ }
10481
10918
  function ptyIoctl(ioctl, deps) {
10482
10919
  return (fd, op, varargs) => {
10483
10920
  const request = op >>> 0;
10921
+ const device2 = deps.fs()?.getStream(fd);
10922
+ if (device2?.sliccDevice && deps.kernel.devIoctl && !FILE_IOCTLS.has(request))
10923
+ return deviceIoctl(device2, request, varargs ?? 0, deps);
10484
10924
  if (request !== TIOCGPTN && request !== TIOCSPTLCK && request !== TIOCSCTTY && request !== TIOCSWINSZ && request !== TIOCPKT) {
10485
10925
  return ioctl(fd, op, varargs);
10486
10926
  }
@@ -10523,6 +10963,7 @@ var init_process_pty = __esm({
10523
10963
  "src/process/process-pty.ts"() {
10524
10964
  "use strict";
10525
10965
  init_captured_globals();
10966
+ init_serial_ioctl();
10526
10967
  init_wasi_errno();
10527
10968
  TIOCGPTN = 2147767344;
10528
10969
  TIOCSPTLCK = 1074025521;
@@ -11018,8 +11459,8 @@ function useDevRemoval(Fs) {
11018
11459
  const remove2 = fs[name2];
11019
11460
  if (typeof remove2 !== "function") continue;
11020
11461
  fs[name2] = (path) => {
11021
- const refused3 = devRemoval(absoluteOf(path, Fs.cwd()), fs.sliccCred);
11022
- if (refused3) throw new Fs.ErrnoError(wasiErrno(refused3));
11462
+ const refused4 = devRemoval(absoluteOf(path, Fs.cwd()), fs.sliccCred);
11463
+ if (refused4) throw new Fs.ErrnoError(wasiErrno(refused4));
11023
11464
  remove2.call(Fs, path);
11024
11465
  };
11025
11466
  }
@@ -11071,16 +11512,16 @@ function useDevFd(Fs) {
11071
11512
  }
11072
11513
  function mountTable(mounts = []) {
11073
11514
  const root = pinnedRoot ?? rootKind();
11074
- const lines = [`${root} / ${root} rw 0 0`, "devfs /dev devfs rw 0 0", "proc /proc proc rw 0 0"];
11515
+ const lines2 = [`${root} / ${root} rw 0 0`, "devfs /dev devfs rw 0 0", "proc /proc proc rw 0 0"];
11075
11516
  for (const m of mounts) {
11076
11517
  const options = capturedObject.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
11077
11518
  if (m.state !== "ok") options.push(m.state);
11078
- const access = options.includes("ro") ? [] : ["rw"];
11079
- lines.push(
11080
- `${field(m.source)} ${field(m.target)} ${m.type} ${[...access, ...options].join(",")} 0 0`
11519
+ const access2 = options.includes("ro") ? [] : ["rw"];
11520
+ lines2.push(
11521
+ `${field(m.source)} ${field(m.target)} ${m.type} ${[...access2, ...options].join(",")} 0 0`
11081
11522
  );
11082
11523
  }
11083
- return `${lines.join("\n")}
11524
+ return `${lines2.join("\n")}
11084
11525
  `;
11085
11526
  }
11086
11527
  function accounts(env = {}) {
@@ -11200,8 +11641,8 @@ function kernelAccess(imports, fs, memory, glue) {
11200
11641
  const Fs = fs();
11201
11642
  const heap = memory();
11202
11643
  const call3 = { dirfd, path, amode: amode & 7, flags };
11203
- const refused3 = Fs?.lookupPath && heap ? readonlyRefusal(Fs, heap, call3) : 0;
11204
- if (refused3 !== 0) return refused3;
11644
+ const refused4 = Fs?.lookupPath && heap ? readonlyRefusal(Fs, heap, call3) : 0;
11645
+ if (refused4 !== 0) return refused4;
11205
11646
  const rc = own2(dirfd, path, amode, flags);
11206
11647
  if (rc !== 0 || !Fs?.lookupPath || !heap) return rc;
11207
11648
  try {
@@ -11240,13 +11681,13 @@ function kernelLink(imports, fs, memory, glue) {
11240
11681
  function refusedCreation(Fs, heap, dirfd, ptr) {
11241
11682
  const name2 = cString(heap, ptr);
11242
11683
  const path = absoluteOf(name2, name2.startsWith("/") ? "/" : baseOf3(Fs, dirfd));
11243
- const refused3 = devCreation(path, Fs.sliccCred);
11244
- if (!refused3 || !Fs.lookupPath) return 0;
11684
+ const refused4 = devCreation(path, Fs.sliccCred);
11685
+ if (!refused4 || !Fs.lookupPath) return 0;
11245
11686
  try {
11246
11687
  Fs.lookupPath(path, { follow: false });
11247
11688
  return 0;
11248
11689
  } catch {
11249
- return -wasiErrno(refused3);
11690
+ return -wasiErrno(refused4);
11250
11691
  }
11251
11692
  }
11252
11693
  function devCreations(imports, fs, memory, glue) {
@@ -12549,7 +12990,7 @@ function sendmsgRequest(fd, data2, options = {}) {
12549
12990
  return {
12550
12991
  op: "sock-sendmsg",
12551
12992
  fd,
12552
- body: typeof data2 === "string" ? encoder2.encode(data2) : data2,
12993
+ body: typeof data2 === "string" ? encoder3.encode(data2) : data2,
12553
12994
  fds: options.fds ?? [],
12554
12995
  nonblock: options.nonblock === true
12555
12996
  };
@@ -12596,14 +13037,14 @@ function rightsAsync(kernel, forget, watch) {
12596
13037
  }
12597
13038
  };
12598
13039
  }
12599
- var jsonOf, bytesOf2, encoder2, RECV_DEFAULT;
13040
+ var jsonOf, bytesOf2, encoder3, RECV_DEFAULT;
12600
13041
  var init_js_rights = __esm({
12601
13042
  "src/process/js/js-rights.ts"() {
12602
13043
  "use strict";
12603
13044
  init_captured_globals();
12604
13045
  jsonOf = (r) => r.ok && r.kind === "json" ? r.json : void 0;
12605
13046
  bytesOf2 = (r) => r.ok && r.kind === "bytes" ? r.bytes : new capturedUint8Array(8);
12606
- encoder2 = new capturedTextEncoder();
13047
+ encoder3 = new capturedTextEncoder();
12607
13048
  RECV_DEFAULT = 64 * 1024;
12608
13049
  }
12609
13050
  });
@@ -12645,7 +13086,7 @@ function runSync(kernel, d, o) {
12645
13086
  const named = argv0Of(o.argv0, "run");
12646
13087
  const stdout = stdioOf(o.stdout);
12647
13088
  const stderr = stderrOf(o.stderr);
12648
- const input = typeof o.input === "string" ? encoder3.encode(o.input) : o.input;
13089
+ const input = typeof o.input === "string" ? encoder4.encode(o.input) : o.input;
12649
13090
  const maxBuffer = limit(o.maxBuffer);
12650
13091
  const timeout = limit(o.timeout);
12651
13092
  const bytes = kernel.bytes({
@@ -12668,14 +13109,14 @@ function runSync(kernel, d, o) {
12668
13109
  });
12669
13110
  return decodeRun(bytes, stdout, stderr);
12670
13111
  }
12671
- var encoder3, STDIO4;
13112
+ var encoder4, STDIO4;
12672
13113
  var init_js_run = __esm({
12673
13114
  "src/process/js/js-run.ts"() {
12674
13115
  "use strict";
12675
13116
  init_captured_globals();
12676
13117
  init_js_children();
12677
13118
  init_js_kernel();
12678
- encoder3 = new capturedTextEncoder();
13119
+ encoder4 = new capturedTextEncoder();
12679
13120
  STDIO4 = new capturedSet(["pipe", "ignore", "inherit"]);
12680
13121
  }
12681
13122
  });
@@ -12738,7 +13179,7 @@ function syncOps(kernel, shared) {
12738
13179
  return r.ok && r.kind === "bytes" ? r.bytes : new capturedUint8Array(0);
12739
13180
  };
12740
13181
  const write2 = (fd, data2, options) => {
12741
- const bytes = typeof data2 === "string" ? encoder4.encode(data2) : data2;
13182
+ const bytes = typeof data2 === "string" ? encoder5.encode(data2) : data2;
12742
13183
  const i = info(fd);
12743
13184
  if (writeDevice(i)) return bytes.length;
12744
13185
  const nonblock = options?.nonblock === true;
@@ -12772,7 +13213,7 @@ function syncOps(kernel, shared) {
12772
13213
  }
12773
13214
  };
12774
13215
  const openDev2 = (abs, options) => {
12775
- const { fd, file, ...access } = devSync(kernel, devOpen(abs, options));
13216
+ const { fd, file, ...access2 } = devSync(kernel, devOpen(abs, options));
12776
13217
  const forget = () => {
12777
13218
  infos.delete(fd);
12778
13219
  handles.delete(fd);
@@ -12782,7 +13223,17 @@ function syncOps(kernel, shared) {
12782
13223
  forget();
12783
13224
  kernel.call({ op: "fd-close", fd: n });
12784
13225
  };
12785
- const handle = file ? syncFile(kernel, fd, abs, options.append === true, forget) : streamSyncFile(fd, abs, { read: (n, max) => read2(n, max), write: write2, close }, access);
13226
+ const handle = file ? syncFile(kernel, fd, abs, options.append === true, forget) : streamSyncFile(
13227
+ fd,
13228
+ abs,
13229
+ {
13230
+ read: (n, max) => read2(n, max),
13231
+ write: write2,
13232
+ close,
13233
+ ioctl: (n, request, arg) => kernel.json(ioctlRequest(n, request, arg))
13234
+ },
13235
+ access2
13236
+ );
12786
13237
  handles.set(fd, handle.invalidate);
12787
13238
  return handle.file;
12788
13239
  };
@@ -12989,14 +13440,14 @@ function syncPathOps(kernel, resolve2, statOf, open, clean) {
12989
13440
  ...mode !== void 0 ? { mode } : {}
12990
13441
  });
12991
13442
  try {
12992
- file.write(0, typeof data2 === "string" ? encoder4.encode(data2) : data2);
13443
+ file.write(0, typeof data2 === "string" ? encoder5.encode(data2) : data2);
12993
13444
  } finally {
12994
13445
  file.close();
12995
13446
  }
12996
13447
  }
12997
13448
  };
12998
13449
  }
12999
- var encoder4, typed2, getter2, bufferOf2, offsetOf3, setInto, noLane, READ_ONLY, JsSyncKernel;
13450
+ var encoder5, typed2, getter2, bufferOf2, offsetOf3, setInto, noLane, READ_ONLY, JsSyncKernel;
13000
13451
  var init_js_sync = __esm({
13001
13452
  "src/process/js/js-sync.ts"() {
13002
13453
  "use strict";
@@ -13010,7 +13461,7 @@ var init_js_sync = __esm({
13010
13461
  init_js_kernel();
13011
13462
  init_js_proc();
13012
13463
  init_js_rights();
13013
- encoder4 = new capturedTextEncoder();
13464
+ encoder5 = new capturedTextEncoder();
13014
13465
  typed2 = capturedObject.getPrototypeOf(capturedUint8Array.prototype);
13015
13466
  getter2 = (name2) => capturedObject.getOwnPropertyDescriptor(typed2, name2)?.get;
13016
13467
  bufferOf2 = getter2("buffer");
@@ -13337,6 +13788,29 @@ var init_fd_metadata = __esm({
13337
13788
  });
13338
13789
 
13339
13790
  // src/process/kernel-streams.ts
13791
+ function deviceStat(node) {
13792
+ return {
13793
+ dev: 22,
13794
+ ino: 4096 + (node.major << 8) + node.minor,
13795
+ mode: 8192 | node.mode,
13796
+ nlink: 1,
13797
+ uid: node.uid,
13798
+ gid: node.gid,
13799
+ rdev: node.major << 8 | node.minor,
13800
+ size: 0,
13801
+ blksize: 4096,
13802
+ blocks: 0,
13803
+ atime: new capturedDate(node.atimeMs),
13804
+ mtime: new capturedDate(node.mtimeMs),
13805
+ ctime: new capturedDate(node.ctimeMs)
13806
+ };
13807
+ }
13808
+ function byIdNames(nodes) {
13809
+ const names = new capturedMap();
13810
+ for (const node of nodes)
13811
+ for (const link of node.links) names.set(link.slice(BY_ID_DIR.length + 1), node.path);
13812
+ return names;
13813
+ }
13340
13814
  function isPtyPath(path) {
13341
13815
  return PTY_PATH.test(path);
13342
13816
  }
@@ -13387,7 +13861,7 @@ function datesOf(real) {
13387
13861
  dates[key] = new capturedDate(value2);
13388
13862
  return dates;
13389
13863
  }
13390
- var POLLIN, POLLOUT, POLLERR, POLLHUP, POLLRDNORM, POLLWRNORM, TCSETSF, O_NONBLOCK2, SOCKET_MODE, PTY_PATH, TTY_PATH, PTS_MAJOR, TTY_NAME, SEALED_PATH2, STRERROR, CLIPBOARD_PATH2, EMBEDDER_PATH2, DEVICE_INO, O_CREAT3, O_EXCL2, O_NOCTTY, O_TRUNC3, O_DIRECTORY, KILLED_BY_SIGPIPE, ProcessExit, SyscallError, KernelStreams;
13864
+ var POLLIN, POLLOUT, POLLERR, POLLHUP, POLLRDNORM, POLLWRNORM, TCSETSF, O_NONBLOCK2, SOCKET_MODE, PTY_PATH, TTY_PATH, PTS_MAJOR, TTY_NAME, DEVICE_PATH2, DEVICE_LEAF, SERIAL_DIR, BY_ID_DIR, SEALED_PATH2, STRERROR, CLIPBOARD_PATH2, EMBEDDER_PATH2, DEVICE_INO, O_CREAT3, O_EXCL2, O_NOCTTY, O_TRUNC3, O_DIRECTORY, KILLED_BY_SIGPIPE, ProcessExit, SyscallError, KernelStreams;
13391
13865
  var init_kernel_streams = __esm({
13392
13866
  "src/process/kernel-streams.ts"() {
13393
13867
  "use strict";
@@ -13407,6 +13881,10 @@ var init_kernel_streams = __esm({
13407
13881
  TTY_PATH = /^\/dev\/tty\d+$/;
13408
13882
  PTS_MAJOR = 136;
13409
13883
  TTY_NAME = /^tty\d+$/;
13884
+ DEVICE_PATH2 = /^\/dev\/tty(?:USB|ACM)\d+$/;
13885
+ DEVICE_LEAF = /^tty(?:USB|ACM)\d+$/;
13886
+ SERIAL_DIR = "/dev/serial";
13887
+ BY_ID_DIR = "/dev/serial/by-id";
13410
13888
  SEALED_PATH2 = "/dev/sealed";
13411
13889
  STRERROR = {
13412
13890
  EACCES: "Permission denied",
@@ -13493,6 +13971,7 @@ var init_kernel_streams = __esm({
13493
13971
  const kfd = stream.sliccKernelFd;
13494
13972
  if (!stream.tty || kfd === void 0) return;
13495
13973
  const name2 = this.sys.ttyName?.(kfd);
13974
+ if (name2 && DEVICE_PATH2.test(name2)) stream.sliccDevice = true;
13496
13975
  if (name2) this.nameStream(stream, name2);
13497
13976
  }
13498
13977
  nameStream(stream, name2) {
@@ -13661,6 +14140,121 @@ var init_kernel_streams = __esm({
13661
14140
  return [...names, ...extra.filter((name2) => !names.includes(name2))];
13662
14141
  };
13663
14142
  }
14143
+ useDeviceNodes() {
14144
+ const fs = this.Fs;
14145
+ const { lookupNode, readdir, getPath, mkdir, mkdev, makedev, registerDevice } = fs;
14146
+ const nodes = this.sys.devNodes?.bind(this.sys);
14147
+ if (!lookupNode || !readdir || !getPath || !mkdir || !mkdev || !makedev || !nodes) return;
14148
+ const serial = (dir, name2) => dir === "/dev" && (DEVICE_LEAF.test(name2) || name2 === "serial") || dir === SERIAL_DIR && name2 === "by-id" || dir === BY_ID_DIR;
14149
+ const make = (dir, name2) => {
14150
+ if (!serial(dir, name2)) return false;
14151
+ const known = nodes();
14152
+ const links = byIdNames(known);
14153
+ if (dir === "/dev" && DEVICE_LEAF.test(name2)) {
14154
+ const node = known.find((n) => n.path === `/dev/${name2}`);
14155
+ if (!node) return false;
14156
+ const dev = makedev.call(fs, node.major, node.minor);
14157
+ registerDevice?.call(fs, dev, {
14158
+ open: (stream) => this.openDeviceNode(stream, node.path)
14159
+ });
14160
+ const made = mkdev.call(fs, node.path, dev);
14161
+ made.node_ops = { ...made.node_ops, getattr: () => this.liveDeviceStat(node.path) };
14162
+ } else if (dir === "/dev" && name2 === "serial" && links.size > 0)
14163
+ mkdir.call(fs, SERIAL_DIR, 493);
14164
+ else if (dir === SERIAL_DIR && name2 === "by-id" && links.size > 0)
14165
+ mkdir.call(fs, BY_ID_DIR, 493);
14166
+ else if (dir === BY_ID_DIR && links.has(name2))
14167
+ fs.symlink?.call(
14168
+ fs,
14169
+ `../..${links.get(name2).slice(4)}`,
14170
+ `${BY_ID_DIR}/${name2}`
14171
+ );
14172
+ else return false;
14173
+ return true;
14174
+ };
14175
+ let making = false;
14176
+ fs.lookupNode = (parent, name2) => {
14177
+ try {
14178
+ return lookupNode.call(fs, parent, name2);
14179
+ } catch (e) {
14180
+ if (making) throw e;
14181
+ making = true;
14182
+ let made;
14183
+ try {
14184
+ made = make(getPath.call(fs, parent), name2);
14185
+ } finally {
14186
+ making = false;
14187
+ }
14188
+ if (!made) throw e;
14189
+ return lookupNode.call(fs, parent, name2);
14190
+ }
14191
+ };
14192
+ this.useDeviceMeta();
14193
+ fs.readdir = (path) => {
14194
+ const at = path.replace(/\/+$/, "");
14195
+ const names = readdir.call(fs, path);
14196
+ if (at !== "/dev" && at !== SERIAL_DIR && at !== BY_ID_DIR) return names;
14197
+ const known = nodes();
14198
+ const links = byIdNames(known);
14199
+ 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()];
14200
+ const stale2 = (name2) => at === "/dev" ? (DEVICE_LEAF.test(name2) || name2 === "serial") && !wanted.includes(name2) : name2 !== "." && name2 !== ".." && !wanted.includes(name2);
14201
+ const kept = names.filter((name2) => !stale2(name2));
14202
+ return [...kept, ...wanted.filter((name2) => !kept.includes(name2))];
14203
+ };
14204
+ }
14205
+ useDeviceMeta() {
14206
+ const fs = this.Fs;
14207
+ const { lookupPath, getPath } = fs;
14208
+ const devMeta = this.sys.devMeta?.bind(this.sys);
14209
+ if (!lookupPath || !getPath || !devMeta) return;
14210
+ const deviceOf2 = (path, follow3) => {
14211
+ try {
14212
+ const at = typeof path === "string" ? lookupPath.call(fs, path, { follow: follow3 }).path : getPath.call(fs, path);
14213
+ return DEVICE_PATH2.test(at) ? at : void 0;
14214
+ } catch {
14215
+ return void 0;
14216
+ }
14217
+ };
14218
+ const set = (path, change) => this.call(() => devMeta(path, change));
14219
+ const { chmod, chown, utime } = fs;
14220
+ if (chmod)
14221
+ fs.chmod = (path, mode, dontFollow) => {
14222
+ const device2 = deviceOf2(path, !dontFollow);
14223
+ if (device2 === void 0) return chmod.call(fs, path, mode, dontFollow);
14224
+ set(device2, { mode: mode & 4095 });
14225
+ };
14226
+ if (chown)
14227
+ fs.chown = (path, uid, gid, dontFollow) => {
14228
+ const device2 = deviceOf2(path, !dontFollow);
14229
+ if (device2 === void 0) return chown.call(fs, path, uid, gid, dontFollow);
14230
+ set(device2, { uid, gid });
14231
+ };
14232
+ if (utime)
14233
+ fs.utime = (path, atime, mtime) => {
14234
+ const device2 = deviceOf2(path, true);
14235
+ if (device2 === void 0) return utime.call(fs, path, atime, mtime);
14236
+ set(device2, {
14237
+ atimeMs: capturedNumber(atime),
14238
+ mtimeMs: capturedNumber(mtime),
14239
+ now: fs.sliccTouch === true
14240
+ });
14241
+ };
14242
+ }
14243
+ liveDeviceStat(path) {
14244
+ const node = this.call(() => this.sys.devNodes?.() ?? []).find((n) => n.path === path);
14245
+ if (!node) throw new this.Fs.ErrnoError(wasiErrno("ENOENT"));
14246
+ return deviceStat(node);
14247
+ }
14248
+ openDeviceNode(stream, path) {
14249
+ const { openDev: openDev2 } = this.sys;
14250
+ stream.sliccDevice = true;
14251
+ this.openNode(
14252
+ stream,
14253
+ path,
14254
+ (flags) => openDev2.call(this.sys, path, flags | stream.flags & O_NONBLOCK2),
14255
+ true
14256
+ );
14257
+ }
13664
14258
  ptyNode(path, dev) {
13665
14259
  const fs = this.Fs;
13666
14260
  fs.registerDevice?.call(fs, dev, {
@@ -13755,6 +14349,7 @@ var init_kernel_streams = __esm({
13755
14349
  this.usePtyPaths();
13756
14350
  this.useTerminalNodes();
13757
14351
  this.usePtsNodes();
14352
+ this.useDeviceNodes();
13758
14353
  this.listSealed();
13759
14354
  const open = this.Fs.open.bind(this.Fs);
13760
14355
  this.Fs.open = (path, flags, mode) => {
@@ -13766,7 +14361,7 @@ var init_kernel_streams = __esm({
13766
14361
  if (pty) return pty;
13767
14362
  this.refuseExclusiveLink(path, flags);
13768
14363
  const stream = this.openRacing(open, path, flags, mode);
13769
- if (!stream.tty) return stream;
14364
+ if (!stream.tty || stream.sliccDevice) return stream;
13770
14365
  if (stream.path === "/dev/tty" && this.sys.openTty) {
13771
14366
  let kfd;
13772
14367
  try {
@@ -14010,6 +14605,7 @@ var init_kernel_streams = __esm({
14010
14605
  if (state.readable) mask |= POLLIN | POLLRDNORM;
14011
14606
  if (state.writable) mask |= POLLOUT | POLLWRNORM;
14012
14607
  if (state.hangup) mask |= state.readable ? POLLHUP : POLLERR;
14608
+ if (state.error) mask |= POLLERR;
14013
14609
  return mask;
14014
14610
  },
14015
14611
  dup: (stream) => {
@@ -14111,9 +14707,9 @@ function kernelEntry2(stream, kernel) {
14111
14707
  }
14112
14708
  const device2 = stream.path === void 0 ? void 0 : NAMED_DEVICES[stream.path];
14113
14709
  const kind = stream.sliccKernelFile ? "file" : stream.tty ? "tty" : device2 ?? "stream";
14114
- const access = kind === "tty" ? O_RDWR4 : (stream.flags ?? O_RDWR4) & O_ACCMODE4;
14710
+ const access2 = kind === "tty" ? O_RDWR4 : (stream.flags ?? O_RDWR4) & O_ACCMODE4;
14115
14711
  const fifo = stream.sliccKernelFifo ? { fifo: stream.sliccKernelFifo } : {};
14116
- return { fd: stream.fd, kernel, kind, flags: access, ...cloexec, ...fifo };
14712
+ return { fd: stream.fd, kernel, kind, flags: access2, ...cloexec, ...fifo };
14117
14713
  }
14118
14714
  function describeInherited(Fs, sys, streams2, livePath, actions = []) {
14119
14715
  const table = new capturedMap();
@@ -14142,8 +14738,8 @@ function describeInherited(Fs, sys, streams2, livePath, actions = []) {
14142
14738
  function deviceOfStream(stream) {
14143
14739
  const device2 = stream.path === void 0 ? void 0 : DEVICES2[stream.path];
14144
14740
  if (!device2 || stream.sliccKernelFd !== void 0) return void 0;
14145
- const access = ACCESS[stream.flags & O_ACCMODE4];
14146
- return { device: device2, ...access ? { access } : {} };
14741
+ const access2 = ACCESS[stream.flags & O_ACCMODE4];
14742
+ return { device: device2, ...access2 ? { access: access2 } : {} };
14147
14743
  }
14148
14744
  function inheritedSlot(fd, stream) {
14149
14745
  if (stream.sliccKernelFd === void 0) {
@@ -14160,12 +14756,12 @@ function place(Fs, stream, fd) {
14160
14756
  return moved;
14161
14757
  }
14162
14758
  function placeholder(Fs, streams2, entry) {
14163
- const access = entry.flags ?? O_RDWR4;
14164
- if (entry.kind === "socket") return streams2.socketStream(access);
14759
+ const access2 = entry.flags ?? O_RDWR4;
14760
+ if (entry.kind === "socket") return streams2.socketStream(access2);
14165
14761
  if (entry.kind === "tty") return Fs.open("/dev/tty", O_RDWR4);
14166
- if (entry.kind === "stream" || DEVICE_KINDS.has(entry.kind)) return Fs.open("/dev/null", access);
14762
+ if (entry.kind === "stream" || DEVICE_KINDS.has(entry.kind)) return Fs.open("/dev/null", access2);
14167
14763
  Fs.mkdirTree(PLACEHOLDER_DIR);
14168
- return Fs.open(`${PLACEHOLDER_DIR}/${entry.fd}`, access | O_CREAT4);
14764
+ return Fs.open(`${PLACEHOLDER_DIR}/${entry.fd}`, access2 | O_CREAT4);
14169
14765
  }
14170
14766
  function asFifo(stream, streams2, identity) {
14171
14767
  const inos = streamInos.get(streams2) ?? new capturedMap();
@@ -14883,6 +15479,29 @@ function ownBytes(bytes) {
14883
15479
  function ownedBuffer(body, given) {
14884
15480
  return body === given ? void 0 : [body.buffer];
14885
15481
  }
15482
+ function deviceSys(call3) {
15483
+ const json2 = (r) => r.ok && r.kind === "json" ? r.json : void 0;
15484
+ return {
15485
+ devNodes() {
15486
+ return json2(call3({ op: "fd-dev-nodes" }, "fd-dev-nodes"));
15487
+ },
15488
+ openDev(path, flags) {
15489
+ return json2(call3({ op: "fd-open-dev", path, flags }, `fd-open-dev ${path}`));
15490
+ },
15491
+ devIoctl(fd, request, arg) {
15492
+ const req = {
15493
+ op: "fd-dev-ioctl",
15494
+ fd,
15495
+ request,
15496
+ ...arg !== void 0 ? { arg } : {}
15497
+ };
15498
+ return json2(call3(req, `fd-dev-ioctl ${fd}`));
15499
+ },
15500
+ devMeta(path, change) {
15501
+ call3({ op: "fd-dev-meta", path, ...change }, `fd-dev-meta ${path}`);
15502
+ }
15503
+ };
15504
+ }
14886
15505
  function kernelSys(transport) {
14887
15506
  const call3 = (req, label, transfer) => {
14888
15507
  const r = transport.call(req, capturedNumber.POSITIVE_INFINITY, label, transfer);
@@ -14891,6 +15510,7 @@ function kernelSys(transport) {
14891
15510
  };
14892
15511
  const json2 = (r) => r.ok && r.kind === "json" ? r.json : void 0;
14893
15512
  return {
15513
+ ...deviceSys(call3),
14894
15514
  read(fd, max, opts) {
14895
15515
  const flags = {
14896
15516
  ...opts?.nonblock ? { nonblock: true } : {},
@@ -15003,8 +15623,9 @@ function kernelSys(transport) {
15003
15623
  tcgets(fd) {
15004
15624
  return json2(call3({ op: "tty-get", fd }, `tty-get ${fd}`));
15005
15625
  },
15006
- tcsets(fd, termios, flush) {
15007
- call3({ op: "tty-set", fd, termios, ...flush ? { flush } : {} }, `tty-set ${fd}`);
15626
+ tcsets(fd, termios, flush, drain2) {
15627
+ const how = { ...flush ? { flush } : {}, ...drain2 ? { drain: drain2 } : {} };
15628
+ call3({ op: "tty-set", fd, termios, ...how }, `tty-set ${fd}`);
15008
15629
  },
15009
15630
  winsize(fd) {
15010
15631
  return json2(call3({ op: "tty-winsz", fd }, `tty-winsz ${fd}`));
@@ -15260,8 +15881,8 @@ async function runWasmProcess(init, port3, deps = {}) {
15260
15881
  };
15261
15882
  const transport = credSyncTransport(gated, () => early(), init.cred);
15262
15883
  const sys = kernelSys(transport);
15263
- const encoder6 = new capturedTextEncoder();
15264
- const say = (fd) => (text) => sys.write(fd, encoder6.encode(`${text}
15884
+ const encoder7 = new capturedTextEncoder();
15885
+ const say = (fd) => (text) => sys.write(fd, encoder7.encode(`${text}
15265
15886
  `));
15266
15887
  let ready;
15267
15888
  let failed5;
@@ -15522,7 +16143,7 @@ var init_process_runtime = __esm({
15522
16143
  O_RDWR6 = 2;
15523
16144
  O_ACCMODE5 = 3;
15524
16145
  O_DIRECTORY2 = 65536;
15525
- accessMode = (access) => access === "read" ? O_RDONLY2 : access === "write" ? O_WRONLY6 : O_RDWR6;
16146
+ accessMode = (access2) => access2 === "read" ? O_RDONLY2 : access2 === "write" ? O_WRONLY6 : O_RDWR6;
15526
16147
  PLACED = new capturedSet([
15527
16148
  "stream",
15528
16149
  "file",
@@ -16399,7 +17020,7 @@ function tracked2(pending, writeAll, writeSome, failed5) {
16399
17020
  return (fd, data2, options) => {
16400
17021
  const status2 = failed5?.();
16401
17022
  if (status2 !== void 0) return capturedPromise.reject(new JsExit(status2));
16402
- const bytes = typeof data2 === "string" ? encoder5.encode(data2) : data2;
17023
+ const bytes = typeof data2 === "string" ? encoder6.encode(data2) : data2;
16403
17024
  let settle;
16404
17025
  const settled2 = new capturedPromise((resolve2) => settle = resolve2);
16405
17026
  pending.add(settled2);
@@ -16623,7 +17244,7 @@ function createContext(o) {
16623
17244
  return { ctx, drain: drain2 };
16624
17245
  }
16625
17246
  async function openDev(kernel, abs, options, state) {
16626
- const { fd, file, ...access } = await devAsync(kernel, devOpen(abs, options));
17247
+ const { fd, file, ...access2 } = await devAsync(kernel, devOpen(abs, options));
16627
17248
  const forget = () => {
16628
17249
  state.infos.delete(fd);
16629
17250
  state.handles.delete(fd);
@@ -16636,8 +17257,13 @@ async function openDev(kernel, abs, options, state) {
16636
17257
  const handle = file ? fileHandle(kernel, fd, abs, options.append === true, forget) : streamFile(
16637
17258
  fd,
16638
17259
  abs,
16639
- { read: (n, max) => state.read(n, max), write: state.write, close },
16640
- access
17260
+ {
17261
+ read: (n, max) => state.read(n, max),
17262
+ write: state.write,
17263
+ close,
17264
+ ioctl: (n, request, arg) => kernel.json(ioctlRequest(n, request, arg))
17265
+ },
17266
+ access2
16641
17267
  );
16642
17268
  state.handles.set(fd, handle.invalidate);
16643
17269
  return handle.file;
@@ -16806,14 +17432,14 @@ function pathOps(kernel, resolve2, statOf, open, clean) {
16806
17432
  true
16807
17433
  );
16808
17434
  try {
16809
- await file.write(0, typeof data2 === "string" ? encoder5.encode(data2) : data2);
17435
+ await file.write(0, typeof data2 === "string" ? encoder6.encode(data2) : data2);
16810
17436
  } finally {
16811
17437
  await file.close();
16812
17438
  }
16813
17439
  }
16814
17440
  };
16815
17441
  }
16816
- var KERNEL_INFO, JsExit, encoder5, cryptoRandom, MEMORY_FIELDS;
17442
+ var KERNEL_INFO, JsExit, encoder6, cryptoRandom, MEMORY_FIELDS;
16817
17443
  var init_js_context = __esm({
16818
17444
  "src/process/js/js-context.ts"() {
16819
17445
  "use strict";
@@ -16849,7 +17475,7 @@ var init_js_context = __esm({
16849
17475
  this.status = status2;
16850
17476
  }
16851
17477
  };
16852
- encoder5 = new capturedTextEncoder();
17478
+ encoder6 = new capturedTextEncoder();
16853
17479
  cryptoRandom = (bytes) => void capturedcrypto.getRandomValues(bytes);
16854
17480
  MEMORY_FIELDS = [
16855
17481
  "heapUsed",
@@ -17206,32 +17832,61 @@ function ptsBridge(fs, call3) {
17206
17832
  }
17207
17833
  };
17208
17834
  const dir = () => devSync(kernel, devStat(PTS, true));
17209
- const statOf = (p, real) => {
17835
+ const statOf = (p, real, follow3 = true) => {
17210
17836
  if (p === PTS) return dir();
17837
+ if (SERIAL_AREA.test(p)) return devSync(kernel, devStat(p, follow3));
17211
17838
  return PTS_ENTRY.test(p) ? entry(p) : real();
17212
17839
  };
17840
+ const serialNames = () => devSync(kernel, devReaddir("/dev")).filter((n) => SERIAL_AREA.test(`/dev/${n}`));
17841
+ const withSerial = (listed2) => [
17842
+ ...listed2,
17843
+ ...serialNames().filter((n) => !listed2.includes(n))
17844
+ ];
17213
17845
  const names = (known = open()) => known.map(capturedString).sort();
17846
+ const serialEntry = (p, name2) => {
17847
+ try {
17848
+ return [name2, devSync(kernel, devStat(p, false))];
17849
+ } catch {
17850
+ return [name2, null];
17851
+ }
17852
+ };
17214
17853
  const over = {
17215
17854
  stat: (p) => statOf(p, () => fs.stat(p)),
17216
- lstat: (p) => statOf(p, () => fs.lstat(p)),
17855
+ lstat: (p) => statOf(p, () => fs.lstat(p), false),
17856
+ readlink: (p) => SERIAL_AREA.test(p) ? devSync(kernel, devReadlink(p)) : fs.readlink(p),
17217
17857
  exists: (p) => {
17218
17858
  if (p === PTS) return true;
17859
+ if (SERIAL_AREA.test(p)) {
17860
+ try {
17861
+ devSync(kernel, devStat(p, true));
17862
+ return true;
17863
+ } catch {
17864
+ return false;
17865
+ }
17866
+ }
17219
17867
  if (!PTS_ENTRY.test(p)) return fs.exists(p);
17220
17868
  return open().includes(capturedNumber(PTS_ENTRY.exec(p)?.[1]));
17221
17869
  },
17222
17870
  readdir: (p) => {
17223
17871
  if (p === PTS) return names();
17872
+ if (SERIAL_AREA.test(p)) return devSync(kernel, devReaddir(p));
17224
17873
  const listed2 = fs.readdir(p);
17225
- return p === "/dev" && !listed2.includes("pts") ? [...listed2, "pts"] : listed2;
17874
+ if (p !== "/dev") return listed2;
17875
+ return withSerial(listed2.includes("pts") ? listed2 : [...listed2, "pts"]);
17226
17876
  },
17227
17877
  readdirStat: (p) => {
17228
17878
  if (p === PTS) {
17229
17879
  const known = open();
17230
17880
  return names(known).map((name2) => listedEntry(name2, known));
17231
17881
  }
17882
+ if (SERIAL_AREA.test(p))
17883
+ return devSync(kernel, devReaddir(p)).map((name2) => serialEntry(`${p}/${name2}`, name2));
17232
17884
  const listed2 = fs.readdirStat(p);
17233
- if (p !== "/dev" || listed2.some(([name2]) => name2 === "pts")) return listed2;
17234
- return [...listed2, ["pts", dir()]];
17885
+ if (p !== "/dev") return listed2;
17886
+ const all = listed2.some(([name2]) => name2 === "pts") ? listed2 : [...listed2, ["pts", dir()]];
17887
+ const have = all.map(([name2]) => name2);
17888
+ const extra = serialNames().filter((n) => !have.includes(n));
17889
+ return [...all, ...extra.map((name2) => serialEntry(`/dev/${name2}`, name2))];
17235
17890
  }
17236
17891
  };
17237
17892
  return new capturedProxy(fs, {
@@ -17242,7 +17897,7 @@ function ptsBridge(fs, call3) {
17242
17897
  }
17243
17898
  });
17244
17899
  }
17245
- var PTS, PTS_ENTRY;
17900
+ var PTS, PTS_ENTRY, SERIAL_AREA;
17246
17901
  var init_wasi_ptsfs = __esm({
17247
17902
  "src/process/wasi/wasi-ptsfs.ts"() {
17248
17903
  "use strict";
@@ -17251,6 +17906,7 @@ var init_wasi_ptsfs = __esm({
17251
17906
  init_js_dev();
17252
17907
  PTS = "/dev/pts";
17253
17908
  PTS_ENTRY = /^\/dev\/pts\/(\d+)$/;
17909
+ SERIAL_AREA = /^\/dev\/(?:tty(?:USB|ACM)\d+|serial(?:\/.*)?)$/;
17254
17910
  }
17255
17911
  });
17256
17912
 
@@ -17464,13 +18120,13 @@ var init_wasi_stats = __esm({
17464
18120
  }
17465
18121
  report() {
17466
18122
  const ms = (v) => v.toFixed(1).padStart(9);
17467
- const lines = this.phases.map(([name2, t]) => `${ms(t)} ms ${name2}`);
17468
- lines.push(`${"total ms".padStart(12)} ${"calls".padStart(7)} ${"max ms".padStart(9)} call`);
18123
+ const lines2 = this.phases.map(([name2, t]) => `${ms(t)} ms ${name2}`);
18124
+ lines2.push(`${"total ms".padStart(12)} ${"calls".padStart(7)} ${"max ms".padStart(9)} call`);
17469
18125
  const sorted = [...this.rows].sort((a, b) => b[1].ms - a[1].ms);
17470
18126
  for (const [key, row] of sorted) {
17471
- lines.push(`${ms(row.ms)} ${capturedString(row.n).padStart(7)} ${ms(row.max)} ${key}`);
18127
+ lines2.push(`${ms(row.ms)} ${capturedString(row.n).padStart(7)} ${ms(row.max)} ${key}`);
17472
18128
  }
17473
- return `${lines.join("\n")}
18129
+ return `${lines2.join("\n")}
17474
18130
  `;
17475
18131
  }
17476
18132
  };
@@ -19016,11 +19672,11 @@ function parseFunctionNames(payload) {
19016
19672
  }
19017
19673
  return names;
19018
19674
  }
19019
- function mainModule(lines) {
19020
- for (let i = lines.length - 1; i >= 0; i--) {
19021
- const m = WASM_MODULE.exec(lines[i]);
19675
+ function mainModule(lines2) {
19676
+ for (let i = lines2.length - 1; i >= 0; i--) {
19677
+ const m = WASM_MODULE.exec(lines2[i]);
19022
19678
  if (!m) continue;
19023
- return lines.slice(i + 1).some((line) => /^\s+at /.test(line)) ? m[1] : void 0;
19679
+ return lines2.slice(i + 1).some((line) => /^\s+at /.test(line)) ? m[1] : void 0;
19024
19680
  }
19025
19681
  return void 0;
19026
19682
  }
@@ -19184,12 +19840,12 @@ function unsupportedImport(module2, memory, extended = false, foreign = {}) {
19184
19840
  const wasix = imports.some((i) => i.module === WASIX);
19185
19841
  const wasi = wasix || imports.some((i) => i.module === PREVIEW1);
19186
19842
  const scope2 = { extended, wasi, wasix, pie: dylinkInfo(module2) !== void 0, memory };
19187
- const refused3 = imports.find((imp) => !accepted(imp, scope2));
19188
- if (refused3) {
19189
- if (refused3.kind === "memory" || refused3.module === "wasi") {
19190
- return `imports ${refused3.module}.${refused3.name}: no WASI program this host runs`;
19843
+ const refused4 = imports.find((imp) => !accepted(imp, scope2));
19844
+ if (refused4) {
19845
+ if (refused4.kind === "memory" || refused4.module === "wasi") {
19846
+ return `imports ${refused4.module}.${refused4.name}: no WASI program this host runs`;
19191
19847
  }
19192
- return `imports ${refused3.module}.${refused3.name}: no WASI preview1 program (an Emscripten one runs with its glue)`;
19848
+ return `imports ${refused4.module}.${refused4.name}: no WASI preview1 program (an Emscripten one runs with its glue)`;
19193
19849
  }
19194
19850
  const unanswerable = extended ? void 0 : firstOther(foreign);
19195
19851
  if (unanswerable) return noErrno(unanswerable);
@@ -19484,14 +20140,14 @@ async function runWasiProcess(init, port3) {
19484
20140
  const stats = init.env.SLICC_WASI_STATS === "1" ? new WasiStats() : void 0;
19485
20141
  const call3 = stats ? timedCalls(stats, kernelCall) : kernelCall;
19486
20142
  const { module: module2 } = init.program;
19487
- const refused3 = unsupportedImport(
20143
+ const refused4 = unsupportedImport(
19488
20144
  module2,
19489
20145
  init.program.memory,
19490
20146
  init.program.imports !== void 0,
19491
20147
  init.program.foreign
19492
20148
  );
19493
- if (refused3) {
19494
- say(refused3);
20149
+ if (refused4) {
20150
+ say(refused4);
19495
20151
  return 126;
19496
20152
  }
19497
20153
  const fork = init.fork?.wasi;
@@ -19579,9 +20235,9 @@ async function runWasiProcess(init, port3) {
19579
20235
  function trapMessage(e, env, where = "", name2) {
19580
20236
  const head = `wasm trap${where}: ${e.message}`;
19581
20237
  if (env.SLICC_WASM_BACKTRACE !== "1") return head;
19582
- const lines = (rawStack(e) ?? "").split("\n");
19583
- const main = name2 && mainModule(lines);
19584
- const frames = lines.filter((line) => /^\s+at .*wasm:\/\/wasm\//.test(line)).slice(0, BACKTRACE_FRAMES).map((line) => name2 && main ? nameFrame(line, main, name2) : line);
20238
+ const lines2 = (rawStack(e) ?? "").split("\n");
20239
+ const main = name2 && mainModule(lines2);
20240
+ const frames = lines2.filter((line) => /^\s+at .*wasm:\/\/wasm\//.test(line)).slice(0, BACKTRACE_FRAMES).map((line) => name2 && main ? nameFrame(line, main, name2) : line);
19585
20241
  return frames.length ? `${head}
19586
20242
  ${frames.join("\n")}` : head;
19587
20243
  }
@@ -20557,17 +21213,17 @@ function processEntry(port3, run = runWasmProcess, wasi = loadWasi, js = loadJs,
20557
21213
  message: err instanceof capturedError ? rawStack(err) ?? err.message : capturedString(err),
20558
21214
  ...code2 === void 0 ? {} : { code: code2 }
20559
21215
  });
20560
- let refused3;
21216
+ let refused4;
20561
21217
  const sealOnce = () => {
20562
- if (refused3 === void 0) {
21218
+ if (refused4 === void 0) {
20563
21219
  try {
20564
21220
  seal2();
20565
- refused3 = null;
21221
+ refused4 = null;
20566
21222
  } catch (error) {
20567
- refused3 = { error };
21223
+ refused4 = { error };
20568
21224
  }
20569
21225
  }
20570
- return refused3;
21226
+ return refused4;
20571
21227
  };
20572
21228
  return (data2) => {
20573
21229
  const type = data2?.type;