@ai-ecoverse/slicc-kernel 1.167.3 → 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.
@@ -1753,9 +1753,11 @@ var OpfsFs = class {
1753
1753
  this.channel.onmessage = ({ data }) => {
1754
1754
  const { paths, trees, outside } = noticeOf(data);
1755
1755
  for (const path of [...paths, ...trees]) this.forget(normalizePath(path));
1756
- const tell = () => remote?.emit({ paths, trees });
1757
- if (!outside) tell();
1758
- else void this.noticed(paths.map(normalizePath), Date.now()).finally(tell);
1756
+ if (!outside) remote?.emit({ paths, trees });
1757
+ else
1758
+ void this.noticed(paths.map(normalizePath), Date.now()).finally(
1759
+ () => remote?.emit({ paths, trees, outside: true })
1760
+ );
1759
1761
  };
1760
1762
  this.channel.unref?.();
1761
1763
  }
@@ -2121,6 +2123,9 @@ var OpfsFs = class {
2121
2123
  this.exact--;
2122
2124
  }
2123
2125
  }
2126
+ refresh(trees) {
2127
+ for (const tree2 of trees) this.forget(normalizePath(tree2));
2128
+ }
2124
2129
  forget(path) {
2125
2130
  this.changes++;
2126
2131
  this.dirs.deleteTree(path);
@@ -2838,6 +2843,10 @@ var OpfsFs = class {
2838
2843
  const children3 = /* @__PURE__ */ new Map();
2839
2844
  this.others++;
2840
2845
  for await (const child of handle.values()) children3.set(child.name, child);
2846
+ if (this.absent.size > 0) {
2847
+ const base = found.path === "/" ? "" : found.path;
2848
+ for (const name2 of children3.keys()) this.absent.delete(`${base}/${name2}`);
2849
+ }
2841
2850
  for (const link2 of await this.meta.links(found.path)) {
2842
2851
  const name2 = baseOf(link2.path);
2843
2852
  if (!children3.has(name2)) children3.set(name2, link2);
@@ -5271,28 +5280,39 @@ async function stickyEntry(g, op, path, dir) {
5271
5280
  if (sticky(g.cred, perm, (await ancestryOf(g.fs, entry)).at(-1))) return;
5272
5281
  if (await present(g.fs, entry, false)) deny(g, op, path, "EPERM");
5273
5282
  }
5283
+ async function unwritable(g, op, path, dir) {
5284
+ if (dir.denied === void 0) return false;
5285
+ if (dir.link) {
5286
+ deny(g, op, dir.denied);
5287
+ return false;
5288
+ }
5289
+ const perm = dir.denied === dir.path ? dir.perm : void 0;
5290
+ if (perm && !await present(g.fs, dir.path, true)) return false;
5291
+ if (perm && permits(g.cred, perm, X_OK)) return true;
5292
+ deny(g, op, path);
5293
+ return false;
5294
+ }
5274
5295
  async function inParent(g, op, path, old = false) {
5275
5296
  await fence(g, op, path);
5276
- if (old === "replaced") {
5277
- const dir2 = await need(g, op, parentOf4(path), W_OK | X_OK, DIR, path);
5278
- return dir2.denied === void 0 ? stickyEntry(g, op, path, dir2) : void 0;
5279
- }
5280
- if (!old) return void await need(g, op, parentOf4(path), W_OK | X_OK, DIR, path);
5281
5297
  const dir = await access(g.fs, g.cred, parentOf4(path), W_OK | X_OK, linked(g, DIR));
5298
+ if (old !== true) {
5299
+ if (await unwritable(g, op, path, dir)) deny(g, op, path);
5300
+ else if (old === "replaced" && dir.denied === void 0) await stickyEntry(g, op, path, dir);
5301
+ return;
5302
+ }
5282
5303
  if (dir.denied === void 0) return stickyEntry(g, op, path, dir);
5283
5304
  if (dir.link) return deny(g, op, dir.denied);
5284
5305
  const lookup2 = dir.denied === dir.path && dir.perm && permits(g.cred, dir.perm, X_OK);
5285
5306
  if (!lookup2 || await present(g.fs, entryIn(dir, path), false)) deny(g, op, path);
5286
5307
  }
5287
- async function makeIn(g, op, path) {
5308
+ async function makeIn(g, op, path, parents = true) {
5288
5309
  for (let dir = parentOf4(path); ; dir = parentOf4(dir)) {
5289
5310
  const walked = await access(g.fs, g.cred, dir, W_OK | X_OK, linked(g, DIR));
5290
- if (walked.link) return deny(g, op, walked.denied);
5291
- if (walked.denied !== void 0) {
5311
+ if (await unwritable(g, op, path, walked)) {
5292
5312
  if (!await present(g.fs, path, true)) deny(g, op, path);
5293
5313
  return;
5294
5314
  }
5295
- if (!walked.missing || dir === "/") return;
5315
+ if (!parents || !walked.missing || dir === "/") return;
5296
5316
  }
5297
5317
  }
5298
5318
  async function tree(g, op, dir, perm) {
@@ -7583,14 +7603,14 @@ var UserDb = class {
7583
7603
  this.fs = fs;
7584
7604
  this.owners = owners;
7585
7605
  }
7586
- async stampHome(row, shared, homeMode) {
7606
+ async stampHome(row, shared2, homeMode) {
7587
7607
  const home = this.fs.resolvePath("/", row.home);
7588
7608
  if (!this.owners || !this.fs.chown || !HOME_DIR.test(home)) return;
7589
7609
  if (!await this.realDir(home)) return;
7590
- const gid = shared?.gid ?? row.gid;
7610
+ const gid = shared2?.gid ?? row.gid;
7591
7611
  await this.fs.chown(home, { uid: row.uid, gid, follow: false, kind: "adopt" });
7592
7612
  const scoop = row.name.startsWith("scoop-");
7593
- await this.fs.chmod(home, homeMode ?? (shared ? 1512 : scoop ? 488 : 448));
7613
+ await this.fs.chmod(home, homeMode ?? (shared2 ? 1512 : scoop ? 488 : 448));
7594
7614
  const pnpm = `${home}/.local/share/pnpm`;
7595
7615
  if (await this.realDir(pnpm)) await this.fs.chmod(pnpm, scoop ? 488 : 448);
7596
7616
  }
@@ -7627,9 +7647,9 @@ var UserDb = class {
7627
7647
  const [passwd, group] = await this.texts();
7628
7648
  return { passwd: parsePasswd(passwd), group: parseGroup(group) };
7629
7649
  }
7630
- async provision(row, umask, shared) {
7631
- const dirs = 511 & ~umask | (shared ? 1024 : 0);
7632
- const gid = shared?.gid ?? row.gid;
7650
+ async provision(row, umask, shared2) {
7651
+ const dirs = 511 & ~umask | (shared2 ? 1024 : 0);
7652
+ const gid = shared2?.gid ?? row.gid;
7633
7653
  const owner = this.owners ? { uid: row.uid, gid } : void 0;
7634
7654
  const made2 = owner ? { recursive: true, owner: { ...owner, mode: dirs, parents: dirs } } : { recursive: true };
7635
7655
  const pnpm = `${row.home}/.local/share/pnpm`;
@@ -7733,7 +7753,7 @@ var UserDb = class {
7733
7753
  const users = parsed(passwd);
7734
7754
  const groups = parsed(group);
7735
7755
  const found = users.find((r) => r.name === name2);
7736
- const shared = homeGroupOf(groups, options.homeGroup);
7756
+ const shared2 = homeGroupOf(groups, options.homeGroup);
7737
7757
  const homeMode = homeModeOf(options.homeMode);
7738
7758
  if (found) {
7739
7759
  const more = groupsOf(groups, options.groups ?? []);
@@ -7741,8 +7761,8 @@ var UserDb = class {
7741
7761
  throw fsError("EINVAL", `a user is no member of its own group ${name2}`);
7742
7762
  }
7743
7763
  if (found.uid >= FIRST_UID && found.uid <= LAST_UID) {
7744
- await this.provision(found, this.umaskFor(options), shared);
7745
- await this.stampHome(found, shared, homeMode);
7764
+ await this.provision(found, this.umaskFor(options), shared2);
7765
+ await this.stampHome(found, shared2, homeMode);
7746
7766
  }
7747
7767
  if (joined(name2, more)) await this.fs.writeFile(GROUP, formatGroup(group));
7748
7768
  remember();
@@ -7759,8 +7779,8 @@ var UserDb = class {
7759
7779
  const fresh3 = !await this.fs.exists(home);
7760
7780
  await this.fs.mkdir(home, { recursive: true });
7761
7781
  try {
7762
- await this.provision(row, this.umaskFor(options), shared);
7763
- await this.stampHome(row, shared, homeMode);
7782
+ await this.provision(row, this.umaskFor(options), shared2);
7783
+ await this.stampHome(row, shared2, homeMode);
7764
7784
  await this.fs.mkdir("/etc", { recursive: true });
7765
7785
  await this.commit(formatPasswd(passwd), formatGroup(group));
7766
7786
  } catch (err) {
@@ -7809,10 +7829,10 @@ var UserDb = class {
7809
7829
  );
7810
7830
  for (const r of parsed(keptGroups)) r.members = r.members.filter((m) => m !== name2);
7811
7831
  const home = this.fs.resolvePath("/", row.home);
7812
- const shared = parsed(passwd).some(
7832
+ const shared2 = parsed(passwd).some(
7813
7833
  (r) => r !== row && this.fs.resolvePath("/", r.home) === home
7814
7834
  );
7815
- const removeHome = !options.keepHome && !shared && HOME_DIR.test(home) && await this.fs.exists(home);
7835
+ const removeHome = !options.keepHome && !shared2 && HOME_DIR.test(home) && await this.fs.exists(home);
7816
7836
  const staged = `/home/.${name2}.removed-${Date.now()}`;
7817
7837
  await this.commit(
7818
7838
  formatPasswd(passwd.filter((r) => r !== row)),
@@ -8477,7 +8497,7 @@ function satisfies(version, range2) {
8477
8497
  }
8478
8498
 
8479
8499
  // src/version.ts
8480
- var KERNEL_VERSION = true ? "1.167.3" : "0.0.0-dev";
8500
+ var KERNEL_VERSION = true ? "1.168.0" : "0.0.0-dev";
8481
8501
  var OS_RELEASE = "6.0.0-slicc";
8482
8502
  function procVersion() {
8483
8503
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -8560,8 +8580,8 @@ function abiOf(raw2, fallback) {
8560
8580
  const abi = raw2 ?? fallback;
8561
8581
  return abi === "emscripten" || abi === "wasi" || abi === "js" ? abi : void 0;
8562
8582
  }
8563
- function commandOf(pkg, name2, raw2, packageAbi, shared) {
8564
- const withEnv = envFields({ ...shared, ...envOf(pkg, raw2.env) });
8583
+ function commandOf(pkg, name2, raw2, packageAbi, shared2) {
8584
+ const withEnv = envFields({ ...shared2, ...envOf(pkg, raw2.env) });
8565
8585
  const script = inside(pkg, raw2.script);
8566
8586
  if (script) {
8567
8587
  return { name: name2, abi: packageAbi, glue: script, wasm: script, argv0: name2, script, ...withEnv };
@@ -8611,13 +8631,13 @@ function commandsOf(pkg, manifest) {
8611
8631
  const packageAbi = abiOf(slicc.abi, "emscripten");
8612
8632
  const entries = slicc.commands;
8613
8633
  if (!packageAbi || !entries || typeof entries !== "object") return [];
8614
- const shared = envOf(pkg, slicc.env);
8634
+ const shared2 = envOf(pkg, slicc.env);
8615
8635
  const out = [];
8616
8636
  for (const [name2, raw2] of Object.entries(entries)) {
8617
8637
  if (!commandName(name2) || !raw2 || typeof raw2 !== "object") {
8618
8638
  continue;
8619
8639
  }
8620
- const command = commandOf(pkg, name2, raw2, packageAbi, shared);
8640
+ const command = commandOf(pkg, name2, raw2, packageAbi, shared2);
8621
8641
  if (command) out.push(command);
8622
8642
  }
8623
8643
  return out;
@@ -9778,8 +9798,8 @@ var UnlinkedKeeper = class {
9778
9798
  void peers.ask(tab, { kept: { items: [], paths } }, PEER_RM_MS);
9779
9799
  throw fsError("EBUSY", `${target}: open in another tab that did not answer`);
9780
9800
  }
9781
- const { whole, sends, shared } = await this.setAside(target, asked);
9782
- return { whole, undoing: (op) => this.undoing(op, peers, sends, heard, shared) };
9801
+ const { whole, sends, shared: shared2 } = await this.setAside(target, asked);
9802
+ return { whole, undoing: (op) => this.undoing(op, peers, sends, heard, shared2) };
9783
9803
  }
9784
9804
  async setAside(target, asked) {
9785
9805
  const mine = this.heldWithin(target);
@@ -9787,25 +9807,25 @@ var UnlinkedKeeper = class {
9787
9807
  for (const { holders } of asked)
9788
9808
  for (const { path } of holders) tabsOf.set(path, (tabsOf.get(path) ?? 0) + 1);
9789
9809
  const done5 = /* @__PURE__ */ new Map();
9790
- const shared = [];
9810
+ const shared2 = [];
9791
9811
  const sends = [];
9792
9812
  for (const { tab, holders, paths } of asked) {
9793
9813
  const items = [];
9794
9814
  for (const holder of holders) {
9795
9815
  if (!done5.has(holder.path)) {
9796
9816
  const many = mine.has(holder.path) || (tabsOf.get(holder.path) ?? 0) > 1;
9797
- done5.set(holder.path, await this.asideOne(holder, many, mine, shared));
9817
+ done5.set(holder.path, await this.asideOne(holder, many, mine, shared2));
9798
9818
  }
9799
9819
  items.push({ path: holder.path, hidden: done5.get(holder.path), fid: holder.fid });
9800
9820
  }
9801
9821
  sends.push({ tab, items, paths });
9802
9822
  }
9803
- return { whole: done5.get(target) !== void 0, sends, shared };
9823
+ return { whole: done5.get(target) !== void 0, sends, shared: shared2 };
9804
9824
  }
9805
- async asideOne(holder, many, mine, shared) {
9825
+ async asideOne(holder, many, mine, shared2) {
9806
9826
  if (!many) return this.asideFor(holder.path, holder.instance);
9807
9827
  const one = await this.sharedAside(holder.path, mine);
9808
- if (one) shared.push(one);
9828
+ if (one) shared2.push(one);
9809
9829
  return one?.hidden;
9810
9830
  }
9811
9831
  async sharedAside(path, mine) {
@@ -9850,16 +9870,16 @@ var UnlinkedKeeper = class {
9850
9870
  sharedLocks() {
9851
9871
  return this.locks;
9852
9872
  }
9853
- async undoing(op, peers, sends, heard, shared = []) {
9873
+ async undoing(op, peers, sends, heard, shared2 = []) {
9854
9874
  let failed3;
9855
9875
  let out;
9856
9876
  try {
9857
9877
  out = await op();
9858
9878
  } catch (err) {
9859
9879
  failed3 = err ?? new Error("failed");
9860
- await this.rollBackAsides(sends, shared);
9880
+ await this.rollBackAsides(sends, shared2);
9861
9881
  }
9862
- const hiddenShared = new Set(shared.map((one) => one.hidden));
9882
+ const hiddenShared = new Set(shared2.map((one) => one.hidden));
9863
9883
  const answers2 = sends.map(({ tab, items, paths }) => {
9864
9884
  const keeps = items.some((item) => item.hidden && hiddenShared.has(item.hidden));
9865
9885
  return {
@@ -9870,7 +9890,7 @@ var UnlinkedKeeper = class {
9870
9890
  await Promise.all(
9871
9891
  answers2.map(({ told }, i) => heard[i] ? Promise.race([told, delay(PEER_RM_MS)]) : void 0)
9872
9892
  );
9873
- for (const one of shared) {
9893
+ for (const one of shared2) {
9874
9894
  const waits = answers2.filter(
9875
9895
  ({ keeps }, i) => keeps && sends[i]?.items.some((item) => item.hidden === one.hidden)
9876
9896
  );
@@ -9880,7 +9900,7 @@ var UnlinkedKeeper = class {
9880
9900
  if (failed3 !== void 0) throw failed3;
9881
9901
  return out;
9882
9902
  }
9883
- async rollBackAsides(sends, shared) {
9903
+ async rollBackAsides(sends, shared2) {
9884
9904
  const back = /* @__PURE__ */ new Map();
9885
9905
  for (const item of sends.flatMap((send2) => send2.items)) {
9886
9906
  const { hidden, path } = item;
@@ -9888,7 +9908,7 @@ var UnlinkedKeeper = class {
9888
9908
  if (!back.has(hidden)) back.set(hidden, await this.moveBack(hidden, path));
9889
9909
  if (back.get(hidden)) item.hidden = void 0;
9890
9910
  }
9891
- for (const { hidden, path, local: local2, file } of shared) {
9911
+ for (const { hidden, path, local: local2, file } of shared2) {
9892
9912
  if (!back.get(hidden) || !file) continue;
9893
9913
  for (const holder of local2) {
9894
9914
  holder.unkeep(path, file);
@@ -13129,8 +13149,8 @@ var CwdTable = class {
13129
13149
  this.inodeOf = inodeOf2;
13130
13150
  }
13131
13151
  async hold(path, from) {
13132
- const shared = from === void 0 ? void 0 : this.held.get(from);
13133
- if (shared && shared.path === path) return this.share(from);
13152
+ const shared2 = from === void 0 ? void 0 : this.held.get(from);
13153
+ if (shared2 && shared2.path === path) return this.share(from);
13134
13154
  const ino = await this.inodeOf(path).catch(() => void 0);
13135
13155
  const slot2 = this.free.pop() ?? (this.next < UNTRACKED ? this.next++ : UNTRACKED);
13136
13156
  if (slot2 === UNTRACKED) return slot2;
@@ -15134,7 +15154,7 @@ async function guard(g, path, req, cwd) {
15134
15154
  return { path: w };
15135
15155
  }
15136
15156
  case "mkdir": {
15137
- await makeIn(g, op, path);
15157
+ await makeIn(g, op, path, !req.exclusive);
15138
15158
  return { path: await walkSafe(g, path, false) };
15139
15159
  }
15140
15160
  case "create":
@@ -17494,13 +17514,13 @@ async function flock(req, ctx) {
17494
17514
  if (!FLOCK_KINDS.has(req.kind)) throw new KernelError("EINVAL");
17495
17515
  const { key, owner } = await flockOf(req.fd);
17496
17516
  const granted2 = req.kind !== "unlock";
17497
- const shared = locks.shared?.() === true;
17498
- if (shared && !granted2) await ctx.settle?.(req.fd, false);
17517
+ const shared2 = locks.shared?.() === true;
17518
+ if (shared2 && !granted2) await ctx.settle?.(req.fd, false);
17499
17519
  if (req.kind !== "unlock" && req.wait)
17500
17520
  await locks.flocks.wait(key, owner, req.kind, ctx.blocking);
17501
17521
  else if (!await locks.flocks.set(key, owner, req.kind)) throw new KernelError("EAGAIN");
17502
- if (shared && granted2) await grantedAcross(ctx, req.fd);
17503
- return shared ? json3(true) : done3;
17522
+ if (shared2 && granted2) await grantedAcross(ctx, req.fd);
17523
+ return shared2 ? json3(true) : done3;
17504
17524
  }
17505
17525
  async function grantedAcross(ctx, fd) {
17506
17526
  ctx.locks?.fresh?.();
@@ -17528,15 +17548,15 @@ async function recordLock2(req, ctx) {
17528
17548
  return json3({ kind: held.kind, pid: held.owner, start: held.start, len });
17529
17549
  }
17530
17550
  const granted2 = req.kind !== "unlock";
17531
- const shared = locks.shared?.() === true;
17532
- const fd = shared ? req.fd : void 0;
17551
+ const shared2 = locks.shared?.() === true;
17552
+ const fd = shared2 ? req.fd : void 0;
17533
17553
  if (!granted2 && fd !== void 0) await ctx.settle?.(fd, false);
17534
17554
  if (req.kind !== "unlock" && req.wait)
17535
17555
  await locks.wait(key, owner, req.kind, range2, ctx.blocking);
17536
17556
  else if (!await locks.set(key, owner, req.kind, range2.start, range2.end))
17537
17557
  throw new KernelError("EAGAIN");
17538
- if (granted2 && shared) await grantedAcross(ctx, fd);
17539
- return shared ? json3(true) : done3;
17558
+ if (granted2 && shared2) await grantedAcross(ctx, fd);
17559
+ return shared2 ? json3(true) : done3;
17540
17560
  }
17541
17561
 
17542
17562
  // src/kernel/net/loopback-transport.ts
@@ -23776,9 +23796,9 @@ function pausing(signal) {
23776
23796
  return { pausedAt, select };
23777
23797
  }
23778
23798
  async function serveSecure(proc, socket, req) {
23779
- const shared = req.listener?.serving;
23780
- if (req.listener && !shared) throw new KernelError("EINVAL", "the listener has no tls");
23781
- const server = shared ?? await serverOf(proc, req);
23799
+ const shared2 = req.listener?.serving;
23800
+ if (req.listener && !shared2) throw new KernelError("EINVAL", "the listener has no tls");
23801
+ const server = shared2 ?? await serverOf(proc, req);
23782
23802
  const { engine, leaf, alpn, store, flags: flags2 } = server;
23783
23803
  const limit = handshakeSignal(proc, server.timeoutMs);
23784
23804
  const { pausedAt, select } = pausing(limit.signal);
@@ -23792,12 +23812,12 @@ async function serveSecure(proc, socket, req) {
23792
23812
  (flags2 & TLS_KEYLOG) !== 0
23793
23813
  ),
23794
23814
  limit.signal,
23795
- (reason) => !shared && reason === timedOut
23815
+ (reason) => !shared2 && reason === timedOut
23796
23816
  ).finally(() => {
23797
23817
  limit.done();
23798
23818
  selections.delete(socket);
23799
23819
  socket.pending = void 0;
23800
- if (!shared) server.release();
23820
+ if (!shared2) server.release();
23801
23821
  });
23802
23822
  run2.catch(() => void 0);
23803
23823
  const first = await Promise.race([run2, pausedAt.then((pause2) => ({ pause: pause2 }))]);
@@ -29032,6 +29052,39 @@ var RenameLog = class {
29032
29052
  }
29033
29053
  };
29034
29054
 
29055
+ // src/kernel/replacing.ts
29056
+ var REPLACING_LOCK = "slicc-kernel-replacing:v1:";
29057
+ function replacingLocks(scope = globalThis) {
29058
+ const locks = scope.navigator?.locks;
29059
+ return typeof locks?.request === "function" ? locks : void 0;
29060
+ }
29061
+ function replacingLock(metadata, root) {
29062
+ return `${REPLACING_LOCK}${metadata}:${root}`;
29063
+ }
29064
+ function shared(locks, names2, run2, waited) {
29065
+ const [name2, ...rest2] = names2;
29066
+ if (name2 === void 0) return run2(waited);
29067
+ const next3 = (after) => shared(locks, rest2, run2, after);
29068
+ return locks.request(
29069
+ name2,
29070
+ { mode: "shared", ifAvailable: true },
29071
+ (lock) => lock ? next3(waited) : locks.request(name2, { mode: "shared" }, () => next3(true))
29072
+ );
29073
+ }
29074
+ var PackageLocks = class {
29075
+ locks;
29076
+ metadata;
29077
+ constructor(metadata, locks = replacingLocks()) {
29078
+ this.metadata = metadata;
29079
+ this.locks = locks;
29080
+ }
29081
+ loading(roots, run2) {
29082
+ if (!this.locks || roots.length === 0) return run2(false);
29083
+ const names2 = roots.map((root) => replacingLock(this.metadata, root));
29084
+ return shared(this.locks, names2, run2, false);
29085
+ }
29086
+ };
29087
+
29035
29088
  // src/kernel/settling.ts
29036
29089
  var SETUP_OPS = /* @__PURE__ */ new Set([
29037
29090
  "proc-list",
@@ -31282,6 +31335,7 @@ var COMMAND2 = /^\/(?:usr\/)?bin\/([^/]+)$/;
31282
31335
  var SHIM_MARKER = /^\/\/ pnpm-wasm-shim=(\{.*\})$/gm;
31283
31336
  var SHIM_MAX = 64 * 1024;
31284
31337
  var PACKAGE_ROOT = /^(.*\/node_modules\/(?:@[^/]+\/)?[^/]+)\//;
31338
+ var NODE_MODULES = /\/node_modules(?:\/|$)/;
31285
31339
  var ENV_REFERENCE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
31286
31340
  var WASM_MAGIC = [0, 97, 115, 109];
31287
31341
  var SHEBANG_MAX = 256;
@@ -31349,6 +31403,21 @@ function packageRootOf(path, roots) {
31349
31403
  }
31350
31404
  return void 0;
31351
31405
  }
31406
+ function planFiles(planned) {
31407
+ const { target } = planned;
31408
+ const files = [
31409
+ target.glue,
31410
+ target.wasm,
31411
+ target.imports,
31412
+ target.copy,
31413
+ ...(target.variants ?? []).map((variant) => variant.wasm),
31414
+ ...planned.sources ?? []
31415
+ ];
31416
+ return files.filter((file) => file !== void 0);
31417
+ }
31418
+ function sameList(a, b) {
31419
+ return a.length === b.length && a.every((item, i) => item === b[i]);
31420
+ }
31352
31421
  function modulePath(glue) {
31353
31422
  return glue.endsWith(".js") ? `${glue.slice(0, -3)}.wasm` : `${glue}.wasm`;
31354
31423
  }
@@ -31522,6 +31591,7 @@ var Launcher = class {
31522
31591
  sudoer;
31523
31592
  jobs = new JobTable();
31524
31593
  locks;
31594
+ packageLocks;
31525
31595
  fifos = new FifoTable();
31526
31596
  memories;
31527
31597
  sems = new SemTable();
@@ -31594,6 +31664,7 @@ var Launcher = class {
31594
31664
  this.memories = memoriesOf(options.wasmMemories);
31595
31665
  this.rootFs = options.fs;
31596
31666
  this.locks = kernelLocks(options.fs.rootName, this.flushingPeers(options.fs));
31667
+ this.packageLocks = new PackageLocks(options.packageLocks ?? META_DB);
31597
31668
  this.dac = this.usersMode === "off" ? void 0 : new Dac(this.usersMode, options.onDenial);
31598
31669
  if (this.dac) this.wireDac(this.dac);
31599
31670
  this.clipboard = new ClipboardBroker(
@@ -31687,15 +31758,7 @@ var Launcher = class {
31687
31758
  this.nodes.keepsOnDisk = true;
31688
31759
  this.openFiles.add(this.nodes);
31689
31760
  options.fs.onPeer?.(() => this.flushOpen());
31690
- options.fs.remoteChanges?.((change) => {
31691
- this.held.doubt(change.paths, change.moved?.to);
31692
- if (change.moved) this.moved(change.moved.from, change.moved.to);
31693
- this.changes.flush();
31694
- this.cwds.touched(change.paths, change.trees);
31695
- this.rewritten(change.paths, change.trees);
31696
- this.watchers.heard(change.content ? "change" : "rename", change.paths);
31697
- this.watchers.heard("rename", change.trees ?? []);
31698
- });
31761
+ options.fs.remoteChanges?.((change) => this.remoteChanged(change));
31699
31762
  const holders = [this.held, this.openFiles];
31700
31763
  options.fs.heldBy?.(
31701
31764
  (dir) => holders.flatMap((set3) => [...set3].flatMap((holder) => [...holder.held()])).filter((p) => p.startsWith(`${dir}/`) && this.mounts.locate(p) === void 0)
@@ -31905,6 +31968,26 @@ var Launcher = class {
31905
31968
  }
31906
31969
  return trusted;
31907
31970
  }
31971
+ remoteChanged(change) {
31972
+ this.held.doubt(change.paths, change.moved?.to);
31973
+ if (change.moved) this.moved(change.moved.from, change.moved.to);
31974
+ this.changes.flush();
31975
+ this.cwds.touched(change.paths, change.trees);
31976
+ this.rewritten(change.paths, change.trees);
31977
+ this.watchers.heard(change.content ? "change" : "rename", change.paths);
31978
+ this.watchers.heard("rename", change.trees ?? []);
31979
+ if (change.outside && this.catalogChanged([...change.paths, ...change.trees ?? []])) {
31980
+ this.resetCatalogs();
31981
+ }
31982
+ }
31983
+ catalogChanged(paths) {
31984
+ const roots = [this.modulesDir, this.pnpmHome, ...this.appended].map(normalizePath);
31985
+ return paths.map(normalizePath).some(
31986
+ (at) => at === "/" || NODE_MODULES.test(at) || roots.some(
31987
+ (root) => at === root || at.startsWith(`${root}/`) || root.startsWith(`${at}/`)
31988
+ )
31989
+ );
31990
+ }
31908
31991
  resetCatalogs() {
31909
31992
  this.catalogs.clear();
31910
31993
  this.ownCatalogs.clear();
@@ -32305,7 +32388,11 @@ var Launcher = class {
32305
32388
  secrets.push({ name: name2, value: value2, placeholder });
32306
32389
  }
32307
32390
  const scrub = scrubberOf(secrets);
32308
- return { env, pid, cwd: booked.cwd, ...scrub ? { scrub } : {} };
32391
+ const recheck = async (next3) => {
32392
+ const same3 = 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;
32393
+ if (!same3) throw new SpawnError("EACCES");
32394
+ };
32395
+ return { env, pid, cwd: booked.cwd, ...scrub ? { scrub } : {}, recheck };
32309
32396
  }
32310
32397
  async interpreterChain(planned, cwd, scope) {
32311
32398
  const out = [];
@@ -32590,25 +32677,16 @@ var Launcher = class {
32590
32677
  async launch(asked, req) {
32591
32678
  const cwd = await physicalPathAsync(this.base, req.cwd);
32592
32679
  const seen = { ...req.env, HOSTNAME: this.hostname, cwd };
32593
- const env = { ...expandDefaults(asked.target.env, seen), ...req.env };
32594
- for (const key of asked.target.unset ?? []) delete env[key];
32595
- delete env[TICKET_ENV];
32596
- const own = env.HOSTNAME;
32597
- if (asked.target.abi === "wasi") env.HOSTNAME = this.hostname;
32598
- const planned = chosen2(asked, env);
32599
- const { target, args } = planned;
32600
- let program;
32601
- let exeId;
32602
32680
  await this.pnpmReady();
32681
+ let loaded;
32603
32682
  try {
32604
- const caller = { pid: req.ppid ?? 0, uid: req.cred.euid, tag: req.tags?.[0] };
32605
- if (this.integrity) await this.checkedSources(planned, caller);
32606
- [program, exeId] = await this.loaded(target, env, caller);
32607
- if (readsEtcHostname(program)) setOrDrop(env, "HOSTNAME", own);
32683
+ loaded = await this.loadedUnder(asked, (planned2) => this.loadPlanned(planned2, req, seen));
32608
32684
  } catch (err) {
32609
32685
  await req.fds.closeAll();
32610
32686
  throw err;
32611
32687
  }
32688
+ const { planned, env, program, exeId } = loaded;
32689
+ const { target, args } = planned;
32612
32690
  const done5 = this.heldUrl(program);
32613
32691
  try {
32614
32692
  const cwdSlot = await this.cwds.hold(cwd, req.cwdFrom);
@@ -32636,6 +32714,52 @@ var Launcher = class {
32636
32714
  throw err;
32637
32715
  }
32638
32716
  }
32717
+ async loadPlanned(asked, req, seen) {
32718
+ const env = { ...expandDefaults(asked.target.env, seen), ...req.env };
32719
+ for (const key of asked.target.unset ?? []) delete env[key];
32720
+ delete env[TICKET_ENV];
32721
+ const own = env.HOSTNAME;
32722
+ if (asked.target.abi === "wasi") env.HOSTNAME = this.hostname;
32723
+ const planned = chosen2(asked, env);
32724
+ const caller = { pid: req.ppid ?? 0, uid: req.cred.euid, tag: req.tags?.[0] };
32725
+ if (this.integrity) await this.checkedSources(planned, caller);
32726
+ const [program, exeId] = await this.loaded(planned.target, env, caller);
32727
+ if (readsEtcHostname(program)) setOrDrop(env, "HOSTNAME", own);
32728
+ return { planned, env, program, exeId };
32729
+ }
32730
+ packageRoots(planned) {
32731
+ const packages2 = [this.modulesDir, ...this.appended];
32732
+ const roots = /* @__PURE__ */ new Set();
32733
+ for (const file of planFiles(planned)) {
32734
+ if (!file.startsWith("/")) continue;
32735
+ const at = normalizePath(file);
32736
+ const root = PACKAGE_ROOT.exec(at)?.[1] ?? packageRootOf(at, packages2);
32737
+ if (root !== void 0 && root !== at) roots.add(root);
32738
+ }
32739
+ return [...roots].sort();
32740
+ }
32741
+ async loadedUnder(asked, load) {
32742
+ const { replan, recheck } = asked;
32743
+ let planned = asked;
32744
+ let roots = this.packageRoots(planned);
32745
+ let stale = false;
32746
+ for (; ; ) {
32747
+ const held = roots;
32748
+ const done5 = await this.packageLocks.loading(held, async (waited) => {
32749
+ if (!waited && !stale) return { value: await load(planned) };
32750
+ this.rootFs.refresh?.(held);
32751
+ this.resetCatalogs();
32752
+ if (replan) planned = await replan() ?? planned;
32753
+ await recheck?.(planned);
32754
+ const now = this.packageRoots(planned);
32755
+ if (now.every((root) => held.includes(root))) return { value: await load(planned) };
32756
+ roots = [.../* @__PURE__ */ new Set([...held, ...now])].sort();
32757
+ stale = true;
32758
+ return void 0;
32759
+ });
32760
+ if (done5) return done5.value;
32761
+ }
32762
+ }
32639
32763
  groupSession(pgid) {
32640
32764
  return this.jobs.sessionOf(pgid);
32641
32765
  }
@@ -33064,7 +33188,7 @@ var Launcher = class {
33064
33188
  const scope = grant ? "" : await this.scopeOf(cred.euid);
33065
33189
  const file = booked === void 0 ? req.file : await this.ticketFile(req, fds);
33066
33190
  const planned = await this.checkedPlan({ ...req, file }, { ppid, cred, scope }, fds);
33067
- const redeemed = await this.redeemed(
33191
+ const { recheck, ...redeemed } = await this.redeemed(
33068
33192
  { ...req, file },
33069
33193
  { ppid, cred, scope, booked },
33070
33194
  planned,
@@ -33072,7 +33196,9 @@ var Launcher = class {
33072
33196
  );
33073
33197
  const exec = req.exec ? { exec: true } : {};
33074
33198
  const deciding = req.exec ? void 0 : this.undecided(ppid);
33075
- const handle = await this.launch(planned, {
33199
+ const replan = () => this.planOrSearch({ ...req, file }, { ppid, cred, scope });
33200
+ const again = { ...planned, replan, recheck };
33201
+ const handle = await this.launch(again, {
33076
33202
  ...deciding ? { decided: deciding.promise } : {},
33077
33203
  cwd: req.cwd,
33078
33204
  cwdFrom: inheritedCwd(req.chdir, this.cwdSlots.get(ppid)),
@@ -33966,9 +34092,13 @@ var Launcher = class {
33966
34092
  const file = await this.execFile(argv[0] ?? "", cwd, this.execGuard(credOf(account), 0, tag));
33967
34093
  if (!await this.base.exists(cwd))
33968
34094
  await this.stackFor(tags, base).fs.mkdir(cwd, { recursive: true });
33969
- const planned = await this.plan(file, argv, cwd, scope);
33970
- await this.mayExec(credOf(account), { file, cwd, pid: 0, tag }, planned);
33971
- return { cwd, file, planned };
34095
+ const plan = async () => {
34096
+ const found = await this.plan(file, argv, cwd, scope);
34097
+ await this.mayExec(credOf(account), { file, cwd, pid: 0, tag }, found);
34098
+ return found;
34099
+ };
34100
+ const planned = await plan();
34101
+ return { cwd, file, planned: planned && { ...planned, replan: plan } };
33972
34102
  }
33973
34103
  async openTerminal(argv, options) {
33974
34104
  const tagged = tagsOption(options);
@@ -34692,6 +34822,7 @@ function serveKernel(port, deps) {
34692
34822
  await fs.reconcile();
34693
34823
  const started = new Launcher({
34694
34824
  fs,
34825
+ ...name2 ? { packageLocks: name2 } : {},
34695
34826
  ...workers(deps),
34696
34827
  ...req.modules ? { modules: req.modules } : {},
34697
34828
  ...req.commands ? { commands: req.commands } : {},