@ai-ecoverse/slicc-kernel 1.124.13 → 1.125.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.
package/dist/node.js CHANGED
@@ -930,7 +930,12 @@ function openPipe(capacity) {
930
930
  }
931
931
  },
932
932
  close: () => pipe.closeRead(),
933
- poll: () => ({ readable: pipe.readReady, writable: false, hangup: pipe.writersGone }),
933
+ poll: () => ({
934
+ readable: pipe.readReady,
935
+ writable: false,
936
+ hangup: pipe.writersGone,
937
+ nread: pipe.buffered
938
+ }),
934
939
  changed: (signal) => pipe.changed(signal),
935
940
  identity: pipe
936
941
  }),
@@ -1718,7 +1723,8 @@ var KernelSocket = class _KernelSocket {
1718
1723
  readable: this.readShut || rx.readReady,
1719
1724
  writable: this.writeShut || tx.writeReady,
1720
1725
  hangup: rx.writersGone && tx.readersGone,
1721
- rdhup: rx.writersGone
1726
+ rdhup: rx.writersGone,
1727
+ nread: this.readShut ? 0 : rx.buffered
1722
1728
  };
1723
1729
  }
1724
1730
  changed(signal) {
@@ -1778,8 +1784,8 @@ var KernelSocket = class _KernelSocket {
1778
1784
  if (this.state !== "open") throw new KernelError("EINVAL");
1779
1785
  if (addr.family !== this.domain) throw new KernelError("EAFNOSUPPORT");
1780
1786
  this.failed = void 0;
1781
- const route = viaUplink && addr.family === "inet" ? this.net.uplinkFor(addr.host) : void 0;
1782
- if (route) return this.dial(addr, route);
1787
+ const route2 = viaUplink && addr.family === "inet" ? this.net.uplinkFor(addr.host) : void 0;
1788
+ if (route2) return this.dial(addr, route2);
1783
1789
  const target = this.net.target(addr, name2);
1784
1790
  if (!this.local) {
1785
1791
  if (addr.family === "inet") {
@@ -1835,9 +1841,9 @@ var KernelSocket = class _KernelSocket {
1835
1841
  }
1836
1842
  }
1837
1843
  switch;
1838
- connectSecure(addr, route, secure2) {
1844
+ connectSecure(addr, route2, secure2) {
1839
1845
  if (this.state !== "open") throw new KernelError("EISCONN");
1840
- return this.dial(addr, route, secure2);
1846
+ return this.dial(addr, route2, secure2);
1841
1847
  }
1842
1848
  upgradeSecure(make) {
1843
1849
  if (this.state !== "connected") throw new KernelError("ENOTCONN");
@@ -1845,7 +1851,7 @@ var KernelSocket = class _KernelSocket {
1845
1851
  if (this.switch.upgraded || this.writeShut || this.readShut) throw new KernelError("EINVAL");
1846
1852
  return this.switch.upgrade(make);
1847
1853
  }
1848
- dial(addr, route, secure2) {
1854
+ dial(addr, route2, secure2) {
1849
1855
  this.state = "connecting";
1850
1856
  const abort = new AbortController();
1851
1857
  this.dialing = abort;
@@ -1853,13 +1859,13 @@ var KernelSocket = class _KernelSocket {
1853
1859
  const timer = setTimeout(() => {
1854
1860
  timedOut = true;
1855
1861
  abort.abort(new KernelError("ETIMEDOUT"));
1856
- }, route.timeoutMs ?? DIAL_TIMEOUT_MS);
1862
+ }, route2.timeoutMs ?? DIAL_TIMEOUT_MS);
1857
1863
  const abandoned = new Promise((_, reject) => {
1858
1864
  abort.signal.addEventListener("abort", () => reject(abort.signal.reason), { once: true });
1859
1865
  });
1860
1866
  const { host, port } = addr;
1861
1867
  const dialled = new Promise(
1862
- (resolve2) => resolve2(route.uplink.dial({ network: "tcp", host, port, signal: abort.signal }))
1868
+ (resolve2) => resolve2(route2.uplink.dial({ network: "tcp", host, port, signal: abort.signal }))
1863
1869
  );
1864
1870
  dialled.then(
1865
1871
  (conn) => abort.signal.aborted && conn.close(),
@@ -2008,14 +2014,14 @@ var LoopbackNet = class {
2008
2014
  if (!this.origins) throw new KernelError("ENETUNREACH");
2009
2015
  void this.origins(conn, origin).catch(() => conn.close());
2010
2016
  }
2011
- useUplink(route) {
2012
- this.route = route;
2017
+ useUplink(route2) {
2018
+ this.route = route2;
2013
2019
  }
2014
2020
  tlsClient;
2015
2021
  uplinkFor(host) {
2016
- const route = this.route;
2017
- if (!route) return void 0;
2018
- return this.destination(host) === "uplink" ? route : void 0;
2022
+ const route2 = this.route;
2023
+ if (!route2) return void 0;
2024
+ return this.destination(host) === "uplink" ? route2 : void 0;
2019
2025
  }
2020
2026
  destination(host) {
2021
2027
  return (this.route?.routes ?? LOCAL_ONLY).classify(host);
@@ -7080,7 +7086,7 @@ function satisfies(version, range2) {
7080
7086
  }
7081
7087
 
7082
7088
  // src/version.ts
7083
- var KERNEL_VERSION = true ? "1.124.13" : "0.0.0-dev";
7089
+ var KERNEL_VERSION = true ? "1.125.0" : "0.0.0-dev";
7084
7090
  var OS_RELEASE = "6.0.0-slicc";
7085
7091
  function procVersion() {
7086
7092
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -9514,6 +9520,8 @@ async function physicalPathAsync(fs, path) {
9514
9520
  }
9515
9521
 
9516
9522
  // src/fs/watch.ts
9523
+ var WATCH_NOTES_LIMIT = 1e3;
9524
+ var baseOf2 = (path) => path.slice(path.lastIndexOf("/") + 1);
9517
9525
  var WATCH_BATCH_LIMIT = 1e3;
9518
9526
  var under3 = (path, dir) => dir === "/" || path.startsWith(`${dir}/`);
9519
9527
  var parentOf6 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
@@ -9523,8 +9531,39 @@ function touches(watcher, changed) {
9523
9531
  );
9524
9532
  }
9525
9533
  var REALS_KEPT = 4096;
9534
+ function noted(watch, note) {
9535
+ if (watch.ended) return;
9536
+ if ("ended" in note) {
9537
+ watch.notes.push(note);
9538
+ watch.ended = true;
9539
+ watch.wake?.();
9540
+ return;
9541
+ }
9542
+ if (watch.overflowed) return;
9543
+ if (watch.notes.length >= WATCH_NOTES_LIMIT) {
9544
+ watch.overflowed = true;
9545
+ watch.notes.push({ overflow: true });
9546
+ } else watch.notes.push(note);
9547
+ watch.wake?.();
9548
+ }
9549
+ function route(watch, type, path) {
9550
+ for (const root of watch.roots) if (routed(watch, root, type, path)) return;
9551
+ }
9552
+ function routed(watch, root, type, path) {
9553
+ if (path === root || type === "rename" && root !== "/" && under3(root, path)) {
9554
+ noted(watch, { type, filename: baseOf2(root) });
9555
+ if (type === "rename") noted(watch, { ended: true });
9556
+ return true;
9557
+ }
9558
+ if (parentOf6(path) === root) noted(watch, { type, filename: baseOf2(path) });
9559
+ else if (watch.recursive && under3(path, root)) {
9560
+ noted(watch, { type, filename: path.slice(root === "/" ? 1 : root.length + 1) });
9561
+ } else return false;
9562
+ return true;
9563
+ }
9526
9564
  var FsWatchers = class {
9527
9565
  watchers = /* @__PURE__ */ new Set();
9566
+ typed = /* @__PURE__ */ new Set();
9528
9567
  taps = [];
9529
9568
  reals = /* @__PURE__ */ new Map();
9530
9569
  reshapes = [];
@@ -9551,7 +9590,65 @@ var FsWatchers = class {
9551
9590
  this.watchers.delete(watcher);
9552
9591
  };
9553
9592
  }
9593
+ typedWatch(root, recursive, alias) {
9594
+ const main = normalizePath(root);
9595
+ const other = alias === void 0 ? main : normalizePath(alias);
9596
+ const watch = {
9597
+ roots: other === main ? [main] : [main, other],
9598
+ recursive,
9599
+ notes: [],
9600
+ overflowed: false,
9601
+ ended: false,
9602
+ wake: void 0
9603
+ };
9604
+ this.typed.add(watch);
9605
+ const close = () => {
9606
+ this.typed.delete(watch);
9607
+ watch.ended = true;
9608
+ watch.wake?.();
9609
+ };
9610
+ return {
9611
+ close,
9612
+ next: async (signal) => {
9613
+ while (watch.notes.length === 0 && !watch.ended && !signal.aborted) {
9614
+ await new Promise((resolve2) => {
9615
+ const done4 = () => {
9616
+ watch.wake = void 0;
9617
+ signal.removeEventListener("abort", done4);
9618
+ resolve2();
9619
+ };
9620
+ watch.wake = done4;
9621
+ signal.addEventListener("abort", done4, { once: true });
9622
+ });
9623
+ }
9624
+ const notes = watch.notes.splice(0);
9625
+ watch.overflowed = false;
9626
+ if (watch.ended) this.typed.delete(watch);
9627
+ return notes.length === 0 && watch.ended ? [{ ended: true }] : notes;
9628
+ }
9629
+ };
9630
+ }
9631
+ covers(path) {
9632
+ for (const watch of this.typed) {
9633
+ for (const root of watch.roots) {
9634
+ if (path === root || parentOf6(path) === root || watch.recursive && under3(path, root))
9635
+ return true;
9636
+ }
9637
+ }
9638
+ return false;
9639
+ }
9640
+ emit(type, paths) {
9641
+ if (this.typed.size === 0) return;
9642
+ for (const path of paths) {
9643
+ const at = normalizePath(path);
9644
+ for (const watch of [...this.typed]) route(watch, type, at);
9645
+ }
9646
+ }
9554
9647
  changed(...paths) {
9648
+ this.report(paths);
9649
+ this.emit("change", paths);
9650
+ }
9651
+ report(paths) {
9555
9652
  for (const tap of this.taps) tap(paths);
9556
9653
  for (const watcher of this.watchers) {
9557
9654
  for (const path of paths) {
@@ -9571,11 +9668,15 @@ var FsWatchers = class {
9571
9668
  watcher.notify(paths.length > WATCH_BATCH_LIMIT ? { overflow: true } : { paths });
9572
9669
  }
9573
9670
  wrap(fs) {
9574
- const after = (run2, changed) => async (...args) => {
9671
+ const after = (run2, changed, type) => async (...args) => {
9672
+ let done4 = false;
9575
9673
  try {
9576
9674
  await run2(...args);
9675
+ done4 = true;
9577
9676
  } finally {
9578
- this.changed(...changed(...args));
9677
+ const paths = changed(...args);
9678
+ this.report(paths);
9679
+ if (done4) this.emit(type, paths);
9579
9680
  }
9580
9681
  };
9581
9682
  const reals = this.reals;
@@ -9594,15 +9695,21 @@ var FsWatchers = class {
9594
9695
  reals.set(at, real);
9595
9696
  return real;
9596
9697
  };
9597
- const content = (run2) => async (...args) => {
9698
+ const content = (run2, types) => async (...args) => {
9699
+ const at = normalizePath(args[0]);
9700
+ const fresh2 = this.covers(at) && !await fs.exists(at).catch(() => true);
9701
+ let done4 = false;
9598
9702
  try {
9599
9703
  await run2(...args);
9704
+ done4 = true;
9600
9705
  } finally {
9601
- const at = normalizePath(args[0]);
9602
9706
  const real = await realOf(at);
9603
- this.changed(...real === at ? [at] : [at, real]);
9707
+ const paths = real === at ? [at] : [at, real];
9708
+ this.report(paths);
9709
+ if (done4) for (const type of types(fresh2)) this.emit(type, paths);
9604
9710
  }
9605
9711
  };
9712
+ const written2 = (fresh2) => fresh2 ? ["rename", "change"] : ["change"];
9606
9713
  const reshaping = (run2) => async (...args) => {
9607
9714
  this.reshaped();
9608
9715
  try {
@@ -9612,8 +9719,8 @@ var FsWatchers = class {
9612
9719
  }
9613
9720
  };
9614
9721
  const overrides = {
9615
- writeFile: content(fs.writeFile.bind(fs)),
9616
- ...fs.createExclusive ? { createExclusive: content(fs.createExclusive.bind(fs)) } : {},
9722
+ writeFile: content(fs.writeFile.bind(fs), written2),
9723
+ ...fs.createExclusive ? { createExclusive: content(fs.createExclusive.bind(fs), () => ["rename"]) } : {},
9617
9724
  mkdir: async (path, options) => {
9618
9725
  const created3 = [];
9619
9726
  if (options?.recursive) {
@@ -9623,33 +9730,39 @@ var FsWatchers = class {
9623
9730
  if (created3.length > 0 || !await fs.exists(ancestor)) created3.push(ancestor);
9624
9731
  }
9625
9732
  }
9733
+ const fresh2 = !options?.recursive || !(this.covers(path) && await fs.exists(path));
9734
+ let done4 = false;
9626
9735
  try {
9627
9736
  await fs.mkdir(path, options);
9737
+ done4 = true;
9628
9738
  } finally {
9629
- this.changed(...created3, path);
9739
+ this.report([...created3, path]);
9740
+ if (done4) this.emit("rename", fresh2 ? [...created3, path] : created3);
9630
9741
  }
9631
9742
  },
9632
- rm: reshaping(after(fs.rm.bind(fs), (path) => [path])),
9633
- rename: reshaping(after(fs.rename.bind(fs), (from, to) => [from, to])),
9743
+ rm: reshaping(after(fs.rm.bind(fs), (path) => [path], "rename")),
9744
+ rename: reshaping(after(fs.rename.bind(fs), (from, to) => [from, to], "rename")),
9634
9745
  link: reshaping(
9635
9746
  after(
9636
9747
  (from, to, follow2) => fs.link(from, to, follow2),
9637
- (_from, to) => [to]
9748
+ (_from, to) => [to],
9749
+ "rename"
9638
9750
  )
9639
9751
  ),
9640
- symlink: reshaping(after(fs.symlink.bind(fs), (_target, path) => [path])),
9641
- chmod: after(fs.chmod.bind(fs), (path) => [path]),
9642
- ...fs.chown ? { chown: after(fs.chown.bind(fs), (path) => [path]) } : {},
9643
- utimes: after(fs.utimes.bind(fs), (path) => [path]),
9752
+ symlink: reshaping(after(fs.symlink.bind(fs), (_target, path) => [path], "rename")),
9753
+ chmod: after(fs.chmod.bind(fs), (path) => [path], "change"),
9754
+ ...fs.chown ? { chown: after(fs.chown.bind(fs), (path) => [path], "change") } : {},
9755
+ utimes: after(fs.utimes.bind(fs), (path) => [path], "change"),
9644
9756
  lutimes: after(
9645
9757
  (path, atime, mtime) => lutimesOf(fs, path, atime, mtime),
9646
- (path) => [path]
9758
+ (path) => [path],
9759
+ "change"
9647
9760
  )
9648
9761
  };
9649
9762
  const ranged2 = rangedOps(fs);
9650
9763
  if (ranged2) {
9651
- overrides.pwrite = content(ranged2.pwrite);
9652
- overrides.truncate = content(ranged2.truncate);
9764
+ overrides.pwrite = content(ranged2.pwrite, () => ["change"]);
9765
+ overrides.truncate = content(ranged2.truncate, () => ["change"]);
9653
9766
  }
9654
9767
  return new Proxy(fs, {
9655
9768
  get(target, key, receiver) {
@@ -10257,7 +10370,8 @@ var FifoTable = class {
10257
10370
  poll: () => ({
10258
10371
  readable: reads && pipe.readReady,
10259
10372
  writable: writes && pipe.writeReady,
10260
- hangup: reads ? pipe.writersGone : pipe.readersGone
10373
+ hangup: reads ? pipe.writersGone : pipe.readersGone,
10374
+ ...reads ? { nread: pipe.buffered } : {}
10261
10375
  }),
10262
10376
  changed: (signal) => pipe.changed(signal)
10263
10377
  });
@@ -10656,8 +10770,8 @@ var MountTable = class {
10656
10770
  ...error ? { error } : {}
10657
10771
  })
10658
10772
  );
10659
- const noted = [...this.notes.values()].filter((note) => !this.mounts.has(note.target));
10660
- return [...mounted, ...noted.map((note) => ({ ...note, options: { ...note.options } }))];
10773
+ const noted2 = [...this.notes.values()].filter((note) => !this.mounts.has(note.target));
10774
+ return [...mounted, ...noted2.map((note) => ({ ...note, options: { ...note.options } }))];
10661
10775
  }
10662
10776
  listed() {
10663
10777
  return this.list().filter((entry) => !this.system.has(entry.target));
@@ -15968,7 +16082,8 @@ var KernelTty = class {
15968
16082
  poll: () => ({
15969
16083
  readable: this.readable.length > 0 || this.hungUp,
15970
16084
  writable: true,
15971
- hangup: this.hungUp
16085
+ hangup: this.hungUp,
16086
+ nread: this.readable.reduce((n, chunk2) => n + (chunk2?.length ?? 0), 0)
15972
16087
  }),
15973
16088
  changed: (signal) => this.changed(signal),
15974
16089
  close: () => {
@@ -16164,7 +16279,8 @@ var PtyPair = class {
16164
16279
  poll: () => ({
16165
16280
  readable: this.queue.length > 0 || this.slaveGone,
16166
16281
  writable: true,
16167
- hangup: this.slaveGone
16282
+ hangup: this.slaveGone,
16283
+ nread: this.queue.reduce((n, chunk2) => n + chunk2.length, 0)
16168
16284
  }),
16169
16285
  changed: (signal) => this.changed(signal),
16170
16286
  close: () => this.closeMaster(),
@@ -16446,13 +16562,17 @@ async function runChild(req, deps) {
16446
16562
  // src/kernel/select.ts
16447
16563
  function ready(fds, read, write) {
16448
16564
  const hangup = /* @__PURE__ */ new Set();
16565
+ const nread = {};
16449
16566
  const pick = (list, dir) => list.filter((fd) => {
16450
16567
  const state = pollFile(fds.get(fd).file);
16451
16568
  if (state.hangup) hangup.add(fd);
16452
- return state[dir] || state.hangup;
16569
+ const picked = state[dir] || state.hangup;
16570
+ if (picked && dir === "readable" && state.nread !== void 0) nread[fd] = state.nread;
16571
+ return picked;
16453
16572
  });
16454
16573
  const out = { read: pick(read, "readable"), write: pick(write, "writable") };
16455
16574
  if (hangup.size > 0) out.hangup = [...hangup];
16575
+ if (Object.keys(nread).length > 0) out.nread = nread;
16456
16576
  return out;
16457
16577
  }
16458
16578
  function interruption(signal) {
@@ -17447,14 +17567,14 @@ async function connectSecure(proc, socket, host, port, req) {
17447
17567
  const servername = serverName(req.servername || host);
17448
17568
  if (isLoopbackHost(servername) || isLoopbackHost(host)) throw new KernelError("ENOTSUP");
17449
17569
  const address = await uplinkAddress(proc, host);
17450
- const route = proc.net.uplinkFor(address);
17451
- if (!route)
17570
+ const route2 = proc.net.uplinkFor(address);
17571
+ if (!route2)
17452
17572
  throw new KernelError("ENOTSUP", "TLS in the kernel needs a destination the uplink routes");
17453
17573
  const support = supportOf(proc.net);
17454
17574
  let info;
17455
17575
  const { make, abandon } = secure(await prepare(support, req, servername));
17456
17576
  try {
17457
- await socket.connectSecure({ family: "inet", host: address, port }, route, async (conn) => {
17577
+ await socket.connectSecure({ family: "inet", host: address, port }, route2, async (conn) => {
17458
17578
  info = await conn.upgrade(make);
17459
17579
  });
17460
17580
  } finally {
@@ -18665,6 +18785,65 @@ function vfsFile(fs, opts, nodes) {
18665
18785
  });
18666
18786
  }
18667
18787
 
18788
+ // src/kernel/watch-ops.ts
18789
+ var WATCH_OPS = ["watch-open", "watch-next", "watch-close"];
18790
+ var WATCHING = new Set(WATCH_OPS);
18791
+ function isWatchSyscall(req) {
18792
+ return WATCHING.has(req.op);
18793
+ }
18794
+ var WATCHES_MAX = 8192;
18795
+ var parentOf8 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
18796
+ function visibleTo2(watch, root, visible) {
18797
+ return {
18798
+ close: () => watch.close(),
18799
+ next: async (signal) => {
18800
+ const notes = await watch.next(signal);
18801
+ const seen = /* @__PURE__ */ new Map();
18802
+ const shown = await Promise.all(
18803
+ notes.map(async (note) => {
18804
+ if (!("filename" in note) || !note.filename.includes("/")) return true;
18805
+ const dir = parentOf8(`${root === "/" ? "" : root}/${note.filename}`);
18806
+ if (!seen.has(dir)) seen.set(dir, visible(dir));
18807
+ return seen.get(dir);
18808
+ })
18809
+ );
18810
+ return notes.filter((_note, i) => shown[i]);
18811
+ }
18812
+ };
18813
+ }
18814
+ var ProcessWatches = class {
18815
+ open = /* @__PURE__ */ new Map();
18816
+ nextId = 1;
18817
+ source;
18818
+ constructor(source) {
18819
+ this.source = source;
18820
+ }
18821
+ async syscall(req, cancelled) {
18822
+ if (req.op === "watch-open") {
18823
+ if (!this.source) throw new KernelError("ENOSYS");
18824
+ if (this.open.size >= WATCHES_MAX) throw new KernelError("ENOSPC");
18825
+ const watch2 = await this.source.open(req.path, req.recursive === true);
18826
+ const id2 = this.nextId++;
18827
+ this.open.set(id2, watch2);
18828
+ return { ok: true, kind: "json", json: id2 };
18829
+ }
18830
+ const watch = this.open.get(req.id);
18831
+ if (req.op === "watch-close") {
18832
+ watch?.close();
18833
+ this.open.delete(req.id);
18834
+ return { ok: true, kind: "void" };
18835
+ }
18836
+ if (!watch) return { ok: true, kind: "json", json: [{ ended: true }] };
18837
+ const notes = await watch.next(cancelled);
18838
+ if (notes.some((note) => "ended" in note)) this.open.delete(req.id);
18839
+ return { ok: true, kind: "json", json: notes };
18840
+ }
18841
+ closeAll() {
18842
+ for (const watch of this.open.values()) watch.close();
18843
+ this.open.clear();
18844
+ }
18845
+ };
18846
+
18668
18847
  // src/kernel/process.ts
18669
18848
  function aborted(signal) {
18670
18849
  return new Promise((_, reject) => {
@@ -18791,7 +18970,8 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
18791
18970
  ...SEM_OPS,
18792
18971
  ...HTTP_OPS,
18793
18972
  ...PTY_OPS,
18794
- ...HOST_OPS
18973
+ ...HOST_OPS,
18974
+ ...WATCH_OPS
18795
18975
  ]);
18796
18976
  function isWasmSyscall(req) {
18797
18977
  const op = req.op;
@@ -18843,6 +19023,7 @@ function timeLeft(req, started) {
18843
19023
  }
18844
19024
  var WasmProcess = class {
18845
19025
  exited = false;
19026
+ watches;
18846
19027
  children;
18847
19028
  deathSignals = /* @__PURE__ */ new Map();
18848
19029
  caught = 0;
@@ -18873,6 +19054,7 @@ var WasmProcess = class {
18873
19054
  this.pid = pid;
18874
19055
  this.fds = fds;
18875
19056
  this.options = options;
19057
+ this.watches = new ProcessWatches(options.watches);
18876
19058
  this.inherited = options.ignored ?? 0;
18877
19059
  this.umask = options.umask ?? DEFAULT_UMASK2;
18878
19060
  this.cred = copyCred(options.cred ?? ROOT);
@@ -19130,6 +19312,7 @@ var WasmProcess = class {
19130
19312
  });
19131
19313
  }
19132
19314
  if (isHttpSyscall(req)) return await this.httpSyscall(req);
19315
+ if (isWatchSyscall(req)) return await this.watches.syscall(req, cancelled);
19133
19316
  if (isHostSyscall(req)) {
19134
19317
  return await hostSyscall(req, {
19135
19318
  pid: this.pid,
@@ -20270,6 +20453,7 @@ var WasmProcess = class {
20270
20453
  this.options.locks?.release(this.pid);
20271
20454
  for (const owner of this.lockOwners) this.options.locks?.release(owner);
20272
20455
  this.asyncOps.close();
20456
+ this.watches.closeAll();
20273
20457
  for (const [target, sig] of this.deathSignals) this.sendDeath(target, sig);
20274
20458
  this.deathSignals.clear();
20275
20459
  for (const pid of this.children.pids()) this.options.onReap?.(pid);
@@ -20531,6 +20715,7 @@ function processOptions(opts, header4, hooks, created3) {
20531
20715
  locks: opts.locks,
20532
20716
  fifos: opts.fifos,
20533
20717
  ...opts.sems ? { sems: opts.sems } : {},
20718
+ watches: opts.watches,
20534
20719
  onReap: opts.onReap,
20535
20720
  resolver: opts.resolver,
20536
20721
  ...notifiers(header4),
@@ -25816,6 +26001,7 @@ var Launcher = class {
25816
26001
  locks: this.locks,
25817
26002
  fifos: this.fifos,
25818
26003
  sems: this.sems,
26004
+ watches: this.watchSource(pid),
25819
26005
  resolver: this.resolver,
25820
26006
  onReap: (child) => this.reaped(child, pid),
25821
26007
  ...req.fork ? { fork: req.fork } : {},
@@ -26359,6 +26545,23 @@ var Launcher = class {
26359
26545
  mounts: this.mounts.list().map((entry) => entry.target).filter((target) => this.mounts.mounted(target) !== void 0)
26360
26546
  });
26361
26547
  }
26548
+ watchSource(pid) {
26549
+ return {
26550
+ open: async (path, recursive) => {
26551
+ const at = normalizePath(path);
26552
+ const g = this.execGuard(this.credOf(pid), pid);
26553
+ if (g) await readable(g, "watch", at);
26554
+ const st = await this.fs.stat(at);
26555
+ const real = await this.fs.realpath?.(at, true).catch(() => at) ?? at;
26556
+ const watch = this.watchers.typedWatch(real, recursive && st.isDirectory, at);
26557
+ if (!g || g.cred.euid === 0) return watch;
26558
+ return visibleTo2(watch, real, async (dir) => {
26559
+ const walked = await access(g.fs, g.cred, dir, R_OK | X_OK, { directory: true });
26560
+ return walked.denied === void 0 && walked.missing !== true;
26561
+ });
26562
+ }
26563
+ };
26564
+ }
26362
26565
  watchFiles(paths, notify) {
26363
26566
  this.fileWatches ??= new FileWatches(this.watchers, this.rootFs);
26364
26567
  return this.fileWatches.watch(paths, notify);