@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.
@@ -97,8 +97,8 @@ var MemoryMeta = class {
97
97
  async get(paths) {
98
98
  return paths.map((path) => this.entries.get(path));
99
99
  }
100
- async fresh(path) {
101
- return this.entries.get(path);
100
+ async fresh(paths) {
101
+ return this.get(paths);
102
102
  }
103
103
  async update(path, change) {
104
104
  this.set(path, change(this.entries.get(path)));
@@ -196,15 +196,13 @@ var IndexedDbMeta = class {
196
196
  }
197
197
  return paths.map((path) => this.cache.get(path) ?? void 0);
198
198
  }
199
- async fresh(path) {
199
+ async fresh(paths) {
200
200
  const [transaction, store] = this.transaction("readonly");
201
- const request2 = store.get(path);
202
- let entry;
203
- request2.onsuccess = () => {
204
- entry = request2.result ?? void 0;
205
- };
206
- await settle(transaction, () => void 0);
207
- return entry;
201
+ const requests = paths.map((path) => store.get(path));
202
+ return settle(
203
+ transaction,
204
+ () => requests.map((request2) => request2.result ?? void 0)
205
+ );
208
206
  }
209
207
  async update(path, change) {
210
208
  this.settleRemoves();
@@ -1020,8 +1018,25 @@ var OpfsFs = class {
1020
1018
  }
1021
1019
  counted = [];
1022
1020
  async stamp(path) {
1023
- const entry = this.meta.fresh ? await this.meta.fresh(path) : (await this.meta.get([path]))[0];
1024
- return String(entry?.writes ?? 0);
1021
+ const paths = [path, "/"];
1022
+ const [entry, root] = this.meta.fresh ? await this.meta.fresh(paths) : await this.meta.get(paths);
1023
+ return `${entry?.writes ?? 0}:${root?.epoch ?? 0}`;
1024
+ }
1025
+ async epoch() {
1026
+ await this.meta.update("/", (entry) => ({
1027
+ ...entry,
1028
+ path: "/",
1029
+ epoch: (entry?.epoch ?? 0) + 1
1030
+ }));
1031
+ }
1032
+ async entering(path, change) {
1033
+ if (!isProtected(this.counted, path) && !holdsProtected(this.counted, path)) return change();
1034
+ await this.epoch();
1035
+ try {
1036
+ await change();
1037
+ } finally {
1038
+ await this.epoch();
1039
+ }
1025
1040
  }
1026
1041
  async counting(target) {
1027
1042
  if (!isProtected(this.counted, target)) return;
@@ -1092,9 +1107,9 @@ var OpfsFs = class {
1092
1107
  if (fields) await this.meta.update(path, (entry) => ({ ...entry, path, ...fields }));
1093
1108
  }
1094
1109
  guarded = [];
1095
- protect(prefixes2) {
1110
+ protect(prefixes2, versions = false) {
1096
1111
  this.guarded = prefixes2;
1097
- this.counted = prefixes2;
1112
+ this.counted = versions ? prefixes2 : [];
1098
1113
  }
1099
1114
  unguarded() {
1100
1115
  return Object.create(this, { guarded: { value: [] } });
@@ -1674,6 +1689,9 @@ var OpfsFs = class {
1674
1689
  const parent = await this.locate(parentOf3(to), true);
1675
1690
  const destination = resolvePath(parent.path, baseOf(to));
1676
1691
  this.refuse(destination, true);
1692
+ await this.entering(destination, () => this.moveTo(source, parent, destination, to));
1693
+ }
1694
+ async moveTo(source, parent, destination, to) {
1677
1695
  const sourceStat = await this.describe(source);
1678
1696
  if (sourceStat.isDirectory && within2(destination, source.path)) throw fsError("EINVAL", to);
1679
1697
  const target = await this.locate(destination, false).catch((err) => {
@@ -1781,15 +1799,18 @@ var OpfsFs = class {
1781
1799
  }
1782
1800
  const now = Date.now();
1783
1801
  const fields = await this.newFields(at, owner && { uid: owner.uid, gid: owner.gid }, false);
1784
- await this.meta.update(at, () => ({
1785
- path: at,
1786
- link: target,
1787
- dir: parent.path,
1788
- ino: inodeOf(at),
1789
- mtimeMs: now,
1790
- ctimeMs: now,
1791
- ...fields
1792
- }));
1802
+ await this.entering(
1803
+ at,
1804
+ () => this.meta.update(at, () => ({
1805
+ path: at,
1806
+ link: target,
1807
+ dir: parent.path,
1808
+ ino: inodeOf(at),
1809
+ mtimeMs: now,
1810
+ ctimeMs: now,
1811
+ ...fields
1812
+ }))
1813
+ );
1793
1814
  this.touch(parent.path);
1794
1815
  }
1795
1816
  async realpath(path, follow2) {
@@ -4559,12 +4580,12 @@ async function interruptible(work, signal) {
4559
4580
  if (signal.aborted) throw new KernelError("EINTR");
4560
4581
  let stop = () => {
4561
4582
  };
4562
- const interrupted2 = new Promise((_, reject) => {
4583
+ const interrupted3 = new Promise((_, reject) => {
4563
4584
  stop = () => reject(new KernelError("EINTR"));
4564
4585
  signal.addEventListener("abort", stop, { once: true });
4565
4586
  });
4566
4587
  try {
4567
- return await Promise.race([work, interrupted2]);
4588
+ return await Promise.race([work, interrupted3]);
4568
4589
  } finally {
4569
4590
  signal.removeEventListener("abort", stop);
4570
4591
  }
@@ -7131,8 +7152,12 @@ function closeAll(entry) {
7131
7152
 
7132
7153
  // src/commands.ts
7133
7154
  var NAME2 = /^[A-Za-z0-9._+-]+$/;
7155
+ var COMMAND_NAME = /^[^/\s\p{Cc}]+$/u;
7134
7156
  var ENV_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
7135
7157
  var PACKAGE = /\$\{package\}/g;
7158
+ function commandName(name2) {
7159
+ return COMMAND_NAME.test(name2) && name2.isWellFormed() && name2 !== "." && name2 !== "..";
7160
+ }
7136
7161
  function inside(pkg, rel2) {
7137
7162
  if (typeof rel2 !== "string" || rel2 === "") return void 0;
7138
7163
  const clean = rel2.replace(/^\.\//, "");
@@ -7222,7 +7247,7 @@ function commandsOf(pkg, manifest) {
7222
7247
  const shared = envOf(pkg, slicc.env);
7223
7248
  const out = [];
7224
7249
  for (const [name2, raw] of Object.entries(entries)) {
7225
- if (!NAME2.test(name2) || name2 === "." || name2 === ".." || !raw || typeof raw !== "object") {
7250
+ if (!commandName(name2) || !raw || typeof raw !== "object") {
7226
7251
  continue;
7227
7252
  }
7228
7253
  const command = commandOf(pkg, name2, raw, packageAbi, shared);
@@ -7274,18 +7299,86 @@ async function packagesIn(fs, roots) {
7274
7299
  dirs.push(...await packages(fs, root));
7275
7300
  return dirs;
7276
7301
  }
7277
- async function scanCommands(fs, modules) {
7278
- const found = /* @__PURE__ */ new Map();
7279
- for (const pkg of await packagesIn(fs, modules)) {
7280
- let manifest;
7281
- try {
7282
- manifest = JSON.parse(await fs.readFile(`${pkg}/package.json`));
7283
- } catch {
7284
- continue;
7302
+ function binEntries(manifest) {
7303
+ const { bin, name: name2 } = manifest;
7304
+ if (typeof bin === "string") {
7305
+ return typeof name2 === "string" ? [[name2.slice(name2.lastIndexOf("/") + 1), bin]] : [];
7306
+ }
7307
+ return bin && typeof bin === "object" ? Object.entries(bin) : [];
7308
+ }
7309
+ var MAX_HOPS = 40;
7310
+ function physicalResolver(fs) {
7311
+ const known = /* @__PURE__ */ new Map();
7312
+ const pending = /* @__PURE__ */ new Set();
7313
+ let hops = 0;
7314
+ const step = async (path) => {
7315
+ if (path === "/") return { path, file: false };
7316
+ const slash = path.lastIndexOf("/");
7317
+ const parent = await resolve2(path.slice(0, slash) || "/");
7318
+ if (parent === void 0) return void 0;
7319
+ const at = resolvePath(parent.path, path.slice(slash + 1));
7320
+ const st = await fs.lstat(at).catch(() => void 0);
7321
+ if (!st?.isSymbolicLink) return st ? { path: at, file: st.isFile === true } : void 0;
7322
+ const target = await fs.readlink(at).catch(() => void 0);
7323
+ if (target === void 0 || ++hops > MAX_HOPS) return void 0;
7324
+ return resolve2(resolvePath(parent.path, target));
7325
+ };
7326
+ const resolve2 = (path) => {
7327
+ if (pending.has(path)) return Promise.resolve(void 0);
7328
+ let found = known.get(path);
7329
+ if (!found) {
7330
+ pending.add(path);
7331
+ found = step(path).finally(() => pending.delete(path));
7332
+ known.set(path, found);
7285
7333
  }
7286
- for (const command of commandsOf(pkg, manifest)) {
7287
- if (!found.has(command.name))
7288
- found.set(command.name, await withPackagePaths(fs, pkg, command));
7334
+ return found;
7335
+ };
7336
+ return (path) => {
7337
+ hops = 0;
7338
+ return resolve2(path);
7339
+ };
7340
+ }
7341
+ async function binCommandsOf(physical, pkg, manifest, taken) {
7342
+ const entries = binEntries(manifest);
7343
+ if (entries.length === 0) return [];
7344
+ const home = (await physical(pkg))?.path;
7345
+ const out = [];
7346
+ for (const [name2, rel2] of entries) {
7347
+ if (taken.has(name2) || !commandName(name2)) continue;
7348
+ const script = inside(pkg, rel2);
7349
+ if (!script) continue;
7350
+ const real = await physical(script);
7351
+ if (!real?.file || !real.path.startsWith(`${home}/`)) continue;
7352
+ out.push({
7353
+ name: name2,
7354
+ abi: "emscripten",
7355
+ glue: script,
7356
+ wasm: script,
7357
+ argv0: name2,
7358
+ script,
7359
+ bin: true
7360
+ });
7361
+ }
7362
+ return out;
7363
+ }
7364
+ async function scanCommands(fs, modules, binsFrom = Number.POSITIVE_INFINITY) {
7365
+ const found = /* @__PURE__ */ new Map();
7366
+ const physical = physicalResolver(fs);
7367
+ for (const [index, root] of (typeof modules === "string" ? [modules] : modules).entries()) {
7368
+ for (const pkg of await packages(fs, root)) {
7369
+ let manifest;
7370
+ try {
7371
+ manifest = JSON.parse(await fs.readFile(`${pkg}/package.json`));
7372
+ } catch {
7373
+ continue;
7374
+ }
7375
+ const own = commandsOf(pkg, manifest);
7376
+ const named = new Set(own.map((command) => command.name));
7377
+ const bins = index >= binsFrom ? await binCommandsOf(physical, pkg, manifest, named) : [];
7378
+ for (const command of [...own, ...bins]) {
7379
+ if (!found.has(command.name))
7380
+ found.set(command.name, await withPackagePaths(fs, pkg, command));
7381
+ }
7289
7382
  }
7290
7383
  }
7291
7384
  if (!found.has("sh") && found.has("bash")) {
@@ -7744,7 +7837,7 @@ var FreshModules = class {
7744
7837
  };
7745
7838
 
7746
7839
  // src/fs/physical-path.ts
7747
- var MAX_HOPS = 40;
7840
+ var MAX_HOPS2 = 40;
7748
7841
  function start(path) {
7749
7842
  return { done: [], todo: path.split("/").filter((p) => p !== ""), hops: 0 };
7750
7843
  }
@@ -7769,7 +7862,7 @@ function rest(walk2, part) {
7769
7862
  return lexical(`/${[...walk2.done, part, ...walk2.todo].join("/")}`);
7770
7863
  }
7771
7864
  function follow(walk2, target) {
7772
- if (++walk2.hops > MAX_HOPS) return false;
7865
+ if (++walk2.hops > MAX_HOPS2) return false;
7773
7866
  if (target.startsWith("/")) walk2.done.length = 0;
7774
7867
  walk2.todo = [...target.split("/").filter((p) => p !== ""), ...walk2.todo];
7775
7868
  return true;
@@ -9501,11 +9594,12 @@ var MountFs = class {
9501
9594
  });
9502
9595
  }
9503
9596
  async chmod(path, mode) {
9504
- const found = this.at(path);
9505
- if (!found) return this.base.chmod(path, mode);
9506
- this.writable(found.mount, normalizePath(path));
9597
+ const real = await this.follow(path);
9598
+ const found = this.at(real);
9599
+ if (!found) return this.base.chmod(real, mode);
9600
+ this.writable(found.mount, real);
9507
9601
  if (!found.mount.caps.chmod) return;
9508
- this.table.forget(found.mount, normalizePath(path));
9602
+ this.table.forget(found.mount, real);
9509
9603
  await found.mount.conn.call({ op: "setattr", path: found.rel, mode });
9510
9604
  }
9511
9605
  async utimes(path, atime, mtime) {
@@ -9975,7 +10069,7 @@ async function dispatchOne(entry, req) {
9975
10069
  }
9976
10070
 
9977
10071
  // src/realm/sync-sab-wire.ts
9978
- var SAB_HEADER_I32 = 16;
10072
+ var SAB_HEADER_I32 = 18;
9979
10073
  var SAB_CLOCK_OFFSET = SAB_HEADER_I32 * 4;
9980
10074
  var SAB_CLOCK_I64 = 3;
9981
10075
  var SAB_HEADER_BYTES = SAB_CLOCK_OFFSET + SAB_CLOCK_I64 * 8;
@@ -9995,6 +10089,7 @@ var SAB_I_OFFSET_HI = 12;
9995
10089
  var SAB_I_KILLED = 13;
9996
10090
  var SAB_I_ASYNC = 14;
9997
10091
  var SAB_I_STOP = 15;
10092
+ var SAB_I_BLOCKED = 16;
9998
10093
  var U32 = 2 ** 32;
9999
10094
  function storeU53(header3, lo, hi, value) {
10000
10095
  Atomics.store(header3, hi, Math.floor(value / U32));
@@ -10520,12 +10615,32 @@ function mountTable(mounts = []) {
10520
10615
  `;
10521
10616
  }
10522
10617
 
10618
+ // src/version.ts
10619
+ var KERNEL_VERSION = true ? "1.88.1" : "0.0.0-dev";
10620
+ var OS_RELEASE = "6.0.0-slicc";
10621
+ function procVersion() {
10622
+ return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
10623
+ `;
10624
+ }
10625
+
10523
10626
  // src/process/procfs.ts
10524
10627
  var HZ = 100;
10525
10628
  var KIB = 1024;
10526
10629
  var FALLBACK_MEMORY = 4 * KIB * KIB * KIB;
10527
10630
  var PID_FILES = ["cmdline", "comm", "stat", "statm", "status"];
10528
- var SYSTEM_FILES = ["loadavg", "meminfo", "stat", "uptime"];
10631
+ var SYSTEM_FILES = ["loadavg", "meminfo", "stat", "uptime", "version"];
10632
+ var SYS_TREE = {
10633
+ kernel: { osrelease: `${OS_RELEASE}
10634
+ `, ostype: "Linux\n" }
10635
+ };
10636
+ function sysEntry(parts2) {
10637
+ let at = SYS_TREE;
10638
+ for (const part of parts2) {
10639
+ if (typeof at !== "object" || !Object.hasOwn(at, part)) return void 0;
10640
+ at = at[part];
10641
+ }
10642
+ return at;
10643
+ }
10529
10644
  function commOf(info) {
10530
10645
  const name2 = info.argv[0] ?? "";
10531
10646
  return name2.slice(name2.lastIndexOf("/") + 1).slice(0, 15);
@@ -10613,7 +10728,7 @@ function pidStatus(info) {
10613
10728
  "SigQ: 0/0",
10614
10729
  "SigPnd: 0000000000000000",
10615
10730
  "ShdPnd: 0000000000000000",
10616
- "SigBlk: 0000000000000000",
10731
+ `SigBlk: ${sigMask(info.sigBlk)}`,
10617
10732
  `SigIgn: ${sigMask(info.sigIgn)}`,
10618
10733
  `SigCgt: ${sigMask(info.sigCgt)}`,
10619
10734
  ""
@@ -10649,6 +10764,7 @@ function memoryInfo(source, used = 0) {
10649
10764
  ].map(([key, value]) => `${`${key}:`.padEnd(16)}${value.padStart(12)}`).concat("").join("\n");
10650
10765
  }
10651
10766
  function systemFile(name2, listing, now, memory) {
10767
+ if (name2 === "version") return procVersion();
10652
10768
  const up = Math.max(0, now - listing.boot) / 1e3;
10653
10769
  if (name2 === "uptime") return `${up.toFixed(2)} ${up.toFixed(2)}
10654
10770
  `;
@@ -10739,11 +10855,17 @@ var ProcTree = class {
10739
10855
  names: () => [
10740
10856
  ...SYSTEM_NAMES,
10741
10857
  "mounts",
10858
+ "sys",
10742
10859
  "self",
10743
10860
  ...this.listing().processes.map((p) => String(p.pid))
10744
10861
  ]
10745
10862
  };
10746
10863
  }
10864
+ if (top === "sys") {
10865
+ const entry = sysEntry(parts2.slice(1));
10866
+ if (typeof entry === "string") return { kind: "file", content: () => entry };
10867
+ return entry && { kind: "dir", names: () => Object.keys(entry) };
10868
+ }
10747
10869
  if (top === "self") {
10748
10870
  return leaf === void 0 ? { kind: "link", target: String(this.pid) } : void 0;
10749
10871
  }
@@ -10821,7 +10943,7 @@ var ProcTree = class {
10821
10943
  }
10822
10944
  }
10823
10945
  };
10824
- var MAX_HOPS2 = 40;
10946
+ var MAX_HOPS3 = 40;
10825
10947
  function* procWalk(path, follow2) {
10826
10948
  let done4 = [];
10827
10949
  let todo = path.split("/").filter((part) => part !== "");
@@ -10840,7 +10962,7 @@ function* procWalk(path, follow2) {
10840
10962
  if (!follow2 && todo.length === 0) return void 0;
10841
10963
  const target = yield { at, check: todo.find((next2) => next2 !== ".") === ".." };
10842
10964
  if (typeof target === "string") {
10843
- if (++hops > MAX_HOPS2) return void 0;
10965
+ if (++hops > MAX_HOPS3) return void 0;
10844
10966
  [done4, todo] = throughLink(target, done4, todo);
10845
10967
  } else {
10846
10968
  broken ??= refusalOf(target);
@@ -12061,16 +12183,16 @@ function interruption(signal) {
12061
12183
  }
12062
12184
  async function selectFds(fds, read, write, timeoutMs, interrupt) {
12063
12185
  const deadline = timeoutMs < 0 ? Number.POSITIVE_INFINITY : Date.now() + timeoutMs;
12064
- const interrupted2 = interruption(interrupt);
12065
- interrupted2.promise.catch(() => {
12186
+ const interrupted3 = interruption(interrupt);
12187
+ interrupted3.promise.catch(() => {
12066
12188
  });
12067
12189
  try {
12068
- return await waitReady(fds, read, write, deadline, interrupted2.promise);
12190
+ return await waitReady(fds, read, write, deadline, interrupted3.promise);
12069
12191
  } finally {
12070
- interrupted2.done();
12192
+ interrupted3.done();
12071
12193
  }
12072
12194
  }
12073
- async function waitReady(fds, read, write, deadline, interrupted2) {
12195
+ async function waitReady(fds, read, write, deadline, interrupted3) {
12074
12196
  for (; ; ) {
12075
12197
  const now = ready(fds, read, write);
12076
12198
  if (now.read.length > 0 || now.write.length > 0 || Date.now() >= deadline) return now;
@@ -12082,7 +12204,7 @@ async function waitReady(fds, read, write, deadline, interrupted2) {
12082
12204
  let timer;
12083
12205
  const expiry = deadline === Number.POSITIVE_INFINITY ? [] : [new Promise((resolve2) => timer = setTimeout(resolve2, deadline - Date.now()))];
12084
12206
  try {
12085
- await Promise.race([...changes, ...expiry, interrupted2]);
12207
+ await Promise.race([...changes, ...expiry, interrupted3]);
12086
12208
  } finally {
12087
12209
  round.abort();
12088
12210
  clearTimeout(timer);
@@ -12399,11 +12521,12 @@ var VfsNodes = class {
12399
12521
  byId = /* @__PURE__ */ new Map();
12400
12522
  keepsOnDisk = false;
12401
12523
  fs;
12402
- entries = /* @__PURE__ */ new Map();
12524
+ entries;
12403
12525
  onClosed;
12404
- constructor(fs, onClosed) {
12526
+ constructor(fs, onClosed, entries = /* @__PURE__ */ new Map()) {
12405
12527
  this.fs = fs;
12406
12528
  this.onClosed = onClosed;
12529
+ this.entries = entries;
12407
12530
  }
12408
12531
  async onEntry(path, op) {
12409
12532
  const key = await this.entryKey(path);
@@ -12893,6 +13016,7 @@ var HOSTS_ONLY = new Resolver({ routes: new Routes() });
12893
13016
  var WasmProcess = class {
12894
13017
  exited = false;
12895
13018
  children;
13019
+ deathSignals = /* @__PURE__ */ new Map();
12896
13020
  caught = 0;
12897
13021
  ignored = 0;
12898
13022
  inherited = 0;
@@ -12927,7 +13051,10 @@ var WasmProcess = class {
12927
13051
  this.nodes = options.nodes ?? new VfsNodes(options.fs);
12928
13052
  if (!options.nodes) options.openFiles?.add(this.nodes);
12929
13053
  this.children.onChildState = () => this.signal(SIG.CHLD);
12930
- this.children.onReap = options.onReap;
13054
+ this.children.onReap = (pid2) => {
13055
+ this.deathSignals.delete(pid2);
13056
+ options.onReap?.(pid2);
13057
+ };
12931
13058
  }
12932
13059
  ignores(bit) {
12933
13060
  return (this.signalMasks().ignored & bit) !== 0;
@@ -12955,6 +13082,10 @@ var WasmProcess = class {
12955
13082
  if (sig === SIG.STOP) return this.stop(sig);
12956
13083
  const bit = sigbit(sig);
12957
13084
  if (this.ignores(bit)) return "ignore";
13085
+ if (((this.options.blockedBits?.() ?? 0) & bit) !== 0) {
13086
+ this.options.onPending?.(sig);
13087
+ return "deliver";
13088
+ }
12958
13089
  if (!(this.caught & bit)) {
12959
13090
  const action = defaultAction(sig);
12960
13091
  return action === "stop" ? this.stop(sig) : action;
@@ -12971,16 +13102,27 @@ var WasmProcess = class {
12971
13102
  return "deliver";
12972
13103
  }
12973
13104
  async spawn(req) {
12974
- const { file, argv, argv0, env, cwd, stdio, inherit: inherit2, exec, pgid, session } = req;
13105
+ const { file, argv, argv0, env, cwd, stdio, inherit: inherit2, exec, pgid, session, deathSignal } = req;
13106
+ if (deathSignal !== void 0 && (exec || !isSignal(deathSignal))) {
13107
+ throw new KernelError("EINVAL");
13108
+ }
12975
13109
  const group = { ...pgid !== void 0 ? { pgid } : {}, ...session ? { session } : {} };
12976
13110
  const named = argv0 !== void 0 ? { argv0 } : {};
12977
13111
  const spawned = { file, argv, ...named, env, cwd, ...group, ...exec ? { exec } : {} };
12978
13112
  const pid = await this.children.spawn(spawned, stdio, inherit2);
13113
+ if (deathSignal !== void 0) this.onDeath(pgid === 0 || session ? -pid : pid, deathSignal);
12979
13114
  if (!exec) return pid;
12980
13115
  if (this.killed) void Promise.allSettled([this.options.kill?.(pid, SIG.KILL)]);
12981
13116
  else this.adoptExec(pid);
12982
13117
  return pid;
12983
13118
  }
13119
+ onDeath(target, sig) {
13120
+ if (this.exited) this.sendDeath(target, sig);
13121
+ else this.deathSignals.set(target, sig);
13122
+ }
13123
+ sendDeath(target, sig) {
13124
+ void Promise.resolve(this.options.kill?.(target, sig)).catch(() => void 0);
13125
+ }
12984
13126
  async killSelf(sig, target, probe) {
12985
13127
  if (!this.options.exitSignal) throw new KernelError("ENOSYS");
12986
13128
  if (!isSignal(sig) || defaultAction(sig) !== "terminate") throw new KernelError("EINVAL");
@@ -13879,6 +14021,8 @@ var WasmProcess = class {
13879
14021
  this.interrupt.abort();
13880
14022
  this.options.locks?.release(this.pid);
13881
14023
  this.asyncOps.close();
14024
+ for (const [target, sig] of this.deathSignals) this.sendDeath(target, sig);
14025
+ this.deathSignals.clear();
13882
14026
  for (const pid of this.children.pids()) this.options.onReap?.(pid);
13883
14027
  await this.stampClipboards();
13884
14028
  await this.fds.closeAll();
@@ -13958,19 +14102,25 @@ var Dying = class {
13958
14102
  }
13959
14103
  };
13960
14104
  function killThread(thread, caught, sig) {
13961
- if (!thread || (caught & sigbit(sig)) === 0) return false;
14105
+ if (!thread) return false;
14106
+ const held = (Atomics.load(thread.header, SAB_I_BLOCKED) & sigbit(sig)) !== 0;
14107
+ if (!held && (caught & sigbit(sig)) === 0) return false;
13962
14108
  Atomics.or(thread.header, SAB_I_SIGNALS, sigbit(sig));
14109
+ if (held) return true;
13963
14110
  Atomics.notify(thread.header, SAB_I_SIGNALS);
13964
14111
  const blocked = thread.interrupt;
13965
14112
  thread.interrupt = new AbortController();
13966
14113
  blocked.abort();
13967
14114
  return true;
13968
14115
  }
14116
+ function unblocked(header3) {
14117
+ return Atomics.load(header3, SAB_I_SIGNALS) & ~Atomics.load(header3, SAB_I_BLOCKED);
14118
+ }
13969
14119
  function threadSignalsOf(sab) {
13970
14120
  return { header: new Int32Array(sab, 0, SAB_HEADER_I32), interrupt: new AbortController() };
13971
14121
  }
13972
14122
  function threadCancel(thread) {
13973
- return Atomics.load(thread.header, SAB_I_SIGNALS) !== 0 ? AbortSignal.abort() : thread.interrupt.signal;
14123
+ return unblocked(thread.header) !== 0 ? AbortSignal.abort() : thread.interrupt.signal;
13974
14124
  }
13975
14125
  function notifiers(header3) {
13976
14126
  return {
@@ -14095,15 +14245,16 @@ function processOptions(opts, header3, hooks, created3) {
14095
14245
  resolver: opts.resolver,
14096
14246
  ...notifiers(header3),
14097
14247
  onTimer: (which) => void Atomics.or(header3, SAB_I_TIMERS, 1 << which),
14098
- hasPending: () => Atomics.load(header3, SAB_I_SIGNALS) !== 0 || Atomics.load(header3, SAB_I_TIMERS) !== 0,
14248
+ hasPending: () => unblocked(header3) !== 0 || Atomics.load(header3, SAB_I_TIMERS) !== 0,
14099
14249
  raise: hooks.raise,
14100
14250
  threadKill: hooks.threadKill,
14101
14251
  ...hooks.exitNow ? { exitNow: hooks.exitNow } : {},
14102
14252
  ...hooks.exitSignal ? { exitSignal: hooks.exitSignal } : {},
14103
- ...opts.program.abi === "wasi" || opts.program.abi === "js" ? { pendingBits: () => Atomics.load(header3, SAB_I_SIGNALS) } : {}
14253
+ ...opts.program.abi === "wasi" || opts.program.abi === "js" ? { pendingBits: () => Atomics.load(header3, SAB_I_SIGNALS) } : {},
14254
+ ...opts.program.abi === "wasi" ? { blockedBits: () => Atomics.load(header3, SAB_I_BLOCKED) } : {}
14104
14255
  };
14105
14256
  }
14106
- function threadInit(opts, program, sab, cred, thread) {
14257
+ function threadInit(opts, program, sab, cred, thread, main) {
14107
14258
  return {
14108
14259
  type: WASM_THREAD_INIT,
14109
14260
  pid: opts.shownPid ?? opts.pid,
@@ -14116,12 +14267,15 @@ function threadInit(opts, program, sab, cred, thread) {
14116
14267
  cred: copyCred(cred),
14117
14268
  owners: opts.owners === true,
14118
14269
  ...opts.ppid !== void 0 ? { ppid: opts.ppid } : {},
14119
- thread
14270
+ thread,
14271
+ signals: main
14120
14272
  };
14121
14273
  }
14122
14274
  function spawnWasmProcess(opts) {
14123
14275
  const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
14124
14276
  const header3 = new Int32Array(sab, 0, SAB_HEADER_I32);
14277
+ if (opts.blocked && opts.program.abi === "wasi")
14278
+ Atomics.store(header3, SAB_I_BLOCKED, opts.blocked);
14125
14279
  const threadSignals = /* @__PURE__ */ new Map();
14126
14280
  const created3 = new Created();
14127
14281
  const process = new WasmProcess(
@@ -14222,6 +14376,7 @@ function spawnWasmProcess(opts) {
14222
14376
  const tsab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
14223
14377
  const tw = opts.createWorker();
14224
14378
  const tsignals = threadSignalsOf(tsab);
14379
+ if (thread.blocked) Atomics.store(tsignals.header, SAB_I_BLOCKED, thread.blocked);
14225
14380
  threadSignals.set(thread.tid, tsignals);
14226
14381
  const tdispatch = (req) => dying.serving(
14227
14382
  () => isWasmSyscall(req) ? process.syscall(req, threadCancel(tsignals)) : dispatchSyncFs(req)
@@ -14243,7 +14398,7 @@ function spawnWasmProcess(opts) {
14243
14398
  tresponder.dispose();
14244
14399
  tw.terminate();
14245
14400
  });
14246
- tw.postMessage(threadInit(opts, program, tsab, process.cred, thread));
14401
+ tw.postMessage(threadInit(opts, program, tsab, process.cred, thread, sab));
14247
14402
  };
14248
14403
  worker.postMessage(
14249
14404
  processInit(opts, sab, process.umask, copyCred(process.cred), lane?.lane),
@@ -14265,6 +14420,7 @@ function spawnWasmProcess(opts) {
14265
14420
  onState: (listener) => process.onState(listener),
14266
14421
  memory: () => Atomics.load(header3, SAB_I_MEMORY) * 65536,
14267
14422
  ignoredSignals: (fork) => process.inheritable(fork),
14423
+ blockedSignals: () => opts.program.abi === "wasi" ? Atomics.load(header3, SAB_I_BLOCKED) : 0,
14268
14424
  signalMasks: () => process.signalMasks(),
14269
14425
  umask: () => process.umask,
14270
14426
  cred: () => copyCred(process.cred),
@@ -14977,63 +15133,137 @@ function compile(secrets, options = {}) {
14977
15133
 
14978
15134
  // src/kernel/scrub-file.ts
14979
15135
  var SCRUB_IDLE_MS = 50;
15136
+ var SCRUB_NONBLOCK_MAX = 64 * 1024;
14980
15137
  var encoder5 = new TextEncoder();
14981
15138
  function scrubberOf(secrets) {
14982
15139
  const usable = secrets.filter((s) => encoder5.encode(s.value).length >= MIN_VALUE);
14983
15140
  return usable.length > 0 ? compile(usable, { idleMs: SCRUB_IDLE_MS }) : void 0;
14984
15141
  }
14985
- async function writeAll2(file, bytes, signal) {
15142
+ async function writeAll2(file, bytes) {
14986
15143
  const write = file.write;
14987
- for (let at = 0; at < bytes.length; ) {
14988
- at += await write.call(file, bytes.subarray(at), at === 0 ? signal : void 0);
14989
- }
15144
+ for (let at = 0; at < bytes.length; ) at += await write.call(file, bytes.subarray(at));
14990
15145
  }
14991
15146
  function bound(file, key) {
14992
15147
  const method = file[key];
14993
15148
  return typeof method === "function" ? { [key]: method.bind(file) } : {};
14994
15149
  }
15150
+ function interrupted2(err) {
15151
+ const code = err?.code;
15152
+ return code === "EINTR" || code === "EAGAIN";
15153
+ }
14995
15154
  function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
14996
15155
  const file = inner.file;
14997
15156
  const stream = scrubber.stream();
14998
- let queue = Promise.resolve();
15157
+ let tail = Promise.resolve();
15158
+ let pending = 0;
15159
+ let busy = false;
14999
15160
  let timer;
15000
15161
  let failed3;
15162
+ const waiters = /* @__PURE__ */ new Set();
15163
+ const quiet = () => !busy && pending === 0;
15164
+ const wake = () => {
15165
+ for (const waiter of [...waiters]) waiter();
15166
+ };
15001
15167
  const raise = () => {
15002
15168
  if (failed3 === void 0) return;
15003
15169
  const err = failed3;
15004
15170
  failed3 = void 0;
15005
15171
  throw err;
15006
15172
  };
15007
- const enqueue = (bytes, signal) => {
15008
- const next2 = queue.then(() => bytes.length > 0 ? writeAll2(file, bytes, signal) : void 0);
15009
- queue = next2.catch(() => void 0);
15010
- return next2;
15173
+ const later = (bytes) => {
15174
+ if (bytes.length === 0) return;
15175
+ pending += 1;
15176
+ tail = tail.then(() => writeAll2(file, bytes)).catch((err) => {
15177
+ if (failed3 === void 0) failed3 = err;
15178
+ }).finally(() => {
15179
+ pending -= 1;
15180
+ if (quiet()) wake();
15181
+ });
15182
+ };
15183
+ const settling = (signal) => new Promise((resolve2, reject) => {
15184
+ const done4 = () => {
15185
+ waiters.delete(done4);
15186
+ signal?.removeEventListener("abort", stop);
15187
+ resolve2();
15188
+ };
15189
+ const stop = () => {
15190
+ waiters.delete(done4);
15191
+ reject(new KernelError("EINTR"));
15192
+ };
15193
+ if (signal?.aborted) return stop();
15194
+ waiters.add(done4);
15195
+ signal?.addEventListener("abort", stop, { once: true });
15196
+ });
15197
+ const settled = async () => {
15198
+ while (!quiet()) await settling();
15199
+ };
15200
+ const changed = async (signal) => {
15201
+ const round = new AbortController();
15202
+ const stop = signal ? AbortSignal.any([signal, round.signal]) : round.signal;
15203
+ try {
15204
+ const own = settling(stop);
15205
+ await Promise.race(file.changed ? [own, file.changed(stop)] : [own]);
15206
+ } catch (err) {
15207
+ throw signal?.aborted ? new KernelError("EINTR") : err;
15208
+ } finally {
15209
+ round.abort();
15210
+ }
15211
+ };
15212
+ const turn = async (signal) => {
15213
+ for (let waited = false; ; waited = true) {
15214
+ if (quiet() && (pollFile(file).writable || !file.changed)) {
15215
+ busy = true;
15216
+ return;
15217
+ }
15218
+ if (signal?.aborted) throw new KernelError(waited ? "EINTR" : "EAGAIN");
15219
+ await changed(signal);
15220
+ }
15221
+ };
15222
+ const send = async (out, signal) => {
15223
+ const write = file.write;
15224
+ for (let at = 0; at < out.length; ) {
15225
+ try {
15226
+ at += await write.call(file, out.subarray(at), signal);
15227
+ } catch (err) {
15228
+ if (!signal?.aborted || !interrupted2(err)) throw err;
15229
+ }
15230
+ if (at < out.length && signal?.aborted) return later(out.subarray(at));
15231
+ }
15011
15232
  };
15012
15233
  const release = () => {
15013
15234
  timer = void 0;
15014
- enqueue(stream.idle()).catch((err) => {
15015
- failed3 = err;
15016
- });
15235
+ later(stream.idle());
15017
15236
  };
15018
15237
  return new OpenFile({
15019
15238
  ...bound(file, "read"),
15020
15239
  ...bound(file, "peek"),
15021
- ...bound(file, "poll"),
15022
- ...bound(file, "changed"),
15023
15240
  ...bound(file, "stat"),
15241
+ poll: () => {
15242
+ const state = pollFile(file);
15243
+ return quiet() ? state : { ...state, writable: false };
15244
+ },
15245
+ changed,
15024
15246
  ...file.tty ? { tty: file.tty } : {},
15025
15247
  ...file.clipboard ? { clipboard: file.clipboard } : {},
15026
15248
  ...file.write ? {
15027
15249
  write: async (bytes, signal) => {
15028
- if (timer !== void 0) clearTimeout(timer);
15029
- raise();
15030
- await enqueue(stream.push(bytes), signal);
15250
+ await turn(signal);
15251
+ const taken = signal?.aborted && bytes.length > SCRUB_NONBLOCK_MAX ? bytes.subarray(0, SCRUB_NONBLOCK_MAX) : bytes;
15252
+ try {
15253
+ raise();
15254
+ if (timer !== void 0) clearTimeout(timer);
15255
+ timer = void 0;
15256
+ await send(stream.push(taken), signal);
15257
+ } finally {
15258
+ busy = false;
15259
+ if (quiet()) wake();
15260
+ }
15031
15261
  timer = setTimeout(release, idleMs);
15032
- return bytes.length;
15262
+ return taken.length;
15033
15263
  }
15034
15264
  } : {},
15035
15265
  flush: async () => {
15036
- await queue;
15266
+ await settled();
15037
15267
  raise();
15038
15268
  await file.flush?.();
15039
15269
  },
@@ -15041,7 +15271,9 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
15041
15271
  if (timer !== void 0) clearTimeout(timer);
15042
15272
  let failure2;
15043
15273
  try {
15044
- await enqueue(stream.end());
15274
+ await settled();
15275
+ later(stream.end());
15276
+ await settled();
15045
15277
  raise();
15046
15278
  } catch (err) {
15047
15279
  failure2 = { err };
@@ -15217,12 +15449,12 @@ var SealedChannel = class {
15217
15449
  if (signal.aborted) throw new KernelError("EINTR");
15218
15450
  let stop = () => {
15219
15451
  };
15220
- const interrupted2 = new Promise((_, reject) => {
15452
+ const interrupted3 = new Promise((_, reject) => {
15221
15453
  stop = () => reject(new KernelError("EINTR"));
15222
15454
  signal.addEventListener("abort", stop, { once: true });
15223
15455
  });
15224
15456
  try {
15225
- await Promise.race([flight.done, interrupted2]);
15457
+ await Promise.race([flight.done, interrupted3]);
15226
15458
  } finally {
15227
15459
  signal.removeEventListener("abort", stop);
15228
15460
  }
@@ -17387,15 +17619,25 @@ function shebang(head) {
17387
17619
  if (head[0] !== 35 || head[1] !== 33) return void 0;
17388
17620
  return new TextDecoder().decode(head.subarray(0, SHEBANG_MAX)).slice(2).split("\n")[0].trim().split(/[ \t]+/);
17389
17621
  }
17390
- async function rootOnly(fs, path) {
17622
+ async function rootOnly(fs, path, dir = false) {
17391
17623
  const names = chain(path);
17392
17624
  for (const [i, at] of names.entries()) {
17393
17625
  const st = await fs.lstat(at).catch(() => void 0);
17394
- const kind = i === names.length - 1 ? st?.isFile : st?.isDirectory;
17626
+ const kind = i === names.length - 1 && !dir ? st?.isFile : st?.isDirectory;
17395
17627
  if (!kind || st?.uid !== 0 || (st.mode & 18) !== 0) return false;
17396
17628
  }
17397
17629
  return true;
17398
17630
  }
17631
+ function appendedRoots(given) {
17632
+ const roots = given?.append ?? [];
17633
+ if (!Array.isArray(roots)) throw new TypeError("commands.append must be an array of paths");
17634
+ for (const root of roots) {
17635
+ if (typeof root !== "string" || !root.startsWith("/") || normalizePath(root) !== root) {
17636
+ throw new TypeError(`commands.append: not an absolute, normalised path: ${String(root)}`);
17637
+ }
17638
+ }
17639
+ return [...roots];
17640
+ }
17399
17641
  function plainEnv(words2) {
17400
17642
  const next2 = words2[1];
17401
17643
  if (baseName(words2[0] ?? "") !== "env" || next2 === void 0) return false;
@@ -17488,6 +17730,8 @@ var Launcher = class {
17488
17730
  base;
17489
17731
  createWorker;
17490
17732
  modulesDir;
17733
+ appended;
17734
+ reportedRoots = /* @__PURE__ */ new Set();
17491
17735
  env;
17492
17736
  catalogs = /* @__PURE__ */ new Map();
17493
17737
  ownCatalogs = /* @__PURE__ */ new Map();
@@ -17594,6 +17838,7 @@ var Launcher = class {
17594
17838
  this.processMounts = options.processMounts ?? true;
17595
17839
  this.pnpmHome = options.env?.PNPM_HOME ?? SYSTEM_PNPM_HOME;
17596
17840
  this.modulesDir = options.modules ?? "/node_modules";
17841
+ this.appended = appendedRoots(options.commands);
17597
17842
  this.cdp = new CdpHosts(options.cdp);
17598
17843
  const hostname = options.hostname ?? DEFAULT_HOSTNAME;
17599
17844
  if (!isHostname(hostname)) throw new Error(`not a host name: ${String(hostname)}`);
@@ -17643,9 +17888,9 @@ var Launcher = class {
17643
17888
  this.unguardedRoot = options.fs.unguarded?.() ?? options.fs;
17644
17889
  this.prefixes = protectedPrefixes(options.protect?.paths ?? []);
17645
17890
  this.processFs = this.prefixes.length > 0 ? new ProtectedFs(this.prefixes).view(this.fs) : this.fs;
17646
- options.fs.protect?.(this.prefixes);
17647
17891
  const integrity = options.protect?.integrity;
17648
17892
  integrityOptions(integrity);
17893
+ options.fs.protect?.(this.prefixes, Boolean(integrity));
17649
17894
  if (integrity && this.prefixes.length > 0) {
17650
17895
  this.integrity = new Integrity(
17651
17896
  this.prefixes,
@@ -17668,7 +17913,7 @@ var Launcher = class {
17668
17913
  });
17669
17914
  void this.unlinked.sweep("/");
17670
17915
  this.watchers.watch(
17671
- [this.modulesDir, this.pnpmHome],
17916
+ [this.modulesDir, this.pnpmHome, ...this.appended],
17672
17917
  { recursive: true },
17673
17918
  () => this.resetCatalogs()
17674
17919
  );
@@ -17699,7 +17944,10 @@ var Launcher = class {
17699
17944
  commands(scope = "") {
17700
17945
  let found = this.catalogs.get(scope);
17701
17946
  if (!found) {
17702
- found = this.roots(scope).then((roots) => scanCommands(this.scanned, roots)).then((commands) => commands.set(SUDO_COMMAND.name, SUDO_COMMAND));
17947
+ found = this.trustedAppended().then(async (appended) => {
17948
+ const roots = await this.roots(scope, appended);
17949
+ return scanCommands(this.scanned, roots, roots.length - appended.length);
17950
+ }).then((commands) => commands.set(SUDO_COMMAND.name, SUDO_COMMAND));
17703
17951
  this.catalogs.set(scope, found);
17704
17952
  }
17705
17953
  return found;
@@ -17726,10 +17974,37 @@ var Launcher = class {
17726
17974
  }
17727
17975
  return found;
17728
17976
  }
17729
- async roots(scope = "") {
17730
- const system = [this.modulesDir, ...await pnpmGlobalRoots(this.base, this.pnpmHome)];
17977
+ async roots(scope = "", appended) {
17978
+ const system = [
17979
+ this.modulesDir,
17980
+ ...await pnpmGlobalRoots(this.base, this.pnpmHome),
17981
+ ...appended ?? await this.trustedAppended()
17982
+ ];
17731
17983
  return scope ? [...await pnpmGlobalRoots(this.base, scope), ...system] : system;
17732
17984
  }
17985
+ async rootOnlyPath(root) {
17986
+ for (const at of chain(root)) {
17987
+ const real = await physicalPathAsync(this.base, at);
17988
+ if (!await rootOnly(this.base, real, true)) return false;
17989
+ }
17990
+ return true;
17991
+ }
17992
+ async trustedAppended() {
17993
+ if (this.appended.length === 0 || this.usersMode === "off") return this.appended;
17994
+ const trusted = [];
17995
+ for (const root of this.appended) {
17996
+ const real = await physicalPathAsync(this.base, root);
17997
+ if (await this.rootOnlyPath(root)) trusted.push(root);
17998
+ else if (this.usersMode === "report" && await this.base.exists(real)) {
17999
+ if (!this.reportedRoots.has(root)) {
18000
+ this.reportedRoots.add(root);
18001
+ this.dac?.denied({ pid: 0, uid: 0, op: "commands", path: root, errno: "EACCES" });
18002
+ }
18003
+ trusted.push(root);
18004
+ }
18005
+ }
18006
+ return trusted;
18007
+ }
17733
18008
  resetCatalogs() {
17734
18009
  this.catalogs.clear();
17735
18010
  this.ownCatalogs.clear();
@@ -17835,14 +18110,22 @@ var Launcher = class {
17835
18110
  const inner = await this.interpreted(interp, [interp, ...passed], cwd, scope, depth + 1);
17836
18111
  const via = [interp, ...inner?.via ?? []];
17837
18112
  const sources = [script, ...inner?.sources ?? []];
17838
- return inner && { ...inner, target: withScriptEnv(inner.target, command), via, sources };
18113
+ const bins = [...command?.bin ? [script] : [], ...inner?.bins ?? []];
18114
+ return inner && {
18115
+ ...inner,
18116
+ target: withScriptEnv(inner.target, command),
18117
+ via,
18118
+ sources,
18119
+ ...bins.length > 0 ? { bins } : {}
18120
+ };
17839
18121
  }
17840
18122
  const target = withScriptEnv(found, command);
17841
18123
  return {
17842
18124
  target,
17843
18125
  args: [...target.prefix ?? [], ...passed],
17844
18126
  via: [interp],
17845
- sources: [script]
18127
+ sources: [script],
18128
+ ...command?.bin ? { bins: [script] } : {}
17846
18129
  };
17847
18130
  }
17848
18131
  async plan(file, argv, cwd, scope, argv0) {
@@ -17868,6 +18151,7 @@ var Launcher = class {
17868
18151
  const want = i < via.length - 1 ? R_OK | X_OK : X_OK;
17869
18152
  await need(g, "exec", this.fs.resolvePath(req.cwd, file), want);
17870
18153
  }
18154
+ for (const bin of planned?.bins ?? []) await need(g, "exec", bin, R_OK | X_OK);
17871
18155
  }
17872
18156
  async checkedPlan(req, { ppid, cred, scope }, fds) {
17873
18157
  const planned = await this.plan(req.file, req.argv, req.cwd, scope, req.argv0);
@@ -18133,11 +18417,9 @@ var Launcher = class {
18133
18417
  groupSession(pgid) {
18134
18418
  return this.jobs.sessionOf(pgid);
18135
18419
  }
18136
- treeScrubs(pid, req) {
18420
+ treeScrubs(req) {
18137
18421
  const inherited2 = req.ppid !== void 0 ? this.scrubs.get(req.ppid) : void 0;
18138
- const scrubs = req.scrub ? [...inherited2 ?? [], req.scrub] : inherited2;
18139
- if (scrubs) this.scrubs.set(pid, scrubs);
18140
- return scrubs;
18422
+ return req.scrub ? [...inherited2 ?? [], req.scrub] : inherited2;
18141
18423
  }
18142
18424
  start(req) {
18143
18425
  this.sampling();
@@ -18145,7 +18427,7 @@ var Launcher = class {
18145
18427
  throw new SpawnError("ESRCH");
18146
18428
  }
18147
18429
  const pid = req.pid ?? this.nextPid++;
18148
- const scrubs = this.treeScrubs(pid, req);
18430
+ const scrubs = this.treeScrubs(req);
18149
18431
  const stack = this.stackFor(req.tag);
18150
18432
  const terminal = req.fds.stdioTerminal();
18151
18433
  const decided = req.decided ?? Promise.resolve(true);
@@ -18166,6 +18448,7 @@ var Launcher = class {
18166
18448
  },
18167
18449
  settling: () => this.settling.isSettling(pid),
18168
18450
  ...req.ignored ? { ignored: req.ignored } : {},
18451
+ ...req.blocked ? { blocked: req.blocked } : {},
18169
18452
  ...req.umask !== void 0 ? { umask: req.umask } : {},
18170
18453
  cred: req.cred,
18171
18454
  ...this.owners && req.program.abi === "wasi" ? { fresh: (path, umask) => this.freshModules.add(path, umask, pid) } : {},
@@ -18214,6 +18497,7 @@ var Launcher = class {
18214
18497
  ...this.shownIds(req)
18215
18498
  });
18216
18499
  this.processes.set(pid, handle);
18500
+ if (scrubs) this.scrubs.set(pid, scrubs);
18217
18501
  const { cred } = req;
18218
18502
  this.creds.set(pid, () => handle.cred?.() ?? copyCred(cred));
18219
18503
  if (req.tag !== void 0) this.tags.set(pid, req.tag);
@@ -18440,6 +18724,7 @@ var Launcher = class {
18440
18724
  report,
18441
18725
  ppid,
18442
18726
  ignored: this.ignoredBy(ppid),
18727
+ blocked: this.blockedBy(ppid),
18443
18728
  umask: this.umaskOf(ppid),
18444
18729
  cred,
18445
18730
  tag: this.tagOf(ppid),
@@ -18501,6 +18786,7 @@ var Launcher = class {
18501
18786
  ppid,
18502
18787
  fork: state,
18503
18788
  ignored: this.ignoredBy(ppid, true),
18789
+ blocked: this.blockedBy(ppid),
18504
18790
  umask: this.umaskOf(ppid),
18505
18791
  cred: this.credOf(ppid),
18506
18792
  tag: this.tagOf(ppid)
@@ -18552,11 +18838,16 @@ var Launcher = class {
18552
18838
  }
18553
18839
  signalMasksOf(pid) {
18554
18840
  const masks = this.processes.get(pid)?.signalMasks?.();
18555
- return masks ? { sigIgn: masks.ignored, sigCgt: masks.caught } : {};
18841
+ const blocked = this.blockedBy(pid);
18842
+ const sigBlk = blocked ? { sigBlk: blocked } : {};
18843
+ return masks ? { sigIgn: masks.ignored, sigCgt: masks.caught, ...sigBlk } : sigBlk;
18556
18844
  }
18557
18845
  controlsJobs(pid) {
18558
18846
  return this.settling.isSettling(pid) || (this.ignoredBy(pid) & sigbit(SIG.TTOU)) !== 0;
18559
18847
  }
18848
+ blockedBy(pid) {
18849
+ return this.processes.get(pid)?.blockedSignals?.() ?? 0;
18850
+ }
18560
18851
  ignoredBy(pid, fork = false) {
18561
18852
  return this.processes.get(pid)?.ignoredSignals(fork) ?? 0;
18562
18853
  }
@@ -18921,7 +19212,7 @@ var Launcher = class {
18921
19212
  (dir) => this.userCommandDir(dir),
18922
19213
  this.owners
18923
19214
  );
18924
- const nodes = new VfsNodes(fs, (path) => this.released(path));
19215
+ const nodes = new VfsNodes(fs, (path) => this.released(path), this.nodes.entries);
18925
19216
  nodes.keepsOnDisk = true;
18926
19217
  this.openFiles.add(nodes);
18927
19218
  const unlinked = new UnlinkedKeeper(
@@ -19325,6 +19616,7 @@ function serveKernel(port, deps) {
19325
19616
  createWorker: deps.createWorker,
19326
19617
  ...deps.createDriverWorker ? { createDriverWorker: deps.createDriverWorker } : {},
19327
19618
  ...req.modules ? { modules: req.modules } : {},
19619
+ ...req.commands ? { commands: req.commands } : {},
19328
19620
  ...req.env ? { env: req.env } : {},
19329
19621
  ...hostOptions(req, port, sealed.hook, clipboard.hook, integrity.lookup),
19330
19622
  ...req.approve ? { approve: approvals.ask } : {},