@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.
@@ -458,6 +458,70 @@ var init_access = __esm({
458
458
  }
459
459
  });
460
460
 
461
+ // src/fs/changes.ts
462
+ function changeHash(path) {
463
+ let h = 2166136261;
464
+ for (let i = 0; i < path.length; i++) {
465
+ h ^= path.charCodeAt(i);
466
+ h = Math.imul(h, 16777619);
467
+ }
468
+ return h === 0 ? 1 : h;
469
+ }
470
+ function normal(path) {
471
+ const parts = [];
472
+ for (const part of path.split("/")) {
473
+ if (part === "" || part === ".") continue;
474
+ if (part === "..") parts.pop();
475
+ else parts.push(part);
476
+ }
477
+ return `/${parts.join("/")}`;
478
+ }
479
+ var GEN, RING, RING_SIZE, SLOT, SHARED_TTL_MS, DIRTY, FOREIGN, CHANGES_BYTES, ChangeView;
480
+ var init_changes = __esm({
481
+ "src/fs/changes.ts"() {
482
+ "use strict";
483
+ GEN = 0;
484
+ RING = 1;
485
+ RING_SIZE = 4096;
486
+ SLOT = 2;
487
+ SHARED_TTL_MS = 100;
488
+ DIRTY = RING + RING_SIZE * SLOT;
489
+ FOREIGN = DIRTY + 1;
490
+ CHANGES_BYTES = (FOREIGN + 1) * 4;
491
+ ChangeView = class {
492
+ view;
493
+ constructor(sab) {
494
+ this.view = new Int32Array(sab);
495
+ }
496
+ now() {
497
+ return Atomics.load(this.view, GEN);
498
+ }
499
+ clean() {
500
+ return this.view.length > DIRTY && Atomics.load(this.view, DIRTY) === 0;
501
+ }
502
+ foreign() {
503
+ return this.view.length > FOREIGN && Atomics.load(this.view, FOREIGN) === 1;
504
+ }
505
+ changedSince(seen, path) {
506
+ const now = Atomics.load(this.view, GEN);
507
+ const distance = now - seen >>> 0;
508
+ if (distance === 0) return { now, changed: false };
509
+ if (distance > RING_SIZE) return { now, changed: true };
510
+ const hash = changeHash(normal(path));
511
+ for (let i = 1; i <= distance; i++) {
512
+ const g = seen + i | 0;
513
+ const at = RING + (g & RING_SIZE - 1) * SLOT;
514
+ if (Atomics.load(this.view, at) !== g) return { now, changed: true };
515
+ const hit = Atomics.load(this.view, at + 1) === hash;
516
+ if (Atomics.load(this.view, at) !== g) return { now, changed: true };
517
+ if (hit) return { now, changed: true };
518
+ }
519
+ return { now, changed: false };
520
+ }
521
+ };
522
+ }
523
+ });
524
+
461
525
  // src/fs/pages.ts
462
526
  var PAGE, READAHEAD, CLEAN_LIMIT, WRITEBACK, RUN, Pages;
463
527
  var init_pages = __esm({
@@ -1036,69 +1100,6 @@ var init_sync_fs_wire = __esm({
1036
1100
  }
1037
1101
  });
1038
1102
 
1039
- // src/fs/changes.ts
1040
- function changeHash(path) {
1041
- let h = 2166136261;
1042
- for (let i = 0; i < path.length; i++) {
1043
- h ^= path.charCodeAt(i);
1044
- h = Math.imul(h, 16777619);
1045
- }
1046
- return h === 0 ? 1 : h;
1047
- }
1048
- function normal(path) {
1049
- const parts = [];
1050
- for (const part of path.split("/")) {
1051
- if (part === "" || part === ".") continue;
1052
- if (part === "..") parts.pop();
1053
- else parts.push(part);
1054
- }
1055
- return `/${parts.join("/")}`;
1056
- }
1057
- var GEN, RING, RING_SIZE, SLOT, DIRTY, FOREIGN, CHANGES_BYTES, ChangeView;
1058
- var init_changes = __esm({
1059
- "src/fs/changes.ts"() {
1060
- "use strict";
1061
- GEN = 0;
1062
- RING = 1;
1063
- RING_SIZE = 4096;
1064
- SLOT = 2;
1065
- DIRTY = RING + RING_SIZE * SLOT;
1066
- FOREIGN = DIRTY + 1;
1067
- CHANGES_BYTES = (FOREIGN + 1) * 4;
1068
- ChangeView = class {
1069
- view;
1070
- constructor(sab) {
1071
- this.view = new Int32Array(sab);
1072
- }
1073
- now() {
1074
- return Atomics.load(this.view, GEN);
1075
- }
1076
- clean() {
1077
- return this.view.length > DIRTY && Atomics.load(this.view, DIRTY) === 0;
1078
- }
1079
- foreign() {
1080
- return this.view.length > FOREIGN && Atomics.load(this.view, FOREIGN) === 1;
1081
- }
1082
- changedSince(seen, path) {
1083
- const now = Atomics.load(this.view, GEN);
1084
- const distance = now - seen >>> 0;
1085
- if (distance === 0) return { now, changed: false };
1086
- if (distance > RING_SIZE) return { now, changed: true };
1087
- const hash = changeHash(normal(path));
1088
- for (let i = 1; i <= distance; i++) {
1089
- const g = seen + i | 0;
1090
- const at = RING + (g & RING_SIZE - 1) * SLOT;
1091
- if (Atomics.load(this.view, at) !== g) return { now, changed: true };
1092
- const hit = Atomics.load(this.view, at + 1) === hash;
1093
- if (Atomics.load(this.view, at) !== g) return { now, changed: true };
1094
- if (hit) return { now, changed: true };
1095
- }
1096
- return { now, changed: false };
1097
- }
1098
- };
1099
- }
1100
- });
1101
-
1102
1103
  // src/realm/sync-sab-bridge.ts
1103
1104
  function syncCallCount() {
1104
1105
  return calls;
@@ -1469,8 +1470,16 @@ function refusal(err) {
1469
1470
  const code2 = err?.code;
1470
1471
  return code2 === "EACCES" || code2 === "EPERM";
1471
1472
  }
1472
- function cachedStat(s, dir, view) {
1473
- if (view?.foreign() && !s.dirty) return false;
1473
+ function young(s) {
1474
+ return performance.now() - (s.filledAt ?? Number.NEGATIVE_INFINITY) < SHARED_TTL_MS;
1475
+ }
1476
+ function cachedStat(node, dir, view) {
1477
+ const s = node.live;
1478
+ if (view?.foreign() && !s.dirty) {
1479
+ const gen = s.statGen ?? s.named;
1480
+ if (s.openCount > 0 || !young(s) || gen === void 0) return false;
1481
+ if (view.changedSince(gen, liveNodePath(node)).changed) return false;
1482
+ }
1474
1483
  return s.stat !== void 0 && (!dir || s.statAt === syncCallCount());
1475
1484
  }
1476
1485
  function searchParents(Fs, node, bridge, call2) {
@@ -1536,9 +1545,22 @@ function linkedStates(bridgeOf) {
1536
1545
  const holder = (node) => opens.get(node.live) ?? node;
1537
1546
  return { opening: opening2, sharedWith, know, idOf: idOf4, holder };
1538
1547
  }
1548
+ function filled(s, stat, asked) {
1549
+ Object.assign(s, { stat, statAt: syncCallCount(), seenAt: syncEpoch(), ...asked });
1550
+ }
1551
+ function asking(bridge) {
1552
+ return { filledAt: performance.now(), statGen: bridge.changes?.now() };
1553
+ }
1554
+ function listing(s) {
1555
+ return { filledAt: s.listedAt ?? Number.NEGATIVE_INFINITY, statGen: s.listedGen };
1556
+ }
1557
+ function answeredAt(node, asked) {
1558
+ Object.assign(node.live, asked);
1559
+ return node;
1560
+ }
1539
1561
  function freshState(stat) {
1540
1562
  const base = { len: 0, loaded: false, dirty: false, openCount: 0 };
1541
- return stat ? { stat, statAt: syncCallCount(), seenAt: syncEpoch(), ...base } : base;
1563
+ return stat ? { stat, statAt: syncCallCount(), filledAt: performance.now(), seenAt: syncEpoch(), ...base } : base;
1542
1564
  }
1543
1565
  function createHelpers(Fs, ops) {
1544
1566
  const bridgeOf = (node) => node.mount.opts.bridge;
@@ -1591,9 +1613,11 @@ function createHelpers(Fs, ops) {
1591
1613
  const size = node.live.ranged?.size() ?? node.live.len;
1592
1614
  return { ...node.live.stat, size };
1593
1615
  }
1594
- if (!cachedStat(node.live, Fs.isDir(node.mode), bridgeOf(node).changes)) {
1616
+ const view = bridgeOf(node).changes;
1617
+ if (!cachedStat(node, Fs.isDir(node.mode), view)) {
1618
+ const asked = { statGen: view?.now(), filledAt: performance.now() };
1595
1619
  const st = call2(() => bridgeOf(node).lstat(liveNodePath(linked.holder(node))));
1596
- Object.assign(node.live, { stat: st, statAt: syncCallCount(), seenAt: syncEpoch() });
1620
+ filled(node.live, st, asked);
1597
1621
  node.mode = modeFromStat(st, exactIn(node));
1598
1622
  }
1599
1623
  return node.live.stat;
@@ -1755,7 +1779,7 @@ function createNodeOps(h) {
1755
1779
  const owned = bridgeOf(node).owners === true;
1756
1780
  if (owned && Fs.isLink(node.mode) && statOf(node).uid === void 0)
1757
1781
  node.live.stat = void 0;
1758
- const cached = owned && cachedStat(node.live, Fs.isDir(node.mode), bridgeOf(node).changes);
1782
+ const cached = owned && cachedStat(node, Fs.isDir(node.mode), bridgeOf(node).changes);
1759
1783
  const st = statOf(node);
1760
1784
  if (cached) searchParents(Fs, node, bridgeOf(node), call2);
1761
1785
  const s = node.live;
@@ -1804,8 +1828,9 @@ function createNodeOps(h) {
1804
1828
  lookup(parent, name2) {
1805
1829
  const listed2 = parent.live.listed?.get(name2);
1806
1830
  parent.live.listed?.delete(name2);
1831
+ const asked = listed2 ? listing(parent.live) : asking(bridgeOf(parent));
1807
1832
  const st = listed2 ?? call2(() => bridgeOf(parent).lstat(childPath(parent, name2)));
1808
- return makeNode(parent, name2, st);
1833
+ return answeredAt(makeNode(parent, name2, st), asked);
1809
1834
  },
1810
1835
  mknod(parent, name2, mode) {
1811
1836
  parent.live.listed = void 0;
@@ -1878,8 +1903,11 @@ function createNodeOps(h) {
1878
1903
  call2(() => bridgeOf(parent).rmdir(childPath(parent, name2)));
1879
1904
  },
1880
1905
  readdir(node) {
1906
+ const listedAt = performance.now();
1907
+ const listedGen = bridgeOf(node).changes?.now();
1881
1908
  const listed2 = call2(() => bridgeOf(node).readdirStat(liveNodePath(node)));
1882
1909
  node.live.listed = /* @__PURE__ */ new Map();
1910
+ Object.assign(node.live, { listedAt, listedGen });
1883
1911
  for (const [name2, st] of listed2) if (st) node.live.listed.set(name2, st);
1884
1912
  return [".", "..", ...listed2.map(([name2]) => name2)];
1885
1913
  },
@@ -2511,6 +2539,7 @@ var init_live_vfs_fs = __esm({
2511
2539
  "src/realm/live-vfs-fs.ts"() {
2512
2540
  "use strict";
2513
2541
  init_access();
2542
+ init_changes();
2514
2543
  init_ranged();
2515
2544
  init_types();
2516
2545
  init_wasi_errno();
@@ -2664,7 +2693,7 @@ var KERNEL_VERSION, OS_RELEASE;
2664
2693
  var init_version = __esm({
2665
2694
  "src/version.ts"() {
2666
2695
  "use strict";
2667
- KERNEL_VERSION = true ? "1.133.3" : "0.0.0-dev";
2696
+ KERNEL_VERSION = true ? "1.133.5" : "0.0.0-dev";
2668
2697
  OS_RELEASE = "6.0.0-slicc";
2669
2698
  }
2670
2699
  });
@@ -5366,8 +5395,8 @@ function sysEntry(parts) {
5366
5395
  }
5367
5396
  return at;
5368
5397
  }
5369
- function sysText(file, listing) {
5370
- return typeof file === "string" ? file : file(listing());
5398
+ function sysText(file, listing2) {
5399
+ return typeof file === "string" ? file : file(listing2());
5371
5400
  }
5372
5401
  function commOf(info) {
5373
5402
  const name2 = info.argv[0] ?? "";
@@ -5495,35 +5524,35 @@ function memoryInfo(source, used = 0) {
5495
5524
  ["SwapFree", kb(0)]
5496
5525
  ].map(([key, value2]) => `${`${key}:`.padEnd(16)}${value2.padStart(12)}`).concat("").join("\n");
5497
5526
  }
5498
- function systemFile(name2, listing, now, memory) {
5527
+ function systemFile(name2, listing2, now, memory) {
5499
5528
  if (name2 === "version") return procVersion();
5500
- const up = Math.max(0, now - listing.boot) / 1e3;
5529
+ const up = Math.max(0, now - listing2.boot) / 1e3;
5501
5530
  if (name2 === "uptime") return `${up.toFixed(2)} ${up.toFixed(2)}
5502
5531
  `;
5503
5532
  if (name2 === "meminfo") {
5504
5533
  return memoryInfo(
5505
5534
  memory,
5506
- listing.processes.reduce((sum2, p) => sum2 + p.memory, 0)
5535
+ listing2.processes.reduce((sum2, p) => sum2 + p.memory, 0)
5507
5536
  );
5508
5537
  }
5509
- const live = listing.processes.filter((p) => p.state !== "Z");
5538
+ const live = listing2.processes.filter((p) => p.state !== "Z");
5510
5539
  if (name2 === "loadavg") {
5511
- const last = Math.max(0, ...listing.processes.map((p) => p.pid));
5512
- const [one, five, fifteen] = (listing.load ?? [0, 0, 0]).map((n) => n.toFixed(2));
5540
+ const last = Math.max(0, ...listing2.processes.map((p) => p.pid));
5541
+ const [one, five, fifteen] = (listing2.load ?? [0, 0, 0]).map((n) => n.toFixed(2));
5513
5542
  return `${one} ${five} ${fifteen} 1/${live.length} ${last}
5514
5543
  `;
5515
5544
  }
5516
- const sum = (key) => listing.cpu?.[key] ?? listing.processes.reduce((n, p) => n + (p[key] ?? 0), 0);
5545
+ const sum = (key) => listing2.cpu?.[key] ?? listing2.processes.reduce((n, p) => n + (p[key] ?? 0), 0);
5517
5546
  const user = ticks(sum("utime"));
5518
5547
  const system = ticks(sum("stime"));
5519
- const idle = listing.cpu ? ticks(listing.cpu.idle) : Math.max(0, ticks(now - listing.boot) - user - system);
5548
+ const idle = listing2.cpu ? ticks(listing2.cpu.idle) : Math.max(0, ticks(now - listing2.boot) - user - system);
5520
5549
  return [
5521
5550
  `cpu ${user} 0 ${system} ${idle} 0 0 0 0 0 0`,
5522
5551
  `cpu0 ${user} 0 ${system} ${idle} 0 0 0 0 0 0`,
5523
5552
  "intr 0",
5524
5553
  "ctxt 0",
5525
- `btime ${Math.floor(listing.boot / 1e3)}`,
5526
- `processes ${listing.processes.length}`,
5554
+ `btime ${Math.floor(listing2.boot / 1e3)}`,
5555
+ `processes ${listing2.processes.length}`,
5527
5556
  "procs_running 1",
5528
5557
  "procs_blocked 0",
5529
5558
  ""
@@ -5608,10 +5637,10 @@ function useProcfs(Fs, sys, pid, memory = memorySource()) {
5608
5637
  const find = (processes2, target, own) => (own ? processes2.find((p) => p.tid === target) : void 0) ?? processes2.find((p) => p.pid === target || p.tid === target);
5609
5638
  const infoOf = (target) => find(procList().processes, target, false);
5610
5639
  const pidContent = (target, name2, own = false) => () => {
5611
- const listing = procList();
5612
- const info = find(listing.processes, target, own);
5640
+ const listing2 = procList();
5641
+ const info = find(listing2.processes, target, own);
5613
5642
  if (!info) throw error();
5614
- return pidFile(name2, info, listing.boot);
5643
+ return pidFile(name2, info, listing2.boot);
5615
5644
  };
5616
5645
  const sysDir = (parent, name2, tree) => {
5617
5646
  const dir = fresh(parent, name2, DIR_MODE2);
@@ -5746,15 +5775,15 @@ var init_procfs = __esm({
5746
5775
  osrelease: `${OS_RELEASE}
5747
5776
  `,
5748
5777
  ostype: "Linux\n",
5749
- slicc_wasm_memories: (listing) => `${listing.wasm?.live ?? 0} ${listing.wasm?.ceiling ?? 0} ${listing.wasm?.source ?? "none"}
5778
+ slicc_wasm_memories: (listing2) => `${listing2.wasm?.live ?? 0} ${listing2.wasm?.ceiling ?? 0} ${listing2.wasm?.source ?? "none"}
5750
5779
  `,
5751
- slicc_shared_root: (listing) => `${listing.peers ?? 0}
5780
+ slicc_shared_root: (listing2) => `${listing2.peers ?? 0}
5752
5781
  `,
5753
- slicc_fs_calls: (listing) => `${listing.fsCalls ?? 0}
5782
+ slicc_fs_calls: (listing2) => `${listing2.fsCalls ?? 0}
5754
5783
  `,
5755
- slicc_opfs_calls: (listing) => `${listing.opfsCalls ?? 0}
5784
+ slicc_opfs_calls: (listing2) => `${listing2.opfsCalls ?? 0}
5756
5785
  `,
5757
- slicc_opfs_ops: (listing) => `${listing.opfsOps ?? 0}
5786
+ slicc_opfs_ops: (listing2) => `${listing2.opfsOps ?? 0}
5758
5787
  `
5759
5788
  }
5760
5789
  };
@@ -6579,6 +6608,59 @@ var init_physical_path = __esm({
6579
6608
  }
6580
6609
  });
6581
6610
 
6611
+ // src/kernel/held-cwd.ts
6612
+ var CWD_SLOTS, UNTRACKED, HeldCwd;
6613
+ var init_held_cwd = __esm({
6614
+ "src/kernel/held-cwd.ts"() {
6615
+ "use strict";
6616
+ CWD_SLOTS = 1024;
6617
+ UNTRACKED = CWD_SLOTS - 1;
6618
+ HeldCwd = class {
6619
+ view;
6620
+ seen;
6621
+ dead = false;
6622
+ slot;
6623
+ at;
6624
+ ask;
6625
+ constructor(sab, slot2, ask, state) {
6626
+ this.view = new Int32Array(sab);
6627
+ this.slot = slot2;
6628
+ this.ask = ask;
6629
+ this.seen = state.seen;
6630
+ this.dead = state.dead;
6631
+ this.at = state.path;
6632
+ }
6633
+ moved(reply, path) {
6634
+ if (reply.slot === void 0) return false;
6635
+ this.slot = reply.slot;
6636
+ this.seen = reply.seen ?? Atomics.load(this.view, reply.slot);
6637
+ this.dead = reply.dead === true;
6638
+ this.at = path;
6639
+ return true;
6640
+ }
6641
+ follow() {
6642
+ const answer2 = this.ask();
6643
+ if (answer2.slot !== void 0) this.slot = answer2.slot;
6644
+ this.seen = answer2.gen;
6645
+ this.dead = !answer2.alive;
6646
+ return answer2.alive;
6647
+ }
6648
+ alive(path) {
6649
+ if (path !== void 0 && this.at !== void 0 && path !== this.at) {
6650
+ this.at = path;
6651
+ return this.follow();
6652
+ }
6653
+ if (this.dead) return false;
6654
+ if (Atomics.load(this.view, this.slot) === this.seen) return true;
6655
+ return this.follow();
6656
+ }
6657
+ get current() {
6658
+ return this.slot;
6659
+ }
6660
+ };
6661
+ }
6662
+ });
6663
+
6582
6664
  // src/process/wasi/dylink.ts
6583
6665
  function dylinkInfo(module) {
6584
6666
  const [section2] = WebAssembly.Module.customSections(module, "dylink.0");
@@ -6741,11 +6823,11 @@ function copying(bridge, mutating, entry) {
6741
6823
  copy: (from, to, mode, exclusive) => mutating(() => copy2.call(bridge, from, to, mode, exclusive), ...entry(to))
6742
6824
  };
6743
6825
  }
6744
- function listed({ cache, seen, filled }, p, list, gen, asked) {
6826
+ function listed({ cache, seen, filled: filled2 }, p, list, gen, asked) {
6745
6827
  const base = p === "/" ? "" : p;
6746
6828
  for (const [name2, st] of list) {
6747
6829
  if (!st) continue;
6748
- filled.set(st, asked);
6830
+ filled2.set(st, asked);
6749
6831
  const at = `${base}/${name2}`;
6750
6832
  cache.put(cache.lstats, at, st);
6751
6833
  if (gen !== void 0) seen.saw(cache.lstats, at, gen);
@@ -6758,7 +6840,7 @@ function fetched({
6758
6840
  view,
6759
6841
  now,
6760
6842
  seen,
6761
- filled,
6843
+ filled: filled2,
6762
6844
  cache
6763
6845
  }, map, path, get) {
6764
6846
  const gen = view?.now();
@@ -6770,7 +6852,7 @@ function fetched({
6770
6852
  } catch (e) {
6771
6853
  hit = { error: e, until: asked + MISS_TTL_MS, ...gen !== void 0 ? { gen } : {} };
6772
6854
  }
6773
- filled.set(hit, asked);
6855
+ filled2.set(hit, asked);
6774
6856
  cache.put(map, path, hit);
6775
6857
  return hit;
6776
6858
  }
@@ -6779,17 +6861,17 @@ function cachingBridge(bridge, now = () => performance.now(), shared) {
6779
6861
  const cache = new StatCache(blind ? void 0 : shared);
6780
6862
  const { stats, lstats } = cache;
6781
6863
  const view = bridge.changes;
6782
- const filled = new Fills(view, now);
6783
- const young = (entry2) => filled.young(entry2);
6784
- const live = (miss, path) => miss.until > now() && young(miss) && (view === void 0 || miss.gen === void 0 || !view.changedSince(miss.gen, path).changed);
6864
+ const filled2 = new Fills(view, now);
6865
+ const young2 = (entry2) => filled2.young(entry2);
6866
+ const live = (miss, path) => miss.until > now() && young2(miss) && (view === void 0 || miss.gen === void 0 || !view.changedSince(miss.gen, path).changed);
6785
6867
  const seen = new SeenAt(view, lstats);
6786
- const kept = remember(cache, seen, blind, (entry2) => filled.set(entry2));
6868
+ const kept = remember(cache, seen, blind, (entry2) => filled2.set(entry2));
6787
6869
  const gone = (path) => goneAbove([lstats, stats], live, path);
6788
6870
  const usable = (map, path) => {
6789
6871
  const hit = map.get(path);
6790
6872
  if (hit === void 0) return void 0;
6791
6873
  if ("until" in hit) return live(hit, path) ? hit : void 0;
6792
- return young(hit) && !seen.changed(map, path) ? hit : void 0;
6874
+ return young2(hit) && !seen.changed(map, path) ? hit : void 0;
6793
6875
  };
6794
6876
  const cached = (map, path, get) => {
6795
6877
  if (blind) return get();
@@ -6798,7 +6880,7 @@ function cachingBridge(bridge, now = () => performance.now(), shared) {
6798
6880
  if (hit === void 0) {
6799
6881
  const missing2 = gone(path);
6800
6882
  if (missing2) throw missing2.error;
6801
- hit = fetched({ view, now, seen, filled, cache }, map, path, get);
6883
+ hit = fetched({ view, now, seen, filled: filled2, cache }, map, path, get);
6802
6884
  }
6803
6885
  if ("until" in hit) throw hit.error;
6804
6886
  return hit;
@@ -6857,14 +6939,14 @@ function cachingBridge(bridge, now = () => performance.now(), shared) {
6857
6939
  const gen = view?.now();
6858
6940
  const asked = now();
6859
6941
  const list = bridge.readdirStat(p);
6860
- if (!blind) listed({ cache, seen, filled }, p, list, gen, asked);
6942
+ if (!blind) listed({ cache, seen, filled: filled2 }, p, list, gen, asked);
6861
6943
  return list;
6862
6944
  },
6863
6945
  readlink: (p) => {
6864
6946
  const known = blind ? void 0 : (cache.fresh(), lstats.get(p));
6865
6947
  const gen = seen.lstat(p);
6866
6948
  const current = view !== void 0 && gen !== void 0 && !view.changedSince(gen, p).changed;
6867
- if (known && current && !("until" in known) && young(known) && !known.isSymbolicLink) {
6949
+ if (known && current && !("until" in known) && young2(known) && !known.isSymbolicLink) {
6868
6950
  throw syncError("EINVAL", `'${p}' is not a symbolic link`);
6869
6951
  }
6870
6952
  return bridge.readlink(p);
@@ -6937,10 +7019,11 @@ function pinnedIo2(bridge, handle, rewriting) {
6937
7019
  } : {}
6938
7020
  };
6939
7021
  }
6940
- var WasiError, FileBuffer, LocalFile, MAX_LINKS, MISS_TTL_MS, SHARED_TTL_MS, CACHE_CAP, FS_GEN, CRED_GEN, UMASK2, RING2, RING_SIZE2, SLOT2, FD_RING, FD_RING_SIZE, SHARED_IDS_BYTES, StatCache, SEEN_CAP, SeenAt, remember, Fills;
7022
+ var WasiError, FileBuffer, LocalFile, MAX_LINKS, MISS_TTL_MS, CACHE_CAP, FS_GEN, CRED_GEN, UMASK2, RING2, RING_SIZE2, SLOT2, FD_RING, FD_RING_SIZE, SHARED_IDS_BYTES, StatCache, SEEN_CAP, SeenAt, remember, Fills;
6941
7023
  var init_wasi_files = __esm({
6942
7024
  "src/process/wasi/wasi-files.ts"() {
6943
7025
  "use strict";
7026
+ init_changes();
6944
7027
  init_ranged();
6945
7028
  init_sync_fs_wire();
6946
7029
  WasiError = class extends Error {
@@ -7164,7 +7247,6 @@ var init_wasi_files = __esm({
7164
7247
  };
7165
7248
  MAX_LINKS = 40;
7166
7249
  MISS_TTL_MS = 20;
7167
- SHARED_TTL_MS = 100;
7168
7250
  CACHE_CAP = 8192;
7169
7251
  FS_GEN = 4;
7170
7252
  CRED_GEN = 5;
@@ -8770,6 +8852,7 @@ var init_wasi_fds = __esm({
8770
8852
  e.path = path;
8771
8853
  this.kernel.call({ op: "fd-meta", fd, meta: metaOf(e) });
8772
8854
  }
8855
+ this.held?.moved(this.kernel.call({ op: "cwd-chdir", path }), path);
8773
8856
  this.bump();
8774
8857
  }
8775
8858
  promote(fd, moved = false) {
@@ -9056,9 +9139,21 @@ var init_wasi_fds = __esm({
9056
9139
  this.buffers.set(next2, buffer);
9057
9140
  }
9058
9141
  }
9142
+ held;
9143
+ cwdEntry(dir) {
9144
+ return dir.preopen === "." || dir.preopen === "./";
9145
+ }
9146
+ goneCwd(dir) {
9147
+ return this.held !== void 0 && this.cwdEntry(dir) && !this.held.alive(dir.path);
9148
+ }
9059
9149
  resolve(dirfd, path) {
9060
9150
  if (path === "") throw new WasiError("ENOENT");
9061
- const joined2 = path.startsWith("/") ? path : `${this.dir(dirfd).path}/${path}`;
9151
+ if (path.startsWith("/")) return this.joined(path);
9152
+ const dir = this.dir(dirfd);
9153
+ if (path.split("/")[0] !== ".." && this.goneCwd(dir)) throw new WasiError("ENOENT");
9154
+ return this.joined(`${dir.path}/${path}`);
9155
+ }
9156
+ joined(joined2) {
9062
9157
  return joined2.split("/").includes("..") ? physical(this.fs, joined2) : normalize(joined2);
9063
9158
  }
9064
9159
  openKernelDevice(path, oflags, fdflags, rights) {
@@ -10355,6 +10450,7 @@ var init_wasi_host = __esm({
10355
10450
  this.o = { ...o, kernel: { sys, call: call2 } };
10356
10451
  this.startCwd = o.cwd;
10357
10452
  this.fds = new WasiFds(this.o.kernel, o.fs);
10453
+ if (o.held) this.fds.held = o.held;
10358
10454
  this.creds = new CredCache(
10359
10455
  () => this.o.kernel.call({ op: "proc-cred" }),
10360
10456
  o.credGen ?? o.shared
@@ -11318,13 +11414,13 @@ var init_wasi_host = __esm({
11318
11414
  const view = this.mem.view();
11319
11415
  const size = Math.min(view.getUint32(at, true), SIGINFO_BYTES);
11320
11416
  const fields = [sig, info.code, info.pid, info.uid, info.status];
11321
- let filled = 4;
11417
+ let filled2 = 4;
11322
11418
  for (const [i, value2] of fields.entries()) {
11323
11419
  if (4 + (i + 1) * 4 > size) break;
11324
11420
  view.setInt32(4 + at + i * 4, value2, true);
11325
- filled = 4 + (i + 1) * 4;
11421
+ filled2 = 4 + (i + 1) * 4;
11326
11422
  }
11327
- view.setUint32(at, filled, true);
11423
+ view.setUint32(at, filled2, true);
11328
11424
  return true;
11329
11425
  }
11330
11426
  fdAlias(path) {
@@ -11436,7 +11532,9 @@ var init_wasi_host = __esm({
11436
11532
  }
11437
11533
  readdir(fd, buf, len, cookie, usedPtr) {
11438
11534
  const dir = this.fds.dir(fd);
11439
- if (cookie === 0 || !dir.listing) dir.listing = this.list(dir.path);
11535
+ if (cookie === 0 || !dir.listing) {
11536
+ dir.listing = this.fds.goneCwd(dir) ? { names: [], stats: /* @__PURE__ */ new Map() } : this.list(dir.path);
11537
+ }
11440
11538
  const { names, stats } = dir.listing;
11441
11539
  let at = 0;
11442
11540
  for (let i = cookie; i < names.length && at < len; i++) {
@@ -12389,10 +12487,10 @@ var init_wasi_procfs = __esm({
12389
12487
  return {
12390
12488
  kind: "file",
12391
12489
  content: () => {
12392
- const listing = this.listing();
12393
- const info = listing.processes.find((p) => p.pid === Number(top));
12490
+ const listing2 = this.listing();
12491
+ const info = listing2.processes.find((p) => p.pid === Number(top));
12394
12492
  if (!info) throw syncError("ENOENT", `/proc/${top}/${leaf}`);
12395
- return pidFile(leaf, info, listing.boot);
12493
+ return pidFile(leaf, info, listing2.boot);
12396
12494
  }
12397
12495
  };
12398
12496
  }
@@ -12410,9 +12508,9 @@ var init_wasi_procfs = __esm({
12410
12508
  return {
12411
12509
  kind: "file",
12412
12510
  content: () => {
12413
- const listing = this.listing();
12414
- const info = listing.processes.find((p) => p.pid === pid);
12415
- const line = info && taskStat(info, Number(tid), listing.boot);
12511
+ const listing2 = this.listing();
12512
+ const info = listing2.processes.find((p) => p.pid === pid);
12513
+ const line = info && taskStat(info, Number(tid), listing2.boot);
12416
12514
  if (line === void 0) throw syncError("ENOENT", `/proc/${pid}/task/${tid}/stat`);
12417
12515
  return line;
12418
12516
  }
@@ -14675,6 +14773,12 @@ function ownersOf(init) {
14675
14773
  ...init.changes ? { changes: init.changes } : {}
14676
14774
  };
14677
14775
  }
14776
+ function heldOf2(init, call2) {
14777
+ const { held: held2 } = init;
14778
+ if (!held2) return {};
14779
+ const ask = () => call2({ op: "cwd-check" });
14780
+ return { held: new HeldCwd(held2.sab, held2.slot, ask, { ...held2, path: init.cwd }) };
14781
+ }
14678
14782
  async function runWasiProcess(init, port2) {
14679
14783
  captureBacktraces(init.env);
14680
14784
  const signals = new WasiSignals(
@@ -14732,6 +14836,7 @@ async function runWasiProcess(init, port2) {
14732
14836
  call2
14733
14837
  ),
14734
14838
  ...descriptors(init, fork, threads),
14839
+ ...heldOf2(init, call2),
14735
14840
  ...threads ? { credGen: threads.ids } : {}
14736
14841
  });
14737
14842
  host.sigmask = sigmaskOf(module, init.sab, gate, threads);
@@ -14840,7 +14945,8 @@ async function runWasiThread(init, port2) {
14840
14945
  ),
14841
14946
  call2
14842
14947
  ),
14843
- shared: threads.ids
14948
+ shared: threads.ids,
14949
+ ...heldOf2(init, call2)
14844
14950
  });
14845
14951
  host.tid = thread.tid;
14846
14952
  host.sigmask = sigmaskOf(init.program.module, init.sab, gate, threads, init.signals);
@@ -14891,6 +14997,7 @@ var init_wasi_runtime = __esm({
14891
14997
  "src/process/wasi/wasi-runtime.ts"() {
14892
14998
  "use strict";
14893
14999
  init_physical_path();
15000
+ init_held_cwd();
14894
15001
  init_protocol();
14895
15002
  init_signals();
14896
15003
  init_sync_sab_bridge();
@@ -16117,10 +16224,10 @@ var init_js_net = __esm({
16117
16224
 
16118
16225
  // src/process/js/js-proc.ts
16119
16226
  function* procStep(abs, pid, run) {
16120
- const listing = yield { op: "proc-list" };
16227
+ const listing2 = yield { op: "proc-list" };
16121
16228
  const mounts = abs === "/proc/mounts" ? yield { op: "mount-list" } : [];
16122
16229
  const tree = new ProcTree(
16123
- { procList: () => listing, mountList: () => mounts },
16230
+ { procList: () => listing2, mountList: () => mounts },
16124
16231
  pid,
16125
16232
  memorySource()
16126
16233
  );