@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.
@@ -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) {
@@ -2580,6 +2601,10 @@ var KernelError = class extends Error {
2580
2601
  if (detail !== void 0) this.detail = detail;
2581
2602
  }
2582
2603
  };
2604
+ function openAccess(flags2) {
2605
+ const mode = flags2 & 3;
2606
+ return mode === 0 ? "read" : mode === 1 ? "write" : "both";
2607
+ }
2583
2608
  function pollFile(file) {
2584
2609
  return file.poll?.() ?? { readable: !!file.read, writable: !!file.write, hangup: false };
2585
2610
  }
@@ -7127,8 +7152,12 @@ function closeAll(entry) {
7127
7152
 
7128
7153
  // src/commands.ts
7129
7154
  var NAME2 = /^[A-Za-z0-9._+-]+$/;
7155
+ var COMMAND_NAME = /^[^/\s\p{Cc}]+$/u;
7130
7156
  var ENV_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
7131
7157
  var PACKAGE = /\$\{package\}/g;
7158
+ function commandName(name2) {
7159
+ return COMMAND_NAME.test(name2) && name2.isWellFormed() && name2 !== "." && name2 !== "..";
7160
+ }
7132
7161
  function inside(pkg, rel2) {
7133
7162
  if (typeof rel2 !== "string" || rel2 === "") return void 0;
7134
7163
  const clean = rel2.replace(/^\.\//, "");
@@ -7218,7 +7247,7 @@ function commandsOf(pkg, manifest) {
7218
7247
  const shared = envOf(pkg, slicc.env);
7219
7248
  const out = [];
7220
7249
  for (const [name2, raw] of Object.entries(entries)) {
7221
- if (!NAME2.test(name2) || name2 === "." || name2 === ".." || !raw || typeof raw !== "object") {
7250
+ if (!commandName(name2) || !raw || typeof raw !== "object") {
7222
7251
  continue;
7223
7252
  }
7224
7253
  const command = commandOf(pkg, name2, raw, packageAbi, shared);
@@ -7270,18 +7299,86 @@ async function packagesIn(fs, roots) {
7270
7299
  dirs.push(...await packages(fs, root));
7271
7300
  return dirs;
7272
7301
  }
7273
- async function scanCommands(fs, modules) {
7274
- const found = /* @__PURE__ */ new Map();
7275
- for (const pkg of await packagesIn(fs, modules)) {
7276
- let manifest;
7277
- try {
7278
- manifest = JSON.parse(await fs.readFile(`${pkg}/package.json`));
7279
- } catch {
7280
- 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);
7281
7333
  }
7282
- for (const command of commandsOf(pkg, manifest)) {
7283
- if (!found.has(command.name))
7284
- 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
+ }
7285
7382
  }
7286
7383
  }
7287
7384
  if (!found.has("sh") && found.has("bash")) {
@@ -7740,7 +7837,7 @@ var FreshModules = class {
7740
7837
  };
7741
7838
 
7742
7839
  // src/fs/physical-path.ts
7743
- var MAX_HOPS = 40;
7840
+ var MAX_HOPS2 = 40;
7744
7841
  function start(path) {
7745
7842
  return { done: [], todo: path.split("/").filter((p) => p !== ""), hops: 0 };
7746
7843
  }
@@ -7765,7 +7862,7 @@ function rest(walk2, part) {
7765
7862
  return lexical(`/${[...walk2.done, part, ...walk2.todo].join("/")}`);
7766
7863
  }
7767
7864
  function follow(walk2, target) {
7768
- if (++walk2.hops > MAX_HOPS) return false;
7865
+ if (++walk2.hops > MAX_HOPS2) return false;
7769
7866
  if (target.startsWith("/")) walk2.done.length = 0;
7770
7867
  walk2.todo = [...target.split("/").filter((p) => p !== ""), ...walk2.todo];
7771
7868
  return true;
@@ -9971,7 +10068,7 @@ async function dispatchOne(entry, req) {
9971
10068
  }
9972
10069
 
9973
10070
  // src/realm/sync-sab-wire.ts
9974
- var SAB_HEADER_I32 = 16;
10071
+ var SAB_HEADER_I32 = 18;
9975
10072
  var SAB_CLOCK_OFFSET = SAB_HEADER_I32 * 4;
9976
10073
  var SAB_CLOCK_I64 = 3;
9977
10074
  var SAB_HEADER_BYTES = SAB_CLOCK_OFFSET + SAB_CLOCK_I64 * 8;
@@ -9991,6 +10088,7 @@ var SAB_I_OFFSET_HI = 12;
9991
10088
  var SAB_I_KILLED = 13;
9992
10089
  var SAB_I_ASYNC = 14;
9993
10090
  var SAB_I_STOP = 15;
10091
+ var SAB_I_BLOCKED = 16;
9994
10092
  var U32 = 2 ** 32;
9995
10093
  function storeU53(header3, lo, hi, value) {
9996
10094
  Atomics.store(header3, hi, Math.floor(value / U32));
@@ -10516,12 +10614,32 @@ function mountTable(mounts = []) {
10516
10614
  `;
10517
10615
  }
10518
10616
 
10617
+ // src/version.ts
10618
+ var KERNEL_VERSION = true ? "1.88.0" : "0.0.0-dev";
10619
+ var OS_RELEASE = "6.0.0-slicc";
10620
+ function procVersion() {
10621
+ return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
10622
+ `;
10623
+ }
10624
+
10519
10625
  // src/process/procfs.ts
10520
10626
  var HZ = 100;
10521
10627
  var KIB = 1024;
10522
10628
  var FALLBACK_MEMORY = 4 * KIB * KIB * KIB;
10523
10629
  var PID_FILES = ["cmdline", "comm", "stat", "statm", "status"];
10524
- var SYSTEM_FILES = ["loadavg", "meminfo", "stat", "uptime"];
10630
+ var SYSTEM_FILES = ["loadavg", "meminfo", "stat", "uptime", "version"];
10631
+ var SYS_TREE = {
10632
+ kernel: { osrelease: `${OS_RELEASE}
10633
+ `, ostype: "Linux\n" }
10634
+ };
10635
+ function sysEntry(parts2) {
10636
+ let at = SYS_TREE;
10637
+ for (const part of parts2) {
10638
+ if (typeof at !== "object" || !Object.hasOwn(at, part)) return void 0;
10639
+ at = at[part];
10640
+ }
10641
+ return at;
10642
+ }
10525
10643
  function commOf(info) {
10526
10644
  const name2 = info.argv[0] ?? "";
10527
10645
  return name2.slice(name2.lastIndexOf("/") + 1).slice(0, 15);
@@ -10609,7 +10727,7 @@ function pidStatus(info) {
10609
10727
  "SigQ: 0/0",
10610
10728
  "SigPnd: 0000000000000000",
10611
10729
  "ShdPnd: 0000000000000000",
10612
- "SigBlk: 0000000000000000",
10730
+ `SigBlk: ${sigMask(info.sigBlk)}`,
10613
10731
  `SigIgn: ${sigMask(info.sigIgn)}`,
10614
10732
  `SigCgt: ${sigMask(info.sigCgt)}`,
10615
10733
  ""
@@ -10645,6 +10763,7 @@ function memoryInfo(source, used = 0) {
10645
10763
  ].map(([key, value]) => `${`${key}:`.padEnd(16)}${value.padStart(12)}`).concat("").join("\n");
10646
10764
  }
10647
10765
  function systemFile(name2, listing, now, memory) {
10766
+ if (name2 === "version") return procVersion();
10648
10767
  const up = Math.max(0, now - listing.boot) / 1e3;
10649
10768
  if (name2 === "uptime") return `${up.toFixed(2)} ${up.toFixed(2)}
10650
10769
  `;
@@ -10735,11 +10854,17 @@ var ProcTree = class {
10735
10854
  names: () => [
10736
10855
  ...SYSTEM_NAMES,
10737
10856
  "mounts",
10857
+ "sys",
10738
10858
  "self",
10739
10859
  ...this.listing().processes.map((p) => String(p.pid))
10740
10860
  ]
10741
10861
  };
10742
10862
  }
10863
+ if (top === "sys") {
10864
+ const entry = sysEntry(parts2.slice(1));
10865
+ if (typeof entry === "string") return { kind: "file", content: () => entry };
10866
+ return entry && { kind: "dir", names: () => Object.keys(entry) };
10867
+ }
10743
10868
  if (top === "self") {
10744
10869
  return leaf === void 0 ? { kind: "link", target: String(this.pid) } : void 0;
10745
10870
  }
@@ -10817,7 +10942,7 @@ var ProcTree = class {
10817
10942
  }
10818
10943
  }
10819
10944
  };
10820
- var MAX_HOPS2 = 40;
10945
+ var MAX_HOPS3 = 40;
10821
10946
  function* procWalk(path, follow2) {
10822
10947
  let done4 = [];
10823
10948
  let todo = path.split("/").filter((part) => part !== "");
@@ -10836,7 +10961,7 @@ function* procWalk(path, follow2) {
10836
10961
  if (!follow2 && todo.length === 0) return void 0;
10837
10962
  const target = yield { at, check: todo.find((next2) => next2 !== ".") === ".." };
10838
10963
  if (typeof target === "string") {
10839
- if (++hops > MAX_HOPS2) return void 0;
10964
+ if (++hops > MAX_HOPS3) return void 0;
10840
10965
  [done4, todo] = throughLink(target, done4, todo);
10841
10966
  } else {
10842
10967
  broken ??= refusalOf(target);
@@ -12395,11 +12520,12 @@ var VfsNodes = class {
12395
12520
  byId = /* @__PURE__ */ new Map();
12396
12521
  keepsOnDisk = false;
12397
12522
  fs;
12398
- entries = /* @__PURE__ */ new Map();
12523
+ entries;
12399
12524
  onClosed;
12400
- constructor(fs, onClosed) {
12525
+ constructor(fs, onClosed, entries = /* @__PURE__ */ new Map()) {
12401
12526
  this.fs = fs;
12402
12527
  this.onClosed = onClosed;
12528
+ this.entries = entries;
12403
12529
  }
12404
12530
  async onEntry(path, op) {
12405
12531
  const key = await this.entryKey(path);
@@ -12889,6 +13015,7 @@ var HOSTS_ONLY = new Resolver({ routes: new Routes() });
12889
13015
  var WasmProcess = class {
12890
13016
  exited = false;
12891
13017
  children;
13018
+ deathSignals = /* @__PURE__ */ new Map();
12892
13019
  caught = 0;
12893
13020
  ignored = 0;
12894
13021
  inherited = 0;
@@ -12923,7 +13050,10 @@ var WasmProcess = class {
12923
13050
  this.nodes = options.nodes ?? new VfsNodes(options.fs);
12924
13051
  if (!options.nodes) options.openFiles?.add(this.nodes);
12925
13052
  this.children.onChildState = () => this.signal(SIG.CHLD);
12926
- this.children.onReap = options.onReap;
13053
+ this.children.onReap = (pid2) => {
13054
+ this.deathSignals.delete(pid2);
13055
+ options.onReap?.(pid2);
13056
+ };
12927
13057
  }
12928
13058
  ignores(bit) {
12929
13059
  return (this.signalMasks().ignored & bit) !== 0;
@@ -12951,6 +13081,10 @@ var WasmProcess = class {
12951
13081
  if (sig === SIG.STOP) return this.stop(sig);
12952
13082
  const bit = sigbit(sig);
12953
13083
  if (this.ignores(bit)) return "ignore";
13084
+ if (((this.options.blockedBits?.() ?? 0) & bit) !== 0) {
13085
+ this.options.onPending?.(sig);
13086
+ return "deliver";
13087
+ }
12954
13088
  if (!(this.caught & bit)) {
12955
13089
  const action = defaultAction(sig);
12956
13090
  return action === "stop" ? this.stop(sig) : action;
@@ -12967,16 +13101,27 @@ var WasmProcess = class {
12967
13101
  return "deliver";
12968
13102
  }
12969
13103
  async spawn(req) {
12970
- const { file, argv, argv0, env, cwd, stdio, inherit: inherit2, exec, pgid, session } = req;
13104
+ const { file, argv, argv0, env, cwd, stdio, inherit: inherit2, exec, pgid, session, deathSignal } = req;
13105
+ if (deathSignal !== void 0 && (exec || !isSignal(deathSignal))) {
13106
+ throw new KernelError("EINVAL");
13107
+ }
12971
13108
  const group = { ...pgid !== void 0 ? { pgid } : {}, ...session ? { session } : {} };
12972
13109
  const named = argv0 !== void 0 ? { argv0 } : {};
12973
13110
  const spawned = { file, argv, ...named, env, cwd, ...group, ...exec ? { exec } : {} };
12974
13111
  const pid = await this.children.spawn(spawned, stdio, inherit2);
13112
+ if (deathSignal !== void 0) this.onDeath(pgid === 0 || session ? -pid : pid, deathSignal);
12975
13113
  if (!exec) return pid;
12976
13114
  if (this.killed) void Promise.allSettled([this.options.kill?.(pid, SIG.KILL)]);
12977
13115
  else this.adoptExec(pid);
12978
13116
  return pid;
12979
13117
  }
13118
+ onDeath(target, sig) {
13119
+ if (this.exited) this.sendDeath(target, sig);
13120
+ else this.deathSignals.set(target, sig);
13121
+ }
13122
+ sendDeath(target, sig) {
13123
+ void Promise.resolve(this.options.kill?.(target, sig)).catch(() => void 0);
13124
+ }
12980
13125
  async killSelf(sig, target, probe) {
12981
13126
  if (!this.options.exitSignal) throw new KernelError("ENOSYS");
12982
13127
  if (!isSignal(sig) || defaultAction(sig) !== "terminate") throw new KernelError("EINVAL");
@@ -13255,17 +13400,15 @@ var WasmProcess = class {
13255
13400
  const file = tty.file();
13256
13401
  return this.fds.install(this.options.outputFilter?.(file) ?? file, 3);
13257
13402
  }
13258
- openSealed() {
13403
+ openSealed(flags2) {
13259
13404
  const broker = this.options.sealed;
13260
13405
  if (!broker) throw new KernelError("ENOENT");
13261
- return this.fds.install(broker.open(), 3);
13406
+ return this.fds.install(broker.open(openAccess(flags2)), 3);
13262
13407
  }
13263
13408
  async openClipboard(flags2) {
13264
13409
  const broker = this.options.clipboard;
13265
13410
  if (!broker) throw new KernelError("ENOENT");
13266
- const mode = flags2 & WRITE_ACCESS;
13267
- const access2 = mode === 0 ? "read" : mode === 1 ? "write" : "both";
13268
- const file = broker.open(access2, await this.clipboardStamp());
13411
+ const file = broker.open(openAccess(flags2), await this.clipboardStamp());
13269
13412
  return this.fds.install(this.options.outputFilter?.(file) ?? file, 3);
13270
13413
  }
13271
13414
  async clipboardStamp() {
@@ -13424,7 +13567,7 @@ var WasmProcess = class {
13424
13567
  case "fd-tty-names":
13425
13568
  return { ok: true, kind: "json", json: this.options.jobs?.terminalNames() ?? [] };
13426
13569
  case "fd-open-sealed":
13427
- return { ok: true, kind: "json", json: this.openSealed() };
13570
+ return { ok: true, kind: "json", json: this.openSealed(req.flags) };
13428
13571
  case "fd-open-clipboard":
13429
13572
  return { ok: true, kind: "json", json: await this.openClipboard(req.flags) };
13430
13573
  case "fd-tty-owner":
@@ -13877,6 +14020,8 @@ var WasmProcess = class {
13877
14020
  this.interrupt.abort();
13878
14021
  this.options.locks?.release(this.pid);
13879
14022
  this.asyncOps.close();
14023
+ for (const [target, sig] of this.deathSignals) this.sendDeath(target, sig);
14024
+ this.deathSignals.clear();
13880
14025
  for (const pid of this.children.pids()) this.options.onReap?.(pid);
13881
14026
  await this.stampClipboards();
13882
14027
  await this.fds.closeAll();
@@ -13956,19 +14101,25 @@ var Dying = class {
13956
14101
  }
13957
14102
  };
13958
14103
  function killThread(thread, caught, sig) {
13959
- if (!thread || (caught & sigbit(sig)) === 0) return false;
14104
+ if (!thread) return false;
14105
+ const held = (Atomics.load(thread.header, SAB_I_BLOCKED) & sigbit(sig)) !== 0;
14106
+ if (!held && (caught & sigbit(sig)) === 0) return false;
13960
14107
  Atomics.or(thread.header, SAB_I_SIGNALS, sigbit(sig));
14108
+ if (held) return true;
13961
14109
  Atomics.notify(thread.header, SAB_I_SIGNALS);
13962
14110
  const blocked = thread.interrupt;
13963
14111
  thread.interrupt = new AbortController();
13964
14112
  blocked.abort();
13965
14113
  return true;
13966
14114
  }
14115
+ function unblocked(header3) {
14116
+ return Atomics.load(header3, SAB_I_SIGNALS) & ~Atomics.load(header3, SAB_I_BLOCKED);
14117
+ }
13967
14118
  function threadSignalsOf(sab) {
13968
14119
  return { header: new Int32Array(sab, 0, SAB_HEADER_I32), interrupt: new AbortController() };
13969
14120
  }
13970
14121
  function threadCancel(thread) {
13971
- return Atomics.load(thread.header, SAB_I_SIGNALS) !== 0 ? AbortSignal.abort() : thread.interrupt.signal;
14122
+ return unblocked(thread.header) !== 0 ? AbortSignal.abort() : thread.interrupt.signal;
13972
14123
  }
13973
14124
  function notifiers(header3) {
13974
14125
  return {
@@ -14093,15 +14244,16 @@ function processOptions(opts, header3, hooks, created3) {
14093
14244
  resolver: opts.resolver,
14094
14245
  ...notifiers(header3),
14095
14246
  onTimer: (which) => void Atomics.or(header3, SAB_I_TIMERS, 1 << which),
14096
- hasPending: () => Atomics.load(header3, SAB_I_SIGNALS) !== 0 || Atomics.load(header3, SAB_I_TIMERS) !== 0,
14247
+ hasPending: () => unblocked(header3) !== 0 || Atomics.load(header3, SAB_I_TIMERS) !== 0,
14097
14248
  raise: hooks.raise,
14098
14249
  threadKill: hooks.threadKill,
14099
14250
  ...hooks.exitNow ? { exitNow: hooks.exitNow } : {},
14100
14251
  ...hooks.exitSignal ? { exitSignal: hooks.exitSignal } : {},
14101
- ...opts.program.abi === "wasi" || opts.program.abi === "js" ? { pendingBits: () => Atomics.load(header3, SAB_I_SIGNALS) } : {}
14252
+ ...opts.program.abi === "wasi" || opts.program.abi === "js" ? { pendingBits: () => Atomics.load(header3, SAB_I_SIGNALS) } : {},
14253
+ ...opts.program.abi === "wasi" ? { blockedBits: () => Atomics.load(header3, SAB_I_BLOCKED) } : {}
14102
14254
  };
14103
14255
  }
14104
- function threadInit(opts, program, sab, cred, thread) {
14256
+ function threadInit(opts, program, sab, cred, thread, main) {
14105
14257
  return {
14106
14258
  type: WASM_THREAD_INIT,
14107
14259
  pid: opts.shownPid ?? opts.pid,
@@ -14114,12 +14266,15 @@ function threadInit(opts, program, sab, cred, thread) {
14114
14266
  cred: copyCred(cred),
14115
14267
  owners: opts.owners === true,
14116
14268
  ...opts.ppid !== void 0 ? { ppid: opts.ppid } : {},
14117
- thread
14269
+ thread,
14270
+ signals: main
14118
14271
  };
14119
14272
  }
14120
14273
  function spawnWasmProcess(opts) {
14121
14274
  const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
14122
14275
  const header3 = new Int32Array(sab, 0, SAB_HEADER_I32);
14276
+ if (opts.blocked && opts.program.abi === "wasi")
14277
+ Atomics.store(header3, SAB_I_BLOCKED, opts.blocked);
14123
14278
  const threadSignals = /* @__PURE__ */ new Map();
14124
14279
  const created3 = new Created();
14125
14280
  const process = new WasmProcess(
@@ -14220,6 +14375,7 @@ function spawnWasmProcess(opts) {
14220
14375
  const tsab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
14221
14376
  const tw = opts.createWorker();
14222
14377
  const tsignals = threadSignalsOf(tsab);
14378
+ if (thread.blocked) Atomics.store(tsignals.header, SAB_I_BLOCKED, thread.blocked);
14223
14379
  threadSignals.set(thread.tid, tsignals);
14224
14380
  const tdispatch = (req) => dying.serving(
14225
14381
  () => isWasmSyscall(req) ? process.syscall(req, threadCancel(tsignals)) : dispatchSyncFs(req)
@@ -14241,7 +14397,7 @@ function spawnWasmProcess(opts) {
14241
14397
  tresponder.dispose();
14242
14398
  tw.terminate();
14243
14399
  });
14244
- tw.postMessage(threadInit(opts, program, tsab, process.cred, thread));
14400
+ tw.postMessage(threadInit(opts, program, tsab, process.cred, thread, sab));
14245
14401
  };
14246
14402
  worker.postMessage(
14247
14403
  processInit(opts, sab, process.umask, copyCred(process.cred), lane?.lane),
@@ -14263,6 +14419,7 @@ function spawnWasmProcess(opts) {
14263
14419
  onState: (listener) => process.onState(listener),
14264
14420
  memory: () => Atomics.load(header3, SAB_I_MEMORY) * 65536,
14265
14421
  ignoredSignals: (fork) => process.inheritable(fork),
14422
+ blockedSignals: () => opts.program.abi === "wasi" ? Atomics.load(header3, SAB_I_BLOCKED) : 0,
14266
14423
  signalMasks: () => process.signalMasks(),
14267
14424
  umask: () => process.umask,
14268
14425
  cred: () => copyCred(process.cred),
@@ -15143,14 +15300,19 @@ var SealedChannel = class {
15143
15300
  offset = 0;
15144
15301
  inflight;
15145
15302
  broker;
15146
- constructor(broker) {
15303
+ access;
15304
+ constructor(broker, access2) {
15147
15305
  this.broker = broker;
15306
+ this.access = access2;
15148
15307
  }
15149
15308
  send(bytes, caller, gone) {
15309
+ if (this.access === "read") {
15310
+ bytes.fill(0);
15311
+ throw new KernelError("EBADF");
15312
+ }
15150
15313
  const length2 = bytes.length;
15151
15314
  const fields = parseSealed(bytes);
15152
- this.reply = void 0;
15153
- this.offset = 0;
15315
+ this.drop();
15154
15316
  this.inflight?.control.abort("replaced");
15155
15317
  const control = new AbortController();
15156
15318
  const withdraw = () => control.abort("withdrawn");
@@ -15186,15 +15348,18 @@ var SealedChannel = class {
15186
15348
  if (!signal.aborted) return reply;
15187
15349
  return signal.reason === "timeout" ? errorReply("ETIMEDOUT", "the vault did not answer within 10 s") : void 0;
15188
15350
  }
15351
+ drop() {
15352
+ this.reply?.fill(0);
15353
+ this.reply = void 0;
15354
+ this.offset = 0;
15355
+ }
15189
15356
  async read(max, signal) {
15357
+ if (this.access === "write") throw new KernelError("EBADF");
15190
15358
  for (; ; ) {
15191
15359
  if (this.reply) {
15192
15360
  const out = this.reply.slice(this.offset, this.offset + max);
15193
15361
  this.offset += out.length;
15194
- if (this.offset >= this.reply.length) {
15195
- this.reply = void 0;
15196
- this.offset = 0;
15197
- }
15362
+ if (this.offset >= this.reply.length) this.drop();
15198
15363
  return out;
15199
15364
  }
15200
15365
  const flight = this.inflight;
@@ -15227,7 +15392,7 @@ var SealedChannel = class {
15227
15392
  close() {
15228
15393
  this.inflight?.control.abort("withdrawn");
15229
15394
  this.inflight = void 0;
15230
- this.reply = void 0;
15395
+ this.drop();
15231
15396
  }
15232
15397
  };
15233
15398
  function swapOf(reply) {
@@ -15284,12 +15449,14 @@ var SealedBroker = class {
15284
15449
  control.abort("done");
15285
15450
  }
15286
15451
  }
15287
- open() {
15288
- const channel = new SealedChannel(this);
15452
+ open(access2 = "both") {
15453
+ const channel = new SealedChannel(this, access2);
15289
15454
  return new OpenFile({
15290
- read: (max, signal) => channel.read(max, signal),
15291
- write: async () => {
15292
- throw new KernelError("EINVAL");
15455
+ ...access2 === "write" ? {} : { read: (max, signal) => channel.read(max, signal) },
15456
+ ...access2 === "read" ? {} : {
15457
+ write: async () => {
15458
+ throw new KernelError("EINVAL");
15459
+ }
15293
15460
  },
15294
15461
  poll: () => channel.poll(),
15295
15462
  changed: (signal) => channel.changed(signal),
@@ -16908,7 +17075,10 @@ var SECURE_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
16908
17075
  var DEFAULT_SUDOERS = "%sudo Cmnd *\n";
16909
17076
  var SUDO_TIMEOUT_MS = 3e5;
16910
17077
  var NAME3 = /^[a-z_][a-z0-9_.-]*$/;
16911
- var RULE = /^(NOPASSWD\s+)?(\S+)\s+(?:\(([^()\s]+)\)\s+)?Cmnd(?:\s+(.*))?$/;
17078
+ var WORD = String.raw`(?:'[^']*'|"(?:[^"\\]|\\[\s\S])*"|\\[\s\S]|[^\s'"\\])+`;
17079
+ var RULE = new RegExp(
17080
+ String.raw`^(NOPASSWD\s+)?(\S+)\s+(?:\(([^()\s]+)\)\s+)?(?:Cwd\s+(${WORD})\s+)?Cmnd(?:\s+(.*))?$`
17081
+ );
16912
17082
  var TERM = /^[A-Za-z0-9._+-]+$/;
16913
17083
  var PLAIN = /^[A-Za-z0-9_@%+=:,./-]+$/;
16914
17084
  var USAGE = "usage: sudo [-n] [-u user] [--] command [args...]\n sudo -l";
@@ -16943,17 +17113,25 @@ function validWho(who) {
16943
17113
  if (who === "ALL") return true;
16944
17114
  return NAME3.test(who.startsWith("%") ? who.slice(1) : who);
16945
17115
  }
17116
+ function cwdOf(word) {
17117
+ const words2 = splitWords(word);
17118
+ const path = words2?.length === 1 ? words2[0] : "";
17119
+ return path.startsWith("/") ? normalizePath(path) : "";
17120
+ }
16946
17121
  function parseRule(line) {
16947
17122
  const text = line.trim();
16948
17123
  if (text === "" || text.startsWith("#")) return null;
16949
17124
  const m = RULE.exec(text);
16950
17125
  if (!m) return void 0;
16951
- const [, nopasswd, who = "", target = "root", rest2 = ""] = m;
17126
+ const [, nopasswd, who = "", target = "root", dir, rest2 = ""] = m;
16952
17127
  if (!validWho(who) || target !== "ALL" && !NAME3.test(target)) return void 0;
16953
- if (rest2.trim() === "*") return { nopasswd: !!nopasswd, who, target, cmnd: "*" };
17128
+ const cwd = dir === void 0 ? void 0 : cwdOf(dir);
17129
+ if (cwd === "") return void 0;
17130
+ const rule = { nopasswd: !!nopasswd, who, target, ...cwd !== void 0 ? { cwd } : {} };
17131
+ if (rest2.trim() === "*") return { ...rule, cmnd: "*" };
16954
17132
  const cmnd = splitWords(rest2);
16955
17133
  if (!cmnd?.[0]?.startsWith("/")) return void 0;
16956
- return { nopasswd: !!nopasswd, who, target, cmnd };
17134
+ return { ...rule, cmnd };
16957
17135
  }
16958
17136
  function flags(parsed2, arg, next2) {
16959
17137
  for (let j = 1; j < arg.length; j++) {
@@ -16992,7 +17170,8 @@ function parseArgs(args) {
16992
17170
  }
16993
17171
  function described(rule) {
16994
17172
  const cmnd = rule.cmnd === "*" ? "ALL" : rule.cmnd.map(quoteWord).join(" ");
16995
- return ` (${rule.target}) ${rule.nopasswd ? "NOPASSWD: " : ""}${cmnd}`;
17173
+ const cwd = rule.cwd === void 0 ? "" : `CWD=${quoteWord(rule.cwd)} `;
17174
+ return ` (${rule.target}) ${cwd}${rule.nopasswd ? "NOPASSWD: " : ""}${cmnd}`;
16996
17175
  }
16997
17176
  var Sudo = class {
16998
17177
  o;
@@ -17132,7 +17311,7 @@ var Sudo = class {
17132
17311
  if (untrusted) return untrusted;
17133
17312
  const argv = [path, ...args];
17134
17313
  const matching = rules.filter(
17135
- (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]))
17314
+ (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]))
17136
17315
  );
17137
17316
  const grant = () => this.grant(self, target, cred, parsed2.command, path, cwd, call.term);
17138
17317
  if (root || matching.some((rule) => rule.nopasswd)) return grant();
@@ -17154,7 +17333,7 @@ var Sudo = class {
17154
17333
  }
17155
17334
  const changed = await this.untrusted(path, cwd, word);
17156
17335
  if (changed) return changed;
17157
- if (answer === "always" && !await this.remember(self, target, argv)) {
17336
+ if (answer === "always" && !await this.remember(self, target, cwd, argv)) {
17158
17337
  return failed2("EIO", "sudo: the rule could not be recorded");
17159
17338
  }
17160
17339
  return grant();
@@ -17219,7 +17398,7 @@ ${lines2}
17219
17398
  requester.removeEventListener("abort", withdraw);
17220
17399
  }
17221
17400
  }
17222
- async remember(self, target, argv) {
17401
+ async remember(self, target, cwd, argv) {
17223
17402
  const { fs } = this.o;
17224
17403
  const path = `${SUDOERS_D}/${self.name}`;
17225
17404
  if (!NAME3.test(self.name)) return false;
@@ -17229,7 +17408,8 @@ ${lines2}
17229
17408
  const st = await fs.lstat(path).catch(() => void 0);
17230
17409
  if (st && !await this.trusted(path)) return false;
17231
17410
  const old = st ? await fs.readFile(path) : "";
17232
- const line = `NOPASSWD ${self.name} (${target.name}) Cmnd ${argv.map(quoteWord).join(" ")}
17411
+ const words2 = argv.map(quoteWord).join(" ");
17412
+ const line = `NOPASSWD ${self.name} (${target.name}) Cwd ${quoteWord(cwd)} Cmnd ${words2}
17233
17413
  `;
17234
17414
  const text = old === "" || old.endsWith("\n") ? `${old}${line}` : `${old}
17235
17415
  ${line}`;
@@ -17362,15 +17542,25 @@ function shebang(head) {
17362
17542
  if (head[0] !== 35 || head[1] !== 33) return void 0;
17363
17543
  return new TextDecoder().decode(head.subarray(0, SHEBANG_MAX)).slice(2).split("\n")[0].trim().split(/[ \t]+/);
17364
17544
  }
17365
- async function rootOnly(fs, path) {
17545
+ async function rootOnly(fs, path, dir = false) {
17366
17546
  const names = chain(path);
17367
17547
  for (const [i, at] of names.entries()) {
17368
17548
  const st = await fs.lstat(at).catch(() => void 0);
17369
- const kind = i === names.length - 1 ? st?.isFile : st?.isDirectory;
17549
+ const kind = i === names.length - 1 && !dir ? st?.isFile : st?.isDirectory;
17370
17550
  if (!kind || st?.uid !== 0 || (st.mode & 18) !== 0) return false;
17371
17551
  }
17372
17552
  return true;
17373
17553
  }
17554
+ function appendedRoots(given) {
17555
+ const roots = given?.append ?? [];
17556
+ if (!Array.isArray(roots)) throw new TypeError("commands.append must be an array of paths");
17557
+ for (const root of roots) {
17558
+ if (typeof root !== "string" || !root.startsWith("/") || normalizePath(root) !== root) {
17559
+ throw new TypeError(`commands.append: not an absolute, normalised path: ${String(root)}`);
17560
+ }
17561
+ }
17562
+ return [...roots];
17563
+ }
17374
17564
  function plainEnv(words2) {
17375
17565
  const next2 = words2[1];
17376
17566
  if (baseName(words2[0] ?? "") !== "env" || next2 === void 0) return false;
@@ -17463,6 +17653,8 @@ var Launcher = class {
17463
17653
  base;
17464
17654
  createWorker;
17465
17655
  modulesDir;
17656
+ appended;
17657
+ reportedRoots = /* @__PURE__ */ new Set();
17466
17658
  env;
17467
17659
  catalogs = /* @__PURE__ */ new Map();
17468
17660
  ownCatalogs = /* @__PURE__ */ new Map();
@@ -17569,6 +17761,7 @@ var Launcher = class {
17569
17761
  this.processMounts = options.processMounts ?? true;
17570
17762
  this.pnpmHome = options.env?.PNPM_HOME ?? SYSTEM_PNPM_HOME;
17571
17763
  this.modulesDir = options.modules ?? "/node_modules";
17764
+ this.appended = appendedRoots(options.commands);
17572
17765
  this.cdp = new CdpHosts(options.cdp);
17573
17766
  const hostname = options.hostname ?? DEFAULT_HOSTNAME;
17574
17767
  if (!isHostname(hostname)) throw new Error(`not a host name: ${String(hostname)}`);
@@ -17618,9 +17811,9 @@ var Launcher = class {
17618
17811
  this.unguardedRoot = options.fs.unguarded?.() ?? options.fs;
17619
17812
  this.prefixes = protectedPrefixes(options.protect?.paths ?? []);
17620
17813
  this.processFs = this.prefixes.length > 0 ? new ProtectedFs(this.prefixes).view(this.fs) : this.fs;
17621
- options.fs.protect?.(this.prefixes);
17622
17814
  const integrity = options.protect?.integrity;
17623
17815
  integrityOptions(integrity);
17816
+ options.fs.protect?.(this.prefixes, Boolean(integrity));
17624
17817
  if (integrity && this.prefixes.length > 0) {
17625
17818
  this.integrity = new Integrity(
17626
17819
  this.prefixes,
@@ -17643,7 +17836,7 @@ var Launcher = class {
17643
17836
  });
17644
17837
  void this.unlinked.sweep("/");
17645
17838
  this.watchers.watch(
17646
- [this.modulesDir, this.pnpmHome],
17839
+ [this.modulesDir, this.pnpmHome, ...this.appended],
17647
17840
  { recursive: true },
17648
17841
  () => this.resetCatalogs()
17649
17842
  );
@@ -17674,7 +17867,10 @@ var Launcher = class {
17674
17867
  commands(scope = "") {
17675
17868
  let found = this.catalogs.get(scope);
17676
17869
  if (!found) {
17677
- found = this.roots(scope).then((roots) => scanCommands(this.scanned, roots)).then((commands) => commands.set(SUDO_COMMAND.name, SUDO_COMMAND));
17870
+ found = this.trustedAppended().then(async (appended) => {
17871
+ const roots = await this.roots(scope, appended);
17872
+ return scanCommands(this.scanned, roots, roots.length - appended.length);
17873
+ }).then((commands) => commands.set(SUDO_COMMAND.name, SUDO_COMMAND));
17678
17874
  this.catalogs.set(scope, found);
17679
17875
  }
17680
17876
  return found;
@@ -17701,10 +17897,37 @@ var Launcher = class {
17701
17897
  }
17702
17898
  return found;
17703
17899
  }
17704
- async roots(scope = "") {
17705
- const system = [this.modulesDir, ...await pnpmGlobalRoots(this.base, this.pnpmHome)];
17900
+ async roots(scope = "", appended) {
17901
+ const system = [
17902
+ this.modulesDir,
17903
+ ...await pnpmGlobalRoots(this.base, this.pnpmHome),
17904
+ ...appended ?? await this.trustedAppended()
17905
+ ];
17706
17906
  return scope ? [...await pnpmGlobalRoots(this.base, scope), ...system] : system;
17707
17907
  }
17908
+ async rootOnlyPath(root) {
17909
+ for (const at of chain(root)) {
17910
+ const real = await physicalPathAsync(this.base, at);
17911
+ if (!await rootOnly(this.base, real, true)) return false;
17912
+ }
17913
+ return true;
17914
+ }
17915
+ async trustedAppended() {
17916
+ if (this.appended.length === 0 || this.usersMode === "off") return this.appended;
17917
+ const trusted = [];
17918
+ for (const root of this.appended) {
17919
+ const real = await physicalPathAsync(this.base, root);
17920
+ if (await this.rootOnlyPath(root)) trusted.push(root);
17921
+ else if (this.usersMode === "report" && await this.base.exists(real)) {
17922
+ if (!this.reportedRoots.has(root)) {
17923
+ this.reportedRoots.add(root);
17924
+ this.dac?.denied({ pid: 0, uid: 0, op: "commands", path: root, errno: "EACCES" });
17925
+ }
17926
+ trusted.push(root);
17927
+ }
17928
+ }
17929
+ return trusted;
17930
+ }
17708
17931
  resetCatalogs() {
17709
17932
  this.catalogs.clear();
17710
17933
  this.ownCatalogs.clear();
@@ -17810,14 +18033,22 @@ var Launcher = class {
17810
18033
  const inner = await this.interpreted(interp, [interp, ...passed], cwd, scope, depth + 1);
17811
18034
  const via = [interp, ...inner?.via ?? []];
17812
18035
  const sources = [script, ...inner?.sources ?? []];
17813
- return inner && { ...inner, target: withScriptEnv(inner.target, command), via, sources };
18036
+ const bins = [...command?.bin ? [script] : [], ...inner?.bins ?? []];
18037
+ return inner && {
18038
+ ...inner,
18039
+ target: withScriptEnv(inner.target, command),
18040
+ via,
18041
+ sources,
18042
+ ...bins.length > 0 ? { bins } : {}
18043
+ };
17814
18044
  }
17815
18045
  const target = withScriptEnv(found, command);
17816
18046
  return {
17817
18047
  target,
17818
18048
  args: [...target.prefix ?? [], ...passed],
17819
18049
  via: [interp],
17820
- sources: [script]
18050
+ sources: [script],
18051
+ ...command?.bin ? { bins: [script] } : {}
17821
18052
  };
17822
18053
  }
17823
18054
  async plan(file, argv, cwd, scope, argv0) {
@@ -17843,6 +18074,7 @@ var Launcher = class {
17843
18074
  const want = i < via.length - 1 ? R_OK | X_OK : X_OK;
17844
18075
  await need(g, "exec", this.fs.resolvePath(req.cwd, file), want);
17845
18076
  }
18077
+ for (const bin of planned?.bins ?? []) await need(g, "exec", bin, R_OK | X_OK);
17846
18078
  }
17847
18079
  async checkedPlan(req, { ppid, cred, scope }, fds) {
17848
18080
  const planned = await this.plan(req.file, req.argv, req.cwd, scope, req.argv0);
@@ -18141,6 +18373,7 @@ var Launcher = class {
18141
18373
  },
18142
18374
  settling: () => this.settling.isSettling(pid),
18143
18375
  ...req.ignored ? { ignored: req.ignored } : {},
18376
+ ...req.blocked ? { blocked: req.blocked } : {},
18144
18377
  ...req.umask !== void 0 ? { umask: req.umask } : {},
18145
18378
  cred: req.cred,
18146
18379
  ...this.owners && req.program.abi === "wasi" ? { fresh: (path, umask) => this.freshModules.add(path, umask, pid) } : {},
@@ -18415,6 +18648,7 @@ var Launcher = class {
18415
18648
  report,
18416
18649
  ppid,
18417
18650
  ignored: this.ignoredBy(ppid),
18651
+ blocked: this.blockedBy(ppid),
18418
18652
  umask: this.umaskOf(ppid),
18419
18653
  cred,
18420
18654
  tag: this.tagOf(ppid),
@@ -18476,6 +18710,7 @@ var Launcher = class {
18476
18710
  ppid,
18477
18711
  fork: state,
18478
18712
  ignored: this.ignoredBy(ppid, true),
18713
+ blocked: this.blockedBy(ppid),
18479
18714
  umask: this.umaskOf(ppid),
18480
18715
  cred: this.credOf(ppid),
18481
18716
  tag: this.tagOf(ppid)
@@ -18527,11 +18762,16 @@ var Launcher = class {
18527
18762
  }
18528
18763
  signalMasksOf(pid) {
18529
18764
  const masks = this.processes.get(pid)?.signalMasks?.();
18530
- return masks ? { sigIgn: masks.ignored, sigCgt: masks.caught } : {};
18765
+ const blocked = this.blockedBy(pid);
18766
+ const sigBlk = blocked ? { sigBlk: blocked } : {};
18767
+ return masks ? { sigIgn: masks.ignored, sigCgt: masks.caught, ...sigBlk } : sigBlk;
18531
18768
  }
18532
18769
  controlsJobs(pid) {
18533
18770
  return this.settling.isSettling(pid) || (this.ignoredBy(pid) & sigbit(SIG.TTOU)) !== 0;
18534
18771
  }
18772
+ blockedBy(pid) {
18773
+ return this.processes.get(pid)?.blockedSignals?.() ?? 0;
18774
+ }
18535
18775
  ignoredBy(pid, fork = false) {
18536
18776
  return this.processes.get(pid)?.ignoredSignals(fork) ?? 0;
18537
18777
  }
@@ -18896,7 +19136,7 @@ var Launcher = class {
18896
19136
  (dir) => this.userCommandDir(dir),
18897
19137
  this.owners
18898
19138
  );
18899
- const nodes = new VfsNodes(fs, (path) => this.released(path));
19139
+ const nodes = new VfsNodes(fs, (path) => this.released(path), this.nodes.entries);
18900
19140
  nodes.keepsOnDisk = true;
18901
19141
  this.openFiles.add(nodes);
18902
19142
  const unlinked = new UnlinkedKeeper(
@@ -19300,6 +19540,7 @@ function serveKernel(port, deps) {
19300
19540
  createWorker: deps.createWorker,
19301
19541
  ...deps.createDriverWorker ? { createDriverWorker: deps.createDriverWorker } : {},
19302
19542
  ...req.modules ? { modules: req.modules } : {},
19543
+ ...req.commands ? { commands: req.commands } : {},
19303
19544
  ...req.env ? { env: req.env } : {},
19304
19545
  ...hostOptions(req, port, sealed.hook, clipboard.hook, integrity.lookup),
19305
19546
  ...req.approve ? { approve: approvals.ask } : {},