@ai-ecoverse/slicc-kernel 1.133.3 → 1.133.5

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.
@@ -265,6 +265,70 @@ var init_wasi_abi = __esm({
265
265
  }
266
266
  });
267
267
 
268
+ // src/fs/changes.ts
269
+ function changeHash(path) {
270
+ let h = 2166136261;
271
+ for (let i = 0; i < path.length; i++) {
272
+ h ^= path.charCodeAt(i);
273
+ h = Math.imul(h, 16777619);
274
+ }
275
+ return h === 0 ? 1 : h;
276
+ }
277
+ function normal(path) {
278
+ const parts = [];
279
+ for (const part of path.split("/")) {
280
+ if (part === "" || part === ".") continue;
281
+ if (part === "..") parts.pop();
282
+ else parts.push(part);
283
+ }
284
+ return `/${parts.join("/")}`;
285
+ }
286
+ var GEN, RING, RING_SIZE, SLOT, SHARED_TTL_MS, DIRTY, FOREIGN, CHANGES_BYTES, ChangeView;
287
+ var init_changes = __esm({
288
+ "src/fs/changes.ts"() {
289
+ "use strict";
290
+ GEN = 0;
291
+ RING = 1;
292
+ RING_SIZE = 4096;
293
+ SLOT = 2;
294
+ SHARED_TTL_MS = 100;
295
+ DIRTY = RING + RING_SIZE * SLOT;
296
+ FOREIGN = DIRTY + 1;
297
+ CHANGES_BYTES = (FOREIGN + 1) * 4;
298
+ ChangeView = class {
299
+ view;
300
+ constructor(sab) {
301
+ this.view = new Int32Array(sab);
302
+ }
303
+ now() {
304
+ return Atomics.load(this.view, GEN);
305
+ }
306
+ clean() {
307
+ return this.view.length > DIRTY && Atomics.load(this.view, DIRTY) === 0;
308
+ }
309
+ foreign() {
310
+ return this.view.length > FOREIGN && Atomics.load(this.view, FOREIGN) === 1;
311
+ }
312
+ changedSince(seen, path) {
313
+ const now = Atomics.load(this.view, GEN);
314
+ const distance = now - seen >>> 0;
315
+ if (distance === 0) return { now, changed: false };
316
+ if (distance > RING_SIZE) return { now, changed: true };
317
+ const hash = changeHash(normal(path));
318
+ for (let i = 1; i <= distance; i++) {
319
+ const g = seen + i | 0;
320
+ const at = RING + (g & RING_SIZE - 1) * SLOT;
321
+ if (Atomics.load(this.view, at) !== g) return { now, changed: true };
322
+ const hit = Atomics.load(this.view, at + 1) === hash;
323
+ if (Atomics.load(this.view, at) !== g) return { now, changed: true };
324
+ if (hit) return { now, changed: true };
325
+ }
326
+ return { now, changed: false };
327
+ }
328
+ };
329
+ }
330
+ });
331
+
268
332
  // src/fs/pages.ts
269
333
  var PAGE, READAHEAD, CLEAN_LIMIT, WRITEBACK, RUN, Pages;
270
334
  var init_pages = __esm({
@@ -788,11 +852,11 @@ function copying(bridge, mutating, entry) {
788
852
  copy: (from, to, mode, exclusive) => mutating(() => copy2.call(bridge, from, to, mode, exclusive), ...entry(to))
789
853
  };
790
854
  }
791
- function listed({ cache, seen, filled }, p, list, gen, asked) {
855
+ function listed({ cache, seen, filled: filled2 }, p, list, gen, asked) {
792
856
  const base = p === "/" ? "" : p;
793
857
  for (const [name2, st] of list) {
794
858
  if (!st) continue;
795
- filled.set(st, asked);
859
+ filled2.set(st, asked);
796
860
  const at = `${base}/${name2}`;
797
861
  cache.put(cache.lstats, at, st);
798
862
  if (gen !== void 0) seen.saw(cache.lstats, at, gen);
@@ -805,7 +869,7 @@ function fetched({
805
869
  view,
806
870
  now,
807
871
  seen,
808
- filled,
872
+ filled: filled2,
809
873
  cache
810
874
  }, map, path, get) {
811
875
  const gen = view?.now();
@@ -817,7 +881,7 @@ function fetched({
817
881
  } catch (e) {
818
882
  hit = { error: e, until: asked + MISS_TTL_MS, ...gen !== void 0 ? { gen } : {} };
819
883
  }
820
- filled.set(hit, asked);
884
+ filled2.set(hit, asked);
821
885
  cache.put(map, path, hit);
822
886
  return hit;
823
887
  }
@@ -826,17 +890,17 @@ function cachingBridge(bridge, now = () => performance.now(), shared) {
826
890
  const cache = new StatCache(blind ? void 0 : shared);
827
891
  const { stats, lstats } = cache;
828
892
  const view = bridge.changes;
829
- const filled = new Fills(view, now);
830
- const young = (entry2) => filled.young(entry2);
831
- const live = (miss, path) => miss.until > now() && young(miss) && (view === void 0 || miss.gen === void 0 || !view.changedSince(miss.gen, path).changed);
893
+ const filled2 = new Fills(view, now);
894
+ const young2 = (entry2) => filled2.young(entry2);
895
+ const live = (miss, path) => miss.until > now() && young2(miss) && (view === void 0 || miss.gen === void 0 || !view.changedSince(miss.gen, path).changed);
832
896
  const seen = new SeenAt(view, lstats);
833
- const kept = remember(cache, seen, blind, (entry2) => filled.set(entry2));
897
+ const kept = remember(cache, seen, blind, (entry2) => filled2.set(entry2));
834
898
  const gone = (path) => goneAbove([lstats, stats], live, path);
835
899
  const usable = (map, path) => {
836
900
  const hit = map.get(path);
837
901
  if (hit === void 0) return void 0;
838
902
  if ("until" in hit) return live(hit, path) ? hit : void 0;
839
- return young(hit) && !seen.changed(map, path) ? hit : void 0;
903
+ return young2(hit) && !seen.changed(map, path) ? hit : void 0;
840
904
  };
841
905
  const cached = (map, path, get) => {
842
906
  if (blind) return get();
@@ -845,7 +909,7 @@ function cachingBridge(bridge, now = () => performance.now(), shared) {
845
909
  if (hit === void 0) {
846
910
  const missing2 = gone(path);
847
911
  if (missing2) throw missing2.error;
848
- hit = fetched({ view, now, seen, filled, cache }, map, path, get);
912
+ hit = fetched({ view, now, seen, filled: filled2, cache }, map, path, get);
849
913
  }
850
914
  if ("until" in hit) throw hit.error;
851
915
  return hit;
@@ -904,14 +968,14 @@ function cachingBridge(bridge, now = () => performance.now(), shared) {
904
968
  const gen = view?.now();
905
969
  const asked = now();
906
970
  const list = bridge.readdirStat(p);
907
- if (!blind) listed({ cache, seen, filled }, p, list, gen, asked);
971
+ if (!blind) listed({ cache, seen, filled: filled2 }, p, list, gen, asked);
908
972
  return list;
909
973
  },
910
974
  readlink: (p) => {
911
975
  const known = blind ? void 0 : (cache.fresh(), lstats.get(p));
912
976
  const gen = seen.lstat(p);
913
977
  const current = view !== void 0 && gen !== void 0 && !view.changedSince(gen, p).changed;
914
- if (known && current && !("until" in known) && young(known) && !known.isSymbolicLink) {
978
+ if (known && current && !("until" in known) && young2(known) && !known.isSymbolicLink) {
915
979
  throw syncError("EINVAL", `'${p}' is not a symbolic link`);
916
980
  }
917
981
  return bridge.readlink(p);
@@ -984,10 +1048,11 @@ function pinnedIo(bridge, handle, rewriting) {
984
1048
  } : {}
985
1049
  };
986
1050
  }
987
- var WasiError, FileBuffer, LocalFile, MAX_LINKS, MISS_TTL_MS, SHARED_TTL_MS, CACHE_CAP, FS_GEN, CRED_GEN, UMASK, RING, RING_SIZE, SLOT, FD_RING, FD_RING_SIZE, SHARED_IDS_BYTES, StatCache, SEEN_CAP, SeenAt, remember, Fills;
1051
+ var WasiError, FileBuffer, LocalFile, MAX_LINKS, MISS_TTL_MS, CACHE_CAP, FS_GEN, CRED_GEN, UMASK, RING2, RING_SIZE2, SLOT2, FD_RING, FD_RING_SIZE, SHARED_IDS_BYTES, StatCache, SEEN_CAP, SeenAt, remember, Fills;
988
1052
  var init_wasi_files = __esm({
989
1053
  "src/process/wasi/wasi-files.ts"() {
990
1054
  "use strict";
1055
+ init_changes();
991
1056
  init_ranged();
992
1057
  init_sync_fs_wire();
993
1058
  WasiError = class extends Error {
@@ -1211,15 +1276,14 @@ var init_wasi_files = __esm({
1211
1276
  };
1212
1277
  MAX_LINKS = 40;
1213
1278
  MISS_TTL_MS = 20;
1214
- SHARED_TTL_MS = 100;
1215
1279
  CACHE_CAP = 8192;
1216
1280
  FS_GEN = 4;
1217
1281
  CRED_GEN = 5;
1218
1282
  UMASK = 6;
1219
- RING = 8;
1220
- RING_SIZE = 64;
1221
- SLOT = 3;
1222
- FD_RING = RING + RING_SIZE * SLOT;
1283
+ RING2 = 8;
1284
+ RING_SIZE2 = 64;
1285
+ SLOT2 = 3;
1286
+ FD_RING = RING2 + RING_SIZE2 * SLOT2;
1223
1287
  FD_RING_SIZE = 64;
1224
1288
  SHARED_IDS_BYTES = (FD_RING + FD_RING_SIZE * 2) * 4;
1225
1289
  StatCache = class {
@@ -1298,7 +1362,7 @@ var init_wasi_files = __esm({
1298
1362
  }
1299
1363
  publish(shared, hash, ino) {
1300
1364
  const g = Atomics.add(shared, FS_GEN, 1) + 1;
1301
- const at = RING + g % RING_SIZE * SLOT;
1365
+ const at = RING2 + g % RING_SIZE2 * SLOT2;
1302
1366
  Atomics.store(shared, at, 0);
1303
1367
  Atomics.store(shared, at + 1, hash);
1304
1368
  Atomics.store(shared, at + 2, ino);
@@ -1308,8 +1372,8 @@ var init_wasi_files = __esm({
1308
1372
  replay(upTo) {
1309
1373
  const shared = this.shared;
1310
1374
  const missed = [];
1311
- for (let g = this.seen + 1; g <= upTo && upTo - this.seen <= RING_SIZE; g++) {
1312
- const at = RING + g % RING_SIZE * SLOT;
1375
+ for (let g = this.seen + 1; g <= upTo && upTo - this.seen <= RING_SIZE2; g++) {
1376
+ const at = RING2 + g % RING_SIZE2 * SLOT2;
1313
1377
  const tag = Atomics.load(shared, at);
1314
1378
  const hash = Atomics.load(shared, at + 1);
1315
1379
  const ino = Atomics.load(shared, at + 2);
@@ -2989,7 +3053,7 @@ function promoteRequest(fd, f, handed) {
2989
3053
  ...f.buffer.handle !== void 0 && f.ino !== void 0 ? { ino: f.ino } : {}
2990
3054
  };
2991
3055
  }
2992
- var SYNTHETIC_DIRS, O_NONBLOCK, O_WRONLY2, O_RDWR2, O_APPEND2, SEALED_PATH, CLIPBOARD_PATH, WasiFds, GEN;
3056
+ var SYNTHETIC_DIRS, O_NONBLOCK, O_WRONLY2, O_RDWR2, O_APPEND2, SEALED_PATH, CLIPBOARD_PATH, WasiFds, GEN2;
2993
3057
  var init_wasi_fds = __esm({
2994
3058
  "src/process/wasi/wasi-fds.ts"() {
2995
3059
  "use strict";
@@ -3100,11 +3164,11 @@ var init_wasi_fds = __esm({
3100
3164
  }
3101
3165
  this.shared = ids;
3102
3166
  this.fs.threaded?.();
3103
- this.seen = Atomics.load(ids, GEN);
3167
+ this.seen = Atomics.load(ids, GEN2);
3104
3168
  }
3105
3169
  sync() {
3106
3170
  const shared = this.shared;
3107
- const gen = Atomics.load(shared, GEN);
3171
+ const gen = Atomics.load(shared, GEN2);
3108
3172
  if (gen === this.seen) return;
3109
3173
  if (!this.replay(shared, gen)) {
3110
3174
  this.table.clear();
@@ -3132,7 +3196,7 @@ var init_wasi_fds = __esm({
3132
3196
  if (!this.shared) return;
3133
3197
  const shared = this.shared;
3134
3198
  for (const fd of fds.length > 0 ? fds : [-1]) {
3135
- const gen = Atomics.add(shared, GEN, 1) + 1;
3199
+ const gen = Atomics.add(shared, GEN2, 1) + 1;
3136
3200
  if (ringed(shared)) {
3137
3201
  const at = FD_RING + gen % FD_RING_SIZE * 2;
3138
3202
  Atomics.store(shared, at, 0);
@@ -3271,6 +3335,7 @@ var init_wasi_fds = __esm({
3271
3335
  e.path = path;
3272
3336
  this.kernel.call({ op: "fd-meta", fd, meta: metaOf(e) });
3273
3337
  }
3338
+ this.held?.moved(this.kernel.call({ op: "cwd-chdir", path }), path);
3274
3339
  this.bump();
3275
3340
  }
3276
3341
  promote(fd, moved = false) {
@@ -3557,9 +3622,21 @@ var init_wasi_fds = __esm({
3557
3622
  this.buffers.set(next2, buffer);
3558
3623
  }
3559
3624
  }
3625
+ held;
3626
+ cwdEntry(dir) {
3627
+ return dir.preopen === "." || dir.preopen === "./";
3628
+ }
3629
+ goneCwd(dir) {
3630
+ return this.held !== void 0 && this.cwdEntry(dir) && !this.held.alive(dir.path);
3631
+ }
3560
3632
  resolve(dirfd, path) {
3561
3633
  if (path === "") throw new WasiError("ENOENT");
3562
- const joined2 = path.startsWith("/") ? path : `${this.dir(dirfd).path}/${path}`;
3634
+ if (path.startsWith("/")) return this.joined(path);
3635
+ const dir = this.dir(dirfd);
3636
+ if (path.split("/")[0] !== ".." && this.goneCwd(dir)) throw new WasiError("ENOENT");
3637
+ return this.joined(`${dir.path}/${path}`);
3638
+ }
3639
+ joined(joined2) {
3563
3640
  return joined2.split("/").includes("..") ? physical(this.fs, joined2) : normalize(joined2);
3564
3641
  }
3565
3642
  openKernelDevice(path, oflags, fdflags, rights) {
@@ -3811,7 +3888,7 @@ var init_wasi_fds = __esm({
3811
3888
  return this.install(e.type === "file" ? e : { ...e });
3812
3889
  }
3813
3890
  };
3814
- GEN = 2;
3891
+ GEN2 = 2;
3815
3892
  }
3816
3893
  });
3817
3894
 
@@ -4902,6 +4979,7 @@ var init_wasi_host = __esm({
4902
4979
  this.o = { ...o, kernel: { sys, call: call2 } };
4903
4980
  this.startCwd = o.cwd;
4904
4981
  this.fds = new WasiFds(this.o.kernel, o.fs);
4982
+ if (o.held) this.fds.held = o.held;
4905
4983
  this.creds = new CredCache(
4906
4984
  () => this.o.kernel.call({ op: "proc-cred" }),
4907
4985
  o.credGen ?? o.shared
@@ -5865,13 +5943,13 @@ var init_wasi_host = __esm({
5865
5943
  const view = this.mem.view();
5866
5944
  const size = Math.min(view.getUint32(at, true), SIGINFO_BYTES);
5867
5945
  const fields = [sig, info.code, info.pid, info.uid, info.status];
5868
- let filled = 4;
5946
+ let filled2 = 4;
5869
5947
  for (const [i, value2] of fields.entries()) {
5870
5948
  if (4 + (i + 1) * 4 > size) break;
5871
5949
  view.setInt32(4 + at + i * 4, value2, true);
5872
- filled = 4 + (i + 1) * 4;
5950
+ filled2 = 4 + (i + 1) * 4;
5873
5951
  }
5874
- view.setUint32(at, filled, true);
5952
+ view.setUint32(at, filled2, true);
5875
5953
  return true;
5876
5954
  }
5877
5955
  fdAlias(path) {
@@ -5983,7 +6061,9 @@ var init_wasi_host = __esm({
5983
6061
  }
5984
6062
  readdir(fd, buf, len, cookie, usedPtr) {
5985
6063
  const dir = this.fds.dir(fd);
5986
- if (cookie === 0 || !dir.listing) dir.listing = this.list(dir.path);
6064
+ if (cookie === 0 || !dir.listing) {
6065
+ dir.listing = this.fds.goneCwd(dir) ? { names: [], stats: /* @__PURE__ */ new Map() } : this.list(dir.path);
6066
+ }
5987
6067
  const { names, stats } = dir.listing;
5988
6068
  let at = 0;
5989
6069
  for (let i = cookie; i < names.length && at < len; i++) {
@@ -6520,7 +6600,7 @@ var KERNEL_VERSION, OS_RELEASE;
6520
6600
  var init_version = __esm({
6521
6601
  "src/version.ts"() {
6522
6602
  "use strict";
6523
- KERNEL_VERSION = true ? "1.133.3" : "0.0.0-dev";
6603
+ KERNEL_VERSION = true ? "1.133.5" : "0.0.0-dev";
6524
6604
  OS_RELEASE = "6.0.0-slicc";
6525
6605
  }
6526
6606
  });
@@ -7662,69 +7742,6 @@ var init_wasi_errno = __esm({
7662
7742
  }
7663
7743
  });
7664
7744
 
7665
- // src/fs/changes.ts
7666
- function changeHash(path) {
7667
- let h = 2166136261;
7668
- for (let i = 0; i < path.length; i++) {
7669
- h ^= path.charCodeAt(i);
7670
- h = Math.imul(h, 16777619);
7671
- }
7672
- return h === 0 ? 1 : h;
7673
- }
7674
- function normal(path) {
7675
- const parts = [];
7676
- for (const part of path.split("/")) {
7677
- if (part === "" || part === ".") continue;
7678
- if (part === "..") parts.pop();
7679
- else parts.push(part);
7680
- }
7681
- return `/${parts.join("/")}`;
7682
- }
7683
- var GEN2, RING2, RING_SIZE2, SLOT2, DIRTY, FOREIGN, CHANGES_BYTES, ChangeView;
7684
- var init_changes = __esm({
7685
- "src/fs/changes.ts"() {
7686
- "use strict";
7687
- GEN2 = 0;
7688
- RING2 = 1;
7689
- RING_SIZE2 = 4096;
7690
- SLOT2 = 2;
7691
- DIRTY = RING2 + RING_SIZE2 * SLOT2;
7692
- FOREIGN = DIRTY + 1;
7693
- CHANGES_BYTES = (FOREIGN + 1) * 4;
7694
- ChangeView = class {
7695
- view;
7696
- constructor(sab) {
7697
- this.view = new Int32Array(sab);
7698
- }
7699
- now() {
7700
- return Atomics.load(this.view, GEN2);
7701
- }
7702
- clean() {
7703
- return this.view.length > DIRTY && Atomics.load(this.view, DIRTY) === 0;
7704
- }
7705
- foreign() {
7706
- return this.view.length > FOREIGN && Atomics.load(this.view, FOREIGN) === 1;
7707
- }
7708
- changedSince(seen, path) {
7709
- const now = Atomics.load(this.view, GEN2);
7710
- const distance = now - seen >>> 0;
7711
- if (distance === 0) return { now, changed: false };
7712
- if (distance > RING_SIZE2) return { now, changed: true };
7713
- const hash = changeHash(normal(path));
7714
- for (let i = 1; i <= distance; i++) {
7715
- const g = seen + i | 0;
7716
- const at = RING2 + (g & RING_SIZE2 - 1) * SLOT2;
7717
- if (Atomics.load(this.view, at) !== g) return { now, changed: true };
7718
- const hit = Atomics.load(this.view, at + 1) === hash;
7719
- if (Atomics.load(this.view, at) !== g) return { now, changed: true };
7720
- if (hit) return { now, changed: true };
7721
- }
7722
- return { now, changed: false };
7723
- }
7724
- };
7725
- }
7726
- });
7727
-
7728
7745
  // src/realm/sync-sab-bridge.ts
7729
7746
  function syncCallCount() {
7730
7747
  return calls;
@@ -8095,8 +8112,16 @@ function refusal(err) {
8095
8112
  const code2 = err?.code;
8096
8113
  return code2 === "EACCES" || code2 === "EPERM";
8097
8114
  }
8098
- function cachedStat(s, dir, view) {
8099
- if (view?.foreign() && !s.dirty) return false;
8115
+ function young(s) {
8116
+ return performance.now() - (s.filledAt ?? Number.NEGATIVE_INFINITY) < SHARED_TTL_MS;
8117
+ }
8118
+ function cachedStat(node, dir, view) {
8119
+ const s = node.live;
8120
+ if (view?.foreign() && !s.dirty) {
8121
+ const gen = s.statGen ?? s.named;
8122
+ if (s.openCount > 0 || !young(s) || gen === void 0) return false;
8123
+ if (view.changedSince(gen, liveNodePath(node)).changed) return false;
8124
+ }
8100
8125
  return s.stat !== void 0 && (!dir || s.statAt === syncCallCount());
8101
8126
  }
8102
8127
  function searchParents(Fs, node, bridge, call2) {
@@ -8162,9 +8187,22 @@ function linkedStates(bridgeOf) {
8162
8187
  const holder = (node) => opens.get(node.live) ?? node;
8163
8188
  return { opening: opening2, sharedWith, know, idOf: idOf4, holder };
8164
8189
  }
8190
+ function filled(s, stat, asked) {
8191
+ Object.assign(s, { stat, statAt: syncCallCount(), seenAt: syncEpoch(), ...asked });
8192
+ }
8193
+ function asking(bridge) {
8194
+ return { filledAt: performance.now(), statGen: bridge.changes?.now() };
8195
+ }
8196
+ function listing(s) {
8197
+ return { filledAt: s.listedAt ?? Number.NEGATIVE_INFINITY, statGen: s.listedGen };
8198
+ }
8199
+ function answeredAt(node, asked) {
8200
+ Object.assign(node.live, asked);
8201
+ return node;
8202
+ }
8165
8203
  function freshState(stat) {
8166
8204
  const base = { len: 0, loaded: false, dirty: false, openCount: 0 };
8167
- return stat ? { stat, statAt: syncCallCount(), seenAt: syncEpoch(), ...base } : base;
8205
+ return stat ? { stat, statAt: syncCallCount(), filledAt: performance.now(), seenAt: syncEpoch(), ...base } : base;
8168
8206
  }
8169
8207
  function createHelpers(Fs, ops) {
8170
8208
  const bridgeOf = (node) => node.mount.opts.bridge;
@@ -8217,9 +8255,11 @@ function createHelpers(Fs, ops) {
8217
8255
  const size = node.live.ranged?.size() ?? node.live.len;
8218
8256
  return { ...node.live.stat, size };
8219
8257
  }
8220
- if (!cachedStat(node.live, Fs.isDir(node.mode), bridgeOf(node).changes)) {
8258
+ const view = bridgeOf(node).changes;
8259
+ if (!cachedStat(node, Fs.isDir(node.mode), view)) {
8260
+ const asked = { statGen: view?.now(), filledAt: performance.now() };
8221
8261
  const st = call2(() => bridgeOf(node).lstat(liveNodePath(linked.holder(node))));
8222
- Object.assign(node.live, { stat: st, statAt: syncCallCount(), seenAt: syncEpoch() });
8262
+ filled(node.live, st, asked);
8223
8263
  node.mode = modeFromStat(st, exactIn(node));
8224
8264
  }
8225
8265
  return node.live.stat;
@@ -8381,7 +8421,7 @@ function createNodeOps(h) {
8381
8421
  const owned = bridgeOf(node).owners === true;
8382
8422
  if (owned && Fs.isLink(node.mode) && statOf(node).uid === void 0)
8383
8423
  node.live.stat = void 0;
8384
- const cached = owned && cachedStat(node.live, Fs.isDir(node.mode), bridgeOf(node).changes);
8424
+ const cached = owned && cachedStat(node, Fs.isDir(node.mode), bridgeOf(node).changes);
8385
8425
  const st = statOf(node);
8386
8426
  if (cached) searchParents(Fs, node, bridgeOf(node), call2);
8387
8427
  const s = node.live;
@@ -8430,8 +8470,9 @@ function createNodeOps(h) {
8430
8470
  lookup(parent, name2) {
8431
8471
  const listed2 = parent.live.listed?.get(name2);
8432
8472
  parent.live.listed?.delete(name2);
8473
+ const asked = listed2 ? listing(parent.live) : asking(bridgeOf(parent));
8433
8474
  const st = listed2 ?? call2(() => bridgeOf(parent).lstat(childPath(parent, name2)));
8434
- return makeNode(parent, name2, st);
8475
+ return answeredAt(makeNode(parent, name2, st), asked);
8435
8476
  },
8436
8477
  mknod(parent, name2, mode) {
8437
8478
  parent.live.listed = void 0;
@@ -8504,8 +8545,11 @@ function createNodeOps(h) {
8504
8545
  call2(() => bridgeOf(parent).rmdir(childPath(parent, name2)));
8505
8546
  },
8506
8547
  readdir(node) {
8548
+ const listedAt = performance.now();
8549
+ const listedGen = bridgeOf(node).changes?.now();
8507
8550
  const listed2 = call2(() => bridgeOf(node).readdirStat(liveNodePath(node)));
8508
8551
  node.live.listed = /* @__PURE__ */ new Map();
8552
+ Object.assign(node.live, { listedAt, listedGen });
8509
8553
  for (const [name2, st] of listed2) if (st) node.live.listed.set(name2, st);
8510
8554
  return [".", "..", ...listed2.map(([name2]) => name2)];
8511
8555
  },
@@ -9137,6 +9181,7 @@ var init_live_vfs_fs = __esm({
9137
9181
  "src/realm/live-vfs-fs.ts"() {
9138
9182
  "use strict";
9139
9183
  init_access();
9184
+ init_changes();
9140
9185
  init_ranged();
9141
9186
  init_types();
9142
9187
  init_wasi_errno();
@@ -10273,8 +10318,8 @@ function sysEntry(parts) {
10273
10318
  }
10274
10319
  return at;
10275
10320
  }
10276
- function sysText(file, listing) {
10277
- return typeof file === "string" ? file : file(listing());
10321
+ function sysText(file, listing2) {
10322
+ return typeof file === "string" ? file : file(listing2());
10278
10323
  }
10279
10324
  function commOf(info) {
10280
10325
  const name2 = info.argv[0] ?? "";
@@ -10402,35 +10447,35 @@ function memoryInfo(source, used = 0) {
10402
10447
  ["SwapFree", kb(0)]
10403
10448
  ].map(([key, value2]) => `${`${key}:`.padEnd(16)}${value2.padStart(12)}`).concat("").join("\n");
10404
10449
  }
10405
- function systemFile(name2, listing, now, memory) {
10450
+ function systemFile(name2, listing2, now, memory) {
10406
10451
  if (name2 === "version") return procVersion();
10407
- const up = Math.max(0, now - listing.boot) / 1e3;
10452
+ const up = Math.max(0, now - listing2.boot) / 1e3;
10408
10453
  if (name2 === "uptime") return `${up.toFixed(2)} ${up.toFixed(2)}
10409
10454
  `;
10410
10455
  if (name2 === "meminfo") {
10411
10456
  return memoryInfo(
10412
10457
  memory,
10413
- listing.processes.reduce((sum2, p) => sum2 + p.memory, 0)
10458
+ listing2.processes.reduce((sum2, p) => sum2 + p.memory, 0)
10414
10459
  );
10415
10460
  }
10416
- const live = listing.processes.filter((p) => p.state !== "Z");
10461
+ const live = listing2.processes.filter((p) => p.state !== "Z");
10417
10462
  if (name2 === "loadavg") {
10418
- const last = Math.max(0, ...listing.processes.map((p) => p.pid));
10419
- const [one, five, fifteen] = (listing.load ?? [0, 0, 0]).map((n) => n.toFixed(2));
10463
+ const last = Math.max(0, ...listing2.processes.map((p) => p.pid));
10464
+ const [one, five, fifteen] = (listing2.load ?? [0, 0, 0]).map((n) => n.toFixed(2));
10420
10465
  return `${one} ${five} ${fifteen} 1/${live.length} ${last}
10421
10466
  `;
10422
10467
  }
10423
- const sum = (key) => listing.cpu?.[key] ?? listing.processes.reduce((n, p) => n + (p[key] ?? 0), 0);
10468
+ const sum = (key) => listing2.cpu?.[key] ?? listing2.processes.reduce((n, p) => n + (p[key] ?? 0), 0);
10424
10469
  const user = ticks(sum("utime"));
10425
10470
  const system = ticks(sum("stime"));
10426
- const idle = listing.cpu ? ticks(listing.cpu.idle) : Math.max(0, ticks(now - listing.boot) - user - system);
10471
+ const idle = listing2.cpu ? ticks(listing2.cpu.idle) : Math.max(0, ticks(now - listing2.boot) - user - system);
10427
10472
  return [
10428
10473
  `cpu ${user} 0 ${system} ${idle} 0 0 0 0 0 0`,
10429
10474
  `cpu0 ${user} 0 ${system} ${idle} 0 0 0 0 0 0`,
10430
10475
  "intr 0",
10431
10476
  "ctxt 0",
10432
- `btime ${Math.floor(listing.boot / 1e3)}`,
10433
- `processes ${listing.processes.length}`,
10477
+ `btime ${Math.floor(listing2.boot / 1e3)}`,
10478
+ `processes ${listing2.processes.length}`,
10434
10479
  "procs_running 1",
10435
10480
  "procs_blocked 0",
10436
10481
  ""
@@ -10515,10 +10560,10 @@ function useProcfs(Fs, sys, pid, memory = memorySource()) {
10515
10560
  const find = (processes2, target, own) => (own ? processes2.find((p) => p.tid === target) : void 0) ?? processes2.find((p) => p.pid === target || p.tid === target);
10516
10561
  const infoOf = (target) => find(procList().processes, target, false);
10517
10562
  const pidContent = (target, name2, own = false) => () => {
10518
- const listing = procList();
10519
- const info = find(listing.processes, target, own);
10563
+ const listing2 = procList();
10564
+ const info = find(listing2.processes, target, own);
10520
10565
  if (!info) throw error();
10521
- return pidFile(name2, info, listing.boot);
10566
+ return pidFile(name2, info, listing2.boot);
10522
10567
  };
10523
10568
  const sysDir = (parent, name2, tree) => {
10524
10569
  const dir = fresh(parent, name2, DIR_MODE2);
@@ -10653,15 +10698,15 @@ var init_procfs = __esm({
10653
10698
  osrelease: `${OS_RELEASE}
10654
10699
  `,
10655
10700
  ostype: "Linux\n",
10656
- slicc_wasm_memories: (listing) => `${listing.wasm?.live ?? 0} ${listing.wasm?.ceiling ?? 0} ${listing.wasm?.source ?? "none"}
10701
+ slicc_wasm_memories: (listing2) => `${listing2.wasm?.live ?? 0} ${listing2.wasm?.ceiling ?? 0} ${listing2.wasm?.source ?? "none"}
10657
10702
  `,
10658
- slicc_shared_root: (listing) => `${listing.peers ?? 0}
10703
+ slicc_shared_root: (listing2) => `${listing2.peers ?? 0}
10659
10704
  `,
10660
- slicc_fs_calls: (listing) => `${listing.fsCalls ?? 0}
10705
+ slicc_fs_calls: (listing2) => `${listing2.fsCalls ?? 0}
10661
10706
  `,
10662
- slicc_opfs_calls: (listing) => `${listing.opfsCalls ?? 0}
10707
+ slicc_opfs_calls: (listing2) => `${listing2.opfsCalls ?? 0}
10663
10708
  `,
10664
- slicc_opfs_ops: (listing) => `${listing.opfsOps ?? 0}
10709
+ slicc_opfs_ops: (listing2) => `${listing2.opfsOps ?? 0}
10665
10710
  `
10666
10711
  }
10667
10712
  };
@@ -11003,10 +11048,10 @@ var init_wasi_procfs = __esm({
11003
11048
  return {
11004
11049
  kind: "file",
11005
11050
  content: () => {
11006
- const listing = this.listing();
11007
- const info = listing.processes.find((p) => p.pid === Number(top));
11051
+ const listing2 = this.listing();
11052
+ const info = listing2.processes.find((p) => p.pid === Number(top));
11008
11053
  if (!info) throw syncError("ENOENT", `/proc/${top}/${leaf}`);
11009
- return pidFile(leaf, info, listing.boot);
11054
+ return pidFile(leaf, info, listing2.boot);
11010
11055
  }
11011
11056
  };
11012
11057
  }
@@ -11024,9 +11069,9 @@ var init_wasi_procfs = __esm({
11024
11069
  return {
11025
11070
  kind: "file",
11026
11071
  content: () => {
11027
- const listing = this.listing();
11028
- const info = listing.processes.find((p) => p.pid === pid);
11029
- const line = info && taskStat(info, Number(tid), listing.boot);
11072
+ const listing2 = this.listing();
11073
+ const info = listing2.processes.find((p) => p.pid === pid);
11074
+ const line = info && taskStat(info, Number(tid), listing2.boot);
11030
11075
  if (line === void 0) throw syncError("ENOENT", `/proc/${pid}/task/${tid}/stat`);
11031
11076
  return line;
11032
11077
  }
@@ -11092,10 +11137,10 @@ var init_wasi_procfs = __esm({
11092
11137
 
11093
11138
  // src/process/js/js-proc.ts
11094
11139
  function* procStep(abs, pid, run) {
11095
- const listing = yield { op: "proc-list" };
11140
+ const listing2 = yield { op: "proc-list" };
11096
11141
  const mounts = abs === "/proc/mounts" ? yield { op: "mount-list" } : [];
11097
11142
  const tree = new ProcTree(
11098
- { procList: () => listing, mountList: () => mounts },
11143
+ { procList: () => listing2, mountList: () => mounts },
11099
11144
  pid,
11100
11145
  memorySource()
11101
11146
  );
@@ -15511,6 +15556,59 @@ var init_js_runtime = __esm({
15511
15556
  }
15512
15557
  });
15513
15558
 
15559
+ // src/kernel/held-cwd.ts
15560
+ var CWD_SLOTS, UNTRACKED, HeldCwd;
15561
+ var init_held_cwd = __esm({
15562
+ "src/kernel/held-cwd.ts"() {
15563
+ "use strict";
15564
+ CWD_SLOTS = 1024;
15565
+ UNTRACKED = CWD_SLOTS - 1;
15566
+ HeldCwd = class {
15567
+ view;
15568
+ seen;
15569
+ dead = false;
15570
+ slot;
15571
+ at;
15572
+ ask;
15573
+ constructor(sab, slot2, ask, state) {
15574
+ this.view = new Int32Array(sab);
15575
+ this.slot = slot2;
15576
+ this.ask = ask;
15577
+ this.seen = state.seen;
15578
+ this.dead = state.dead;
15579
+ this.at = state.path;
15580
+ }
15581
+ moved(reply, path) {
15582
+ if (reply.slot === void 0) return false;
15583
+ this.slot = reply.slot;
15584
+ this.seen = reply.seen ?? Atomics.load(this.view, reply.slot);
15585
+ this.dead = reply.dead === true;
15586
+ this.at = path;
15587
+ return true;
15588
+ }
15589
+ follow() {
15590
+ const answer2 = this.ask();
15591
+ if (answer2.slot !== void 0) this.slot = answer2.slot;
15592
+ this.seen = answer2.gen;
15593
+ this.dead = !answer2.alive;
15594
+ return answer2.alive;
15595
+ }
15596
+ alive(path) {
15597
+ if (path !== void 0 && this.at !== void 0 && path !== this.at) {
15598
+ this.at = path;
15599
+ return this.follow();
15600
+ }
15601
+ if (this.dead) return false;
15602
+ if (Atomics.load(this.view, this.slot) === this.seen) return true;
15603
+ return this.follow();
15604
+ }
15605
+ get current() {
15606
+ return this.slot;
15607
+ }
15608
+ };
15609
+ }
15610
+ });
15611
+
15514
15612
  // src/process/wasi/dylink.ts
15515
15613
  function dylinkInfo(module) {
15516
15614
  const [section2] = WebAssembly.Module.customSections(module, "dylink.0");
@@ -17790,6 +17888,12 @@ function ownersOf(init) {
17790
17888
  ...init.changes ? { changes: init.changes } : {}
17791
17889
  };
17792
17890
  }
17891
+ function heldOf2(init, call2) {
17892
+ const { held: held2 } = init;
17893
+ if (!held2) return {};
17894
+ const ask = () => call2({ op: "cwd-check" });
17895
+ return { held: new HeldCwd(held2.sab, held2.slot, ask, { ...held2, path: init.cwd }) };
17896
+ }
17793
17897
  async function runWasiProcess(init, port3) {
17794
17898
  captureBacktraces(init.env);
17795
17899
  const signals = new WasiSignals(
@@ -17847,6 +17951,7 @@ async function runWasiProcess(init, port3) {
17847
17951
  call2
17848
17952
  ),
17849
17953
  ...descriptors(init, fork, threads),
17954
+ ...heldOf2(init, call2),
17850
17955
  ...threads ? { credGen: threads.ids } : {}
17851
17956
  });
17852
17957
  host.sigmask = sigmaskOf(module, init.sab, gate, threads);
@@ -17955,7 +18060,8 @@ async function runWasiThread(init, port3) {
17955
18060
  ),
17956
18061
  call2
17957
18062
  ),
17958
- shared: threads.ids
18063
+ shared: threads.ids,
18064
+ ...heldOf2(init, call2)
17959
18065
  });
17960
18066
  host.tid = thread.tid;
17961
18067
  host.sigmask = sigmaskOf(init.program.module, init.sab, gate, threads, init.signals);
@@ -18006,6 +18112,7 @@ var init_wasi_runtime = __esm({
18006
18112
  "src/process/wasi/wasi-runtime.ts"() {
18007
18113
  "use strict";
18008
18114
  init_physical_path();
18115
+ init_held_cwd();
18009
18116
  init_protocol();
18010
18117
  init_signals();
18011
18118
  init_sync_sab_bridge();