@ai-ecoverse/slicc-kernel 1.167.4 → 1.168.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
@@ -3695,7 +3695,7 @@ function satisfies(version, range2) {
3695
3695
  }
3696
3696
 
3697
3697
  // src/version.ts
3698
- var KERNEL_VERSION = true ? "1.167.4" : "0.0.0-dev";
3698
+ var KERNEL_VERSION = true ? "1.168.0" : "0.0.0-dev";
3699
3699
  var OS_RELEASE = "6.0.0-slicc";
3700
3700
  function procVersion() {
3701
3701
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -3778,8 +3778,8 @@ function abiOf(raw2, fallback) {
3778
3778
  const abi = raw2 ?? fallback;
3779
3779
  return abi === "emscripten" || abi === "wasi" || abi === "js" ? abi : void 0;
3780
3780
  }
3781
- function commandOf(pkg, name2, raw2, packageAbi, shared) {
3782
- const withEnv = envFields({ ...shared, ...envOf(pkg, raw2.env) });
3781
+ function commandOf(pkg, name2, raw2, packageAbi, shared2) {
3782
+ const withEnv = envFields({ ...shared2, ...envOf(pkg, raw2.env) });
3783
3783
  const script = inside(pkg, raw2.script);
3784
3784
  if (script) {
3785
3785
  return { name: name2, abi: packageAbi, glue: script, wasm: script, argv0: name2, script, ...withEnv };
@@ -3829,13 +3829,13 @@ function commandsOf(pkg, manifest) {
3829
3829
  const packageAbi = abiOf(slicc.abi, "emscripten");
3830
3830
  const entries = slicc.commands;
3831
3831
  if (!packageAbi || !entries || typeof entries !== "object") return [];
3832
- const shared = envOf(pkg, slicc.env);
3832
+ const shared2 = envOf(pkg, slicc.env);
3833
3833
  const out = [];
3834
3834
  for (const [name2, raw2] of Object.entries(entries)) {
3835
3835
  if (!commandName(name2) || !raw2 || typeof raw2 !== "object") {
3836
3836
  continue;
3837
3837
  }
3838
- const command = commandOf(pkg, name2, raw2, packageAbi, shared);
3838
+ const command = commandOf(pkg, name2, raw2, packageAbi, shared2);
3839
3839
  if (command) out.push(command);
3840
3840
  }
3841
3841
  return out;
@@ -5009,8 +5009,8 @@ var UnlinkedKeeper = class {
5009
5009
  void peers.ask(tab, { kept: { items: [], paths } }, PEER_RM_MS);
5010
5010
  throw fsError("EBUSY", `${target}: open in another tab that did not answer`);
5011
5011
  }
5012
- const { whole, sends, shared } = await this.setAside(target, asked);
5013
- return { whole, undoing: (op) => this.undoing(op, peers, sends, heard, shared) };
5012
+ const { whole, sends, shared: shared2 } = await this.setAside(target, asked);
5013
+ return { whole, undoing: (op) => this.undoing(op, peers, sends, heard, shared2) };
5014
5014
  }
5015
5015
  async setAside(target, asked) {
5016
5016
  const mine = this.heldWithin(target);
@@ -5018,25 +5018,25 @@ var UnlinkedKeeper = class {
5018
5018
  for (const { holders } of asked)
5019
5019
  for (const { path } of holders) tabsOf.set(path, (tabsOf.get(path) ?? 0) + 1);
5020
5020
  const done4 = /* @__PURE__ */ new Map();
5021
- const shared = [];
5021
+ const shared2 = [];
5022
5022
  const sends = [];
5023
5023
  for (const { tab, holders, paths } of asked) {
5024
5024
  const items = [];
5025
5025
  for (const holder of holders) {
5026
5026
  if (!done4.has(holder.path)) {
5027
5027
  const many = mine.has(holder.path) || (tabsOf.get(holder.path) ?? 0) > 1;
5028
- done4.set(holder.path, await this.asideOne(holder, many, mine, shared));
5028
+ done4.set(holder.path, await this.asideOne(holder, many, mine, shared2));
5029
5029
  }
5030
5030
  items.push({ path: holder.path, hidden: done4.get(holder.path), fid: holder.fid });
5031
5031
  }
5032
5032
  sends.push({ tab, items, paths });
5033
5033
  }
5034
- return { whole: done4.get(target) !== void 0, sends, shared };
5034
+ return { whole: done4.get(target) !== void 0, sends, shared: shared2 };
5035
5035
  }
5036
- async asideOne(holder, many, mine, shared) {
5036
+ async asideOne(holder, many, mine, shared2) {
5037
5037
  if (!many) return this.asideFor(holder.path, holder.instance);
5038
5038
  const one = await this.sharedAside(holder.path, mine);
5039
- if (one) shared.push(one);
5039
+ if (one) shared2.push(one);
5040
5040
  return one?.hidden;
5041
5041
  }
5042
5042
  async sharedAside(path, mine) {
@@ -5081,16 +5081,16 @@ var UnlinkedKeeper = class {
5081
5081
  sharedLocks() {
5082
5082
  return this.locks;
5083
5083
  }
5084
- async undoing(op, peers, sends, heard, shared = []) {
5084
+ async undoing(op, peers, sends, heard, shared2 = []) {
5085
5085
  let failed3;
5086
5086
  let out;
5087
5087
  try {
5088
5088
  out = await op();
5089
5089
  } catch (err) {
5090
5090
  failed3 = err ?? new Error("failed");
5091
- await this.rollBackAsides(sends, shared);
5091
+ await this.rollBackAsides(sends, shared2);
5092
5092
  }
5093
- const hiddenShared = new Set(shared.map((one) => one.hidden));
5093
+ const hiddenShared = new Set(shared2.map((one) => one.hidden));
5094
5094
  const answers2 = sends.map(({ tab, items, paths }) => {
5095
5095
  const keeps = items.some((item) => item.hidden && hiddenShared.has(item.hidden));
5096
5096
  return {
@@ -5101,7 +5101,7 @@ var UnlinkedKeeper = class {
5101
5101
  await Promise.all(
5102
5102
  answers2.map(({ told }, i) => heard[i] ? Promise.race([told, delay(PEER_RM_MS)]) : void 0)
5103
5103
  );
5104
- for (const one of shared) {
5104
+ for (const one of shared2) {
5105
5105
  const waits = answers2.filter(
5106
5106
  ({ keeps }, i) => keeps && sends[i]?.items.some((item) => item.hidden === one.hidden)
5107
5107
  );
@@ -5111,7 +5111,7 @@ var UnlinkedKeeper = class {
5111
5111
  if (failed3 !== void 0) throw failed3;
5112
5112
  return out;
5113
5113
  }
5114
- async rollBackAsides(sends, shared) {
5114
+ async rollBackAsides(sends, shared2) {
5115
5115
  const back = /* @__PURE__ */ new Map();
5116
5116
  for (const item of sends.flatMap((send2) => send2.items)) {
5117
5117
  const { hidden, path } = item;
@@ -5119,7 +5119,7 @@ var UnlinkedKeeper = class {
5119
5119
  if (!back.has(hidden)) back.set(hidden, await this.moveBack(hidden, path));
5120
5120
  if (back.get(hidden)) item.hidden = void 0;
5121
5121
  }
5122
- for (const { hidden, path, local: local2, file } of shared) {
5122
+ for (const { hidden, path, local: local2, file } of shared2) {
5123
5123
  if (!back.get(hidden) || !file) continue;
5124
5124
  for (const holder of local2) {
5125
5125
  holder.unkeep(path, file);
@@ -7475,6 +7475,142 @@ var Integrity = class {
7475
7475
  }
7476
7476
  };
7477
7477
 
7478
+ // src/fs/path-map.ts
7479
+ function parentOf4(path) {
7480
+ if (path === "/" || path === "") return void 0;
7481
+ const at = path.lastIndexOf("/");
7482
+ return at <= 0 ? "/" : path.slice(0, at);
7483
+ }
7484
+ var PathMap = class {
7485
+ values = /* @__PURE__ */ new Map();
7486
+ children = /* @__PURE__ */ new Map();
7487
+ get size() {
7488
+ return this.values.size;
7489
+ }
7490
+ has(path) {
7491
+ return this.values.has(path);
7492
+ }
7493
+ get(path) {
7494
+ return this.values.get(path);
7495
+ }
7496
+ keys() {
7497
+ return this.values.keys();
7498
+ }
7499
+ set(path, value2) {
7500
+ if (!this.values.has(path)) this.link(path);
7501
+ this.values.set(path, value2);
7502
+ return this;
7503
+ }
7504
+ delete(path) {
7505
+ if (!this.values.delete(path)) return false;
7506
+ this.prune(path);
7507
+ return true;
7508
+ }
7509
+ subtree(root) {
7510
+ const out = [];
7511
+ const stack = [root];
7512
+ for (let path = stack.pop(); path !== void 0; path = stack.pop()) {
7513
+ if (this.values.has(path)) out.push(path);
7514
+ const below = this.children.get(path);
7515
+ if (below) for (const child of below) stack.push(child);
7516
+ }
7517
+ return out;
7518
+ }
7519
+ entries() {
7520
+ return this.values.entries();
7521
+ }
7522
+ deleteTree(root) {
7523
+ const stack = [root];
7524
+ for (let path = stack.pop(); path !== void 0; path = stack.pop()) {
7525
+ this.values.delete(path);
7526
+ const below = this.children.get(path);
7527
+ if (below) for (const child of below) stack.push(child);
7528
+ this.children.delete(path);
7529
+ }
7530
+ this.prune(root);
7531
+ }
7532
+ clear() {
7533
+ this.values.clear();
7534
+ this.children.clear();
7535
+ }
7536
+ link(path) {
7537
+ for (let child = path, parent = parentOf4(path); parent !== void 0; ) {
7538
+ const set3 = this.children.get(parent);
7539
+ if (set3?.has(child)) return;
7540
+ if (set3) set3.add(child);
7541
+ else this.children.set(parent, /* @__PURE__ */ new Set([child]));
7542
+ child = parent;
7543
+ parent = parentOf4(parent);
7544
+ }
7545
+ }
7546
+ prune(path) {
7547
+ for (let child = path, parent = parentOf4(path); parent !== void 0; ) {
7548
+ if (this.values.has(child) || this.children.has(child)) return;
7549
+ const set3 = this.children.get(parent);
7550
+ set3?.delete(child);
7551
+ if (set3 && set3.size > 0) return;
7552
+ this.children.delete(parent);
7553
+ child = parent;
7554
+ parent = parentOf4(parent);
7555
+ }
7556
+ }
7557
+ };
7558
+
7559
+ // src/fs/meta.ts
7560
+ var META_DB = "slicc-kernel";
7561
+ function parentOf5(path) {
7562
+ return path.slice(0, path.lastIndexOf("/")) || "/";
7563
+ }
7564
+ function moved(entry, path) {
7565
+ return entry.link === void 0 ? { ...entry, path } : { ...entry, path, dir: parentOf5(path) };
7566
+ }
7567
+ function under2(path, root) {
7568
+ return path === root || path.startsWith(`${root}/`);
7569
+ }
7570
+ var MemoryMeta = class {
7571
+ entries = new PathMap();
7572
+ async get(paths) {
7573
+ return paths.map((path) => this.entries.get(path));
7574
+ }
7575
+ async fresh(paths) {
7576
+ return this.get(paths);
7577
+ }
7578
+ async update(path, change) {
7579
+ this.set(path, change(this.entries.get(path)));
7580
+ }
7581
+ set(path, entry) {
7582
+ if (entry) this.entries.set(path, entry);
7583
+ else this.entries.delete(path);
7584
+ }
7585
+ async move(from, to, root, left) {
7586
+ await this.remove([to]);
7587
+ const carried = this.entries.subtree(from).map((path) => this.entries.get(path));
7588
+ for (const entry of carried) this.entries.delete(entry.path);
7589
+ for (const entry of carried) {
7590
+ const path = to + entry.path.slice(from.length);
7591
+ if (path !== to) this.entries.set(path, moved(entry, path));
7592
+ }
7593
+ this.entries.set(to, moved(root(carried.find((entry) => entry.path === from)), to));
7594
+ if (left) this.entries.set(from, left);
7595
+ }
7596
+ async remove(paths, left) {
7597
+ for (const path of paths) this.entries.deleteTree(path);
7598
+ if (left) this.entries.set(left.path, left);
7599
+ }
7600
+ absent(path) {
7601
+ return !this.entries.has(path);
7602
+ }
7603
+ async links(dir) {
7604
+ return [...this.entries.entries()].map(([, e]) => e).filter((entry) => entry.dir === dir);
7605
+ }
7606
+ async under(path) {
7607
+ return [...this.entries.entries()].map(([, e]) => e).filter((entry) => under2(entry.path, path));
7608
+ }
7609
+ async all() {
7610
+ return [...this.entries.entries()].map(([, e]) => e);
7611
+ }
7612
+ };
7613
+
7478
7614
  // src/fs/physical-path.ts
7479
7615
  var MAX_HOPS3 = 40;
7480
7616
  function start(path) {
@@ -7526,11 +7662,11 @@ async function physicalPathAsync(fs, path) {
7526
7662
  var WATCH_NOTES_LIMIT = 1e3;
7527
7663
  var baseOf2 = (path) => path.slice(path.lastIndexOf("/") + 1);
7528
7664
  var WATCH_BATCH_LIMIT = 1e3;
7529
- var under2 = (path, dir) => dir === "/" || path.startsWith(`${dir}/`);
7530
- var parentOf4 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
7665
+ var under3 = (path, dir) => dir === "/" || path.startsWith(`${dir}/`);
7666
+ var parentOf6 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
7531
7667
  function touches(watcher, changed) {
7532
7668
  return watcher.paths.some(
7533
- (watched) => changed === watched || under2(watched, changed) || (watcher.recursive ? under2(changed, watched) : parentOf4(changed) === watched)
7669
+ (watched) => changed === watched || under3(watched, changed) || (watcher.recursive ? under3(changed, watched) : parentOf6(changed) === watched)
7534
7670
  );
7535
7671
  }
7536
7672
  var REALS_KEPT = 4096;
@@ -7553,13 +7689,13 @@ function route(watch, type, path, seq2) {
7553
7689
  for (const root of watch.roots) if (routed(watch, root, type, path, seq2)) return;
7554
7690
  }
7555
7691
  function routed(watch, root, type, path, seq2) {
7556
- if (path === root || type === "rename" && root !== "/" && under2(root, path)) {
7692
+ if (path === root || type === "rename" && root !== "/" && under3(root, path)) {
7557
7693
  noted(watch, { type, filename: watch.names.get(root) ?? baseOf2(root), seq: seq2 });
7558
7694
  if (type === "rename") noted(watch, { ended: true, seq: seq2 });
7559
7695
  return true;
7560
7696
  }
7561
- if (parentOf4(path) === root) noted(watch, { type, filename: baseOf2(path), seq: seq2 });
7562
- else if (watch.recursive && under2(path, root)) {
7697
+ if (parentOf6(path) === root) noted(watch, { type, filename: baseOf2(path), seq: seq2 });
7698
+ else if (watch.recursive && under3(path, root)) {
7563
7699
  noted(watch, { type, filename: path.slice(root === "/" ? 1 : root.length + 1), seq: seq2 });
7564
7700
  } else return false;
7565
7701
  return true;
@@ -7643,7 +7779,7 @@ var FsWatchers = class {
7643
7779
  covers(path) {
7644
7780
  for (const watch of this.typed) {
7645
7781
  for (const root of watch.roots) {
7646
- if (path === root || parentOf4(path) === root || watch.recursive && under2(path, root))
7782
+ if (path === root || parentOf6(path) === root || watch.recursive && under3(path, root))
7647
7783
  return true;
7648
7784
  }
7649
7785
  }
@@ -7700,7 +7836,7 @@ var FsWatchers = class {
7700
7836
  const at = normalizePath(path);
7701
7837
  const seq2 = ++this.seq;
7702
7838
  for (const watch of [...this.typed]) {
7703
- if (type === "rename" && watch.roots.some((root) => at === root || under2(root, at)))
7839
+ if (type === "rename" && watch.roots.some((root) => at === root || under3(root, at)))
7704
7840
  continue;
7705
7841
  if (type === "change" && watch.directory && watch.roots.includes(at)) continue;
7706
7842
  route(watch, type, at, seq2);
@@ -8932,7 +9068,7 @@ var FifoTable = class {
8932
9068
  // src/kernel/held-cwd.ts
8933
9069
  var CWD_SLOTS = 1024;
8934
9070
  var UNTRACKED = CWD_SLOTS - 1;
8935
- var under3 = (path, changed) => changed === "/" || path === changed || path.startsWith(`${changed}/`);
9071
+ var under4 = (path, changed) => changed === "/" || path === changed || path.startsWith(`${changed}/`);
8936
9072
  var CwdTable = class {
8937
9073
  sab = new SharedArrayBuffer(CWD_SLOTS * 4);
8938
9074
  view = new Int32Array(this.sab);
@@ -8945,8 +9081,8 @@ var CwdTable = class {
8945
9081
  this.inodeOf = inodeOf2;
8946
9082
  }
8947
9083
  async hold(path, from) {
8948
- const shared = from === void 0 ? void 0 : this.held.get(from);
8949
- if (shared && shared.path === path) return this.share(from);
9084
+ const shared2 = from === void 0 ? void 0 : this.held.get(from);
9085
+ if (shared2 && shared2.path === path) return this.share(from);
8950
9086
  const ino = await this.inodeOf(path).catch(() => void 0);
8951
9087
  const slot2 = this.free.pop() ?? (this.next < UNTRACKED ? this.next++ : UNTRACKED);
8952
9088
  if (slot2 === UNTRACKED) return slot2;
@@ -8980,7 +9116,7 @@ var CwdTable = class {
8980
9116
  if (this.held.size === 0) return;
8981
9117
  for (const [slot2, held] of this.held) {
8982
9118
  if (held.dead) continue;
8983
- if (paths.some((p) => under3(held.path, p)) || trees.some((t) => under3(held.path, t))) {
9119
+ if (paths.some((p) => under4(held.path, p)) || trees.some((t) => under4(held.path, t))) {
8984
9120
  Atomics.add(this.view, slot2, 1);
8985
9121
  }
8986
9122
  }
@@ -9438,7 +9574,7 @@ var HeldPaths = class extends Map {
9438
9574
  return this.revoked.has(path);
9439
9575
  }
9440
9576
  };
9441
- var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
9577
+ var parentOf7 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
9442
9578
  var readonly = (mount) => mount.caps.readonly === true || mount.options.ro !== void 0;
9443
9579
  function inodeless(mount, attr2) {
9444
9580
  if (attr2.ino !== void 0) return false;
@@ -9738,7 +9874,7 @@ var MountTable = class {
9738
9874
  for (const [key, cached] of [...mount.attrs])
9739
9875
  if (within5(key, path) || linked2(cached.value)) mount.attrs.delete(key);
9740
9876
  mount.lists.delete(path);
9741
- mount.lists.delete(parentOf5(path));
9877
+ mount.lists.delete(parentOf7(path));
9742
9878
  for (const [key, cached] of [...mount.lists])
9743
9879
  if (within5(key, path) || cached.value.some((e) => linked2(e.attr))) mount.lists.delete(key);
9744
9880
  }
@@ -9859,7 +9995,7 @@ var MountFs = class _MountFs {
9859
9995
  const real = normalizePath(path);
9860
9996
  if (this.at(real)) return real;
9861
9997
  try {
9862
- await this.base.stat(parentOf5(real));
9998
+ await this.base.stat(parentOf7(real));
9863
9999
  return real;
9864
10000
  } catch (err) {
9865
10001
  const moved2 = redirectOf(err);
@@ -9901,7 +10037,7 @@ var MountFs = class _MountFs {
9901
10037
  if (this.rejectLinks) throw errnoError("ELOOP", path);
9902
10038
  if (hops >= MAX_LINKS5) throw errnoError("ELOOP", path);
9903
10039
  const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
9904
- return this.follow(this.base.resolvePath(parentOf5(real), target), hops + 1, create);
10040
+ return this.follow(this.base.resolvePath(parentOf7(real), target), hops + 1, create);
9905
10041
  }
9906
10042
  async open(mount, rel2, write, owner, create = write) {
9907
10043
  return await mount.conn.call({
@@ -9917,8 +10053,8 @@ var MountFs = class _MountFs {
9917
10053
  async driverOwner(found, real, owner, directory) {
9918
10054
  const { mount, rel: rel2 } = found;
9919
10055
  if (!owner || !mount.owners || !mount.caps.owners) return void 0;
9920
- const dir = parentOf5(real);
9921
- const above = statOf2(await this.table.getattr(mount, dir, parentOf5(rel2)), dir, mount);
10056
+ const dir = parentOf7(real);
10057
+ const above = statOf2(await this.table.getattr(mount, dir, parentOf7(rel2)), dir, mount);
9922
10058
  const setgid = (above.mode & S_ISGID3) !== 0;
9923
10059
  let mode = owner.mode;
9924
10060
  if (directory && setgid) mode = (mode ?? 493) | S_ISGID3;
@@ -10437,7 +10573,7 @@ var MountFs = class _MountFs {
10437
10573
  }
10438
10574
  async named(path) {
10439
10575
  const at = normalizePath(path);
10440
- return joinName(await realDir(this, parentOf5(at)), at.slice(at.lastIndexOf("/") + 1));
10576
+ return joinName(await realDir(this, parentOf7(at)), at.slice(at.lastIndexOf("/") + 1));
10441
10577
  }
10442
10578
  async link(source, target, follow2 = false) {
10443
10579
  const from = follow2 ? await realTarget(this, normalizePath(source)) : await this.named(source);
@@ -13327,13 +13463,13 @@ async function flock(req, ctx) {
13327
13463
  if (!FLOCK_KINDS.has(req.kind)) throw new KernelError("EINVAL");
13328
13464
  const { key, owner } = await flockOf(req.fd);
13329
13465
  const granted2 = req.kind !== "unlock";
13330
- const shared = locks.shared?.() === true;
13331
- if (shared && !granted2) await ctx.settle?.(req.fd, false);
13466
+ const shared2 = locks.shared?.() === true;
13467
+ if (shared2 && !granted2) await ctx.settle?.(req.fd, false);
13332
13468
  if (req.kind !== "unlock" && req.wait)
13333
13469
  await locks.flocks.wait(key, owner, req.kind, ctx.blocking);
13334
13470
  else if (!await locks.flocks.set(key, owner, req.kind)) throw new KernelError("EAGAIN");
13335
- if (shared && granted2) await grantedAcross(ctx, req.fd);
13336
- return shared ? json3(true) : done2;
13471
+ if (shared2 && granted2) await grantedAcross(ctx, req.fd);
13472
+ return shared2 ? json3(true) : done2;
13337
13473
  }
13338
13474
  async function grantedAcross(ctx, fd) {
13339
13475
  ctx.locks?.fresh?.();
@@ -13361,15 +13497,15 @@ async function recordLock2(req, ctx) {
13361
13497
  return json3({ kind: held.kind, pid: held.owner, start: held.start, len });
13362
13498
  }
13363
13499
  const granted2 = req.kind !== "unlock";
13364
- const shared = locks.shared?.() === true;
13365
- const fd = shared ? req.fd : void 0;
13500
+ const shared2 = locks.shared?.() === true;
13501
+ const fd = shared2 ? req.fd : void 0;
13366
13502
  if (!granted2 && fd !== void 0) await ctx.settle?.(fd, false);
13367
13503
  if (req.kind !== "unlock" && req.wait)
13368
13504
  await locks.wait(key, owner, req.kind, range2, ctx.blocking);
13369
13505
  else if (!await locks.set(key, owner, req.kind, range2.start, range2.end))
13370
13506
  throw new KernelError("EAGAIN");
13371
- if (granted2 && shared) await grantedAcross(ctx, fd);
13372
- return shared ? json3(true) : done2;
13507
+ if (granted2 && shared2) await grantedAcross(ctx, fd);
13508
+ return shared2 ? json3(true) : done2;
13373
13509
  }
13374
13510
 
13375
13511
  // src/kernel/net/loopback-transport.ts
@@ -19449,9 +19585,9 @@ function pausing(signal) {
19449
19585
  return { pausedAt, select };
19450
19586
  }
19451
19587
  async function serveSecure(proc, socket, req) {
19452
- const shared = req.listener?.serving;
19453
- if (req.listener && !shared) throw new KernelError("EINVAL", "the listener has no tls");
19454
- const server = shared ?? await serverOf(proc, req);
19588
+ const shared2 = req.listener?.serving;
19589
+ if (req.listener && !shared2) throw new KernelError("EINVAL", "the listener has no tls");
19590
+ const server = shared2 ?? await serverOf(proc, req);
19455
19591
  const { engine, leaf, alpn, store, flags: flags2 } = server;
19456
19592
  const limit = handshakeSignal(proc, server.timeoutMs);
19457
19593
  const { pausedAt, select } = pausing(limit.signal);
@@ -19465,12 +19601,12 @@ async function serveSecure(proc, socket, req) {
19465
19601
  (flags2 & TLS_KEYLOG) !== 0
19466
19602
  ),
19467
19603
  limit.signal,
19468
- (reason) => !shared && reason === timedOut
19604
+ (reason) => !shared2 && reason === timedOut
19469
19605
  ).finally(() => {
19470
19606
  limit.done();
19471
19607
  selections.delete(socket);
19472
19608
  socket.pending = void 0;
19473
- if (!shared) server.release();
19609
+ if (!shared2) server.release();
19474
19610
  });
19475
19611
  run2.catch(() => void 0);
19476
19612
  const first = await Promise.race([run2, pausedAt.then((pause2) => ({ pause: pause2 }))]);
@@ -21086,7 +21222,7 @@ function isWatchSyscall(req) {
21086
21222
  return WATCHING.has(req.op);
21087
21223
  }
21088
21224
  var WATCHES_MAX = 8192;
21089
- var parentOf6 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
21225
+ var parentOf8 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
21090
21226
  function visibleTo(watch, root, visible) {
21091
21227
  return {
21092
21228
  close: () => watch.close(),
@@ -21097,7 +21233,7 @@ function visibleTo(watch, root, visible) {
21097
21233
  notes.map(async (note) => {
21098
21234
  if (!("filename" in note) || !note.filename.includes("/")) return true;
21099
21235
  const at = watch.root?.() ?? root;
21100
- const dir = parentOf6(`${at === "/" ? "" : at}/${note.filename}`);
21236
+ const dir = parentOf8(`${at === "/" ? "" : at}/${note.filename}`);
21101
21237
  if (!seen.has(dir)) seen.set(dir, visible(dir));
21102
21238
  return seen.get(dir);
21103
21239
  })
@@ -25140,6 +25276,39 @@ var RenameLog = class {
25140
25276
  }
25141
25277
  };
25142
25278
 
25279
+ // src/kernel/replacing.ts
25280
+ var REPLACING_LOCK = "slicc-kernel-replacing:v1:";
25281
+ function replacingLocks(scope = globalThis) {
25282
+ const locks = scope.navigator?.locks;
25283
+ return typeof locks?.request === "function" ? locks : void 0;
25284
+ }
25285
+ function replacingLock(metadata, root) {
25286
+ return `${REPLACING_LOCK}${metadata}:${root}`;
25287
+ }
25288
+ function shared(locks, names2, run2, waited) {
25289
+ const [name2, ...rest2] = names2;
25290
+ if (name2 === void 0) return run2(waited);
25291
+ const next3 = (after) => shared(locks, rest2, run2, after);
25292
+ return locks.request(
25293
+ name2,
25294
+ { mode: "shared", ifAvailable: true },
25295
+ (lock) => lock ? next3(waited) : locks.request(name2, { mode: "shared" }, () => next3(true))
25296
+ );
25297
+ }
25298
+ var PackageLocks = class {
25299
+ locks;
25300
+ metadata;
25301
+ constructor(metadata, locks = replacingLocks()) {
25302
+ this.metadata = metadata;
25303
+ this.locks = locks;
25304
+ }
25305
+ loading(roots, run2) {
25306
+ if (!this.locks || roots.length === 0) return run2(false);
25307
+ const names2 = roots.map((root) => replacingLock(this.metadata, root));
25308
+ return shared(this.locks, names2, run2, false);
25309
+ }
25310
+ };
25311
+
25143
25312
  // src/kernel/settling.ts
25144
25313
  var SETUP_OPS = /* @__PURE__ */ new Set([
25145
25314
  "proc-list",
@@ -25436,141 +25605,6 @@ function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
25436
25605
  return { invalidate: (paths) => reply({ invalidate: paths }) };
25437
25606
  }
25438
25607
 
25439
- // src/fs/path-map.ts
25440
- function parentOf7(path) {
25441
- if (path === "/" || path === "") return void 0;
25442
- const at = path.lastIndexOf("/");
25443
- return at <= 0 ? "/" : path.slice(0, at);
25444
- }
25445
- var PathMap = class {
25446
- values = /* @__PURE__ */ new Map();
25447
- children = /* @__PURE__ */ new Map();
25448
- get size() {
25449
- return this.values.size;
25450
- }
25451
- has(path) {
25452
- return this.values.has(path);
25453
- }
25454
- get(path) {
25455
- return this.values.get(path);
25456
- }
25457
- keys() {
25458
- return this.values.keys();
25459
- }
25460
- set(path, value2) {
25461
- if (!this.values.has(path)) this.link(path);
25462
- this.values.set(path, value2);
25463
- return this;
25464
- }
25465
- delete(path) {
25466
- if (!this.values.delete(path)) return false;
25467
- this.prune(path);
25468
- return true;
25469
- }
25470
- subtree(root) {
25471
- const out = [];
25472
- const stack = [root];
25473
- for (let path = stack.pop(); path !== void 0; path = stack.pop()) {
25474
- if (this.values.has(path)) out.push(path);
25475
- const below = this.children.get(path);
25476
- if (below) for (const child of below) stack.push(child);
25477
- }
25478
- return out;
25479
- }
25480
- entries() {
25481
- return this.values.entries();
25482
- }
25483
- deleteTree(root) {
25484
- const stack = [root];
25485
- for (let path = stack.pop(); path !== void 0; path = stack.pop()) {
25486
- this.values.delete(path);
25487
- const below = this.children.get(path);
25488
- if (below) for (const child of below) stack.push(child);
25489
- this.children.delete(path);
25490
- }
25491
- this.prune(root);
25492
- }
25493
- clear() {
25494
- this.values.clear();
25495
- this.children.clear();
25496
- }
25497
- link(path) {
25498
- for (let child = path, parent = parentOf7(path); parent !== void 0; ) {
25499
- const set3 = this.children.get(parent);
25500
- if (set3?.has(child)) return;
25501
- if (set3) set3.add(child);
25502
- else this.children.set(parent, /* @__PURE__ */ new Set([child]));
25503
- child = parent;
25504
- parent = parentOf7(parent);
25505
- }
25506
- }
25507
- prune(path) {
25508
- for (let child = path, parent = parentOf7(path); parent !== void 0; ) {
25509
- if (this.values.has(child) || this.children.has(child)) return;
25510
- const set3 = this.children.get(parent);
25511
- set3?.delete(child);
25512
- if (set3 && set3.size > 0) return;
25513
- this.children.delete(parent);
25514
- child = parent;
25515
- parent = parentOf7(parent);
25516
- }
25517
- }
25518
- };
25519
-
25520
- // src/fs/meta.ts
25521
- function parentOf8(path) {
25522
- return path.slice(0, path.lastIndexOf("/")) || "/";
25523
- }
25524
- function moved(entry, path) {
25525
- return entry.link === void 0 ? { ...entry, path } : { ...entry, path, dir: parentOf8(path) };
25526
- }
25527
- function under4(path, root) {
25528
- return path === root || path.startsWith(`${root}/`);
25529
- }
25530
- var MemoryMeta = class {
25531
- entries = new PathMap();
25532
- async get(paths) {
25533
- return paths.map((path) => this.entries.get(path));
25534
- }
25535
- async fresh(paths) {
25536
- return this.get(paths);
25537
- }
25538
- async update(path, change) {
25539
- this.set(path, change(this.entries.get(path)));
25540
- }
25541
- set(path, entry) {
25542
- if (entry) this.entries.set(path, entry);
25543
- else this.entries.delete(path);
25544
- }
25545
- async move(from, to, root, left) {
25546
- await this.remove([to]);
25547
- const carried = this.entries.subtree(from).map((path) => this.entries.get(path));
25548
- for (const entry of carried) this.entries.delete(entry.path);
25549
- for (const entry of carried) {
25550
- const path = to + entry.path.slice(from.length);
25551
- if (path !== to) this.entries.set(path, moved(entry, path));
25552
- }
25553
- this.entries.set(to, moved(root(carried.find((entry) => entry.path === from)), to));
25554
- if (left) this.entries.set(from, left);
25555
- }
25556
- async remove(paths, left) {
25557
- for (const path of paths) this.entries.deleteTree(path);
25558
- if (left) this.entries.set(left.path, left);
25559
- }
25560
- absent(path) {
25561
- return !this.entries.has(path);
25562
- }
25563
- async links(dir) {
25564
- return [...this.entries.entries()].map(([, e]) => e).filter((entry) => entry.dir === dir);
25565
- }
25566
- async under(path) {
25567
- return [...this.entries.entries()].map(([, e]) => e).filter((entry) => under4(entry.path, path));
25568
- }
25569
- async all() {
25570
- return [...this.entries.entries()].map(([, e]) => e);
25571
- }
25572
- };
25573
-
25574
25608
  // src/fs/notice.ts
25575
25609
  function noticeOf(data) {
25576
25610
  if (Array.isArray(data)) return { paths: data.filter((p) => typeof p === "string"), trees: [] };
@@ -25879,9 +25913,11 @@ var OpfsFs = class {
25879
25913
  this.channel.onmessage = ({ data }) => {
25880
25914
  const { paths, trees, outside } = noticeOf(data);
25881
25915
  for (const path of [...paths, ...trees]) this.forget(normalizePath(path));
25882
- const tell = () => remote?.emit({ paths, trees });
25883
- if (!outside) tell();
25884
- else void this.noticed(paths.map(normalizePath), Date.now()).finally(tell);
25916
+ if (!outside) remote?.emit({ paths, trees });
25917
+ else
25918
+ void this.noticed(paths.map(normalizePath), Date.now()).finally(
25919
+ () => remote?.emit({ paths, trees, outside: true })
25920
+ );
25885
25921
  };
25886
25922
  this.channel.unref?.();
25887
25923
  }
@@ -26247,6 +26283,9 @@ var OpfsFs = class {
26247
26283
  this.exact--;
26248
26284
  }
26249
26285
  }
26286
+ refresh(trees) {
26287
+ for (const tree2 of trees) this.forget(normalizePath(tree2));
26288
+ }
26250
26289
  forget(path) {
26251
26290
  this.changes++;
26252
26291
  this.dirs.deleteTree(path);
@@ -29686,14 +29725,14 @@ var UserDb = class {
29686
29725
  this.fs = fs;
29687
29726
  this.owners = owners;
29688
29727
  }
29689
- async stampHome(row, shared, homeMode) {
29728
+ async stampHome(row, shared2, homeMode) {
29690
29729
  const home = this.fs.resolvePath("/", row.home);
29691
29730
  if (!this.owners || !this.fs.chown || !HOME_DIR.test(home)) return;
29692
29731
  if (!await this.realDir(home)) return;
29693
- const gid = shared?.gid ?? row.gid;
29732
+ const gid = shared2?.gid ?? row.gid;
29694
29733
  await this.fs.chown(home, { uid: row.uid, gid, follow: false, kind: "adopt" });
29695
29734
  const scoop = row.name.startsWith("scoop-");
29696
- await this.fs.chmod(home, homeMode ?? (shared ? 1512 : scoop ? 488 : 448));
29735
+ await this.fs.chmod(home, homeMode ?? (shared2 ? 1512 : scoop ? 488 : 448));
29697
29736
  const pnpm = `${home}/.local/share/pnpm`;
29698
29737
  if (await this.realDir(pnpm)) await this.fs.chmod(pnpm, scoop ? 488 : 448);
29699
29738
  }
@@ -29730,9 +29769,9 @@ var UserDb = class {
29730
29769
  const [passwd, group] = await this.texts();
29731
29770
  return { passwd: parsePasswd(passwd), group: parseGroup(group) };
29732
29771
  }
29733
- async provision(row, umask, shared) {
29734
- const dirs = 511 & ~umask | (shared ? 1024 : 0);
29735
- const gid = shared?.gid ?? row.gid;
29772
+ async provision(row, umask, shared2) {
29773
+ const dirs = 511 & ~umask | (shared2 ? 1024 : 0);
29774
+ const gid = shared2?.gid ?? row.gid;
29736
29775
  const owner = this.owners ? { uid: row.uid, gid } : void 0;
29737
29776
  const made2 = owner ? { recursive: true, owner: { ...owner, mode: dirs, parents: dirs } } : { recursive: true };
29738
29777
  const pnpm = `${row.home}/.local/share/pnpm`;
@@ -29836,7 +29875,7 @@ var UserDb = class {
29836
29875
  const users = parsed(passwd);
29837
29876
  const groups = parsed(group);
29838
29877
  const found = users.find((r) => r.name === name2);
29839
- const shared = homeGroupOf(groups, options.homeGroup);
29878
+ const shared2 = homeGroupOf(groups, options.homeGroup);
29840
29879
  const homeMode = homeModeOf(options.homeMode);
29841
29880
  if (found) {
29842
29881
  const more = groupsOf(groups, options.groups ?? []);
@@ -29844,8 +29883,8 @@ var UserDb = class {
29844
29883
  throw fsError("EINVAL", `a user is no member of its own group ${name2}`);
29845
29884
  }
29846
29885
  if (found.uid >= FIRST_UID && found.uid <= LAST_UID) {
29847
- await this.provision(found, this.umaskFor(options), shared);
29848
- await this.stampHome(found, shared, homeMode);
29886
+ await this.provision(found, this.umaskFor(options), shared2);
29887
+ await this.stampHome(found, shared2, homeMode);
29849
29888
  }
29850
29889
  if (joined4(name2, more)) await this.fs.writeFile(GROUP, formatGroup(group));
29851
29890
  remember();
@@ -29862,8 +29901,8 @@ var UserDb = class {
29862
29901
  const fresh3 = !await this.fs.exists(home);
29863
29902
  await this.fs.mkdir(home, { recursive: true });
29864
29903
  try {
29865
- await this.provision(row, this.umaskFor(options), shared);
29866
- await this.stampHome(row, shared, homeMode);
29904
+ await this.provision(row, this.umaskFor(options), shared2);
29905
+ await this.stampHome(row, shared2, homeMode);
29867
29906
  await this.fs.mkdir("/etc", { recursive: true });
29868
29907
  await this.commit(formatPasswd(passwd), formatGroup(group));
29869
29908
  } catch (err) {
@@ -29912,10 +29951,10 @@ var UserDb = class {
29912
29951
  );
29913
29952
  for (const r of parsed(keptGroups)) r.members = r.members.filter((m) => m !== name2);
29914
29953
  const home = this.fs.resolvePath("/", row.home);
29915
- const shared = parsed(passwd).some(
29954
+ const shared2 = parsed(passwd).some(
29916
29955
  (r) => r !== row && this.fs.resolvePath("/", r.home) === home
29917
29956
  );
29918
- const removeHome = !options.keepHome && !shared && HOME_DIR.test(home) && await this.fs.exists(home);
29957
+ const removeHome = !options.keepHome && !shared2 && HOME_DIR.test(home) && await this.fs.exists(home);
29919
29958
  const staged = `/home/.${name2}.removed-${Date.now()}`;
29920
29959
  await this.commit(
29921
29960
  formatPasswd(passwd.filter((r) => r !== row)),
@@ -30404,6 +30443,7 @@ var COMMAND2 = /^\/(?:usr\/)?bin\/([^/]+)$/;
30404
30443
  var SHIM_MARKER = /^\/\/ pnpm-wasm-shim=(\{.*\})$/gm;
30405
30444
  var SHIM_MAX = 64 * 1024;
30406
30445
  var PACKAGE_ROOT = /^(.*\/node_modules\/(?:@[^/]+\/)?[^/]+)\//;
30446
+ var NODE_MODULES = /\/node_modules(?:\/|$)/;
30407
30447
  var ENV_REFERENCE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
30408
30448
  var WASM_MAGIC = [0, 97, 115, 109];
30409
30449
  var SHEBANG_MAX = 256;
@@ -30471,6 +30511,21 @@ function packageRootOf(path, roots) {
30471
30511
  }
30472
30512
  return void 0;
30473
30513
  }
30514
+ function planFiles(planned) {
30515
+ const { target } = planned;
30516
+ const files = [
30517
+ target.glue,
30518
+ target.wasm,
30519
+ target.imports,
30520
+ target.copy,
30521
+ ...(target.variants ?? []).map((variant) => variant.wasm),
30522
+ ...planned.sources ?? []
30523
+ ];
30524
+ return files.filter((file) => file !== void 0);
30525
+ }
30526
+ function sameList(a, b) {
30527
+ return a.length === b.length && a.every((item, i) => item === b[i]);
30528
+ }
30474
30529
  function modulePath(glue) {
30475
30530
  return glue.endsWith(".js") ? `${glue.slice(0, -3)}.wasm` : `${glue}.wasm`;
30476
30531
  }
@@ -30644,6 +30699,7 @@ var Launcher = class {
30644
30699
  sudoer;
30645
30700
  jobs = new JobTable();
30646
30701
  locks;
30702
+ packageLocks;
30647
30703
  fifos = new FifoTable();
30648
30704
  memories;
30649
30705
  sems = new SemTable();
@@ -30716,6 +30772,7 @@ var Launcher = class {
30716
30772
  this.memories = memoriesOf(options.wasmMemories);
30717
30773
  this.rootFs = options.fs;
30718
30774
  this.locks = kernelLocks(options.fs.rootName, this.flushingPeers(options.fs));
30775
+ this.packageLocks = new PackageLocks(options.packageLocks ?? META_DB);
30719
30776
  this.dac = this.usersMode === "off" ? void 0 : new Dac(this.usersMode, options.onDenial);
30720
30777
  if (this.dac) this.wireDac(this.dac);
30721
30778
  this.clipboard = new ClipboardBroker(
@@ -30809,15 +30866,7 @@ var Launcher = class {
30809
30866
  this.nodes.keepsOnDisk = true;
30810
30867
  this.openFiles.add(this.nodes);
30811
30868
  options.fs.onPeer?.(() => this.flushOpen());
30812
- options.fs.remoteChanges?.((change) => {
30813
- this.held.doubt(change.paths, change.moved?.to);
30814
- if (change.moved) this.moved(change.moved.from, change.moved.to);
30815
- this.changes.flush();
30816
- this.cwds.touched(change.paths, change.trees);
30817
- this.rewritten(change.paths, change.trees);
30818
- this.watchers.heard(change.content ? "change" : "rename", change.paths);
30819
- this.watchers.heard("rename", change.trees ?? []);
30820
- });
30869
+ options.fs.remoteChanges?.((change) => this.remoteChanged(change));
30821
30870
  const holders = [this.held, this.openFiles];
30822
30871
  options.fs.heldBy?.(
30823
30872
  (dir) => holders.flatMap((set3) => [...set3].flatMap((holder) => [...holder.held()])).filter((p) => p.startsWith(`${dir}/`) && this.mounts.locate(p) === void 0)
@@ -31027,6 +31076,26 @@ var Launcher = class {
31027
31076
  }
31028
31077
  return trusted;
31029
31078
  }
31079
+ remoteChanged(change) {
31080
+ this.held.doubt(change.paths, change.moved?.to);
31081
+ if (change.moved) this.moved(change.moved.from, change.moved.to);
31082
+ this.changes.flush();
31083
+ this.cwds.touched(change.paths, change.trees);
31084
+ this.rewritten(change.paths, change.trees);
31085
+ this.watchers.heard(change.content ? "change" : "rename", change.paths);
31086
+ this.watchers.heard("rename", change.trees ?? []);
31087
+ if (change.outside && this.catalogChanged([...change.paths, ...change.trees ?? []])) {
31088
+ this.resetCatalogs();
31089
+ }
31090
+ }
31091
+ catalogChanged(paths) {
31092
+ const roots = [this.modulesDir, this.pnpmHome, ...this.appended].map(normalizePath);
31093
+ return paths.map(normalizePath).some(
31094
+ (at) => at === "/" || NODE_MODULES.test(at) || roots.some(
31095
+ (root) => at === root || at.startsWith(`${root}/`) || root.startsWith(`${at}/`)
31096
+ )
31097
+ );
31098
+ }
31030
31099
  resetCatalogs() {
31031
31100
  this.catalogs.clear();
31032
31101
  this.ownCatalogs.clear();
@@ -31427,7 +31496,11 @@ var Launcher = class {
31427
31496
  secrets.push({ name: name2, value: value2, placeholder });
31428
31497
  }
31429
31498
  const scrub = scrubberOf(secrets);
31430
- return { env, pid, cwd: booked.cwd, ...scrub ? { scrub } : {} };
31499
+ const recheck = async (next3) => {
31500
+ const same2 = sameList(planFiles(next3), planFiles(planned)) && sameList(await this.interpreterChain(next3, booked.cwd, scope), interpreters) && (this.usersMode === "enforce" && await this.trustedFiles(next3, req.file, booked.cwd, scope)) === trusted;
31501
+ if (!same2) throw new SpawnError("EACCES");
31502
+ };
31503
+ return { env, pid, cwd: booked.cwd, ...scrub ? { scrub } : {}, recheck };
31431
31504
  }
31432
31505
  async interpreterChain(planned, cwd, scope) {
31433
31506
  const out = [];
@@ -31712,25 +31785,16 @@ var Launcher = class {
31712
31785
  async launch(asked, req) {
31713
31786
  const cwd = await physicalPathAsync(this.base, req.cwd);
31714
31787
  const seen = { ...req.env, HOSTNAME: this.hostname, cwd };
31715
- const env = { ...expandDefaults(asked.target.env, seen), ...req.env };
31716
- for (const key of asked.target.unset ?? []) delete env[key];
31717
- delete env[TICKET_ENV];
31718
- const own = env.HOSTNAME;
31719
- if (asked.target.abi === "wasi") env.HOSTNAME = this.hostname;
31720
- const planned = chosen2(asked, env);
31721
- const { target, args } = planned;
31722
- let program;
31723
- let exeId;
31724
31788
  await this.pnpmReady();
31789
+ let loaded;
31725
31790
  try {
31726
- const caller = { pid: req.ppid ?? 0, uid: req.cred.euid, tag: req.tags?.[0] };
31727
- if (this.integrity) await this.checkedSources(planned, caller);
31728
- [program, exeId] = await this.loaded(target, env, caller);
31729
- if (readsEtcHostname(program)) setOrDrop(env, "HOSTNAME", own);
31791
+ loaded = await this.loadedUnder(asked, (planned2) => this.loadPlanned(planned2, req, seen));
31730
31792
  } catch (err) {
31731
31793
  await req.fds.closeAll();
31732
31794
  throw err;
31733
31795
  }
31796
+ const { planned, env, program, exeId } = loaded;
31797
+ const { target, args } = planned;
31734
31798
  const done4 = this.heldUrl(program);
31735
31799
  try {
31736
31800
  const cwdSlot = await this.cwds.hold(cwd, req.cwdFrom);
@@ -31758,6 +31822,52 @@ var Launcher = class {
31758
31822
  throw err;
31759
31823
  }
31760
31824
  }
31825
+ async loadPlanned(asked, req, seen) {
31826
+ const env = { ...expandDefaults(asked.target.env, seen), ...req.env };
31827
+ for (const key of asked.target.unset ?? []) delete env[key];
31828
+ delete env[TICKET_ENV];
31829
+ const own = env.HOSTNAME;
31830
+ if (asked.target.abi === "wasi") env.HOSTNAME = this.hostname;
31831
+ const planned = chosen2(asked, env);
31832
+ const caller = { pid: req.ppid ?? 0, uid: req.cred.euid, tag: req.tags?.[0] };
31833
+ if (this.integrity) await this.checkedSources(planned, caller);
31834
+ const [program, exeId] = await this.loaded(planned.target, env, caller);
31835
+ if (readsEtcHostname(program)) setOrDrop(env, "HOSTNAME", own);
31836
+ return { planned, env, program, exeId };
31837
+ }
31838
+ packageRoots(planned) {
31839
+ const packages2 = [this.modulesDir, ...this.appended];
31840
+ const roots = /* @__PURE__ */ new Set();
31841
+ for (const file of planFiles(planned)) {
31842
+ if (!file.startsWith("/")) continue;
31843
+ const at = normalizePath(file);
31844
+ const root = PACKAGE_ROOT.exec(at)?.[1] ?? packageRootOf(at, packages2);
31845
+ if (root !== void 0 && root !== at) roots.add(root);
31846
+ }
31847
+ return [...roots].sort();
31848
+ }
31849
+ async loadedUnder(asked, load) {
31850
+ const { replan, recheck } = asked;
31851
+ let planned = asked;
31852
+ let roots = this.packageRoots(planned);
31853
+ let stale = false;
31854
+ for (; ; ) {
31855
+ const held = roots;
31856
+ const done4 = await this.packageLocks.loading(held, async (waited) => {
31857
+ if (!waited && !stale) return { value: await load(planned) };
31858
+ this.rootFs.refresh?.(held);
31859
+ this.resetCatalogs();
31860
+ if (replan) planned = await replan() ?? planned;
31861
+ await recheck?.(planned);
31862
+ const now = this.packageRoots(planned);
31863
+ if (now.every((root) => held.includes(root))) return { value: await load(planned) };
31864
+ roots = [.../* @__PURE__ */ new Set([...held, ...now])].sort();
31865
+ stale = true;
31866
+ return void 0;
31867
+ });
31868
+ if (done4) return done4.value;
31869
+ }
31870
+ }
31761
31871
  groupSession(pgid) {
31762
31872
  return this.jobs.sessionOf(pgid);
31763
31873
  }
@@ -32186,7 +32296,7 @@ var Launcher = class {
32186
32296
  const scope = grant ? "" : await this.scopeOf(cred.euid);
32187
32297
  const file = booked === void 0 ? req.file : await this.ticketFile(req, fds);
32188
32298
  const planned = await this.checkedPlan({ ...req, file }, { ppid, cred, scope }, fds);
32189
- const redeemed = await this.redeemed(
32299
+ const { recheck, ...redeemed } = await this.redeemed(
32190
32300
  { ...req, file },
32191
32301
  { ppid, cred, scope, booked },
32192
32302
  planned,
@@ -32194,7 +32304,9 @@ var Launcher = class {
32194
32304
  );
32195
32305
  const exec = req.exec ? { exec: true } : {};
32196
32306
  const deciding = req.exec ? void 0 : this.undecided(ppid);
32197
- const handle = await this.launch(planned, {
32307
+ const replan = () => this.planOrSearch({ ...req, file }, { ppid, cred, scope });
32308
+ const again = { ...planned, replan, recheck };
32309
+ const handle = await this.launch(again, {
32198
32310
  ...deciding ? { decided: deciding.promise } : {},
32199
32311
  cwd: req.cwd,
32200
32312
  cwdFrom: inheritedCwd(req.chdir, this.cwdSlots.get(ppid)),
@@ -33088,9 +33200,13 @@ var Launcher = class {
33088
33200
  const file = await this.execFile(argv[0] ?? "", cwd, this.execGuard(credOf(account), 0, tag));
33089
33201
  if (!await this.base.exists(cwd))
33090
33202
  await this.stackFor(tags, base).fs.mkdir(cwd, { recursive: true });
33091
- const planned = await this.plan(file, argv, cwd, scope);
33092
- await this.mayExec(credOf(account), { file, cwd, pid: 0, tag }, planned);
33093
- return { cwd, file, planned };
33203
+ const plan = async () => {
33204
+ const found = await this.plan(file, argv, cwd, scope);
33205
+ await this.mayExec(credOf(account), { file, cwd, pid: 0, tag }, found);
33206
+ return found;
33207
+ };
33208
+ const planned = await plan();
33209
+ return { cwd, file, planned: planned && { ...planned, replan: plan } };
33094
33210
  }
33095
33211
  async openTerminal(argv, options) {
33096
33212
  const tagged = tagsOption(options);