@ai-ecoverse/slicc-kernel 1.87.0 → 1.88.1

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
@@ -3014,8 +3014,8 @@ var MemoryMeta = class {
3014
3014
  async get(paths) {
3015
3015
  return paths.map((path) => this.entries.get(path));
3016
3016
  }
3017
- async fresh(path) {
3018
- return this.entries.get(path);
3017
+ async fresh(paths) {
3018
+ return this.get(paths);
3019
3019
  }
3020
3020
  async update(path, change) {
3021
3021
  this.set(path, change(this.entries.get(path)));
@@ -3615,8 +3615,25 @@ var OpfsFs = class {
3615
3615
  }
3616
3616
  counted = [];
3617
3617
  async stamp(path) {
3618
- const entry = this.meta.fresh ? await this.meta.fresh(path) : (await this.meta.get([path]))[0];
3619
- return String(entry?.writes ?? 0);
3618
+ const paths = [path, "/"];
3619
+ const [entry, root] = this.meta.fresh ? await this.meta.fresh(paths) : await this.meta.get(paths);
3620
+ return `${entry?.writes ?? 0}:${root?.epoch ?? 0}`;
3621
+ }
3622
+ async epoch() {
3623
+ await this.meta.update("/", (entry) => ({
3624
+ ...entry,
3625
+ path: "/",
3626
+ epoch: (entry?.epoch ?? 0) + 1
3627
+ }));
3628
+ }
3629
+ async entering(path, change) {
3630
+ if (!isProtected(this.counted, path) && !holdsProtected(this.counted, path)) return change();
3631
+ await this.epoch();
3632
+ try {
3633
+ await change();
3634
+ } finally {
3635
+ await this.epoch();
3636
+ }
3620
3637
  }
3621
3638
  async counting(target) {
3622
3639
  if (!isProtected(this.counted, target)) return;
@@ -3687,9 +3704,9 @@ var OpfsFs = class {
3687
3704
  if (fields) await this.meta.update(path, (entry) => ({ ...entry, path, ...fields }));
3688
3705
  }
3689
3706
  guarded = [];
3690
- protect(prefixes2) {
3707
+ protect(prefixes2, versions = false) {
3691
3708
  this.guarded = prefixes2;
3692
- this.counted = prefixes2;
3709
+ this.counted = versions ? prefixes2 : [];
3693
3710
  }
3694
3711
  unguarded() {
3695
3712
  return Object.create(this, { guarded: { value: [] } });
@@ -4269,6 +4286,9 @@ var OpfsFs = class {
4269
4286
  const parent = await this.locate(parentOf4(to), true);
4270
4287
  const destination = resolvePath(parent.path, baseOf(to));
4271
4288
  this.refuse(destination, true);
4289
+ await this.entering(destination, () => this.moveTo(source, parent, destination, to));
4290
+ }
4291
+ async moveTo(source, parent, destination, to) {
4272
4292
  const sourceStat = await this.describe(source);
4273
4293
  if (sourceStat.isDirectory && within2(destination, source.path)) throw fsError("EINVAL", to);
4274
4294
  const target = await this.locate(destination, false).catch((err) => {
@@ -4376,15 +4396,18 @@ var OpfsFs = class {
4376
4396
  }
4377
4397
  const now = Date.now();
4378
4398
  const fields = await this.newFields(at, owner && { uid: owner.uid, gid: owner.gid }, false);
4379
- await this.meta.update(at, () => ({
4380
- path: at,
4381
- link: target,
4382
- dir: parent.path,
4383
- ino: inodeOf(at),
4384
- mtimeMs: now,
4385
- ctimeMs: now,
4386
- ...fields
4387
- }));
4399
+ await this.entering(
4400
+ at,
4401
+ () => this.meta.update(at, () => ({
4402
+ path: at,
4403
+ link: target,
4404
+ dir: parent.path,
4405
+ ino: inodeOf(at),
4406
+ mtimeMs: now,
4407
+ ctimeMs: now,
4408
+ ...fields
4409
+ }))
4410
+ );
4388
4411
  this.touch(parent.path);
4389
4412
  }
4390
4413
  async realpath(path, follow2) {
@@ -5480,8 +5503,12 @@ function closeAll(entry) {
5480
5503
 
5481
5504
  // src/commands.ts
5482
5505
  var NAME2 = /^[A-Za-z0-9._+-]+$/;
5506
+ var COMMAND_NAME = /^[^/\s\p{Cc}]+$/u;
5483
5507
  var ENV_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
5484
5508
  var PACKAGE = /\$\{package\}/g;
5509
+ function commandName(name2) {
5510
+ return COMMAND_NAME.test(name2) && name2.isWellFormed() && name2 !== "." && name2 !== "..";
5511
+ }
5485
5512
  function inside(pkg, rel2) {
5486
5513
  if (typeof rel2 !== "string" || rel2 === "") return void 0;
5487
5514
  const clean = rel2.replace(/^\.\//, "");
@@ -5571,7 +5598,7 @@ function commandsOf(pkg, manifest) {
5571
5598
  const shared = envOf(pkg, slicc.env);
5572
5599
  const out = [];
5573
5600
  for (const [name2, raw] of Object.entries(entries)) {
5574
- if (!NAME2.test(name2) || name2 === "." || name2 === ".." || !raw || typeof raw !== "object") {
5601
+ if (!commandName(name2) || !raw || typeof raw !== "object") {
5575
5602
  continue;
5576
5603
  }
5577
5604
  const command = commandOf(pkg, name2, raw, packageAbi, shared);
@@ -5623,18 +5650,86 @@ async function packagesIn(fs, roots) {
5623
5650
  dirs.push(...await packages(fs, root));
5624
5651
  return dirs;
5625
5652
  }
5626
- async function scanCommands(fs, modules) {
5627
- const found = /* @__PURE__ */ new Map();
5628
- for (const pkg of await packagesIn(fs, modules)) {
5629
- let manifest;
5630
- try {
5631
- manifest = JSON.parse(await fs.readFile(`${pkg}/package.json`));
5632
- } catch {
5633
- continue;
5653
+ function binEntries(manifest) {
5654
+ const { bin, name: name2 } = manifest;
5655
+ if (typeof bin === "string") {
5656
+ return typeof name2 === "string" ? [[name2.slice(name2.lastIndexOf("/") + 1), bin]] : [];
5657
+ }
5658
+ return bin && typeof bin === "object" ? Object.entries(bin) : [];
5659
+ }
5660
+ var MAX_HOPS = 40;
5661
+ function physicalResolver(fs) {
5662
+ const known = /* @__PURE__ */ new Map();
5663
+ const pending = /* @__PURE__ */ new Set();
5664
+ let hops = 0;
5665
+ const step = async (path) => {
5666
+ if (path === "/") return { path, file: false };
5667
+ const slash = path.lastIndexOf("/");
5668
+ const parent = await resolve2(path.slice(0, slash) || "/");
5669
+ if (parent === void 0) return void 0;
5670
+ const at = resolvePath(parent.path, path.slice(slash + 1));
5671
+ const st = await fs.lstat(at).catch(() => void 0);
5672
+ if (!st?.isSymbolicLink) return st ? { path: at, file: st.isFile === true } : void 0;
5673
+ const target = await fs.readlink(at).catch(() => void 0);
5674
+ if (target === void 0 || ++hops > MAX_HOPS) return void 0;
5675
+ return resolve2(resolvePath(parent.path, target));
5676
+ };
5677
+ const resolve2 = (path) => {
5678
+ if (pending.has(path)) return Promise.resolve(void 0);
5679
+ let found = known.get(path);
5680
+ if (!found) {
5681
+ pending.add(path);
5682
+ found = step(path).finally(() => pending.delete(path));
5683
+ known.set(path, found);
5634
5684
  }
5635
- for (const command of commandsOf(pkg, manifest)) {
5636
- if (!found.has(command.name))
5637
- found.set(command.name, await withPackagePaths(fs, pkg, command));
5685
+ return found;
5686
+ };
5687
+ return (path) => {
5688
+ hops = 0;
5689
+ return resolve2(path);
5690
+ };
5691
+ }
5692
+ async function binCommandsOf(physical, pkg, manifest, taken) {
5693
+ const entries = binEntries(manifest);
5694
+ if (entries.length === 0) return [];
5695
+ const home = (await physical(pkg))?.path;
5696
+ const out = [];
5697
+ for (const [name2, rel2] of entries) {
5698
+ if (taken.has(name2) || !commandName(name2)) continue;
5699
+ const script = inside(pkg, rel2);
5700
+ if (!script) continue;
5701
+ const real = await physical(script);
5702
+ if (!real?.file || !real.path.startsWith(`${home}/`)) continue;
5703
+ out.push({
5704
+ name: name2,
5705
+ abi: "emscripten",
5706
+ glue: script,
5707
+ wasm: script,
5708
+ argv0: name2,
5709
+ script,
5710
+ bin: true
5711
+ });
5712
+ }
5713
+ return out;
5714
+ }
5715
+ async function scanCommands(fs, modules, binsFrom = Number.POSITIVE_INFINITY) {
5716
+ const found = /* @__PURE__ */ new Map();
5717
+ const physical = physicalResolver(fs);
5718
+ for (const [index, root] of (typeof modules === "string" ? [modules] : modules).entries()) {
5719
+ for (const pkg of await packages(fs, root)) {
5720
+ let manifest;
5721
+ try {
5722
+ manifest = JSON.parse(await fs.readFile(`${pkg}/package.json`));
5723
+ } catch {
5724
+ continue;
5725
+ }
5726
+ const own = commandsOf(pkg, manifest);
5727
+ const named = new Set(own.map((command) => command.name));
5728
+ const bins = index >= binsFrom ? await binCommandsOf(physical, pkg, manifest, named) : [];
5729
+ for (const command of [...own, ...bins]) {
5730
+ if (!found.has(command.name))
5731
+ found.set(command.name, await withPackagePaths(fs, pkg, command));
5732
+ }
5638
5733
  }
5639
5734
  }
5640
5735
  if (!found.has("sh") && found.has("bash")) {
@@ -6093,7 +6188,7 @@ var FreshModules = class {
6093
6188
  };
6094
6189
 
6095
6190
  // src/fs/physical-path.ts
6096
- var MAX_HOPS = 40;
6191
+ var MAX_HOPS2 = 40;
6097
6192
  function start(path) {
6098
6193
  return { done: [], todo: path.split("/").filter((p) => p !== ""), hops: 0 };
6099
6194
  }
@@ -6118,7 +6213,7 @@ function rest(walk2, part) {
6118
6213
  return lexical(`/${[...walk2.done, part, ...walk2.todo].join("/")}`);
6119
6214
  }
6120
6215
  function follow(walk2, target) {
6121
- if (++walk2.hops > MAX_HOPS) return false;
6216
+ if (++walk2.hops > MAX_HOPS2) return false;
6122
6217
  if (target.startsWith("/")) walk2.done.length = 0;
6123
6218
  walk2.todo = [...target.split("/").filter((p) => p !== ""), ...walk2.todo];
6124
6219
  return true;
@@ -6490,12 +6585,12 @@ async function interruptible(work, signal) {
6490
6585
  if (signal.aborted) throw new KernelError("EINTR");
6491
6586
  let stop = () => {
6492
6587
  };
6493
- const interrupted2 = new Promise((_, reject) => {
6588
+ const interrupted3 = new Promise((_, reject) => {
6494
6589
  stop = () => reject(new KernelError("EINTR"));
6495
6590
  signal.addEventListener("abort", stop, { once: true });
6496
6591
  });
6497
6592
  try {
6498
- return await Promise.race([work, interrupted2]);
6593
+ return await Promise.race([work, interrupted3]);
6499
6594
  } finally {
6500
6595
  signal.removeEventListener("abort", stop);
6501
6596
  }
@@ -7795,11 +7890,12 @@ var MountFs = class {
7795
7890
  });
7796
7891
  }
7797
7892
  async chmod(path, mode) {
7798
- const found = this.at(path);
7799
- if (!found) return this.base.chmod(path, mode);
7800
- this.writable(found.mount, normalizePath(path));
7893
+ const real = await this.follow(path);
7894
+ const found = this.at(real);
7895
+ if (!found) return this.base.chmod(real, mode);
7896
+ this.writable(found.mount, real);
7801
7897
  if (!found.mount.caps.chmod) return;
7802
- this.table.forget(found.mount, normalizePath(path));
7898
+ this.table.forget(found.mount, real);
7803
7899
  await found.mount.conn.call({ op: "setattr", path: found.rel, mode });
7804
7900
  }
7805
7901
  async utimes(path, atime, mtime) {
@@ -8269,7 +8365,7 @@ async function dispatchOne(entry, req) {
8269
8365
  }
8270
8366
 
8271
8367
  // src/realm/sync-sab-wire.ts
8272
- var SAB_HEADER_I32 = 16;
8368
+ var SAB_HEADER_I32 = 18;
8273
8369
  var SAB_CLOCK_OFFSET = SAB_HEADER_I32 * 4;
8274
8370
  var SAB_CLOCK_I64 = 3;
8275
8371
  var SAB_HEADER_BYTES = SAB_CLOCK_OFFSET + SAB_CLOCK_I64 * 8;
@@ -8289,6 +8385,7 @@ var SAB_I_OFFSET_HI = 12;
8289
8385
  var SAB_I_KILLED = 13;
8290
8386
  var SAB_I_ASYNC = 14;
8291
8387
  var SAB_I_STOP = 15;
8388
+ var SAB_I_BLOCKED = 16;
8292
8389
  var U32 = 2 ** 32;
8293
8390
  function storeU53(header4, lo, hi, value) {
8294
8391
  Atomics.store(header4, hi, Math.floor(value / U32));
@@ -8841,12 +8938,32 @@ function mountTable(mounts = []) {
8841
8938
  `;
8842
8939
  }
8843
8940
 
8941
+ // src/version.ts
8942
+ var KERNEL_VERSION = true ? "1.88.1" : "0.0.0-dev";
8943
+ var OS_RELEASE = "6.0.0-slicc";
8944
+ function procVersion() {
8945
+ return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
8946
+ `;
8947
+ }
8948
+
8844
8949
  // src/process/procfs.ts
8845
8950
  var HZ = 100;
8846
8951
  var KIB = 1024;
8847
8952
  var FALLBACK_MEMORY = 4 * KIB * KIB * KIB;
8848
8953
  var PID_FILES = ["cmdline", "comm", "stat", "statm", "status"];
8849
- var SYSTEM_FILES = ["loadavg", "meminfo", "stat", "uptime"];
8954
+ var SYSTEM_FILES = ["loadavg", "meminfo", "stat", "uptime", "version"];
8955
+ var SYS_TREE = {
8956
+ kernel: { osrelease: `${OS_RELEASE}
8957
+ `, ostype: "Linux\n" }
8958
+ };
8959
+ function sysEntry(parts2) {
8960
+ let at = SYS_TREE;
8961
+ for (const part of parts2) {
8962
+ if (typeof at !== "object" || !Object.hasOwn(at, part)) return void 0;
8963
+ at = at[part];
8964
+ }
8965
+ return at;
8966
+ }
8850
8967
  function commOf(info) {
8851
8968
  const name2 = info.argv[0] ?? "";
8852
8969
  return name2.slice(name2.lastIndexOf("/") + 1).slice(0, 15);
@@ -8934,7 +9051,7 @@ function pidStatus(info) {
8934
9051
  "SigQ: 0/0",
8935
9052
  "SigPnd: 0000000000000000",
8936
9053
  "ShdPnd: 0000000000000000",
8937
- "SigBlk: 0000000000000000",
9054
+ `SigBlk: ${sigMask(info.sigBlk)}`,
8938
9055
  `SigIgn: ${sigMask(info.sigIgn)}`,
8939
9056
  `SigCgt: ${sigMask(info.sigCgt)}`,
8940
9057
  ""
@@ -8970,6 +9087,7 @@ function memoryInfo(source, used = 0) {
8970
9087
  ].map(([key, value]) => `${`${key}:`.padEnd(16)}${value.padStart(12)}`).concat("").join("\n");
8971
9088
  }
8972
9089
  function systemFile(name2, listing, now, memory) {
9090
+ if (name2 === "version") return procVersion();
8973
9091
  const up = Math.max(0, now - listing.boot) / 1e3;
8974
9092
  if (name2 === "uptime") return `${up.toFixed(2)} ${up.toFixed(2)}
8975
9093
  `;
@@ -9060,11 +9178,17 @@ var ProcTree = class {
9060
9178
  names: () => [
9061
9179
  ...SYSTEM_NAMES,
9062
9180
  "mounts",
9181
+ "sys",
9063
9182
  "self",
9064
9183
  ...this.listing().processes.map((p) => String(p.pid))
9065
9184
  ]
9066
9185
  };
9067
9186
  }
9187
+ if (top === "sys") {
9188
+ const entry = sysEntry(parts2.slice(1));
9189
+ if (typeof entry === "string") return { kind: "file", content: () => entry };
9190
+ return entry && { kind: "dir", names: () => Object.keys(entry) };
9191
+ }
9068
9192
  if (top === "self") {
9069
9193
  return leaf === void 0 ? { kind: "link", target: String(this.pid) } : void 0;
9070
9194
  }
@@ -9142,7 +9266,7 @@ var ProcTree = class {
9142
9266
  }
9143
9267
  }
9144
9268
  };
9145
- var MAX_HOPS2 = 40;
9269
+ var MAX_HOPS3 = 40;
9146
9270
  function* procWalk(path, follow2) {
9147
9271
  let done4 = [];
9148
9272
  let todo = path.split("/").filter((part) => part !== "");
@@ -9161,7 +9285,7 @@ function* procWalk(path, follow2) {
9161
9285
  if (!follow2 && todo.length === 0) return void 0;
9162
9286
  const target = yield { at, check: todo.find((next2) => next2 !== ".") === ".." };
9163
9287
  if (typeof target === "string") {
9164
- if (++hops > MAX_HOPS2) return void 0;
9288
+ if (++hops > MAX_HOPS3) return void 0;
9165
9289
  [done4, todo] = throughLink(target, done4, todo);
9166
9290
  } else {
9167
9291
  broken ??= refusalOf(target);
@@ -11703,16 +11827,16 @@ function interruption(signal) {
11703
11827
  }
11704
11828
  async function selectFds(fds, read, write, timeoutMs, interrupt) {
11705
11829
  const deadline = timeoutMs < 0 ? Number.POSITIVE_INFINITY : Date.now() + timeoutMs;
11706
- const interrupted2 = interruption(interrupt);
11707
- interrupted2.promise.catch(() => {
11830
+ const interrupted3 = interruption(interrupt);
11831
+ interrupted3.promise.catch(() => {
11708
11832
  });
11709
11833
  try {
11710
- return await waitReady(fds, read, write, deadline, interrupted2.promise);
11834
+ return await waitReady(fds, read, write, deadline, interrupted3.promise);
11711
11835
  } finally {
11712
- interrupted2.done();
11836
+ interrupted3.done();
11713
11837
  }
11714
11838
  }
11715
- async function waitReady(fds, read, write, deadline, interrupted2) {
11839
+ async function waitReady(fds, read, write, deadline, interrupted3) {
11716
11840
  for (; ; ) {
11717
11841
  const now = ready(fds, read, write);
11718
11842
  if (now.read.length > 0 || now.write.length > 0 || Date.now() >= deadline) return now;
@@ -11724,7 +11848,7 @@ async function waitReady(fds, read, write, deadline, interrupted2) {
11724
11848
  let timer;
11725
11849
  const expiry = deadline === Number.POSITIVE_INFINITY ? [] : [new Promise((resolve2) => timer = setTimeout(resolve2, deadline - Date.now()))];
11726
11850
  try {
11727
- await Promise.race([...changes, ...expiry, interrupted2]);
11851
+ await Promise.race([...changes, ...expiry, interrupted3]);
11728
11852
  } finally {
11729
11853
  round.abort();
11730
11854
  clearTimeout(timer);
@@ -12041,11 +12165,12 @@ var VfsNodes = class {
12041
12165
  byId = /* @__PURE__ */ new Map();
12042
12166
  keepsOnDisk = false;
12043
12167
  fs;
12044
- entries = /* @__PURE__ */ new Map();
12168
+ entries;
12045
12169
  onClosed;
12046
- constructor(fs, onClosed) {
12170
+ constructor(fs, onClosed, entries = /* @__PURE__ */ new Map()) {
12047
12171
  this.fs = fs;
12048
12172
  this.onClosed = onClosed;
12173
+ this.entries = entries;
12049
12174
  }
12050
12175
  async onEntry(path, op) {
12051
12176
  const key = await this.entryKey(path);
@@ -12535,6 +12660,7 @@ var HOSTS_ONLY = new Resolver({ routes: new Routes() });
12535
12660
  var WasmProcess = class {
12536
12661
  exited = false;
12537
12662
  children;
12663
+ deathSignals = /* @__PURE__ */ new Map();
12538
12664
  caught = 0;
12539
12665
  ignored = 0;
12540
12666
  inherited = 0;
@@ -12569,7 +12695,10 @@ var WasmProcess = class {
12569
12695
  this.nodes = options.nodes ?? new VfsNodes(options.fs);
12570
12696
  if (!options.nodes) options.openFiles?.add(this.nodes);
12571
12697
  this.children.onChildState = () => this.signal(SIG.CHLD);
12572
- this.children.onReap = options.onReap;
12698
+ this.children.onReap = (pid2) => {
12699
+ this.deathSignals.delete(pid2);
12700
+ options.onReap?.(pid2);
12701
+ };
12573
12702
  }
12574
12703
  ignores(bit) {
12575
12704
  return (this.signalMasks().ignored & bit) !== 0;
@@ -12597,6 +12726,10 @@ var WasmProcess = class {
12597
12726
  if (sig === SIG.STOP) return this.stop(sig);
12598
12727
  const bit = sigbit(sig);
12599
12728
  if (this.ignores(bit)) return "ignore";
12729
+ if (((this.options.blockedBits?.() ?? 0) & bit) !== 0) {
12730
+ this.options.onPending?.(sig);
12731
+ return "deliver";
12732
+ }
12600
12733
  if (!(this.caught & bit)) {
12601
12734
  const action = defaultAction(sig);
12602
12735
  return action === "stop" ? this.stop(sig) : action;
@@ -12613,16 +12746,27 @@ var WasmProcess = class {
12613
12746
  return "deliver";
12614
12747
  }
12615
12748
  async spawn(req) {
12616
- const { file, argv, argv0, env, cwd, stdio, inherit: inherit2, exec, pgid, session } = req;
12749
+ const { file, argv, argv0, env, cwd, stdio, inherit: inherit2, exec, pgid, session, deathSignal } = req;
12750
+ if (deathSignal !== void 0 && (exec || !isSignal(deathSignal))) {
12751
+ throw new KernelError("EINVAL");
12752
+ }
12617
12753
  const group = { ...pgid !== void 0 ? { pgid } : {}, ...session ? { session } : {} };
12618
12754
  const named = argv0 !== void 0 ? { argv0 } : {};
12619
12755
  const spawned = { file, argv, ...named, env, cwd, ...group, ...exec ? { exec } : {} };
12620
12756
  const pid = await this.children.spawn(spawned, stdio, inherit2);
12757
+ if (deathSignal !== void 0) this.onDeath(pgid === 0 || session ? -pid : pid, deathSignal);
12621
12758
  if (!exec) return pid;
12622
12759
  if (this.killed) void Promise.allSettled([this.options.kill?.(pid, SIG.KILL)]);
12623
12760
  else this.adoptExec(pid);
12624
12761
  return pid;
12625
12762
  }
12763
+ onDeath(target, sig) {
12764
+ if (this.exited) this.sendDeath(target, sig);
12765
+ else this.deathSignals.set(target, sig);
12766
+ }
12767
+ sendDeath(target, sig) {
12768
+ void Promise.resolve(this.options.kill?.(target, sig)).catch(() => void 0);
12769
+ }
12626
12770
  async killSelf(sig, target, probe2) {
12627
12771
  if (!this.options.exitSignal) throw new KernelError("ENOSYS");
12628
12772
  if (!isSignal(sig) || defaultAction(sig) !== "terminate") throw new KernelError("EINVAL");
@@ -13521,6 +13665,8 @@ var WasmProcess = class {
13521
13665
  this.interrupt.abort();
13522
13666
  this.options.locks?.release(this.pid);
13523
13667
  this.asyncOps.close();
13668
+ for (const [target, sig] of this.deathSignals) this.sendDeath(target, sig);
13669
+ this.deathSignals.clear();
13524
13670
  for (const pid of this.children.pids()) this.options.onReap?.(pid);
13525
13671
  await this.stampClipboards();
13526
13672
  await this.fds.closeAll();
@@ -13600,19 +13746,25 @@ var Dying = class {
13600
13746
  }
13601
13747
  };
13602
13748
  function killThread(thread, caught, sig) {
13603
- if (!thread || (caught & sigbit(sig)) === 0) return false;
13749
+ if (!thread) return false;
13750
+ const held = (Atomics.load(thread.header, SAB_I_BLOCKED) & sigbit(sig)) !== 0;
13751
+ if (!held && (caught & sigbit(sig)) === 0) return false;
13604
13752
  Atomics.or(thread.header, SAB_I_SIGNALS, sigbit(sig));
13753
+ if (held) return true;
13605
13754
  Atomics.notify(thread.header, SAB_I_SIGNALS);
13606
13755
  const blocked = thread.interrupt;
13607
13756
  thread.interrupt = new AbortController();
13608
13757
  blocked.abort();
13609
13758
  return true;
13610
13759
  }
13760
+ function unblocked(header4) {
13761
+ return Atomics.load(header4, SAB_I_SIGNALS) & ~Atomics.load(header4, SAB_I_BLOCKED);
13762
+ }
13611
13763
  function threadSignalsOf(sab) {
13612
13764
  return { header: new Int32Array(sab, 0, SAB_HEADER_I32), interrupt: new AbortController() };
13613
13765
  }
13614
13766
  function threadCancel(thread) {
13615
- return Atomics.load(thread.header, SAB_I_SIGNALS) !== 0 ? AbortSignal.abort() : thread.interrupt.signal;
13767
+ return unblocked(thread.header) !== 0 ? AbortSignal.abort() : thread.interrupt.signal;
13616
13768
  }
13617
13769
  function notifiers(header4) {
13618
13770
  return {
@@ -13737,15 +13889,16 @@ function processOptions(opts, header4, hooks, created3) {
13737
13889
  resolver: opts.resolver,
13738
13890
  ...notifiers(header4),
13739
13891
  onTimer: (which) => void Atomics.or(header4, SAB_I_TIMERS, 1 << which),
13740
- hasPending: () => Atomics.load(header4, SAB_I_SIGNALS) !== 0 || Atomics.load(header4, SAB_I_TIMERS) !== 0,
13892
+ hasPending: () => unblocked(header4) !== 0 || Atomics.load(header4, SAB_I_TIMERS) !== 0,
13741
13893
  raise: hooks.raise,
13742
13894
  threadKill: hooks.threadKill,
13743
13895
  ...hooks.exitNow ? { exitNow: hooks.exitNow } : {},
13744
13896
  ...hooks.exitSignal ? { exitSignal: hooks.exitSignal } : {},
13745
- ...opts.program.abi === "wasi" || opts.program.abi === "js" ? { pendingBits: () => Atomics.load(header4, SAB_I_SIGNALS) } : {}
13897
+ ...opts.program.abi === "wasi" || opts.program.abi === "js" ? { pendingBits: () => Atomics.load(header4, SAB_I_SIGNALS) } : {},
13898
+ ...opts.program.abi === "wasi" ? { blockedBits: () => Atomics.load(header4, SAB_I_BLOCKED) } : {}
13746
13899
  };
13747
13900
  }
13748
- function threadInit(opts, program, sab, cred, thread) {
13901
+ function threadInit(opts, program, sab, cred, thread, main) {
13749
13902
  return {
13750
13903
  type: WASM_THREAD_INIT,
13751
13904
  pid: opts.shownPid ?? opts.pid,
@@ -13758,12 +13911,15 @@ function threadInit(opts, program, sab, cred, thread) {
13758
13911
  cred: copyCred(cred),
13759
13912
  owners: opts.owners === true,
13760
13913
  ...opts.ppid !== void 0 ? { ppid: opts.ppid } : {},
13761
- thread
13914
+ thread,
13915
+ signals: main
13762
13916
  };
13763
13917
  }
13764
13918
  function spawnWasmProcess(opts) {
13765
13919
  const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
13766
13920
  const header4 = new Int32Array(sab, 0, SAB_HEADER_I32);
13921
+ if (opts.blocked && opts.program.abi === "wasi")
13922
+ Atomics.store(header4, SAB_I_BLOCKED, opts.blocked);
13767
13923
  const threadSignals = /* @__PURE__ */ new Map();
13768
13924
  const created3 = new Created();
13769
13925
  const process2 = new WasmProcess(
@@ -13864,6 +14020,7 @@ function spawnWasmProcess(opts) {
13864
14020
  const tsab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
13865
14021
  const tw = opts.createWorker();
13866
14022
  const tsignals = threadSignalsOf(tsab);
14023
+ if (thread.blocked) Atomics.store(tsignals.header, SAB_I_BLOCKED, thread.blocked);
13867
14024
  threadSignals.set(thread.tid, tsignals);
13868
14025
  const tdispatch = (req) => dying.serving(
13869
14026
  () => isWasmSyscall(req) ? process2.syscall(req, threadCancel(tsignals)) : dispatchSyncFs(req)
@@ -13885,7 +14042,7 @@ function spawnWasmProcess(opts) {
13885
14042
  tresponder.dispose();
13886
14043
  tw.terminate();
13887
14044
  });
13888
- tw.postMessage(threadInit(opts, program, tsab, process2.cred, thread));
14045
+ tw.postMessage(threadInit(opts, program, tsab, process2.cred, thread, sab));
13889
14046
  };
13890
14047
  worker.postMessage(
13891
14048
  processInit(opts, sab, process2.umask, copyCred(process2.cred), lane?.lane),
@@ -13907,6 +14064,7 @@ function spawnWasmProcess(opts) {
13907
14064
  onState: (listener) => process2.onState(listener),
13908
14065
  memory: () => Atomics.load(header4, SAB_I_MEMORY) * 65536,
13909
14066
  ignoredSignals: (fork) => process2.inheritable(fork),
14067
+ blockedSignals: () => opts.program.abi === "wasi" ? Atomics.load(header4, SAB_I_BLOCKED) : 0,
13910
14068
  signalMasks: () => process2.signalMasks(),
13911
14069
  umask: () => process2.umask,
13912
14070
  cred: () => copyCred(process2.cred),
@@ -14619,63 +14777,137 @@ function compile(secrets, options = {}) {
14619
14777
 
14620
14778
  // src/kernel/scrub-file.ts
14621
14779
  var SCRUB_IDLE_MS = 50;
14780
+ var SCRUB_NONBLOCK_MAX = 64 * 1024;
14622
14781
  var encoder5 = new TextEncoder();
14623
14782
  function scrubberOf(secrets) {
14624
14783
  const usable = secrets.filter((s) => encoder5.encode(s.value).length >= MIN_VALUE);
14625
14784
  return usable.length > 0 ? compile(usable, { idleMs: SCRUB_IDLE_MS }) : void 0;
14626
14785
  }
14627
- async function writeAll2(file, bytes, signal) {
14786
+ async function writeAll2(file, bytes) {
14628
14787
  const write = file.write;
14629
- for (let at = 0; at < bytes.length; ) {
14630
- at += await write.call(file, bytes.subarray(at), at === 0 ? signal : void 0);
14631
- }
14788
+ for (let at = 0; at < bytes.length; ) at += await write.call(file, bytes.subarray(at));
14632
14789
  }
14633
14790
  function bound(file, key) {
14634
14791
  const method = file[key];
14635
14792
  return typeof method === "function" ? { [key]: method.bind(file) } : {};
14636
14793
  }
14794
+ function interrupted2(err) {
14795
+ const code = err?.code;
14796
+ return code === "EINTR" || code === "EAGAIN";
14797
+ }
14637
14798
  function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
14638
14799
  const file = inner.file;
14639
14800
  const stream2 = scrubber.stream();
14640
- let queue = Promise.resolve();
14801
+ let tail = Promise.resolve();
14802
+ let pending = 0;
14803
+ let busy = false;
14641
14804
  let timer;
14642
14805
  let failed3;
14806
+ const waiters = /* @__PURE__ */ new Set();
14807
+ const quiet2 = () => !busy && pending === 0;
14808
+ const wake = () => {
14809
+ for (const waiter of [...waiters]) waiter();
14810
+ };
14643
14811
  const raise = () => {
14644
14812
  if (failed3 === void 0) return;
14645
14813
  const err = failed3;
14646
14814
  failed3 = void 0;
14647
14815
  throw err;
14648
14816
  };
14649
- const enqueue = (bytes, signal) => {
14650
- const next2 = queue.then(() => bytes.length > 0 ? writeAll2(file, bytes, signal) : void 0);
14651
- queue = next2.catch(() => void 0);
14652
- return next2;
14817
+ const later = (bytes) => {
14818
+ if (bytes.length === 0) return;
14819
+ pending += 1;
14820
+ tail = tail.then(() => writeAll2(file, bytes)).catch((err) => {
14821
+ if (failed3 === void 0) failed3 = err;
14822
+ }).finally(() => {
14823
+ pending -= 1;
14824
+ if (quiet2()) wake();
14825
+ });
14826
+ };
14827
+ const settling = (signal) => new Promise((resolve2, reject) => {
14828
+ const done4 = () => {
14829
+ waiters.delete(done4);
14830
+ signal?.removeEventListener("abort", stop);
14831
+ resolve2();
14832
+ };
14833
+ const stop = () => {
14834
+ waiters.delete(done4);
14835
+ reject(new KernelError("EINTR"));
14836
+ };
14837
+ if (signal?.aborted) return stop();
14838
+ waiters.add(done4);
14839
+ signal?.addEventListener("abort", stop, { once: true });
14840
+ });
14841
+ const settled = async () => {
14842
+ while (!quiet2()) await settling();
14843
+ };
14844
+ const changed = async (signal) => {
14845
+ const round = new AbortController();
14846
+ const stop = signal ? AbortSignal.any([signal, round.signal]) : round.signal;
14847
+ try {
14848
+ const own = settling(stop);
14849
+ await Promise.race(file.changed ? [own, file.changed(stop)] : [own]);
14850
+ } catch (err) {
14851
+ throw signal?.aborted ? new KernelError("EINTR") : err;
14852
+ } finally {
14853
+ round.abort();
14854
+ }
14855
+ };
14856
+ const turn = async (signal) => {
14857
+ for (let waited = false; ; waited = true) {
14858
+ if (quiet2() && (pollFile(file).writable || !file.changed)) {
14859
+ busy = true;
14860
+ return;
14861
+ }
14862
+ if (signal?.aborted) throw new KernelError(waited ? "EINTR" : "EAGAIN");
14863
+ await changed(signal);
14864
+ }
14865
+ };
14866
+ const send = async (out, signal) => {
14867
+ const write = file.write;
14868
+ for (let at = 0; at < out.length; ) {
14869
+ try {
14870
+ at += await write.call(file, out.subarray(at), signal);
14871
+ } catch (err) {
14872
+ if (!signal?.aborted || !interrupted2(err)) throw err;
14873
+ }
14874
+ if (at < out.length && signal?.aborted) return later(out.subarray(at));
14875
+ }
14653
14876
  };
14654
14877
  const release = () => {
14655
14878
  timer = void 0;
14656
- enqueue(stream2.idle()).catch((err) => {
14657
- failed3 = err;
14658
- });
14879
+ later(stream2.idle());
14659
14880
  };
14660
14881
  return new OpenFile({
14661
14882
  ...bound(file, "read"),
14662
14883
  ...bound(file, "peek"),
14663
- ...bound(file, "poll"),
14664
- ...bound(file, "changed"),
14665
14884
  ...bound(file, "stat"),
14885
+ poll: () => {
14886
+ const state = pollFile(file);
14887
+ return quiet2() ? state : { ...state, writable: false };
14888
+ },
14889
+ changed,
14666
14890
  ...file.tty ? { tty: file.tty } : {},
14667
14891
  ...file.clipboard ? { clipboard: file.clipboard } : {},
14668
14892
  ...file.write ? {
14669
14893
  write: async (bytes, signal) => {
14670
- if (timer !== void 0) clearTimeout(timer);
14671
- raise();
14672
- await enqueue(stream2.push(bytes), signal);
14894
+ await turn(signal);
14895
+ const taken = signal?.aborted && bytes.length > SCRUB_NONBLOCK_MAX ? bytes.subarray(0, SCRUB_NONBLOCK_MAX) : bytes;
14896
+ try {
14897
+ raise();
14898
+ if (timer !== void 0) clearTimeout(timer);
14899
+ timer = void 0;
14900
+ await send(stream2.push(taken), signal);
14901
+ } finally {
14902
+ busy = false;
14903
+ if (quiet2()) wake();
14904
+ }
14673
14905
  timer = setTimeout(release, idleMs);
14674
- return bytes.length;
14906
+ return taken.length;
14675
14907
  }
14676
14908
  } : {},
14677
14909
  flush: async () => {
14678
- await queue;
14910
+ await settled();
14679
14911
  raise();
14680
14912
  await file.flush?.();
14681
14913
  },
@@ -14683,7 +14915,9 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
14683
14915
  if (timer !== void 0) clearTimeout(timer);
14684
14916
  let failure;
14685
14917
  try {
14686
- await enqueue(stream2.end());
14918
+ await settled();
14919
+ later(stream2.end());
14920
+ await settled();
14687
14921
  raise();
14688
14922
  } catch (err) {
14689
14923
  failure = { err };
@@ -14859,12 +15093,12 @@ var SealedChannel = class {
14859
15093
  if (signal.aborted) throw new KernelError("EINTR");
14860
15094
  let stop = () => {
14861
15095
  };
14862
- const interrupted2 = new Promise((_, reject) => {
15096
+ const interrupted3 = new Promise((_, reject) => {
14863
15097
  stop = () => reject(new KernelError("EINTR"));
14864
15098
  signal.addEventListener("abort", stop, { once: true });
14865
15099
  });
14866
15100
  try {
14867
- await Promise.race([flight.done, interrupted2]);
15101
+ await Promise.race([flight.done, interrupted3]);
14868
15102
  } finally {
14869
15103
  signal.removeEventListener("abort", stop);
14870
15104
  }
@@ -17132,15 +17366,25 @@ function shebang(head) {
17132
17366
  if (head[0] !== 35 || head[1] !== 33) return void 0;
17133
17367
  return new TextDecoder().decode(head.subarray(0, SHEBANG_MAX)).slice(2).split("\n")[0].trim().split(/[ \t]+/);
17134
17368
  }
17135
- async function rootOnly(fs, path) {
17369
+ async function rootOnly(fs, path, dir = false) {
17136
17370
  const names = chain(path);
17137
17371
  for (const [i, at] of names.entries()) {
17138
17372
  const st = await fs.lstat(at).catch(() => void 0);
17139
- const kind = i === names.length - 1 ? st?.isFile : st?.isDirectory;
17373
+ const kind = i === names.length - 1 && !dir ? st?.isFile : st?.isDirectory;
17140
17374
  if (!kind || st?.uid !== 0 || (st.mode & 18) !== 0) return false;
17141
17375
  }
17142
17376
  return true;
17143
17377
  }
17378
+ function appendedRoots(given) {
17379
+ const roots = given?.append ?? [];
17380
+ if (!Array.isArray(roots)) throw new TypeError("commands.append must be an array of paths");
17381
+ for (const root of roots) {
17382
+ if (typeof root !== "string" || !root.startsWith("/") || normalizePath(root) !== root) {
17383
+ throw new TypeError(`commands.append: not an absolute, normalised path: ${String(root)}`);
17384
+ }
17385
+ }
17386
+ return [...roots];
17387
+ }
17144
17388
  function plainEnv(words2) {
17145
17389
  const next2 = words2[1];
17146
17390
  if (baseName(words2[0] ?? "") !== "env" || next2 === void 0) return false;
@@ -17233,6 +17477,8 @@ var Launcher = class {
17233
17477
  base;
17234
17478
  createWorker;
17235
17479
  modulesDir;
17480
+ appended;
17481
+ reportedRoots = /* @__PURE__ */ new Set();
17236
17482
  env;
17237
17483
  catalogs = /* @__PURE__ */ new Map();
17238
17484
  ownCatalogs = /* @__PURE__ */ new Map();
@@ -17339,6 +17585,7 @@ var Launcher = class {
17339
17585
  this.processMounts = options.processMounts ?? true;
17340
17586
  this.pnpmHome = options.env?.PNPM_HOME ?? SYSTEM_PNPM_HOME;
17341
17587
  this.modulesDir = options.modules ?? "/node_modules";
17588
+ this.appended = appendedRoots(options.commands);
17342
17589
  this.cdp = new CdpHosts(options.cdp);
17343
17590
  const hostname = options.hostname ?? DEFAULT_HOSTNAME;
17344
17591
  if (!isHostname(hostname)) throw new Error(`not a host name: ${String(hostname)}`);
@@ -17388,9 +17635,9 @@ var Launcher = class {
17388
17635
  this.unguardedRoot = options.fs.unguarded?.() ?? options.fs;
17389
17636
  this.prefixes = protectedPrefixes(options.protect?.paths ?? []);
17390
17637
  this.processFs = this.prefixes.length > 0 ? new ProtectedFs(this.prefixes).view(this.fs) : this.fs;
17391
- options.fs.protect?.(this.prefixes);
17392
17638
  const integrity = options.protect?.integrity;
17393
17639
  integrityOptions(integrity);
17640
+ options.fs.protect?.(this.prefixes, Boolean(integrity));
17394
17641
  if (integrity && this.prefixes.length > 0) {
17395
17642
  this.integrity = new Integrity(
17396
17643
  this.prefixes,
@@ -17413,7 +17660,7 @@ var Launcher = class {
17413
17660
  });
17414
17661
  void this.unlinked.sweep("/");
17415
17662
  this.watchers.watch(
17416
- [this.modulesDir, this.pnpmHome],
17663
+ [this.modulesDir, this.pnpmHome, ...this.appended],
17417
17664
  { recursive: true },
17418
17665
  () => this.resetCatalogs()
17419
17666
  );
@@ -17444,7 +17691,10 @@ var Launcher = class {
17444
17691
  commands(scope = "") {
17445
17692
  let found = this.catalogs.get(scope);
17446
17693
  if (!found) {
17447
- found = this.roots(scope).then((roots) => scanCommands(this.scanned, roots)).then((commands) => commands.set(SUDO_COMMAND.name, SUDO_COMMAND));
17694
+ found = this.trustedAppended().then(async (appended) => {
17695
+ const roots = await this.roots(scope, appended);
17696
+ return scanCommands(this.scanned, roots, roots.length - appended.length);
17697
+ }).then((commands) => commands.set(SUDO_COMMAND.name, SUDO_COMMAND));
17448
17698
  this.catalogs.set(scope, found);
17449
17699
  }
17450
17700
  return found;
@@ -17471,10 +17721,37 @@ var Launcher = class {
17471
17721
  }
17472
17722
  return found;
17473
17723
  }
17474
- async roots(scope = "") {
17475
- const system = [this.modulesDir, ...await pnpmGlobalRoots(this.base, this.pnpmHome)];
17724
+ async roots(scope = "", appended) {
17725
+ const system = [
17726
+ this.modulesDir,
17727
+ ...await pnpmGlobalRoots(this.base, this.pnpmHome),
17728
+ ...appended ?? await this.trustedAppended()
17729
+ ];
17476
17730
  return scope ? [...await pnpmGlobalRoots(this.base, scope), ...system] : system;
17477
17731
  }
17732
+ async rootOnlyPath(root) {
17733
+ for (const at of chain(root)) {
17734
+ const real = await physicalPathAsync(this.base, at);
17735
+ if (!await rootOnly(this.base, real, true)) return false;
17736
+ }
17737
+ return true;
17738
+ }
17739
+ async trustedAppended() {
17740
+ if (this.appended.length === 0 || this.usersMode === "off") return this.appended;
17741
+ const trusted = [];
17742
+ for (const root of this.appended) {
17743
+ const real = await physicalPathAsync(this.base, root);
17744
+ if (await this.rootOnlyPath(root)) trusted.push(root);
17745
+ else if (this.usersMode === "report" && await this.base.exists(real)) {
17746
+ if (!this.reportedRoots.has(root)) {
17747
+ this.reportedRoots.add(root);
17748
+ this.dac?.denied({ pid: 0, uid: 0, op: "commands", path: root, errno: "EACCES" });
17749
+ }
17750
+ trusted.push(root);
17751
+ }
17752
+ }
17753
+ return trusted;
17754
+ }
17478
17755
  resetCatalogs() {
17479
17756
  this.catalogs.clear();
17480
17757
  this.ownCatalogs.clear();
@@ -17580,14 +17857,22 @@ var Launcher = class {
17580
17857
  const inner = await this.interpreted(interp, [interp, ...passed], cwd, scope, depth + 1);
17581
17858
  const via = [interp, ...inner?.via ?? []];
17582
17859
  const sources = [script, ...inner?.sources ?? []];
17583
- return inner && { ...inner, target: withScriptEnv(inner.target, command), via, sources };
17860
+ const bins = [...command?.bin ? [script] : [], ...inner?.bins ?? []];
17861
+ return inner && {
17862
+ ...inner,
17863
+ target: withScriptEnv(inner.target, command),
17864
+ via,
17865
+ sources,
17866
+ ...bins.length > 0 ? { bins } : {}
17867
+ };
17584
17868
  }
17585
17869
  const target = withScriptEnv(found, command);
17586
17870
  return {
17587
17871
  target,
17588
17872
  args: [...target.prefix ?? [], ...passed],
17589
17873
  via: [interp],
17590
- sources: [script]
17874
+ sources: [script],
17875
+ ...command?.bin ? { bins: [script] } : {}
17591
17876
  };
17592
17877
  }
17593
17878
  async plan(file, argv, cwd, scope, argv0) {
@@ -17613,6 +17898,7 @@ var Launcher = class {
17613
17898
  const want = i < via.length - 1 ? R_OK | X_OK : X_OK;
17614
17899
  await need(g, "exec", this.fs.resolvePath(req.cwd, file), want);
17615
17900
  }
17901
+ for (const bin of planned?.bins ?? []) await need(g, "exec", bin, R_OK | X_OK);
17616
17902
  }
17617
17903
  async checkedPlan(req, { ppid, cred, scope }, fds) {
17618
17904
  const planned = await this.plan(req.file, req.argv, req.cwd, scope, req.argv0);
@@ -17878,11 +18164,9 @@ var Launcher = class {
17878
18164
  groupSession(pgid) {
17879
18165
  return this.jobs.sessionOf(pgid);
17880
18166
  }
17881
- treeScrubs(pid, req) {
18167
+ treeScrubs(req) {
17882
18168
  const inherited2 = req.ppid !== void 0 ? this.scrubs.get(req.ppid) : void 0;
17883
- const scrubs = req.scrub ? [...inherited2 ?? [], req.scrub] : inherited2;
17884
- if (scrubs) this.scrubs.set(pid, scrubs);
17885
- return scrubs;
18169
+ return req.scrub ? [...inherited2 ?? [], req.scrub] : inherited2;
17886
18170
  }
17887
18171
  start(req) {
17888
18172
  this.sampling();
@@ -17890,7 +18174,7 @@ var Launcher = class {
17890
18174
  throw new SpawnError("ESRCH");
17891
18175
  }
17892
18176
  const pid = req.pid ?? this.nextPid++;
17893
- const scrubs = this.treeScrubs(pid, req);
18177
+ const scrubs = this.treeScrubs(req);
17894
18178
  const stack = this.stackFor(req.tag);
17895
18179
  const terminal = req.fds.stdioTerminal();
17896
18180
  const decided = req.decided ?? Promise.resolve(true);
@@ -17911,6 +18195,7 @@ var Launcher = class {
17911
18195
  },
17912
18196
  settling: () => this.settling.isSettling(pid),
17913
18197
  ...req.ignored ? { ignored: req.ignored } : {},
18198
+ ...req.blocked ? { blocked: req.blocked } : {},
17914
18199
  ...req.umask !== void 0 ? { umask: req.umask } : {},
17915
18200
  cred: req.cred,
17916
18201
  ...this.owners && req.program.abi === "wasi" ? { fresh: (path, umask) => this.freshModules.add(path, umask, pid) } : {},
@@ -17959,6 +18244,7 @@ var Launcher = class {
17959
18244
  ...this.shownIds(req)
17960
18245
  });
17961
18246
  this.processes.set(pid, handle);
18247
+ if (scrubs) this.scrubs.set(pid, scrubs);
17962
18248
  const { cred } = req;
17963
18249
  this.creds.set(pid, () => handle.cred?.() ?? copyCred(cred));
17964
18250
  if (req.tag !== void 0) this.tags.set(pid, req.tag);
@@ -18185,6 +18471,7 @@ var Launcher = class {
18185
18471
  report,
18186
18472
  ppid,
18187
18473
  ignored: this.ignoredBy(ppid),
18474
+ blocked: this.blockedBy(ppid),
18188
18475
  umask: this.umaskOf(ppid),
18189
18476
  cred,
18190
18477
  tag: this.tagOf(ppid),
@@ -18246,6 +18533,7 @@ var Launcher = class {
18246
18533
  ppid,
18247
18534
  fork: state,
18248
18535
  ignored: this.ignoredBy(ppid, true),
18536
+ blocked: this.blockedBy(ppid),
18249
18537
  umask: this.umaskOf(ppid),
18250
18538
  cred: this.credOf(ppid),
18251
18539
  tag: this.tagOf(ppid)
@@ -18297,11 +18585,16 @@ var Launcher = class {
18297
18585
  }
18298
18586
  signalMasksOf(pid) {
18299
18587
  const masks = this.processes.get(pid)?.signalMasks?.();
18300
- return masks ? { sigIgn: masks.ignored, sigCgt: masks.caught } : {};
18588
+ const blocked = this.blockedBy(pid);
18589
+ const sigBlk = blocked ? { sigBlk: blocked } : {};
18590
+ return masks ? { sigIgn: masks.ignored, sigCgt: masks.caught, ...sigBlk } : sigBlk;
18301
18591
  }
18302
18592
  controlsJobs(pid) {
18303
18593
  return this.settling.isSettling(pid) || (this.ignoredBy(pid) & sigbit(SIG.TTOU)) !== 0;
18304
18594
  }
18595
+ blockedBy(pid) {
18596
+ return this.processes.get(pid)?.blockedSignals?.() ?? 0;
18597
+ }
18305
18598
  ignoredBy(pid, fork = false) {
18306
18599
  return this.processes.get(pid)?.ignoredSignals(fork) ?? 0;
18307
18600
  }
@@ -18666,7 +18959,7 @@ var Launcher = class {
18666
18959
  (dir) => this.userCommandDir(dir),
18667
18960
  this.owners
18668
18961
  );
18669
- const nodes = new VfsNodes(fs, (path) => this.released(path));
18962
+ const nodes = new VfsNodes(fs, (path) => this.released(path), this.nodes.entries);
18670
18963
  nodes.keepsOnDisk = true;
18671
18964
  this.openFiles.add(nodes);
18672
18965
  const unlinked = new UnlinkedKeeper(
@@ -20059,6 +20352,7 @@ async function createNodeKernel(options = {}) {
20059
20352
  terminated
20060
20353
  ),
20061
20354
  ...options.modules ? { modules: options.modules } : {},
20355
+ ...options.commands ? { commands: options.commands } : {},
20062
20356
  ...options.env ? { env: options.env } : {},
20063
20357
  ...transport ? { transport } : {},
20064
20358
  ...options.network?.uplink ? { uplink: options.network.uplink } : {},