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