@ai-ecoverse/slicc-kernel 1.86.0 → 1.88.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
@@ -753,6 +753,10 @@ var KernelError = class extends Error {
753
753
  if (detail !== void 0) this.detail = detail;
754
754
  }
755
755
  };
756
+ function openAccess(flags2) {
757
+ const mode = flags2 & 3;
758
+ return mode === 0 ? "read" : mode === 1 ? "write" : "both";
759
+ }
756
760
  function pollFile(file) {
757
761
  return file.poll?.() ?? { readable: !!file.read, writable: !!file.write, hangup: false };
758
762
  }
@@ -3010,8 +3014,8 @@ var MemoryMeta = class {
3010
3014
  async get(paths) {
3011
3015
  return paths.map((path) => this.entries.get(path));
3012
3016
  }
3013
- async fresh(path) {
3014
- return this.entries.get(path);
3017
+ async fresh(paths) {
3018
+ return this.get(paths);
3015
3019
  }
3016
3020
  async update(path, change) {
3017
3021
  this.set(path, change(this.entries.get(path)));
@@ -3611,8 +3615,25 @@ var OpfsFs = class {
3611
3615
  }
3612
3616
  counted = [];
3613
3617
  async stamp(path) {
3614
- const entry = this.meta.fresh ? await this.meta.fresh(path) : (await this.meta.get([path]))[0];
3615
- 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
+ }
3616
3637
  }
3617
3638
  async counting(target) {
3618
3639
  if (!isProtected(this.counted, target)) return;
@@ -3683,9 +3704,9 @@ var OpfsFs = class {
3683
3704
  if (fields) await this.meta.update(path, (entry) => ({ ...entry, path, ...fields }));
3684
3705
  }
3685
3706
  guarded = [];
3686
- protect(prefixes2) {
3707
+ protect(prefixes2, versions = false) {
3687
3708
  this.guarded = prefixes2;
3688
- this.counted = prefixes2;
3709
+ this.counted = versions ? prefixes2 : [];
3689
3710
  }
3690
3711
  unguarded() {
3691
3712
  return Object.create(this, { guarded: { value: [] } });
@@ -4265,6 +4286,9 @@ var OpfsFs = class {
4265
4286
  const parent = await this.locate(parentOf4(to), true);
4266
4287
  const destination = resolvePath(parent.path, baseOf(to));
4267
4288
  this.refuse(destination, true);
4289
+ await this.entering(destination, () => this.moveTo(source, parent, destination, to));
4290
+ }
4291
+ async moveTo(source, parent, destination, to) {
4268
4292
  const sourceStat = await this.describe(source);
4269
4293
  if (sourceStat.isDirectory && within2(destination, source.path)) throw fsError("EINVAL", to);
4270
4294
  const target = await this.locate(destination, false).catch((err) => {
@@ -4372,15 +4396,18 @@ var OpfsFs = class {
4372
4396
  }
4373
4397
  const now = Date.now();
4374
4398
  const fields = await this.newFields(at, owner && { uid: owner.uid, gid: owner.gid }, false);
4375
- await this.meta.update(at, () => ({
4376
- path: at,
4377
- link: target,
4378
- dir: parent.path,
4379
- ino: inodeOf(at),
4380
- mtimeMs: now,
4381
- ctimeMs: now,
4382
- ...fields
4383
- }));
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
+ );
4384
4411
  this.touch(parent.path);
4385
4412
  }
4386
4413
  async realpath(path, follow2) {
@@ -5476,8 +5503,12 @@ function closeAll(entry) {
5476
5503
 
5477
5504
  // src/commands.ts
5478
5505
  var NAME2 = /^[A-Za-z0-9._+-]+$/;
5506
+ var COMMAND_NAME = /^[^/\s\p{Cc}]+$/u;
5479
5507
  var ENV_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
5480
5508
  var PACKAGE = /\$\{package\}/g;
5509
+ function commandName(name2) {
5510
+ return COMMAND_NAME.test(name2) && name2.isWellFormed() && name2 !== "." && name2 !== "..";
5511
+ }
5481
5512
  function inside(pkg, rel2) {
5482
5513
  if (typeof rel2 !== "string" || rel2 === "") return void 0;
5483
5514
  const clean = rel2.replace(/^\.\//, "");
@@ -5567,7 +5598,7 @@ function commandsOf(pkg, manifest) {
5567
5598
  const shared = envOf(pkg, slicc.env);
5568
5599
  const out = [];
5569
5600
  for (const [name2, raw] of Object.entries(entries)) {
5570
- if (!NAME2.test(name2) || name2 === "." || name2 === ".." || !raw || typeof raw !== "object") {
5601
+ if (!commandName(name2) || !raw || typeof raw !== "object") {
5571
5602
  continue;
5572
5603
  }
5573
5604
  const command = commandOf(pkg, name2, raw, packageAbi, shared);
@@ -5619,18 +5650,86 @@ async function packagesIn(fs, roots) {
5619
5650
  dirs.push(...await packages(fs, root));
5620
5651
  return dirs;
5621
5652
  }
5622
- async function scanCommands(fs, modules) {
5623
- const found = /* @__PURE__ */ new Map();
5624
- for (const pkg of await packagesIn(fs, modules)) {
5625
- let manifest;
5626
- try {
5627
- manifest = JSON.parse(await fs.readFile(`${pkg}/package.json`));
5628
- } catch {
5629
- 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);
5630
5684
  }
5631
- for (const command of commandsOf(pkg, manifest)) {
5632
- if (!found.has(command.name))
5633
- 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
+ }
5634
5733
  }
5635
5734
  }
5636
5735
  if (!found.has("sh") && found.has("bash")) {
@@ -6089,7 +6188,7 @@ var FreshModules = class {
6089
6188
  };
6090
6189
 
6091
6190
  // src/fs/physical-path.ts
6092
- var MAX_HOPS = 40;
6191
+ var MAX_HOPS2 = 40;
6093
6192
  function start(path) {
6094
6193
  return { done: [], todo: path.split("/").filter((p) => p !== ""), hops: 0 };
6095
6194
  }
@@ -6114,7 +6213,7 @@ function rest(walk2, part) {
6114
6213
  return lexical(`/${[...walk2.done, part, ...walk2.todo].join("/")}`);
6115
6214
  }
6116
6215
  function follow(walk2, target) {
6117
- if (++walk2.hops > MAX_HOPS) return false;
6216
+ if (++walk2.hops > MAX_HOPS2) return false;
6118
6217
  if (target.startsWith("/")) walk2.done.length = 0;
6119
6218
  walk2.todo = [...target.split("/").filter((p) => p !== ""), ...walk2.todo];
6120
6219
  return true;
@@ -8265,7 +8364,7 @@ async function dispatchOne(entry, req) {
8265
8364
  }
8266
8365
 
8267
8366
  // src/realm/sync-sab-wire.ts
8268
- var SAB_HEADER_I32 = 16;
8367
+ var SAB_HEADER_I32 = 18;
8269
8368
  var SAB_CLOCK_OFFSET = SAB_HEADER_I32 * 4;
8270
8369
  var SAB_CLOCK_I64 = 3;
8271
8370
  var SAB_HEADER_BYTES = SAB_CLOCK_OFFSET + SAB_CLOCK_I64 * 8;
@@ -8285,6 +8384,7 @@ var SAB_I_OFFSET_HI = 12;
8285
8384
  var SAB_I_KILLED = 13;
8286
8385
  var SAB_I_ASYNC = 14;
8287
8386
  var SAB_I_STOP = 15;
8387
+ var SAB_I_BLOCKED = 16;
8288
8388
  var U32 = 2 ** 32;
8289
8389
  function storeU53(header4, lo, hi, value) {
8290
8390
  Atomics.store(header4, hi, Math.floor(value / U32));
@@ -8837,12 +8937,32 @@ function mountTable(mounts = []) {
8837
8937
  `;
8838
8938
  }
8839
8939
 
8940
+ // src/version.ts
8941
+ var KERNEL_VERSION = true ? "1.88.0" : "0.0.0-dev";
8942
+ var OS_RELEASE = "6.0.0-slicc";
8943
+ function procVersion() {
8944
+ return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
8945
+ `;
8946
+ }
8947
+
8840
8948
  // src/process/procfs.ts
8841
8949
  var HZ = 100;
8842
8950
  var KIB = 1024;
8843
8951
  var FALLBACK_MEMORY = 4 * KIB * KIB * KIB;
8844
8952
  var PID_FILES = ["cmdline", "comm", "stat", "statm", "status"];
8845
- var SYSTEM_FILES = ["loadavg", "meminfo", "stat", "uptime"];
8953
+ var SYSTEM_FILES = ["loadavg", "meminfo", "stat", "uptime", "version"];
8954
+ var SYS_TREE = {
8955
+ kernel: { osrelease: `${OS_RELEASE}
8956
+ `, ostype: "Linux\n" }
8957
+ };
8958
+ function sysEntry(parts2) {
8959
+ let at = SYS_TREE;
8960
+ for (const part of parts2) {
8961
+ if (typeof at !== "object" || !Object.hasOwn(at, part)) return void 0;
8962
+ at = at[part];
8963
+ }
8964
+ return at;
8965
+ }
8846
8966
  function commOf(info) {
8847
8967
  const name2 = info.argv[0] ?? "";
8848
8968
  return name2.slice(name2.lastIndexOf("/") + 1).slice(0, 15);
@@ -8930,7 +9050,7 @@ function pidStatus(info) {
8930
9050
  "SigQ: 0/0",
8931
9051
  "SigPnd: 0000000000000000",
8932
9052
  "ShdPnd: 0000000000000000",
8933
- "SigBlk: 0000000000000000",
9053
+ `SigBlk: ${sigMask(info.sigBlk)}`,
8934
9054
  `SigIgn: ${sigMask(info.sigIgn)}`,
8935
9055
  `SigCgt: ${sigMask(info.sigCgt)}`,
8936
9056
  ""
@@ -8966,6 +9086,7 @@ function memoryInfo(source, used = 0) {
8966
9086
  ].map(([key, value]) => `${`${key}:`.padEnd(16)}${value.padStart(12)}`).concat("").join("\n");
8967
9087
  }
8968
9088
  function systemFile(name2, listing, now, memory) {
9089
+ if (name2 === "version") return procVersion();
8969
9090
  const up = Math.max(0, now - listing.boot) / 1e3;
8970
9091
  if (name2 === "uptime") return `${up.toFixed(2)} ${up.toFixed(2)}
8971
9092
  `;
@@ -9056,11 +9177,17 @@ var ProcTree = class {
9056
9177
  names: () => [
9057
9178
  ...SYSTEM_NAMES,
9058
9179
  "mounts",
9180
+ "sys",
9059
9181
  "self",
9060
9182
  ...this.listing().processes.map((p) => String(p.pid))
9061
9183
  ]
9062
9184
  };
9063
9185
  }
9186
+ if (top === "sys") {
9187
+ const entry = sysEntry(parts2.slice(1));
9188
+ if (typeof entry === "string") return { kind: "file", content: () => entry };
9189
+ return entry && { kind: "dir", names: () => Object.keys(entry) };
9190
+ }
9064
9191
  if (top === "self") {
9065
9192
  return leaf === void 0 ? { kind: "link", target: String(this.pid) } : void 0;
9066
9193
  }
@@ -9138,7 +9265,7 @@ var ProcTree = class {
9138
9265
  }
9139
9266
  }
9140
9267
  };
9141
- var MAX_HOPS2 = 40;
9268
+ var MAX_HOPS3 = 40;
9142
9269
  function* procWalk(path, follow2) {
9143
9270
  let done4 = [];
9144
9271
  let todo = path.split("/").filter((part) => part !== "");
@@ -9157,7 +9284,7 @@ function* procWalk(path, follow2) {
9157
9284
  if (!follow2 && todo.length === 0) return void 0;
9158
9285
  const target = yield { at, check: todo.find((next2) => next2 !== ".") === ".." };
9159
9286
  if (typeof target === "string") {
9160
- if (++hops > MAX_HOPS2) return void 0;
9287
+ if (++hops > MAX_HOPS3) return void 0;
9161
9288
  [done4, todo] = throughLink(target, done4, todo);
9162
9289
  } else {
9163
9290
  broken ??= refusalOf(target);
@@ -12037,11 +12164,12 @@ var VfsNodes = class {
12037
12164
  byId = /* @__PURE__ */ new Map();
12038
12165
  keepsOnDisk = false;
12039
12166
  fs;
12040
- entries = /* @__PURE__ */ new Map();
12167
+ entries;
12041
12168
  onClosed;
12042
- constructor(fs, onClosed) {
12169
+ constructor(fs, onClosed, entries = /* @__PURE__ */ new Map()) {
12043
12170
  this.fs = fs;
12044
12171
  this.onClosed = onClosed;
12172
+ this.entries = entries;
12045
12173
  }
12046
12174
  async onEntry(path, op) {
12047
12175
  const key = await this.entryKey(path);
@@ -12531,6 +12659,7 @@ var HOSTS_ONLY = new Resolver({ routes: new Routes() });
12531
12659
  var WasmProcess = class {
12532
12660
  exited = false;
12533
12661
  children;
12662
+ deathSignals = /* @__PURE__ */ new Map();
12534
12663
  caught = 0;
12535
12664
  ignored = 0;
12536
12665
  inherited = 0;
@@ -12565,7 +12694,10 @@ var WasmProcess = class {
12565
12694
  this.nodes = options.nodes ?? new VfsNodes(options.fs);
12566
12695
  if (!options.nodes) options.openFiles?.add(this.nodes);
12567
12696
  this.children.onChildState = () => this.signal(SIG.CHLD);
12568
- this.children.onReap = options.onReap;
12697
+ this.children.onReap = (pid2) => {
12698
+ this.deathSignals.delete(pid2);
12699
+ options.onReap?.(pid2);
12700
+ };
12569
12701
  }
12570
12702
  ignores(bit) {
12571
12703
  return (this.signalMasks().ignored & bit) !== 0;
@@ -12593,6 +12725,10 @@ var WasmProcess = class {
12593
12725
  if (sig === SIG.STOP) return this.stop(sig);
12594
12726
  const bit = sigbit(sig);
12595
12727
  if (this.ignores(bit)) return "ignore";
12728
+ if (((this.options.blockedBits?.() ?? 0) & bit) !== 0) {
12729
+ this.options.onPending?.(sig);
12730
+ return "deliver";
12731
+ }
12596
12732
  if (!(this.caught & bit)) {
12597
12733
  const action = defaultAction(sig);
12598
12734
  return action === "stop" ? this.stop(sig) : action;
@@ -12609,16 +12745,27 @@ var WasmProcess = class {
12609
12745
  return "deliver";
12610
12746
  }
12611
12747
  async spawn(req) {
12612
- const { file, argv, argv0, env, cwd, stdio, inherit: inherit2, exec, pgid, session } = req;
12748
+ const { file, argv, argv0, env, cwd, stdio, inherit: inherit2, exec, pgid, session, deathSignal } = req;
12749
+ if (deathSignal !== void 0 && (exec || !isSignal(deathSignal))) {
12750
+ throw new KernelError("EINVAL");
12751
+ }
12613
12752
  const group = { ...pgid !== void 0 ? { pgid } : {}, ...session ? { session } : {} };
12614
12753
  const named = argv0 !== void 0 ? { argv0 } : {};
12615
12754
  const spawned = { file, argv, ...named, env, cwd, ...group, ...exec ? { exec } : {} };
12616
12755
  const pid = await this.children.spawn(spawned, stdio, inherit2);
12756
+ if (deathSignal !== void 0) this.onDeath(pgid === 0 || session ? -pid : pid, deathSignal);
12617
12757
  if (!exec) return pid;
12618
12758
  if (this.killed) void Promise.allSettled([this.options.kill?.(pid, SIG.KILL)]);
12619
12759
  else this.adoptExec(pid);
12620
12760
  return pid;
12621
12761
  }
12762
+ onDeath(target, sig) {
12763
+ if (this.exited) this.sendDeath(target, sig);
12764
+ else this.deathSignals.set(target, sig);
12765
+ }
12766
+ sendDeath(target, sig) {
12767
+ void Promise.resolve(this.options.kill?.(target, sig)).catch(() => void 0);
12768
+ }
12622
12769
  async killSelf(sig, target, probe2) {
12623
12770
  if (!this.options.exitSignal) throw new KernelError("ENOSYS");
12624
12771
  if (!isSignal(sig) || defaultAction(sig) !== "terminate") throw new KernelError("EINVAL");
@@ -12897,17 +13044,15 @@ var WasmProcess = class {
12897
13044
  const file = tty.file();
12898
13045
  return this.fds.install(this.options.outputFilter?.(file) ?? file, 3);
12899
13046
  }
12900
- openSealed() {
13047
+ openSealed(flags2) {
12901
13048
  const broker = this.options.sealed;
12902
13049
  if (!broker) throw new KernelError("ENOENT");
12903
- return this.fds.install(broker.open(), 3);
13050
+ return this.fds.install(broker.open(openAccess(flags2)), 3);
12904
13051
  }
12905
13052
  async openClipboard(flags2) {
12906
13053
  const broker = this.options.clipboard;
12907
13054
  if (!broker) throw new KernelError("ENOENT");
12908
- const mode = flags2 & WRITE_ACCESS;
12909
- const access2 = mode === 0 ? "read" : mode === 1 ? "write" : "both";
12910
- const file = broker.open(access2, await this.clipboardStamp());
13055
+ const file = broker.open(openAccess(flags2), await this.clipboardStamp());
12911
13056
  return this.fds.install(this.options.outputFilter?.(file) ?? file, 3);
12912
13057
  }
12913
13058
  async clipboardStamp() {
@@ -13066,7 +13211,7 @@ var WasmProcess = class {
13066
13211
  case "fd-tty-names":
13067
13212
  return { ok: true, kind: "json", json: this.options.jobs?.terminalNames() ?? [] };
13068
13213
  case "fd-open-sealed":
13069
- return { ok: true, kind: "json", json: this.openSealed() };
13214
+ return { ok: true, kind: "json", json: this.openSealed(req.flags) };
13070
13215
  case "fd-open-clipboard":
13071
13216
  return { ok: true, kind: "json", json: await this.openClipboard(req.flags) };
13072
13217
  case "fd-tty-owner":
@@ -13519,6 +13664,8 @@ var WasmProcess = class {
13519
13664
  this.interrupt.abort();
13520
13665
  this.options.locks?.release(this.pid);
13521
13666
  this.asyncOps.close();
13667
+ for (const [target, sig] of this.deathSignals) this.sendDeath(target, sig);
13668
+ this.deathSignals.clear();
13522
13669
  for (const pid of this.children.pids()) this.options.onReap?.(pid);
13523
13670
  await this.stampClipboards();
13524
13671
  await this.fds.closeAll();
@@ -13598,19 +13745,25 @@ var Dying = class {
13598
13745
  }
13599
13746
  };
13600
13747
  function killThread(thread, caught, sig) {
13601
- if (!thread || (caught & sigbit(sig)) === 0) return false;
13748
+ if (!thread) return false;
13749
+ const held = (Atomics.load(thread.header, SAB_I_BLOCKED) & sigbit(sig)) !== 0;
13750
+ if (!held && (caught & sigbit(sig)) === 0) return false;
13602
13751
  Atomics.or(thread.header, SAB_I_SIGNALS, sigbit(sig));
13752
+ if (held) return true;
13603
13753
  Atomics.notify(thread.header, SAB_I_SIGNALS);
13604
13754
  const blocked = thread.interrupt;
13605
13755
  thread.interrupt = new AbortController();
13606
13756
  blocked.abort();
13607
13757
  return true;
13608
13758
  }
13759
+ function unblocked(header4) {
13760
+ return Atomics.load(header4, SAB_I_SIGNALS) & ~Atomics.load(header4, SAB_I_BLOCKED);
13761
+ }
13609
13762
  function threadSignalsOf(sab) {
13610
13763
  return { header: new Int32Array(sab, 0, SAB_HEADER_I32), interrupt: new AbortController() };
13611
13764
  }
13612
13765
  function threadCancel(thread) {
13613
- return Atomics.load(thread.header, SAB_I_SIGNALS) !== 0 ? AbortSignal.abort() : thread.interrupt.signal;
13766
+ return unblocked(thread.header) !== 0 ? AbortSignal.abort() : thread.interrupt.signal;
13614
13767
  }
13615
13768
  function notifiers(header4) {
13616
13769
  return {
@@ -13735,15 +13888,16 @@ function processOptions(opts, header4, hooks, created3) {
13735
13888
  resolver: opts.resolver,
13736
13889
  ...notifiers(header4),
13737
13890
  onTimer: (which) => void Atomics.or(header4, SAB_I_TIMERS, 1 << which),
13738
- hasPending: () => Atomics.load(header4, SAB_I_SIGNALS) !== 0 || Atomics.load(header4, SAB_I_TIMERS) !== 0,
13891
+ hasPending: () => unblocked(header4) !== 0 || Atomics.load(header4, SAB_I_TIMERS) !== 0,
13739
13892
  raise: hooks.raise,
13740
13893
  threadKill: hooks.threadKill,
13741
13894
  ...hooks.exitNow ? { exitNow: hooks.exitNow } : {},
13742
13895
  ...hooks.exitSignal ? { exitSignal: hooks.exitSignal } : {},
13743
- ...opts.program.abi === "wasi" || opts.program.abi === "js" ? { pendingBits: () => Atomics.load(header4, SAB_I_SIGNALS) } : {}
13896
+ ...opts.program.abi === "wasi" || opts.program.abi === "js" ? { pendingBits: () => Atomics.load(header4, SAB_I_SIGNALS) } : {},
13897
+ ...opts.program.abi === "wasi" ? { blockedBits: () => Atomics.load(header4, SAB_I_BLOCKED) } : {}
13744
13898
  };
13745
13899
  }
13746
- function threadInit(opts, program, sab, cred, thread) {
13900
+ function threadInit(opts, program, sab, cred, thread, main) {
13747
13901
  return {
13748
13902
  type: WASM_THREAD_INIT,
13749
13903
  pid: opts.shownPid ?? opts.pid,
@@ -13756,12 +13910,15 @@ function threadInit(opts, program, sab, cred, thread) {
13756
13910
  cred: copyCred(cred),
13757
13911
  owners: opts.owners === true,
13758
13912
  ...opts.ppid !== void 0 ? { ppid: opts.ppid } : {},
13759
- thread
13913
+ thread,
13914
+ signals: main
13760
13915
  };
13761
13916
  }
13762
13917
  function spawnWasmProcess(opts) {
13763
13918
  const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
13764
13919
  const header4 = new Int32Array(sab, 0, SAB_HEADER_I32);
13920
+ if (opts.blocked && opts.program.abi === "wasi")
13921
+ Atomics.store(header4, SAB_I_BLOCKED, opts.blocked);
13765
13922
  const threadSignals = /* @__PURE__ */ new Map();
13766
13923
  const created3 = new Created();
13767
13924
  const process2 = new WasmProcess(
@@ -13862,6 +14019,7 @@ function spawnWasmProcess(opts) {
13862
14019
  const tsab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
13863
14020
  const tw = opts.createWorker();
13864
14021
  const tsignals = threadSignalsOf(tsab);
14022
+ if (thread.blocked) Atomics.store(tsignals.header, SAB_I_BLOCKED, thread.blocked);
13865
14023
  threadSignals.set(thread.tid, tsignals);
13866
14024
  const tdispatch = (req) => dying.serving(
13867
14025
  () => isWasmSyscall(req) ? process2.syscall(req, threadCancel(tsignals)) : dispatchSyncFs(req)
@@ -13883,7 +14041,7 @@ function spawnWasmProcess(opts) {
13883
14041
  tresponder.dispose();
13884
14042
  tw.terminate();
13885
14043
  });
13886
- tw.postMessage(threadInit(opts, program, tsab, process2.cred, thread));
14044
+ tw.postMessage(threadInit(opts, program, tsab, process2.cred, thread, sab));
13887
14045
  };
13888
14046
  worker.postMessage(
13889
14047
  processInit(opts, sab, process2.umask, copyCred(process2.cred), lane?.lane),
@@ -13905,6 +14063,7 @@ function spawnWasmProcess(opts) {
13905
14063
  onState: (listener) => process2.onState(listener),
13906
14064
  memory: () => Atomics.load(header4, SAB_I_MEMORY) * 65536,
13907
14065
  ignoredSignals: (fork) => process2.inheritable(fork),
14066
+ blockedSignals: () => opts.program.abi === "wasi" ? Atomics.load(header4, SAB_I_BLOCKED) : 0,
13908
14067
  signalMasks: () => process2.signalMasks(),
13909
14068
  umask: () => process2.umask,
13910
14069
  cred: () => copyCred(process2.cred),
@@ -14785,14 +14944,19 @@ var SealedChannel = class {
14785
14944
  offset = 0;
14786
14945
  inflight;
14787
14946
  broker;
14788
- constructor(broker) {
14947
+ access;
14948
+ constructor(broker, access2) {
14789
14949
  this.broker = broker;
14950
+ this.access = access2;
14790
14951
  }
14791
14952
  send(bytes, caller, gone) {
14953
+ if (this.access === "read") {
14954
+ bytes.fill(0);
14955
+ throw new KernelError("EBADF");
14956
+ }
14792
14957
  const length2 = bytes.length;
14793
14958
  const fields = parseSealed(bytes);
14794
- this.reply = void 0;
14795
- this.offset = 0;
14959
+ this.drop();
14796
14960
  this.inflight?.control.abort("replaced");
14797
14961
  const control = new AbortController();
14798
14962
  const withdraw = () => control.abort("withdrawn");
@@ -14828,15 +14992,18 @@ var SealedChannel = class {
14828
14992
  if (!signal.aborted) return reply;
14829
14993
  return signal.reason === "timeout" ? errorReply("ETIMEDOUT", "the vault did not answer within 10 s") : void 0;
14830
14994
  }
14995
+ drop() {
14996
+ this.reply?.fill(0);
14997
+ this.reply = void 0;
14998
+ this.offset = 0;
14999
+ }
14831
15000
  async read(max, signal) {
15001
+ if (this.access === "write") throw new KernelError("EBADF");
14832
15002
  for (; ; ) {
14833
15003
  if (this.reply) {
14834
15004
  const out = this.reply.slice(this.offset, this.offset + max);
14835
15005
  this.offset += out.length;
14836
- if (this.offset >= this.reply.length) {
14837
- this.reply = void 0;
14838
- this.offset = 0;
14839
- }
15006
+ if (this.offset >= this.reply.length) this.drop();
14840
15007
  return out;
14841
15008
  }
14842
15009
  const flight = this.inflight;
@@ -14869,7 +15036,7 @@ var SealedChannel = class {
14869
15036
  close() {
14870
15037
  this.inflight?.control.abort("withdrawn");
14871
15038
  this.inflight = void 0;
14872
- this.reply = void 0;
15039
+ this.drop();
14873
15040
  }
14874
15041
  };
14875
15042
  function swapOf(reply) {
@@ -14926,12 +15093,14 @@ var SealedBroker = class {
14926
15093
  control.abort("done");
14927
15094
  }
14928
15095
  }
14929
- open() {
14930
- const channel = new SealedChannel(this);
15096
+ open(access2 = "both") {
15097
+ const channel = new SealedChannel(this, access2);
14931
15098
  return new OpenFile({
14932
- read: (max, signal) => channel.read(max, signal),
14933
- write: async () => {
14934
- throw new KernelError("EINVAL");
15099
+ ...access2 === "write" ? {} : { read: (max, signal) => channel.read(max, signal) },
15100
+ ...access2 === "read" ? {} : {
15101
+ write: async () => {
15102
+ throw new KernelError("EINVAL");
15103
+ }
14935
15104
  },
14936
15105
  poll: () => channel.poll(),
14937
15106
  changed: (signal) => channel.changed(signal),
@@ -16653,7 +16822,10 @@ var SECURE_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
16653
16822
  var DEFAULT_SUDOERS = "%sudo Cmnd *\n";
16654
16823
  var SUDO_TIMEOUT_MS = 3e5;
16655
16824
  var NAME3 = /^[a-z_][a-z0-9_.-]*$/;
16656
- var RULE = /^(NOPASSWD\s+)?(\S+)\s+(?:\(([^()\s]+)\)\s+)?Cmnd(?:\s+(.*))?$/;
16825
+ var WORD = String.raw`(?:'[^']*'|"(?:[^"\\]|\\[\s\S])*"|\\[\s\S]|[^\s'"\\])+`;
16826
+ var RULE = new RegExp(
16827
+ String.raw`^(NOPASSWD\s+)?(\S+)\s+(?:\(([^()\s]+)\)\s+)?(?:Cwd\s+(${WORD})\s+)?Cmnd(?:\s+(.*))?$`
16828
+ );
16657
16829
  var TERM = /^[A-Za-z0-9._+-]+$/;
16658
16830
  var PLAIN = /^[A-Za-z0-9_@%+=:,./-]+$/;
16659
16831
  var USAGE = "usage: sudo [-n] [-u user] [--] command [args...]\n sudo -l";
@@ -16688,17 +16860,25 @@ function validWho(who) {
16688
16860
  if (who === "ALL") return true;
16689
16861
  return NAME3.test(who.startsWith("%") ? who.slice(1) : who);
16690
16862
  }
16863
+ function cwdOf(word) {
16864
+ const words2 = splitWords(word);
16865
+ const path = words2?.length === 1 ? words2[0] : "";
16866
+ return path.startsWith("/") ? normalizePath(path) : "";
16867
+ }
16691
16868
  function parseRule(line) {
16692
16869
  const text = line.trim();
16693
16870
  if (text === "" || text.startsWith("#")) return null;
16694
16871
  const m = RULE.exec(text);
16695
16872
  if (!m) return void 0;
16696
- const [, nopasswd, who = "", target = "root", rest2 = ""] = m;
16873
+ const [, nopasswd, who = "", target = "root", dir, rest2 = ""] = m;
16697
16874
  if (!validWho(who) || target !== "ALL" && !NAME3.test(target)) return void 0;
16698
- if (rest2.trim() === "*") return { nopasswd: !!nopasswd, who, target, cmnd: "*" };
16875
+ const cwd = dir === void 0 ? void 0 : cwdOf(dir);
16876
+ if (cwd === "") return void 0;
16877
+ const rule = { nopasswd: !!nopasswd, who, target, ...cwd !== void 0 ? { cwd } : {} };
16878
+ if (rest2.trim() === "*") return { ...rule, cmnd: "*" };
16699
16879
  const cmnd = splitWords(rest2);
16700
16880
  if (!cmnd?.[0]?.startsWith("/")) return void 0;
16701
- return { nopasswd: !!nopasswd, who, target, cmnd };
16881
+ return { ...rule, cmnd };
16702
16882
  }
16703
16883
  function flags(parsed2, arg, next2) {
16704
16884
  for (let j = 1; j < arg.length; j++) {
@@ -16737,7 +16917,8 @@ function parseArgs(args) {
16737
16917
  }
16738
16918
  function described(rule) {
16739
16919
  const cmnd = rule.cmnd === "*" ? "ALL" : rule.cmnd.map(quoteWord).join(" ");
16740
- return ` (${rule.target}) ${rule.nopasswd ? "NOPASSWD: " : ""}${cmnd}`;
16920
+ const cwd = rule.cwd === void 0 ? "" : `CWD=${quoteWord(rule.cwd)} `;
16921
+ return ` (${rule.target}) ${cwd}${rule.nopasswd ? "NOPASSWD: " : ""}${cmnd}`;
16741
16922
  }
16742
16923
  var Sudo = class {
16743
16924
  o;
@@ -16877,7 +17058,7 @@ var Sudo = class {
16877
17058
  if (untrusted) return untrusted;
16878
17059
  const argv = [path, ...args];
16879
17060
  const matching = rules.filter(
16880
- (rule) => applies(rule) && (rule.target === "ALL" || rule.target === target.name) && (rule.cmnd === "*" || rule.cmnd.length === argv.length && rule.cmnd.every((w, i) => w === argv[i]))
17061
+ (rule) => applies(rule) && (rule.target === "ALL" || rule.target === target.name) && (rule.cwd === void 0 || rule.cwd === cwd) && (rule.cmnd === "*" || rule.cmnd.length === argv.length && rule.cmnd.every((w, i) => w === argv[i]))
16881
17062
  );
16882
17063
  const grant = () => this.grant(self, target, cred, parsed2.command, path, cwd, call.term);
16883
17064
  if (root || matching.some((rule) => rule.nopasswd)) return grant();
@@ -16899,7 +17080,7 @@ var Sudo = class {
16899
17080
  }
16900
17081
  const changed = await this.untrusted(path, cwd, word);
16901
17082
  if (changed) return changed;
16902
- if (answer === "always" && !await this.remember(self, target, argv)) {
17083
+ if (answer === "always" && !await this.remember(self, target, cwd, argv)) {
16903
17084
  return failed2("EIO", "sudo: the rule could not be recorded");
16904
17085
  }
16905
17086
  return grant();
@@ -16964,7 +17145,7 @@ ${lines2}
16964
17145
  requester.removeEventListener("abort", withdraw);
16965
17146
  }
16966
17147
  }
16967
- async remember(self, target, argv) {
17148
+ async remember(self, target, cwd, argv) {
16968
17149
  const { fs } = this.o;
16969
17150
  const path = `${SUDOERS_D}/${self.name}`;
16970
17151
  if (!NAME3.test(self.name)) return false;
@@ -16974,7 +17155,8 @@ ${lines2}
16974
17155
  const st = await fs.lstat(path).catch(() => void 0);
16975
17156
  if (st && !await this.trusted(path)) return false;
16976
17157
  const old = st ? await fs.readFile(path) : "";
16977
- const line = `NOPASSWD ${self.name} (${target.name}) Cmnd ${argv.map(quoteWord).join(" ")}
17158
+ const words2 = argv.map(quoteWord).join(" ");
17159
+ const line = `NOPASSWD ${self.name} (${target.name}) Cwd ${quoteWord(cwd)} Cmnd ${words2}
16978
17160
  `;
16979
17161
  const text = old === "" || old.endsWith("\n") ? `${old}${line}` : `${old}
16980
17162
  ${line}`;
@@ -17107,15 +17289,25 @@ function shebang(head) {
17107
17289
  if (head[0] !== 35 || head[1] !== 33) return void 0;
17108
17290
  return new TextDecoder().decode(head.subarray(0, SHEBANG_MAX)).slice(2).split("\n")[0].trim().split(/[ \t]+/);
17109
17291
  }
17110
- async function rootOnly(fs, path) {
17292
+ async function rootOnly(fs, path, dir = false) {
17111
17293
  const names = chain(path);
17112
17294
  for (const [i, at] of names.entries()) {
17113
17295
  const st = await fs.lstat(at).catch(() => void 0);
17114
- const kind = i === names.length - 1 ? st?.isFile : st?.isDirectory;
17296
+ const kind = i === names.length - 1 && !dir ? st?.isFile : st?.isDirectory;
17115
17297
  if (!kind || st?.uid !== 0 || (st.mode & 18) !== 0) return false;
17116
17298
  }
17117
17299
  return true;
17118
17300
  }
17301
+ function appendedRoots(given) {
17302
+ const roots = given?.append ?? [];
17303
+ if (!Array.isArray(roots)) throw new TypeError("commands.append must be an array of paths");
17304
+ for (const root of roots) {
17305
+ if (typeof root !== "string" || !root.startsWith("/") || normalizePath(root) !== root) {
17306
+ throw new TypeError(`commands.append: not an absolute, normalised path: ${String(root)}`);
17307
+ }
17308
+ }
17309
+ return [...roots];
17310
+ }
17119
17311
  function plainEnv(words2) {
17120
17312
  const next2 = words2[1];
17121
17313
  if (baseName(words2[0] ?? "") !== "env" || next2 === void 0) return false;
@@ -17208,6 +17400,8 @@ var Launcher = class {
17208
17400
  base;
17209
17401
  createWorker;
17210
17402
  modulesDir;
17403
+ appended;
17404
+ reportedRoots = /* @__PURE__ */ new Set();
17211
17405
  env;
17212
17406
  catalogs = /* @__PURE__ */ new Map();
17213
17407
  ownCatalogs = /* @__PURE__ */ new Map();
@@ -17314,6 +17508,7 @@ var Launcher = class {
17314
17508
  this.processMounts = options.processMounts ?? true;
17315
17509
  this.pnpmHome = options.env?.PNPM_HOME ?? SYSTEM_PNPM_HOME;
17316
17510
  this.modulesDir = options.modules ?? "/node_modules";
17511
+ this.appended = appendedRoots(options.commands);
17317
17512
  this.cdp = new CdpHosts(options.cdp);
17318
17513
  const hostname = options.hostname ?? DEFAULT_HOSTNAME;
17319
17514
  if (!isHostname(hostname)) throw new Error(`not a host name: ${String(hostname)}`);
@@ -17363,9 +17558,9 @@ var Launcher = class {
17363
17558
  this.unguardedRoot = options.fs.unguarded?.() ?? options.fs;
17364
17559
  this.prefixes = protectedPrefixes(options.protect?.paths ?? []);
17365
17560
  this.processFs = this.prefixes.length > 0 ? new ProtectedFs(this.prefixes).view(this.fs) : this.fs;
17366
- options.fs.protect?.(this.prefixes);
17367
17561
  const integrity = options.protect?.integrity;
17368
17562
  integrityOptions(integrity);
17563
+ options.fs.protect?.(this.prefixes, Boolean(integrity));
17369
17564
  if (integrity && this.prefixes.length > 0) {
17370
17565
  this.integrity = new Integrity(
17371
17566
  this.prefixes,
@@ -17388,7 +17583,7 @@ var Launcher = class {
17388
17583
  });
17389
17584
  void this.unlinked.sweep("/");
17390
17585
  this.watchers.watch(
17391
- [this.modulesDir, this.pnpmHome],
17586
+ [this.modulesDir, this.pnpmHome, ...this.appended],
17392
17587
  { recursive: true },
17393
17588
  () => this.resetCatalogs()
17394
17589
  );
@@ -17419,7 +17614,10 @@ var Launcher = class {
17419
17614
  commands(scope = "") {
17420
17615
  let found = this.catalogs.get(scope);
17421
17616
  if (!found) {
17422
- found = this.roots(scope).then((roots) => scanCommands(this.scanned, roots)).then((commands) => commands.set(SUDO_COMMAND.name, SUDO_COMMAND));
17617
+ found = this.trustedAppended().then(async (appended) => {
17618
+ const roots = await this.roots(scope, appended);
17619
+ return scanCommands(this.scanned, roots, roots.length - appended.length);
17620
+ }).then((commands) => commands.set(SUDO_COMMAND.name, SUDO_COMMAND));
17423
17621
  this.catalogs.set(scope, found);
17424
17622
  }
17425
17623
  return found;
@@ -17446,10 +17644,37 @@ var Launcher = class {
17446
17644
  }
17447
17645
  return found;
17448
17646
  }
17449
- async roots(scope = "") {
17450
- const system = [this.modulesDir, ...await pnpmGlobalRoots(this.base, this.pnpmHome)];
17647
+ async roots(scope = "", appended) {
17648
+ const system = [
17649
+ this.modulesDir,
17650
+ ...await pnpmGlobalRoots(this.base, this.pnpmHome),
17651
+ ...appended ?? await this.trustedAppended()
17652
+ ];
17451
17653
  return scope ? [...await pnpmGlobalRoots(this.base, scope), ...system] : system;
17452
17654
  }
17655
+ async rootOnlyPath(root) {
17656
+ for (const at of chain(root)) {
17657
+ const real = await physicalPathAsync(this.base, at);
17658
+ if (!await rootOnly(this.base, real, true)) return false;
17659
+ }
17660
+ return true;
17661
+ }
17662
+ async trustedAppended() {
17663
+ if (this.appended.length === 0 || this.usersMode === "off") return this.appended;
17664
+ const trusted = [];
17665
+ for (const root of this.appended) {
17666
+ const real = await physicalPathAsync(this.base, root);
17667
+ if (await this.rootOnlyPath(root)) trusted.push(root);
17668
+ else if (this.usersMode === "report" && await this.base.exists(real)) {
17669
+ if (!this.reportedRoots.has(root)) {
17670
+ this.reportedRoots.add(root);
17671
+ this.dac?.denied({ pid: 0, uid: 0, op: "commands", path: root, errno: "EACCES" });
17672
+ }
17673
+ trusted.push(root);
17674
+ }
17675
+ }
17676
+ return trusted;
17677
+ }
17453
17678
  resetCatalogs() {
17454
17679
  this.catalogs.clear();
17455
17680
  this.ownCatalogs.clear();
@@ -17555,14 +17780,22 @@ var Launcher = class {
17555
17780
  const inner = await this.interpreted(interp, [interp, ...passed], cwd, scope, depth + 1);
17556
17781
  const via = [interp, ...inner?.via ?? []];
17557
17782
  const sources = [script, ...inner?.sources ?? []];
17558
- return inner && { ...inner, target: withScriptEnv(inner.target, command), via, sources };
17783
+ const bins = [...command?.bin ? [script] : [], ...inner?.bins ?? []];
17784
+ return inner && {
17785
+ ...inner,
17786
+ target: withScriptEnv(inner.target, command),
17787
+ via,
17788
+ sources,
17789
+ ...bins.length > 0 ? { bins } : {}
17790
+ };
17559
17791
  }
17560
17792
  const target = withScriptEnv(found, command);
17561
17793
  return {
17562
17794
  target,
17563
17795
  args: [...target.prefix ?? [], ...passed],
17564
17796
  via: [interp],
17565
- sources: [script]
17797
+ sources: [script],
17798
+ ...command?.bin ? { bins: [script] } : {}
17566
17799
  };
17567
17800
  }
17568
17801
  async plan(file, argv, cwd, scope, argv0) {
@@ -17588,6 +17821,7 @@ var Launcher = class {
17588
17821
  const want = i < via.length - 1 ? R_OK | X_OK : X_OK;
17589
17822
  await need(g, "exec", this.fs.resolvePath(req.cwd, file), want);
17590
17823
  }
17824
+ for (const bin of planned?.bins ?? []) await need(g, "exec", bin, R_OK | X_OK);
17591
17825
  }
17592
17826
  async checkedPlan(req, { ppid, cred, scope }, fds) {
17593
17827
  const planned = await this.plan(req.file, req.argv, req.cwd, scope, req.argv0);
@@ -17886,6 +18120,7 @@ var Launcher = class {
17886
18120
  },
17887
18121
  settling: () => this.settling.isSettling(pid),
17888
18122
  ...req.ignored ? { ignored: req.ignored } : {},
18123
+ ...req.blocked ? { blocked: req.blocked } : {},
17889
18124
  ...req.umask !== void 0 ? { umask: req.umask } : {},
17890
18125
  cred: req.cred,
17891
18126
  ...this.owners && req.program.abi === "wasi" ? { fresh: (path, umask) => this.freshModules.add(path, umask, pid) } : {},
@@ -18160,6 +18395,7 @@ var Launcher = class {
18160
18395
  report,
18161
18396
  ppid,
18162
18397
  ignored: this.ignoredBy(ppid),
18398
+ blocked: this.blockedBy(ppid),
18163
18399
  umask: this.umaskOf(ppid),
18164
18400
  cred,
18165
18401
  tag: this.tagOf(ppid),
@@ -18221,6 +18457,7 @@ var Launcher = class {
18221
18457
  ppid,
18222
18458
  fork: state,
18223
18459
  ignored: this.ignoredBy(ppid, true),
18460
+ blocked: this.blockedBy(ppid),
18224
18461
  umask: this.umaskOf(ppid),
18225
18462
  cred: this.credOf(ppid),
18226
18463
  tag: this.tagOf(ppid)
@@ -18272,11 +18509,16 @@ var Launcher = class {
18272
18509
  }
18273
18510
  signalMasksOf(pid) {
18274
18511
  const masks = this.processes.get(pid)?.signalMasks?.();
18275
- return masks ? { sigIgn: masks.ignored, sigCgt: masks.caught } : {};
18512
+ const blocked = this.blockedBy(pid);
18513
+ const sigBlk = blocked ? { sigBlk: blocked } : {};
18514
+ return masks ? { sigIgn: masks.ignored, sigCgt: masks.caught, ...sigBlk } : sigBlk;
18276
18515
  }
18277
18516
  controlsJobs(pid) {
18278
18517
  return this.settling.isSettling(pid) || (this.ignoredBy(pid) & sigbit(SIG.TTOU)) !== 0;
18279
18518
  }
18519
+ blockedBy(pid) {
18520
+ return this.processes.get(pid)?.blockedSignals?.() ?? 0;
18521
+ }
18280
18522
  ignoredBy(pid, fork = false) {
18281
18523
  return this.processes.get(pid)?.ignoredSignals(fork) ?? 0;
18282
18524
  }
@@ -18641,7 +18883,7 @@ var Launcher = class {
18641
18883
  (dir) => this.userCommandDir(dir),
18642
18884
  this.owners
18643
18885
  );
18644
- const nodes = new VfsNodes(fs, (path) => this.released(path));
18886
+ const nodes = new VfsNodes(fs, (path) => this.released(path), this.nodes.entries);
18645
18887
  nodes.keepsOnDisk = true;
18646
18888
  this.openFiles.add(nodes);
18647
18889
  const unlinked = new UnlinkedKeeper(
@@ -20034,6 +20276,7 @@ async function createNodeKernel(options = {}) {
20034
20276
  terminated
20035
20277
  ),
20036
20278
  ...options.modules ? { modules: options.modules } : {},
20279
+ ...options.commands ? { commands: options.commands } : {},
20037
20280
  ...options.env ? { env: options.env } : {},
20038
20281
  ...transport ? { transport } : {},
20039
20282
  ...options.network?.uplink ? { uplink: options.network.uplink } : {},