@ai-ecoverse/slicc-kernel 1.74.0 → 1.75.1

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.
@@ -1343,7 +1343,7 @@ function promoteRequest(fd, f, handed) {
1343
1343
  ...f.buffer.handle !== void 0 ? { handle: f.buffer.handle } : {}
1344
1344
  };
1345
1345
  }
1346
- var SYNTHETIC_DIRS, O_NONBLOCK, O_WRONLY, O_RDWR, O_APPEND, SEALED_PATH, WasiFds, GEN;
1346
+ var SYNTHETIC_DIRS, O_NONBLOCK, O_WRONLY, O_RDWR, O_APPEND, SEALED_PATH, CLIPBOARD_PATH, WasiFds, GEN;
1347
1347
  var init_wasi_fds = __esm({
1348
1348
  "src/process/wasi/wasi-fds.ts"() {
1349
1349
  "use strict";
@@ -1357,6 +1357,7 @@ var init_wasi_fds = __esm({
1357
1357
  O_RDWR = 2;
1358
1358
  O_APPEND = 1024;
1359
1359
  SEALED_PATH = "/dev/sealed";
1360
+ CLIPBOARD_PATH = "/dev/clipboard";
1360
1361
  WasiFds = class {
1361
1362
  table = /* @__PURE__ */ new Map();
1362
1363
  buffers = /* @__PURE__ */ new Map();
@@ -1657,9 +1658,12 @@ var init_wasi_fds = __esm({
1657
1658
  const e = this.get(fd);
1658
1659
  this.table.delete(fd);
1659
1660
  this.cloexec.delete(fd);
1660
- this.kernel.sys.close(fd);
1661
- this.release(e);
1662
- this.bump();
1661
+ try {
1662
+ this.kernel.sys.close(fd);
1663
+ } finally {
1664
+ this.release(e);
1665
+ this.bump();
1666
+ }
1663
1667
  }
1664
1668
  renumber(from, to, keep = false) {
1665
1669
  const e = this.get(from);
@@ -1822,8 +1826,8 @@ var init_wasi_fds = __esm({
1822
1826
  const joined2 = path.startsWith("/") ? path : `${this.dir(dirfd).path}/${path}`;
1823
1827
  return joined2.split("/").includes("..") ? physical(this.fs, joined2) : normalize(joined2);
1824
1828
  }
1825
- openKernelDevice(path, oflags, fdflags) {
1826
- const { openSealed, openTty } = this.kernel.sys;
1829
+ openKernelDevice(path, oflags, fdflags, rights) {
1830
+ const { openSealed, openClipboard, openTty } = this.kernel.sys;
1827
1831
  const nonblock = (fdflags & FDFLAGS.NONBLOCK) !== 0;
1828
1832
  let fd;
1829
1833
  let kind;
@@ -1832,6 +1836,9 @@ var init_wasi_fds = __esm({
1832
1836
  if (oflags & OFLAGS.CREAT && oflags & OFLAGS.EXCL) throw new WasiError("EEXIST");
1833
1837
  fd = openSealed.call(this.kernel.sys);
1834
1838
  kind = "sealed";
1839
+ } else if (path === CLIPBOARD_PATH && openClipboard) {
1840
+ fd = this.openClipboard(openClipboard, oflags, rights);
1841
+ kind = "clipboard";
1835
1842
  } else if (path === "/dev/tty" || /^\/dev\/tty\d+$/.test(path)) {
1836
1843
  fd = openTty?.call(this.kernel.sys, path === "/dev/tty" ? void 0 : path);
1837
1844
  if (fd === void 0) throw new WasiError("ENXIO");
@@ -1841,10 +1848,17 @@ var init_wasi_fds = __esm({
1841
1848
  this.bump();
1842
1849
  return fd;
1843
1850
  }
1851
+ openClipboard(openClipboard, oflags, rights) {
1852
+ if (oflags & OFLAGS.DIRECTORY) throw new WasiError("ENOTDIR");
1853
+ if (oflags & OFLAGS.CREAT && oflags & OFLAGS.EXCL) throw new WasiError("EEXIST");
1854
+ const want = wantOf(oflags, rights);
1855
+ const flags = want === 6 ? O_RDWR : want === 2 ? O_WRONLY : 0;
1856
+ return openClipboard.call(this.kernel.sys, flags);
1857
+ }
1844
1858
  open(path, oflags, rights, fdflags) {
1845
1859
  const device2 = deviceOf(path);
1846
1860
  if (device2) return this.install({ type: "device", device: device2 });
1847
- const kernelDevice = this.openKernelDevice(path, oflags, fdflags);
1861
+ const kernelDevice = this.openKernelDevice(path, oflags, fdflags, rights);
1848
1862
  if (kernelDevice !== void 0) return kernelDevice;
1849
1863
  const alias = stdioAlias(path);
1850
1864
  if (alias !== void 0) return this.reopen(alias);
@@ -2287,6 +2301,7 @@ function kernelFiletype(kind) {
2287
2301
  switch (kind) {
2288
2302
  case "tty":
2289
2303
  case "sealed":
2304
+ case "clipboard":
2290
2305
  return { filetype: FILETYPE.CHARACTER_DEVICE, seeks: false };
2291
2306
  case "device":
2292
2307
  return { filetype: FILETYPE.CHARACTER_DEVICE, seeks: true };
@@ -2444,7 +2459,7 @@ var init_wasi_host = __esm({
2444
2459
  const e = fds.get(fd);
2445
2460
  if (e.type === "device") return ROOT_IDS;
2446
2461
  const kind = e.type === "kernel" ? fds.kind(fd, e) : "file";
2447
- if (kind === "sealed") return ROOT_IDS;
2462
+ if (kind === "sealed" || kind === "clipboard") return ROOT_IDS;
2448
2463
  if (kind !== "file") return void 0;
2449
2464
  try {
2450
2465
  return this.o.fs.stat(pathOf(fd));
@@ -2501,12 +2516,12 @@ var init_wasi_host = __esm({
2501
2516
  fd_mode: (fd, out) => {
2502
2517
  const e = fds.get(fd);
2503
2518
  const kind = e.type === "kernel" ? fds.kind(fd, e) : void 0;
2504
- const mode = kind === "stream" ? S_IFIFO | 384 : kind === "sealed" ? SEALED_MODE : modeOf(this.o.fs.stat(pathOf(fd)));
2519
+ const mode = kind === "stream" ? S_IFIFO | 384 : kind === "sealed" || kind === "clipboard" ? SEALED_MODE : modeOf(this.o.fs.stat(pathOf(fd)));
2505
2520
  mem.view().setUint32(out, mode, true);
2506
2521
  },
2507
2522
  path_mode: (dirfd, p, l, flags, out) => {
2508
2523
  const path = at(dirfd, p, l);
2509
- if (path === SEALED_PATH && this.o.kernel.sys.openSealed)
2524
+ if (this.synthetic(path) && deviceOf(path) === void 0)
2510
2525
  return void mem.view().setUint32(out, SEALED_MODE, true);
2511
2526
  const st = flags & AT_SYMLINK_NOFOLLOW ? this.o.fs.lstat(path) : this.o.fs.stat(path);
2512
2527
  mem.view().setUint32(out, modeOf(st), true);
@@ -3009,7 +3024,7 @@ var init_wasi_host = __esm({
3009
3024
  return gone ? { ...out, nlink: 0n } : out;
3010
3025
  }
3011
3026
  synthetic(path) {
3012
- return deviceOf(path) !== void 0 || path === SEALED_PATH && !!this.o.kernel.sys.openSealed;
3027
+ return deviceOf(path) !== void 0 || path === SEALED_PATH && !!this.o.kernel.sys.openSealed || path === CLIPBOARD_PATH && !!this.o.kernel.sys.openClipboard;
3013
3028
  }
3014
3029
  pathFilestat(path, lookup2) {
3015
3030
  if (this.synthetic(path)) {
@@ -3737,6 +3752,11 @@ var init_signals = __esm({
3737
3752
  });
3738
3753
 
3739
3754
  // src/process/js/js-kernel.ts
3755
+ function callError(result, op) {
3756
+ const { errno: errno2, message: message2 } = result;
3757
+ const detailed = typeof message2 === "string" && message2 !== errno2 && DETAILED.has(op);
3758
+ return new JsCallError(errno2, detailed ? `${op}: ${message2}` : op);
3759
+ }
3740
3760
  function untilAborted(work, signal) {
3741
3761
  if (!signal) return work;
3742
3762
  return new Promise((resolve) => {
@@ -3753,7 +3773,7 @@ function caughtOf(handlers) {
3753
3773
  for (const sig of handlers.keys()) caught |= sigbit(sig);
3754
3774
  return caught;
3755
3775
  }
3756
- var JsCallError, JsKernel;
3776
+ var JsCallError, DETAILED, JsKernel;
3757
3777
  var init_js_kernel = __esm({
3758
3778
  "src/process/js/js-kernel.ts"() {
3759
3779
  "use strict";
@@ -3768,6 +3788,12 @@ var init_js_kernel = __esm({
3768
3788
  this.code = code2;
3769
3789
  }
3770
3790
  };
3791
+ DETAILED = /* @__PURE__ */ new Set([
3792
+ "fd-read",
3793
+ "fd-write",
3794
+ "fd-close",
3795
+ "fd-open-clipboard"
3796
+ ]);
3771
3797
  JsKernel = class {
3772
3798
  header;
3773
3799
  transport;
@@ -3794,7 +3820,7 @@ var init_js_kernel = __esm({
3794
3820
  }
3795
3821
  async call(req) {
3796
3822
  const result = await this.raw(req);
3797
- if (!result.ok) throw new JsCallError(result.errno, req.op);
3823
+ if (!result.ok) throw callError(result, req.op);
3798
3824
  return result;
3799
3825
  }
3800
3826
  async json(req) {
@@ -3810,7 +3836,7 @@ var init_js_kernel = __esm({
3810
3836
  const id = await this.submit(req, track);
3811
3837
  const result = await this.settled(id, signal);
3812
3838
  if (result.ok) return result;
3813
- if (result.errno !== "EINTR") throw new JsCallError(result.errno, req.op);
3839
+ if (result.errno !== "EINTR") throw callError(result, req.op);
3814
3840
  }
3815
3841
  }
3816
3842
  acks = [];
@@ -4190,6 +4216,7 @@ function* lookup(abs) {
4190
4216
  const name2 = abs.slice("/dev/".length);
4191
4217
  if (DEVICES.includes(name2)) return { kind: "device" };
4192
4218
  if (name2 === "tty") return { kind: "tty" };
4219
+ if (name2 === "clipboard") return { kind: "clipboard" };
4193
4220
  const std = STDIO3.indexOf(name2);
4194
4221
  if (std >= 0) return { kind: "link", target: `${SELF_FD}/${std}`, fd: std };
4195
4222
  if (name2 === "fd") return { kind: "link", target: SELF_FD };
@@ -4233,7 +4260,8 @@ function* fdStat(fd, path) {
4233
4260
  return yield* pathStat(file.path);
4234
4261
  }
4235
4262
  if (info.terminal) return yield* terminalStat(info.terminal);
4236
- if (info.kind === "device" || info.kind === "tty") return nodeStat(CHAR | 438);
4263
+ if (info.kind === "device" || info.kind === "tty" || info.kind === "clipboard")
4264
+ return nodeStat(CHAR | 438);
4237
4265
  if (info.kind === "socket") return nodeStat(49663);
4238
4266
  return nodeStat(4480);
4239
4267
  }
@@ -4243,6 +4271,7 @@ function* fdTarget(fd, path) {
4243
4271
  if (info.kind === "file") return (yield { op: "fd-vfs-stat", fd }).path;
4244
4272
  if (info.terminal) return info.terminal;
4245
4273
  if (info.meta && "device" in info.meta) return `/dev/${info.meta.device}`;
4274
+ if (info.kind === "clipboard") return "/dev/clipboard";
4246
4275
  return `${info.kind === "socket" ? "socket" : "pipe"}:[${fd}]`;
4247
4276
  }
4248
4277
  function linkStat(target) {
@@ -4255,6 +4284,7 @@ function* devStat(abs, follow2) {
4255
4284
  return nodeStat(node.mode);
4256
4285
  case "device":
4257
4286
  case "tty":
4287
+ case "clipboard":
4258
4288
  return nodeStat(CHAR | 438);
4259
4289
  case "terminal":
4260
4290
  return yield* terminalStat(node.name);
@@ -4289,7 +4319,7 @@ function* devReaddir(abs) {
4289
4319
  if (node.kind !== "dir") throw new JsCallError("ENOTDIR", abs);
4290
4320
  const names = yield { op: "fd-tty-names" };
4291
4321
  const ttys = names.filter((n) => /^\/dev\/tty\d+$/.test(n)).map((n) => n.slice(5));
4292
- return [...DEVICES, "tty", "fd", ...STDIO3, ...ttys].sort();
4322
+ return [...DEVICES, "clipboard", "tty", "fd", ...STDIO3, ...ttys].sort();
4293
4323
  }
4294
4324
  function* aliasOf(fd, abs, o) {
4295
4325
  const info = yield* fdInfo(fd, abs);
@@ -4318,6 +4348,11 @@ function* opened(abs, o) {
4318
4348
  return { fd: yield openRequest(abs, { ...o, create: false }), file: false };
4319
4349
  case "tty":
4320
4350
  return { fd: yield { op: "fd-open-tty" }, file: false };
4351
+ case "clipboard": {
4352
+ const write2 = o.write === true || o.append === true || o.truncate === true;
4353
+ const flags = write2 ? o.read === true ? 2 : 1 : 0;
4354
+ return { fd: yield { op: "fd-open-clipboard", flags }, file: false };
4355
+ }
4321
4356
  case "terminal": {
4322
4357
  const req = node.pts === void 0 ? { op: "fd-open-tty", name: node.name } : { op: "pty-slave-open", n: node.pts, noctty: true };
4323
4358
  return { fd: yield req, file: false };
@@ -5618,7 +5653,7 @@ var init_js_sync = __esm({
5618
5653
  }
5619
5654
  call(req) {
5620
5655
  const result = this.raw(req);
5621
- if (!result.ok) throw new JsCallError(result.errno, req.op);
5656
+ if (!result.ok) throw callError(result, req.op);
5622
5657
  return result;
5623
5658
  }
5624
5659
  json(req) {
@@ -5633,7 +5668,7 @@ var init_js_sync = __esm({
5633
5668
  for (; ; ) {
5634
5669
  const result = this.raw(req);
5635
5670
  if (result.ok) return result;
5636
- if (result.errno !== "EINTR") throw new JsCallError(result.errno, req.op);
5671
+ if (result.errno !== "EINTR") throw callError(result, req.op);
5637
5672
  }
5638
5673
  }
5639
5674
  };
@@ -6225,7 +6260,7 @@ async function receiveSome(kernel, fd, max, signal) {
6225
6260
  signal.throwIfAborted();
6226
6261
  const r = await kernel.raw({ op: "fd-read", fd, max, nonblock: true });
6227
6262
  if (r.ok) return r.kind === "bytes" ? r.bytes : new Uint8Array(0);
6228
- if (r.errno !== "EAGAIN") throw new JsCallError(r.errno, "fd-read");
6263
+ if (r.errno !== "EAGAIN") throw callError(r, "fd-read");
6229
6264
  await kernel.blocking({ op: "fd-select", read: [fd], write: [], timeoutMs: -1 }, signal);
6230
6265
  }
6231
6266
  }
@@ -6234,7 +6269,7 @@ async function sendSome(kernel, fd, body, signal) {
6234
6269
  signal.throwIfAborted();
6235
6270
  const r = await kernel.raw({ op: "fd-write", fd, body, nonblock: true });
6236
6271
  if (r.ok) return written(r, body.length);
6237
- if (r.errno !== "EAGAIN") throw new JsCallError(r.errno, "fd-write");
6272
+ if (r.errno !== "EAGAIN") throw callError(r, "fd-write");
6238
6273
  await kernel.blocking({ op: "fd-select", read: [], write: [fd], timeoutMs: -1 }, signal);
6239
6274
  }
6240
6275
  }
@@ -7408,7 +7443,7 @@ function ptyStat(path) {
7408
7443
  ctime: now
7409
7444
  };
7410
7445
  }
7411
- function sealedStat() {
7446
+ function sealedStat(minor = 250) {
7412
7447
  const now = /* @__PURE__ */ new Date();
7413
7448
  return {
7414
7449
  dev: 22,
@@ -7417,7 +7452,7 @@ function sealedStat() {
7417
7452
  nlink: 1,
7418
7453
  uid: 0,
7419
7454
  gid: 0,
7420
- rdev: 10 << 8 | 250,
7455
+ rdev: 10 << 8 | minor,
7421
7456
  size: 0,
7422
7457
  blksize: 4096,
7423
7458
  blocks: 0,
@@ -7429,7 +7464,7 @@ function sealedStat() {
7429
7464
  function nonblocking(stream) {
7430
7465
  return (stream.flags & O_NONBLOCK2) !== 0 ? { nonblock: true } : void 0;
7431
7466
  }
7432
- var POLLIN, POLLOUT, POLLERR, POLLHUP, POLLRDNORM, POLLWRNORM, TCSETSF, O_NONBLOCK2, SOCKET_MODE, PTY_PATH, TTY_PATH, TTY_NAME, SEALED_PATH2, O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC2, KILLED_BY_SIGPIPE, ProcessExit, SyscallError, KernelStreams;
7467
+ var POLLIN, POLLOUT, POLLERR, POLLHUP, POLLRDNORM, POLLWRNORM, TCSETSF, O_NONBLOCK2, SOCKET_MODE, PTY_PATH, TTY_PATH, TTY_NAME, SEALED_PATH2, STRERROR, CLIPBOARD_PATH2, O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC2, KILLED_BY_SIGPIPE, ProcessExit, SyscallError, KernelStreams;
7433
7468
  var init_kernel_streams = __esm({
7434
7469
  "src/process/kernel-streams.ts"() {
7435
7470
  "use strict";
@@ -7447,6 +7482,16 @@ var init_kernel_streams = __esm({
7447
7482
  TTY_PATH = /^\/dev\/tty\d+$/;
7448
7483
  TTY_NAME = /^tty\d+$/;
7449
7484
  SEALED_PATH2 = "/dev/sealed";
7485
+ STRERROR = {
7486
+ EACCES: "Permission denied",
7487
+ EIO: "I/O error",
7488
+ ENOTSUP: "Not supported",
7489
+ EOPNOTSUPP: "Not supported",
7490
+ EFBIG: "File too large",
7491
+ EILSEQ: "Illegal byte sequence",
7492
+ EINTR: "Interrupted system call"
7493
+ };
7494
+ CLIPBOARD_PATH2 = "/dev/clipboard";
7450
7495
  O_CREAT = 64;
7451
7496
  O_EXCL = 128;
7452
7497
  O_NOCTTY = 256;
@@ -7522,10 +7567,64 @@ var init_kernel_streams = __esm({
7522
7567
  }
7523
7568
  listSealed() {
7524
7569
  const { analyzePath, mkdev, makedev } = this.Fs;
7525
- if (!this.sys.openSealed || !analyzePath || !mkdev || !makedev) return;
7526
- if (analyzePath.call(this.Fs, SEALED_PATH2).exists) return;
7570
+ if (!analyzePath || !mkdev || !makedev) return;
7571
+ const nodes = [
7572
+ [SEALED_PATH2, 250, this.sys.openSealed],
7573
+ [CLIPBOARD_PATH2, 251, this.sys.openClipboard]
7574
+ ];
7575
+ for (const [path, minor, present] of nodes) {
7576
+ if (!present || analyzePath.call(this.Fs, path).exists) continue;
7577
+ try {
7578
+ mkdev.call(this.Fs, path, makedev.call(this.Fs, 10, minor));
7579
+ } catch {
7580
+ }
7581
+ }
7582
+ }
7583
+ reporting = /* @__PURE__ */ new Set();
7584
+ reportClose(kfd) {
7585
+ this.reporting.add(kfd);
7586
+ }
7587
+ quitting = false;
7588
+ atExit = false;
7589
+ failedAtExit = false;
7590
+ committed = /* @__PURE__ */ new Set();
7591
+ exiting() {
7592
+ this.atExit = true;
7593
+ return () => {
7594
+ this.atExit = false;
7595
+ };
7596
+ }
7597
+ commitStdout() {
7598
+ const kfd = this.Fs.getStream(1)?.sliccKernelFd;
7599
+ if (kfd === void 0 || !this.reporting.delete(kfd)) return;
7600
+ this.committed.add(kfd);
7601
+ try {
7602
+ this.sys.close(kfd);
7603
+ } catch (e) {
7604
+ if (e instanceof SyscallError) this.stdoutFailed(e.code);
7605
+ }
7606
+ }
7607
+ exitStatus(status2) {
7608
+ return this.failedAtExit && status2 === 0 ? 1 : status2;
7609
+ }
7610
+ reportedClose(stream, kfd) {
7527
7611
  try {
7528
- mkdev.call(this.Fs, SEALED_PATH2, makedev.call(this.Fs, 10, 250));
7612
+ this.sys.close(kfd);
7613
+ } catch (e) {
7614
+ if (this.quitting && stream.fd === 1 && e instanceof SyscallError) this.stdoutFailed(e.code);
7615
+ this.call(() => {
7616
+ throw e;
7617
+ });
7618
+ }
7619
+ }
7620
+ stdoutFailed(code2) {
7621
+ this.failedAtExit = true;
7622
+ const stderr = this.Fs.getStream(2)?.sliccKernelFd;
7623
+ if (stderr === void 0) return;
7624
+ const line = `${this.options.program ?? "program"}: write error: ${STRERROR[code2] ?? code2}
7625
+ `;
7626
+ try {
7627
+ this.sys.write(stderr, new TextEncoder().encode(line));
7529
7628
  } catch {
7530
7629
  }
7531
7630
  }
@@ -7628,7 +7727,7 @@ var init_kernel_streams = __esm({
7628
7727
  if (device2 !== void 0 && TTY_PATH.test(device2) && !this.terminalExists(device2)) {
7629
7728
  throw new this.Fs.ErrnoError(wasiErrno("ENOENT"));
7630
7729
  }
7631
- const pty = this.openPty(open, path, flags, mode) ?? this.openSealed(open, path, flags, mode);
7730
+ const pty = this.openPty(open, path, flags, mode) ?? this.openSealed(open, path, flags, mode) ?? this.openClipboard(open, path, flags, mode);
7632
7731
  if (pty) return pty;
7633
7732
  this.refuseExclusiveLink(path, flags);
7634
7733
  const stream = this.openRacing(open, path, flags, mode);
@@ -7694,6 +7793,14 @@ var init_kernel_streams = __esm({
7694
7793
  const kfd = this.call(() => openSealed.call(this.sys));
7695
7794
  return this.vessel(open, kfd, { path, flags, mode }, false);
7696
7795
  }
7796
+ openClipboard(open, path, flags, mode) {
7797
+ const { openClipboard } = this.sys;
7798
+ if (path !== CLIPBOARD_PATH2 || !openClipboard) return void 0;
7799
+ const kfd = this.call(() => openClipboard.call(this.sys, flags & 3));
7800
+ const stream = this.vessel(open, kfd, { path, flags, mode }, false);
7801
+ this.reportClose(kfd);
7802
+ return stream;
7803
+ }
7697
7804
  vessel(open, kfd, at, terminal) {
7698
7805
  let stream;
7699
7806
  try {
@@ -7718,6 +7825,7 @@ var init_kernel_streams = __esm({
7718
7825
  if (typeof original !== "function") continue;
7719
7826
  fs[name2] = (path, ...rest2) => {
7720
7827
  if (path === SEALED_PATH2 && this.sys.openSealed) return sealedStat();
7828
+ if (path === CLIPBOARD_PATH2 && this.sys.openClipboard) return sealedStat(251);
7721
7829
  const owned = this.ownedTty(path);
7722
7830
  if (owned && isPtyPath(path)) return this.owned(path, ptyStat(path));
7723
7831
  if (!isPtyPath(path)) {
@@ -7858,6 +7966,7 @@ var init_kernel_streams = __esm({
7858
7966
  this.refs.set(kfd, (this.refs.get(kfd) ?? 0) + 1);
7859
7967
  },
7860
7968
  close: (stream) => {
7969
+ if (this.atExit && stream.fd === 2 && this.reporting.size > 0) this.commitStdout();
7861
7970
  try {
7862
7971
  base.close?.(stream);
7863
7972
  } catch {
@@ -7868,6 +7977,11 @@ var init_kernel_streams = __esm({
7868
7977
  return;
7869
7978
  }
7870
7979
  this.refs.delete(kfd);
7980
+ if (this.committed.size > 0 && this.committed.delete(kfd)) return;
7981
+ if (this.reporting.size > 0 && this.reporting.delete(kfd)) {
7982
+ this.reportedClose(stream, kfd);
7983
+ return;
7984
+ }
7871
7985
  try {
7872
7986
  this.sys.close(kfd);
7873
7987
  } catch {
@@ -8715,7 +8829,7 @@ function place(Fs, stream, fd) {
8715
8829
  function placeholder(Fs, streams, entry) {
8716
8830
  if (entry.kind === "socket") return streams.socketStream(entry.flags ?? O_RDWR4);
8717
8831
  if (entry.kind === "tty") return Fs.open("/dev/tty", O_RDWR4);
8718
- if (entry.kind === "stream") return Fs.open("/dev/null", O_RDWR4);
8832
+ if (entry.kind === "stream" || entry.kind === "clipboard") return Fs.open("/dev/null", O_RDWR4);
8719
8833
  Fs.mkdirTree(PLACEHOLDER_DIR);
8720
8834
  return Fs.open(`${PLACEHOLDER_DIR}/${entry.fd}`, O_RDWR4 | O_CREAT3);
8721
8835
  }
@@ -8739,6 +8853,7 @@ function placeKernelStream(Fs, streams, entry) {
8739
8853
  streams.sizeFromKernel(stream, entry.kernel);
8740
8854
  } else if (entry.kind === "socket") streams.attachSocket(stream, entry.kernel);
8741
8855
  else streams.attach(stream, entry.kernel, entry.kind === "tty");
8856
+ if (entry.kind === "clipboard") streams.reportClose(entry.kernel);
8742
8857
  if (entry.kind === "tty") streams.nameTerminal(stream);
8743
8858
  if (entry.kind === "stream") {
8744
8859
  asFifo(stream, streams, entry.desc !== void 0 ? `d${entry.desc}` : `k${entry.kernel}`);
@@ -9879,6 +9994,9 @@ function kernelSys(transport) {
9879
9994
  openSealed() {
9880
9995
  return json2(call2({ op: "fd-open-sealed" }, "fd-open-sealed"));
9881
9996
  },
9997
+ openClipboard(flags) {
9998
+ return json2(call2({ op: "fd-open-clipboard", flags }, "fd-open-clipboard"));
9999
+ },
9882
10000
  ptyNumbers() {
9883
10001
  return json2(call2({ op: "pty-list" }, "pty-list"));
9884
10002
  },
@@ -10035,6 +10153,8 @@ async function runWasmProcess(init, port3, deps = {}) {
10035
10153
  let failed5;
10036
10154
  let memory;
10037
10155
  let exports;
10156
+ let atExit = () => () => {
10157
+ };
10038
10158
  const initialized = new Promise((resolve, reject) => {
10039
10159
  ready = resolve;
10040
10160
  failed5 = reject;
@@ -10076,7 +10196,10 @@ async function runWasmProcess(init, port3, deps = {}) {
10076
10196
  memory = wasmMemory(instance, imports);
10077
10197
  exports = instance.exports;
10078
10198
  publishMemory(init.sab, memory?.buffer.byteLength ?? 0);
10079
- done(instance, init.program.module);
10199
+ done(
10200
+ withExitHook(instance, () => atExit()),
10201
+ init.program.module
10202
+ );
10080
10203
  }, failed5);
10081
10204
  return {};
10082
10205
  },
@@ -10109,9 +10232,17 @@ async function runWasmProcess(init, port3, deps = {}) {
10109
10232
  const sigpipe = () => running.sliccSigpipe?.() === 1;
10110
10233
  const restartable = () => signals.restartable();
10111
10234
  const owners = init.owners ? { owners: true } : {};
10112
- const streams = new KernelStreams(running.FS, sys, { sigpipe, restartable, ...owners });
10235
+ const streams = new KernelStreams(running.FS, sys, {
10236
+ sigpipe,
10237
+ restartable,
10238
+ ...owners,
10239
+ program: init.argv0
10240
+ });
10113
10241
  trackCloseOnExec(running.FS);
10114
- quietQuit(running.FS);
10242
+ quietQuit(running.FS, (on) => {
10243
+ streams.quitting = on;
10244
+ });
10245
+ atExit = () => streams.exiting();
10115
10246
  useMounts(running.FS, init.env, true);
10116
10247
  useZeroDevices(running.FS);
10117
10248
  useProcfs(running.FS, sys, init.pid);
@@ -10157,7 +10288,7 @@ async function runWasmProcess(init, port3, deps = {}) {
10157
10288
  restartable
10158
10289
  });
10159
10290
  try {
10160
- return runMain(running, init);
10291
+ return streams.exitStatus(runMain(running, init));
10161
10292
  } finally {
10162
10293
  try {
10163
10294
  vfs.flush();
@@ -10165,10 +10296,27 @@ async function runWasmProcess(init, port3, deps = {}) {
10165
10296
  }
10166
10297
  }
10167
10298
  }
10168
- function quietQuit(Fs) {
10299
+ function withExitHook(instance, begin) {
10300
+ const funcs = instance.exports.__funcs_on_exit;
10301
+ if (typeof funcs !== "function") return instance;
10302
+ const run = funcs;
10303
+ const exported = Object.assign(/* @__PURE__ */ Object.create(null), instance.exports, {
10304
+ __funcs_on_exit: () => {
10305
+ const end = begin();
10306
+ try {
10307
+ run();
10308
+ } finally {
10309
+ end();
10310
+ }
10311
+ }
10312
+ });
10313
+ return { exports: exported };
10314
+ }
10315
+ function quietQuit(Fs, quitting) {
10169
10316
  const { quit, close } = Fs;
10170
10317
  if (!quit) return;
10171
10318
  Fs.quit = () => {
10319
+ quitting?.(true);
10172
10320
  Fs.close = (stream) => {
10173
10321
  try {
10174
10322
  close.call(Fs, stream);
@@ -10178,6 +10326,7 @@ function quietQuit(Fs) {
10178
10326
  try {
10179
10327
  quit.call(Fs);
10180
10328
  } finally {
10329
+ quitting?.(false);
10181
10330
  Fs.close = close;
10182
10331
  }
10183
10332
  };
@@ -10230,7 +10379,7 @@ var init_process_runtime = __esm({
10230
10379
  O_ACCMODE4 = 3;
10231
10380
  O_DIRECTORY = 65536;
10232
10381
  accessMode = (access) => access === "read" ? O_RDONLY : access === "write" ? O_WRONLY6 : O_RDWR6;
10233
- PLACED = /* @__PURE__ */ new Set(["stream", "file", "socket"]);
10382
+ PLACED = /* @__PURE__ */ new Set(["stream", "file", "socket", "clipboard"]);
10234
10383
  GLUE_TRAILER = [
10235
10384
  "if (typeof ENV !== 'undefined') Object.assign(ENV, Module.sliccEnv);",
10236
10385
  "const __sliccTake = (name, value) => {",