@ai-ecoverse/slicc-kernel 1.62.4 → 1.63.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
@@ -331,15 +331,24 @@ async function present(fs, path, directory) {
331
331
  const st = await fs.lstat(path).catch(() => void 0);
332
332
  return st !== void 0 && (!directory || st.isDirectory);
333
333
  }
334
+ async function finalOf(fs, walked, want, options) {
335
+ const perm = walked.perm;
336
+ if ((want & X_OK) === 0 || options.directory !== void 0) {
337
+ return [perm, options.directory !== false];
338
+ }
339
+ const look = options.follow === false ? fs.lstat : fs.stat;
340
+ const st = await look.call(fs, walked.path).catch(() => void 0);
341
+ return st ? [permOf(st), st.isDirectory] : [perm, false];
342
+ }
334
343
  async function access(fs, cred, path, want, options = {}) {
335
344
  const walked = await walk(fs, cred, path, options.follow !== false);
336
345
  if (walked.denied !== void 0) {
337
346
  if (await present(fs, walked.denied, true)) return walked;
338
347
  return { path, perm: void 0, missing: true };
339
348
  }
340
- const { perm } = walked;
341
- if (!perm) return { ...walked, missing: true };
342
- if (want === 0 || permits(cred, perm, want, options.directory !== false)) return walked;
349
+ if (!walked.perm) return { ...walked, missing: true };
350
+ const [perm, directory] = await finalOf(fs, walked, want, options);
351
+ if (want === 0 || permits(cred, perm, want, directory)) return walked;
343
352
  if (await present(fs, walked.path, false)) return { ...walked, denied: walked.path };
344
353
  return { ...walked, missing: true };
345
354
  }
@@ -350,6 +359,7 @@ function refused(path, errno = "EACCES") {
350
359
  // src/fs/dac.ts
351
360
  var MAX_DENIAL_KEYS = 1e4;
352
361
  var S_ISGID2 = 1024;
362
+ var DIR = { directory: true };
353
363
  var Dac = class {
354
364
  mode;
355
365
  hook;
@@ -386,6 +396,17 @@ var Dac = class {
386
396
  );
387
397
  }
388
398
  };
399
+ var MAX_CREATED = 1024;
400
+ var Created = class {
401
+ paths = /* @__PURE__ */ new Set();
402
+ add(path) {
403
+ if (this.paths.size >= MAX_CREATED) this.paths.clear();
404
+ this.paths.add(normalizePath(path));
405
+ }
406
+ take(path) {
407
+ return this.paths.delete(normalizePath(path));
408
+ }
409
+ };
389
410
  function deny(g, op, path, errno = "EACCES") {
390
411
  g.dac.denied({ pid: g.pid, uid: g.cred.euid, op, path, errno });
391
412
  }
@@ -394,20 +415,30 @@ async function need(g, op, path, want, options = {}, shown = path) {
394
415
  if (walked.denied !== void 0) deny(g, op, shown);
395
416
  return walked;
396
417
  }
418
+ function entryIn(dir, path) {
419
+ return joinName(dir.path, path.slice(path.lastIndexOf("/") + 1));
420
+ }
397
421
  async function stickyEntry(g, op, path, dir) {
398
422
  const perm = dir.perm;
399
423
  if (!perm || sticky(g.cred, perm, perm)) return;
400
- const entry = joinName(dir.path, path.slice(path.lastIndexOf("/") + 1));
424
+ const entry = entryIn(dir, path);
401
425
  if (sticky(g.cred, perm, (await ancestryOf(g.fs, entry)).at(-1))) return;
402
426
  if (await present(g.fs, entry, false)) deny(g, op, path, "EPERM");
403
427
  }
404
428
  async function inParent(g, op, path, old = false) {
405
- const dir = await need(g, op, parentOf(path), W_OK | X_OK, {}, path);
406
- if (old && dir.denied === void 0) await stickyEntry(g, op, path, dir);
429
+ if (old === "replaced") {
430
+ const dir2 = await need(g, op, parentOf(path), W_OK | X_OK, DIR, path);
431
+ return dir2.denied === void 0 ? stickyEntry(g, op, path, dir2) : void 0;
432
+ }
433
+ if (!old) return void await need(g, op, parentOf(path), W_OK | X_OK, DIR, path);
434
+ const dir = await access(g.fs, g.cred, parentOf(path), W_OK | X_OK, DIR);
435
+ if (dir.denied === void 0) return stickyEntry(g, op, path, dir);
436
+ const lookup2 = dir.denied === dir.path && dir.perm && permits(g.cred, dir.perm, X_OK);
437
+ if (!lookup2 || await present(g.fs, entryIn(dir, path), false)) deny(g, op, path);
407
438
  }
408
439
  async function makeIn(g, op, path) {
409
440
  for (let dir = parentOf(path); ; dir = parentOf(dir)) {
410
- const walked = await access(g.fs, g.cred, dir, W_OK | X_OK);
441
+ const walked = await access(g.fs, g.cred, dir, W_OK | X_OK, DIR);
411
442
  if (walked.denied !== void 0) {
412
443
  if (!await present(g.fs, path, true)) deny(g, op, path);
413
444
  return;
@@ -433,7 +464,7 @@ async function removable(g, op, path) {
433
464
  async function renamable(g, from, to) {
434
465
  if (from === to) return void await need(g, "rename", from, 0, { follow: false });
435
466
  await inParent(g, "rename", from, true);
436
- await inParent(g, "rename", to, true);
467
+ await inParent(g, "rename", to, "replaced");
437
468
  if (g.cred.euid === 0 || parentOf(from) === parentOf(to)) return;
438
469
  const st = await g.fs.lstat(from).catch(() => void 0);
439
470
  if (st?.isDirectory && !permits(g.cred, permOf(st), W_OK)) deny(g, "rename", from);
@@ -459,8 +490,20 @@ async function chmodMode(g, path, mode) {
459
490
  if (!g.dac.enforcing || !perm || g.cred.euid === 0 || inGroup(g.cred, perm.gid)) return mode;
460
491
  return mode & ~S_ISGID2;
461
492
  }
462
- async function retimable(g, op, path) {
463
- await ownerOnly(g, op, path, op !== "lutimes");
493
+ async function retimable(g, op, path, now = false) {
494
+ const follow2 = op !== "lutimes";
495
+ if (!now) return void await ownerOnly(g, op, path, follow2);
496
+ const walked = await need(g, op, path, 0, { follow: follow2 });
497
+ const perm = walked.perm;
498
+ if (!perm || g.cred.euid === 0 || perm.uid === g.cred.euid) return;
499
+ if (permits(g.cred, perm, W_OK, false)) return;
500
+ if (await present(g.fs, walked.path, false)) deny(g, op, path);
501
+ }
502
+ async function fileWritable(g, op, path) {
503
+ await need(g, op, path, W_OK, { directory: false });
504
+ }
505
+ function realIds(cred) {
506
+ return { ...cred, groups: [...cred.groups], euid: cred.ruid, egid: cred.rgid };
464
507
  }
465
508
  async function listable(g, op, path) {
466
509
  await need(g, op, path, R_OK | X_OK, { directory: true });
@@ -472,10 +515,16 @@ async function readable(g, op, path) {
472
515
  await need(g, op, path, R_OK, { directory: false });
473
516
  }
474
517
  async function writable(g, op, path) {
475
- const st = await g.fs.stat(path).catch(() => void 0);
476
- if (st) return void await need(g, op, path, W_OK, { directory: st.isDirectory });
518
+ await openable(g, op, path, W_OK, true);
519
+ }
520
+ async function creatable(g, op, path) {
477
521
  const target = await need(g, op, path, 0);
478
- if (target.denied === void 0) await need(g, op, parentOf(target.path), W_OK | X_OK, {}, path);
522
+ if (target.denied === void 0) await need(g, op, parentOf(target.path), W_OK | X_OK, DIR, path);
523
+ }
524
+ async function openable(g, op, path, want, create) {
525
+ const st = create ? await g.fs.stat(path).catch(() => void 0) : void 0;
526
+ if (create && !st) return creatable(g, op, path);
527
+ await need(g, op, path, want, { directory: st?.isDirectory ?? false });
479
528
  }
480
529
 
481
530
  // src/kernel/pipe.ts
@@ -2983,11 +3032,16 @@ var OpfsFs = class {
2983
3032
  }
2984
3033
  async ancestry(path) {
2985
3034
  const entries = await this.meta.get(chain(path));
2986
- return entries.map((entry, i) => ({
2987
- ...ownOf(entry) ?? (i === 0 ? ROOT_OWNER : inherit(entries.slice(0, i))),
2988
- ...entry?.mode !== void 0 ? { mode: entry.mode } : {},
2989
- ...entry?.link !== void 0 ? { link: entry.link } : {}
2990
- }));
3035
+ const out = [];
3036
+ for (const [i, entry] of entries.entries()) {
3037
+ const link2 = entry?.link !== void 0 && !await this.real(entry.path);
3038
+ out.push({
3039
+ ...ownOf(entry) ?? (i === 0 ? ROOT_OWNER : inherit(entries.slice(0, i))),
3040
+ ...entry?.mode !== void 0 ? { mode: entry.mode } : {},
3041
+ ...link2 ? { link: entry.link } : {}
3042
+ });
3043
+ }
3044
+ return out;
2991
3045
  }
2992
3046
  async handedTo(dir) {
2993
3047
  return inherit(await this.meta.get(chain(dir)));
@@ -6477,6 +6531,9 @@ function revokeSyncFsToken(token) {
6477
6531
  }
6478
6532
 
6479
6533
  // src/realm/sync-fs-wire.ts
6534
+ var HOLD_READ = 4;
6535
+ var HOLD_WRITE = 8;
6536
+ var HOLD_CREATED = 16;
6480
6537
  var SYNC_FS_REQUEST_TIMEOUT_MS = 25e3;
6481
6538
  function syncError(code, label) {
6482
6539
  return Object.assign(new Error(`${code}: ${label}`), { code });
@@ -6600,6 +6657,7 @@ async function run(entry, path, req) {
6600
6657
  return done;
6601
6658
  case "create":
6602
6659
  await createExclusive(fs, path, ownerFor2(entry, req.mode ?? 438));
6660
+ entry.created?.add(path);
6603
6661
  return done;
6604
6662
  case "readlink":
6605
6663
  return json2(await fs.readlink(path));
@@ -6656,11 +6714,47 @@ async function guard(g, path, req, cwd) {
6656
6714
  return linkable(g, path, other(), req.mode === 1);
6657
6715
  case "utimes":
6658
6716
  case "lutimes":
6659
- return retimable(g, op, path);
6717
+ return retimable(g, op, path, req.now === true);
6718
+ case "read":
6719
+ case "pread":
6720
+ return req.handle === void 0 ? readable(g, op, path) : void 0;
6721
+ case "pwrite":
6722
+ case "truncate":
6723
+ return req.handle === void 0 ? fileWritable(g, op, path) : void 0;
6724
+ case "write":
6725
+ return req.handle === void 0 && req.mode !== 1 ? writable(g, op, path) : void 0;
6660
6726
  default:
6661
6727
  return void 0;
6662
6728
  }
6663
6729
  }
6730
+ async function opening(entry, path, mode) {
6731
+ const g = guardOf(entry);
6732
+ const want = (mode & HOLD_READ ? R_OK : 0) | (mode & HOLD_WRITE ? W_OK : 0);
6733
+ const created3 = (mode & HOLD_CREATED) !== 0 && entry.created?.take(path) === true;
6734
+ if (g && want !== 0 && !created3) {
6735
+ await need(g, "open", path, want, { directory: false });
6736
+ }
6737
+ }
6738
+ async function holdOp(entry, path, req) {
6739
+ const mode = req.mode ?? 0;
6740
+ const [on, open] = [(mode & 1) === 1, (mode & 2) === 2];
6741
+ if (on && open) {
6742
+ await opening(entry, path, mode);
6743
+ const real = await entry.fs.realpath?.(path, true).catch(() => path);
6744
+ return json2(entry.hold?.(path, on, open, real) ?? null);
6745
+ }
6746
+ entry.hold?.(path, on, open, void 0, req.handle);
6747
+ return done;
6748
+ }
6749
+ async function accessOp(entry, path, req) {
6750
+ const g = guardOf(entry);
6751
+ if (!g) return done;
6752
+ const open = req.arg2 === "open";
6753
+ const cred = open || req.arg2 === "eaccess" ? g.cred : realIds(g.cred);
6754
+ const options = { follow: req.nofollow !== true, ...open ? { directory: false } : {} };
6755
+ await need({ ...g, cred }, open ? "open" : "access", path, req.mode ?? 0, options);
6756
+ return done;
6757
+ }
6664
6758
  async function guarded(entry, path, req) {
6665
6759
  const g = guardOf(entry);
6666
6760
  if (!g) return void 0;
@@ -6724,16 +6818,6 @@ async function owned(entry, own, req, path, target) {
6724
6818
  }
6725
6819
  return own(target, () => run(entry, path, req));
6726
6820
  }
6727
- async function holdOp(entry, path, req) {
6728
- const mode = req.mode ?? 0;
6729
- const [on, open] = [(mode & 1) === 1, (mode & 2) === 2];
6730
- if (on && open) {
6731
- const real = await entry.fs.realpath?.(path, true).catch(() => path);
6732
- return json2(entry.hold?.(path, on, open, real) ?? null);
6733
- }
6734
- entry.hold?.(path, on, open, void 0, req.handle);
6735
- return done;
6736
- }
6737
6821
  var IDENTITY_OPS = /* @__PURE__ */ new Set([
6738
6822
  "write",
6739
6823
  "pwrite",
@@ -6770,7 +6854,8 @@ async function dispatchOne(entry, req) {
6770
6854
  const path = fs.resolvePath(cwd, req.path);
6771
6855
  if (req.op === "kept") return json2(entry.keptFor?.(req.handle ?? -1) !== void 0);
6772
6856
  if (req.op === "stat" && req.handle !== void 0) return await keptStat(entry, req);
6773
- if (req.op === "hold") return holdOp(entry, path, req);
6857
+ if (req.op === "access") return await accessOp(entry, path, req);
6858
+ if (req.op === "hold") return await holdOp(entry, path, req);
6774
6859
  const refused2 = await guarded(entry, path, req);
6775
6860
  if (refused2) return refused2;
6776
6861
  if (req.op === "statfs") return json2(await entry.statfs?.(path) ?? await storageUsage());
@@ -6966,188 +7051,6 @@ function ownNames(hostname) {
6966
7051
  return [.../* @__PURE__ */ new Set([hostname.toLowerCase(), ...LIBC_HOSTNAMES])];
6967
7052
  }
6968
7053
 
6969
- // src/process/process-fds.ts
6970
- var field2 = (text) => (text || "none").replace(/[\s\\]/g, (c) => `\\${c.charCodeAt(0).toString(8).padStart(3, "0")}`);
6971
- function mountTable(mounts = []) {
6972
- const root = globalThis.navigator?.storage ? "opfs" : "memory";
6973
- const lines2 = [`${root} / ${root} rw 0 0`, "devfs /dev devfs rw 0 0", "proc /proc proc rw 0 0"];
6974
- for (const m of mounts) {
6975
- const options = Object.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
6976
- if (m.state !== "ok") options.push(m.state);
6977
- const access2 = options.includes("ro") ? [] : ["rw"];
6978
- lines2.push(
6979
- `${field2(m.source)} ${field2(m.target)} ${m.type} ${[...access2, ...options].join(",")} 0 0`
6980
- );
6981
- }
6982
- return `${lines2.join("\n")}
6983
- `;
6984
- }
6985
-
6986
- // src/process/procfs.ts
6987
- var HZ = 100;
6988
- var KIB = 1024;
6989
- var FALLBACK_MEMORY = 4 * KIB * KIB * KIB;
6990
- var PID_FILES = ["cmdline", "comm", "stat", "statm", "status"];
6991
- var SYSTEM_FILES = ["loadavg", "meminfo", "stat", "uptime"];
6992
- function commOf(info) {
6993
- const name2 = info.argv[0] ?? "";
6994
- return name2.slice(name2.lastIndexOf("/") + 1).slice(0, 15);
6995
- }
6996
- function ttyNumber(tty) {
6997
- const pts = tty && /^\/dev\/pts\/(\d+)$/.exec(tty);
6998
- if (pts) return 136 << 8 | Number(pts[1]);
6999
- const vt = tty && /^\/dev\/tty(\d+)$/.exec(tty);
7000
- return vt ? 4 << 8 | Number(vt[1]) : 0;
7001
- }
7002
- function pages(bytes) {
7003
- return Math.ceil(bytes / 4096);
7004
- }
7005
- function ticks(ms) {
7006
- return Math.max(0, Math.floor(ms * HZ / 1e3));
7007
- }
7008
- function pidStat(info, boot) {
7009
- const fields = [
7010
- info.pid,
7011
- `(${commOf(info)})`,
7012
- info.state,
7013
- info.ppid,
7014
- info.pgid,
7015
- info.sid,
7016
- ttyNumber(info.tty),
7017
- info.tty ? info.pgid : -1,
7018
- 4194304,
7019
- 0,
7020
- 0,
7021
- 0,
7022
- 0,
7023
- 0,
7024
- 0,
7025
- 0,
7026
- 0,
7027
- 20,
7028
- 0,
7029
- 1,
7030
- 0,
7031
- ticks(info.started - boot),
7032
- info.memory,
7033
- pages(info.memory),
7034
- "18446744073709551615",
7035
- ...Array(12).fill(0),
7036
- 17,
7037
- 0,
7038
- 0,
7039
- 0,
7040
- 0,
7041
- 0,
7042
- 0,
7043
- ...Array(7).fill(0),
7044
- 0
7045
- ];
7046
- return `${fields.join(" ")}
7047
- `;
7048
- }
7049
- var sigMask = (mask = 0) => (mask >>> 1).toString(16).padStart(16, "0");
7050
- function pidStatus(info) {
7051
- const state = info.state === "Z" ? "Z (zombie)" : "S (sleeping)";
7052
- const c = info.cred ?? ROOT;
7053
- const ids = (r, e, s) => `${r} ${e} ${s} ${e}`;
7054
- return [
7055
- `Name: ${commOf(info)}`,
7056
- `Umask: ${(info.umask ?? 18).toString(8).padStart(4, "0")}`,
7057
- `State: ${state}`,
7058
- `Tgid: ${info.pid}`,
7059
- "Ngid: 0",
7060
- `Pid: ${info.pid}`,
7061
- `PPid: ${info.ppid}`,
7062
- "TracerPid: 0",
7063
- `Uid: ${ids(c.ruid, c.euid, c.suid)}`,
7064
- `Gid: ${ids(c.rgid, c.egid, c.sgid)}`,
7065
- "FDSize: 64",
7066
- `VmSize: ${Math.ceil(info.memory / KIB)} kB`,
7067
- `VmRSS: ${Math.ceil(info.memory / KIB)} kB`,
7068
- `Groups: ${c.groups.join(" ")}`,
7069
- `NSpid: ${info.pid}`,
7070
- `NSpgid: ${info.pgid}`,
7071
- `NSsid: ${info.sid}`,
7072
- "Threads: 1",
7073
- "SigQ: 0/0",
7074
- "SigPnd: 0000000000000000",
7075
- "ShdPnd: 0000000000000000",
7076
- "SigBlk: 0000000000000000",
7077
- `SigIgn: ${sigMask(info.sigIgn)}`,
7078
- `SigCgt: ${sigMask(info.sigCgt)}`,
7079
- ""
7080
- ].join("\n");
7081
- }
7082
- function pidFile(name2, info, boot) {
7083
- if (name2 === "cmdline") return info.state === "Z" ? "" : `${info.argv.join("\0")}\0`;
7084
- if (name2 === "comm") return `${commOf(info)}
7085
- `;
7086
- if (name2 === "stat") return pidStat(info, boot);
7087
- if (name2 === "statm") {
7088
- const p = pages(info.memory);
7089
- return `${p} ${p} 0 0 0 ${p} 0
7090
- `;
7091
- }
7092
- return pidStatus(info);
7093
- }
7094
- function memoryInfo(source, used = 0) {
7095
- const total = source.deviceMemory ? source.deviceMemory * KIB * KIB * KIB : FALLBACK_MEMORY;
7096
- const free = Math.max(0, total - used - (source.usedHeap ?? 0));
7097
- const kb = (bytes) => `${Math.floor(bytes / KIB)} kB`;
7098
- return [
7099
- ["MemTotal", kb(total)],
7100
- ["MemFree", kb(free)],
7101
- ["MemAvailable", kb(free)],
7102
- ["Buffers", kb(0)],
7103
- ["Cached", kb(0)],
7104
- ["SwapCached", kb(0)],
7105
- ["Shmem", kb(0)],
7106
- ["SReclaimable", kb(0)],
7107
- ["SwapTotal", kb(0)],
7108
- ["SwapFree", kb(0)]
7109
- ].map(([key, value]) => `${`${key}:`.padEnd(16)}${value.padStart(12)}`).concat("").join("\n");
7110
- }
7111
- function systemFile(name2, listing, now, memory) {
7112
- const up = Math.max(0, now - listing.boot) / 1e3;
7113
- if (name2 === "uptime") return `${up.toFixed(2)} ${up.toFixed(2)}
7114
- `;
7115
- if (name2 === "meminfo") {
7116
- return memoryInfo(
7117
- memory,
7118
- listing.processes.reduce((sum, p) => sum + p.memory, 0)
7119
- );
7120
- }
7121
- const live = listing.processes.filter((p) => p.state !== "Z");
7122
- if (name2 === "loadavg") {
7123
- const last = Math.max(0, ...listing.processes.map((p) => p.pid));
7124
- return `0.00 0.00 0.00 1/${live.length} ${last}
7125
- `;
7126
- }
7127
- const idle = ticks(now - listing.boot);
7128
- return [
7129
- `cpu 0 0 0 ${idle} 0 0 0 0 0 0`,
7130
- `cpu0 0 0 0 ${idle} 0 0 0 0 0 0`,
7131
- "intr 0",
7132
- "ctxt 0",
7133
- `btime ${Math.floor(listing.boot / 1e3)}`,
7134
- `processes ${listing.processes.length}`,
7135
- "procs_running 1",
7136
- "procs_blocked 0",
7137
- ""
7138
- ].join("\n");
7139
- }
7140
- function memorySource(scope = globalThis) {
7141
- const nav = scope.navigator;
7142
- const perf = scope.performance;
7143
- const deviceMemory = nav?.deviceMemory;
7144
- const used = perf?.memory?.usedJSHeapSize;
7145
- return {
7146
- ...typeof deviceMemory === "number" && deviceMemory > 0 ? { deviceMemory } : {},
7147
- ...typeof used === "number" ? { usedHeap: used } : {}
7148
- };
7149
- }
7150
-
7151
7054
  // src/fs/pages.ts
7152
7055
  var PAGE = 64 * 1024;
7153
7056
  var READAHEAD = 1024 * 1024;
@@ -7373,6 +7276,191 @@ var AsyncRangedFile = class {
7373
7276
  }
7374
7277
  };
7375
7278
 
7279
+ // src/realm/live-vfs-fs.ts
7280
+ var EARLY_WHOLE_FILE = 64 * 1024;
7281
+
7282
+ // src/process/process-fds.ts
7283
+ var field2 = (text) => (text || "none").replace(/[\s\\]/g, (c) => `\\${c.charCodeAt(0).toString(8).padStart(3, "0")}`);
7284
+ function mountTable(mounts = []) {
7285
+ const root = globalThis.navigator?.storage ? "opfs" : "memory";
7286
+ const lines2 = [`${root} / ${root} rw 0 0`, "devfs /dev devfs rw 0 0", "proc /proc proc rw 0 0"];
7287
+ for (const m of mounts) {
7288
+ const options = Object.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
7289
+ if (m.state !== "ok") options.push(m.state);
7290
+ const access2 = options.includes("ro") ? [] : ["rw"];
7291
+ lines2.push(
7292
+ `${field2(m.source)} ${field2(m.target)} ${m.type} ${[...access2, ...options].join(",")} 0 0`
7293
+ );
7294
+ }
7295
+ return `${lines2.join("\n")}
7296
+ `;
7297
+ }
7298
+
7299
+ // src/process/procfs.ts
7300
+ var HZ = 100;
7301
+ var KIB = 1024;
7302
+ var FALLBACK_MEMORY = 4 * KIB * KIB * KIB;
7303
+ var PID_FILES = ["cmdline", "comm", "stat", "statm", "status"];
7304
+ var SYSTEM_FILES = ["loadavg", "meminfo", "stat", "uptime"];
7305
+ function commOf(info) {
7306
+ const name2 = info.argv[0] ?? "";
7307
+ return name2.slice(name2.lastIndexOf("/") + 1).slice(0, 15);
7308
+ }
7309
+ function ttyNumber(tty) {
7310
+ const pts = tty && /^\/dev\/pts\/(\d+)$/.exec(tty);
7311
+ if (pts) return 136 << 8 | Number(pts[1]);
7312
+ const vt = tty && /^\/dev\/tty(\d+)$/.exec(tty);
7313
+ return vt ? 4 << 8 | Number(vt[1]) : 0;
7314
+ }
7315
+ function pages(bytes) {
7316
+ return Math.ceil(bytes / 4096);
7317
+ }
7318
+ function ticks(ms) {
7319
+ return Math.max(0, Math.floor(ms * HZ / 1e3));
7320
+ }
7321
+ function pidStat(info, boot) {
7322
+ const fields = [
7323
+ info.pid,
7324
+ `(${commOf(info)})`,
7325
+ info.state,
7326
+ info.ppid,
7327
+ info.pgid,
7328
+ info.sid,
7329
+ ttyNumber(info.tty),
7330
+ info.tty ? info.pgid : -1,
7331
+ 4194304,
7332
+ 0,
7333
+ 0,
7334
+ 0,
7335
+ 0,
7336
+ 0,
7337
+ 0,
7338
+ 0,
7339
+ 0,
7340
+ 20,
7341
+ 0,
7342
+ 1,
7343
+ 0,
7344
+ ticks(info.started - boot),
7345
+ info.memory,
7346
+ pages(info.memory),
7347
+ "18446744073709551615",
7348
+ ...Array(12).fill(0),
7349
+ 17,
7350
+ 0,
7351
+ 0,
7352
+ 0,
7353
+ 0,
7354
+ 0,
7355
+ 0,
7356
+ ...Array(7).fill(0),
7357
+ 0
7358
+ ];
7359
+ return `${fields.join(" ")}
7360
+ `;
7361
+ }
7362
+ var sigMask = (mask = 0) => (mask >>> 1).toString(16).padStart(16, "0");
7363
+ function pidStatus(info) {
7364
+ const state = info.state === "Z" ? "Z (zombie)" : "S (sleeping)";
7365
+ const c = info.cred ?? ROOT;
7366
+ const ids = (r, e, s) => `${r} ${e} ${s} ${e}`;
7367
+ return [
7368
+ `Name: ${commOf(info)}`,
7369
+ `Umask: ${(info.umask ?? 18).toString(8).padStart(4, "0")}`,
7370
+ `State: ${state}`,
7371
+ `Tgid: ${info.pid}`,
7372
+ "Ngid: 0",
7373
+ `Pid: ${info.pid}`,
7374
+ `PPid: ${info.ppid}`,
7375
+ "TracerPid: 0",
7376
+ `Uid: ${ids(c.ruid, c.euid, c.suid)}`,
7377
+ `Gid: ${ids(c.rgid, c.egid, c.sgid)}`,
7378
+ "FDSize: 64",
7379
+ `VmSize: ${Math.ceil(info.memory / KIB)} kB`,
7380
+ `VmRSS: ${Math.ceil(info.memory / KIB)} kB`,
7381
+ `Groups: ${c.groups.join(" ")}`,
7382
+ `NSpid: ${info.pid}`,
7383
+ `NSpgid: ${info.pgid}`,
7384
+ `NSsid: ${info.sid}`,
7385
+ "Threads: 1",
7386
+ "SigQ: 0/0",
7387
+ "SigPnd: 0000000000000000",
7388
+ "ShdPnd: 0000000000000000",
7389
+ "SigBlk: 0000000000000000",
7390
+ `SigIgn: ${sigMask(info.sigIgn)}`,
7391
+ `SigCgt: ${sigMask(info.sigCgt)}`,
7392
+ ""
7393
+ ].join("\n");
7394
+ }
7395
+ function pidFile(name2, info, boot) {
7396
+ if (name2 === "cmdline") return info.state === "Z" ? "" : `${info.argv.join("\0")}\0`;
7397
+ if (name2 === "comm") return `${commOf(info)}
7398
+ `;
7399
+ if (name2 === "stat") return pidStat(info, boot);
7400
+ if (name2 === "statm") {
7401
+ const p = pages(info.memory);
7402
+ return `${p} ${p} 0 0 0 ${p} 0
7403
+ `;
7404
+ }
7405
+ return pidStatus(info);
7406
+ }
7407
+ function memoryInfo(source, used = 0) {
7408
+ const total = source.deviceMemory ? source.deviceMemory * KIB * KIB * KIB : FALLBACK_MEMORY;
7409
+ const free = Math.max(0, total - used - (source.usedHeap ?? 0));
7410
+ const kb = (bytes) => `${Math.floor(bytes / KIB)} kB`;
7411
+ return [
7412
+ ["MemTotal", kb(total)],
7413
+ ["MemFree", kb(free)],
7414
+ ["MemAvailable", kb(free)],
7415
+ ["Buffers", kb(0)],
7416
+ ["Cached", kb(0)],
7417
+ ["SwapCached", kb(0)],
7418
+ ["Shmem", kb(0)],
7419
+ ["SReclaimable", kb(0)],
7420
+ ["SwapTotal", kb(0)],
7421
+ ["SwapFree", kb(0)]
7422
+ ].map(([key, value]) => `${`${key}:`.padEnd(16)}${value.padStart(12)}`).concat("").join("\n");
7423
+ }
7424
+ function systemFile(name2, listing, now, memory) {
7425
+ const up = Math.max(0, now - listing.boot) / 1e3;
7426
+ if (name2 === "uptime") return `${up.toFixed(2)} ${up.toFixed(2)}
7427
+ `;
7428
+ if (name2 === "meminfo") {
7429
+ return memoryInfo(
7430
+ memory,
7431
+ listing.processes.reduce((sum, p) => sum + p.memory, 0)
7432
+ );
7433
+ }
7434
+ const live = listing.processes.filter((p) => p.state !== "Z");
7435
+ if (name2 === "loadavg") {
7436
+ const last = Math.max(0, ...listing.processes.map((p) => p.pid));
7437
+ return `0.00 0.00 0.00 1/${live.length} ${last}
7438
+ `;
7439
+ }
7440
+ const idle = ticks(now - listing.boot);
7441
+ return [
7442
+ `cpu 0 0 0 ${idle} 0 0 0 0 0 0`,
7443
+ `cpu0 0 0 0 ${idle} 0 0 0 0 0 0`,
7444
+ "intr 0",
7445
+ "ctxt 0",
7446
+ `btime ${Math.floor(listing.boot / 1e3)}`,
7447
+ `processes ${listing.processes.length}`,
7448
+ "procs_running 1",
7449
+ "procs_blocked 0",
7450
+ ""
7451
+ ].join("\n");
7452
+ }
7453
+ function memorySource(scope = globalThis) {
7454
+ const nav = scope.navigator;
7455
+ const perf = scope.performance;
7456
+ const deviceMemory = nav?.deviceMemory;
7457
+ const used = perf?.memory?.usedJSHeapSize;
7458
+ return {
7459
+ ...typeof deviceMemory === "number" && deviceMemory > 0 ? { deviceMemory } : {},
7460
+ ...typeof used === "number" ? { usedHeap: used } : {}
7461
+ };
7462
+ }
7463
+
7376
7464
  // src/process/wasi/wasi-files.ts
7377
7465
  function normalize(path) {
7378
7466
  const out = [];
@@ -8561,11 +8649,11 @@ function realmCa(owner, store) {
8561
8649
  opened.set(store, cas);
8562
8650
  let ca = cas.get(owner);
8563
8651
  if (!ca) {
8564
- const opening = RealmCa.open(owner, store);
8565
- ca = opening;
8566
- cas.set(owner, opening);
8567
- opening.catch(() => {
8568
- if (cas.get(owner) === opening) cas.delete(owner);
8652
+ const opening2 = RealmCa.open(owner, store);
8653
+ ca = opening2;
8654
+ cas.set(owner, opening2);
8655
+ opening2.catch(() => {
8656
+ if (cas.get(owner) === opening2) cas.delete(owner);
8569
8657
  });
8570
8658
  }
8571
8659
  return ca;
@@ -10972,6 +11060,7 @@ var WasmProcess = class {
10972
11060
  throw Object.assign(new Error(alias.refused), { code: alias.refused });
10973
11061
  if (alias !== void 0) return this.openProc(req, alias);
10974
11062
  await refuseReadonly(this.options.fs, req.path, req.flags, req);
11063
+ await this.mayOpen(req);
10975
11064
  const fd = this.fds.install(heldFile(), 3);
10976
11065
  try {
10977
11066
  const nodes = this.nodes;
@@ -11017,6 +11106,17 @@ var WasmProcess = class {
11017
11106
  };
11018
11107
  return fs.exclusive ? fs.exclusive(first.path, look) : look();
11019
11108
  }
11109
+ async mayOpen(req) {
11110
+ const { guard: guard2 } = this.options;
11111
+ const inherited2 = req.orphan || req.contents !== void 0 || req.handle !== void 0;
11112
+ if (!guard2 || inherited2 || req.pin !== void 0) return;
11113
+ const g = { fs: guard2.fs, dac: guard2.dac, cred: this.cred, pid: this.pid };
11114
+ if (req.exclusive) return inParent(g, "open", req.path);
11115
+ if (req.created && guard2.created?.take(req.path)) return;
11116
+ const access2 = req.flags & WRITE_ACCESS;
11117
+ const want = (access2 === 1 ? 0 : R_OK) | (access2 !== 0 || req.truncate ? W_OK : 0);
11118
+ await openable(g, "open", req.path, want, req.create === true);
11119
+ }
11020
11120
  newOwner() {
11021
11121
  if (!this.options.owners) return void 0;
11022
11122
  return { uid: this.cred.euid, gid: this.cred.egid, mode: 438 & ~this.umask };
@@ -11701,7 +11801,7 @@ function inherited(opts) {
11701
11801
  function holdingToken(opts, holds, caller) {
11702
11802
  return mintSyncFsToken({
11703
11803
  ...opts.owners ? caller : {},
11704
- ...opts.dac ? { dac: opts.dac, pid: opts.pid } : {},
11804
+ ...opts.dac ? { dac: opts.dac, pid: opts.pid, created: caller.created } : {},
11705
11805
  fs: opts.fs,
11706
11806
  cwd: opts.cwd,
11707
11807
  ...opts.statfs ? { statfs: opts.statfs } : {},
@@ -11714,10 +11814,11 @@ function holdingToken(opts, holds, caller) {
11714
11814
  renamed: (from, to) => holds.renamed(from, to)
11715
11815
  });
11716
11816
  }
11717
- function processOptions(opts, header4, hooks) {
11817
+ function processOptions(opts, header4, hooks, created3) {
11718
11818
  return {
11719
11819
  ...inherited(opts),
11720
11820
  ...opts.owners ? { owners: true } : {},
11821
+ ...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3 } } : {},
11721
11822
  ...opts.identity ? { identity: opts.identity } : {},
11722
11823
  ...opts.onSyscall ? { onSyscall: opts.onSyscall } : {},
11723
11824
  ...opts.settling ? { settling: opts.settling } : {},
@@ -11751,20 +11852,27 @@ function spawnWasmProcess(opts) {
11751
11852
  const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
11752
11853
  const header4 = new Int32Array(sab, 0, SAB_HEADER_I32);
11753
11854
  const threadSignals = /* @__PURE__ */ new Map();
11855
+ const created3 = new Created();
11754
11856
  const process2 = new WasmProcess(
11755
11857
  opts.pid,
11756
11858
  opts.fds,
11757
- processOptions(opts, header4, {
11758
- raise: (sig) => signal(sig),
11759
- threadKill: (tid, sig) => killThread(threadSignals.get(tid), process2.signalMasks().caught, sig)
11760
- })
11859
+ processOptions(
11860
+ opts,
11861
+ header4,
11862
+ {
11863
+ raise: (sig) => signal(sig),
11864
+ threadKill: (tid, sig) => killThread(threadSignals.get(tid), process2.signalMasks().caught, sig)
11865
+ },
11866
+ created3
11867
+ )
11761
11868
  );
11762
11869
  publishStops(process2, header4);
11763
11870
  const holds = new HeldPaths();
11764
11871
  opts.held?.add(holds);
11765
11872
  const token = holdingToken(opts, holds, {
11766
11873
  cred: () => process2.cred,
11767
- umask: () => process2.umask
11874
+ umask: () => process2.umask,
11875
+ created: created3
11768
11876
  });
11769
11877
  process2.keptPath = (path) => holds.keptAt(path)?.hidden;
11770
11878
  process2.keptHandle = (handle2) => holds.keptFor(handle2)?.hidden;
@@ -12877,16 +12985,16 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
12877
12985
  });
12878
12986
  async function writable2(client, path, flags) {
12879
12987
  const { ifMatch: _, ...asked } = flags;
12880
- const opening = () => client.call("open", { path: rel(path), ...asked });
12988
+ const opening2 = () => client.call("open", { path: rel(path), ...asked });
12881
12989
  try {
12882
- return { opened: await opening(), restore: void 0 };
12990
+ return { opened: await opening2(), restore: void 0 };
12883
12991
  } catch (err) {
12884
12992
  if (!flags.write || codeOf4(err) !== "EACCES") throw err;
12885
12993
  const was = await client.call("stat", { path: rel(path) });
12886
12994
  const mode = was.mode ?? 0;
12887
12995
  if (mode & 128) throw err;
12888
12996
  const restore = { path: rel(path), mode, was };
12889
- const opened2 = await client.call("setattr", { path: rel(path), mode: mode | 128 }).then(opening).catch(async (again) => {
12997
+ const opened2 = await client.call("setattr", { path: rel(path), mode: mode | 128 }).then(opening2).catch(async (again) => {
12890
12998
  await settle(client, restore).catch(() => {
12891
12999
  });
12892
13000
  throw again;
@@ -14247,15 +14355,33 @@ var Launcher = class {
14247
14355
  return void 0;
14248
14356
  }
14249
14357
  const inner = await this.interpreted(interp, [interp, ...passed], cwd, scope, depth + 1);
14250
- return inner && { ...inner, target: withScriptEnv(inner.target, command) };
14358
+ const via = [interp, ...inner?.via ?? []];
14359
+ return inner && { ...inner, target: withScriptEnv(inner.target, command), via };
14251
14360
  }
14252
14361
  const target = withScriptEnv(found, command);
14253
- return { target, args: [...target.prefix ?? [], ...passed] };
14362
+ return { target, args: [...target.prefix ?? [], ...passed], via: [interp] };
14254
14363
  }
14255
14364
  async plan(file, argv, cwd, scope) {
14256
14365
  const direct = await this.resolve(file, argv[0] ?? file, cwd, scope);
14257
14366
  if (direct) return { target: direct, args: [...direct.prefix ?? [], ...argv.slice(1)] };
14258
- return this.interpreted(file, argv, cwd, scope);
14367
+ const script = await this.interpreted(file, argv, cwd, scope);
14368
+ return script && { ...script, script: true };
14369
+ }
14370
+ async mayExec(cred, req, planned) {
14371
+ const { dac } = this;
14372
+ if (!dac) return;
14373
+ const g = { fs: this.fs, cred, pid: req.pid, dac };
14374
+ const byPath = (file) => file.includes("/") && !COMMAND2.test(file);
14375
+ if (byPath(req.file)) {
14376
+ const path = this.fs.resolvePath(req.cwd, req.file);
14377
+ await need(g, "exec", path, planned?.script ? R_OK | X_OK : X_OK);
14378
+ }
14379
+ const via = planned?.via ?? [];
14380
+ for (const [i, file] of via.entries()) {
14381
+ if (!byPath(file)) continue;
14382
+ const want = i < via.length - 1 ? R_OK | X_OK : X_OK;
14383
+ await need(g, "exec", this.fs.resolvePath(req.cwd, file), want);
14384
+ }
14259
14385
  }
14260
14386
  async unrunnable(file, cwd, scope) {
14261
14387
  const path = this.fs.resolvePath(cwd, file);
@@ -14475,6 +14601,12 @@ var Launcher = class {
14475
14601
  return async (req, fds) => {
14476
14602
  const scope = await this.scopeOf(this.credOf(ppid).euid);
14477
14603
  const planned = await this.plan(req.file, req.argv, req.cwd, scope);
14604
+ try {
14605
+ await this.mayExec(this.credOf(ppid), { ...req, pid: ppid }, planned);
14606
+ } catch (err) {
14607
+ await fds.closeAll();
14608
+ throw err.code === "EACCES" ? new SpawnError("EACCES") : err;
14609
+ }
14478
14610
  if (!planned) {
14479
14611
  await fds.closeAll();
14480
14612
  throw new SpawnError(await this.unrunnable(req.file, req.cwd, scope));
@@ -14758,6 +14890,7 @@ var Launcher = class {
14758
14890
  }
14759
14891
  async prepare() {
14760
14892
  for (const dir of SHARED_DIRS) await this.base.mkdir(dir, { recursive: true });
14893
+ if (this.owners) await this.base.chmod("/tmp", 1023);
14761
14894
  await this.provisionPnpmHome();
14762
14895
  await this.users.ensure();
14763
14896
  await writeCaFile(this.base, this.ca).catch(() => void 0);
@@ -14805,7 +14938,9 @@ var Launcher = class {
14805
14938
  const cwd = await physicalPathAsync(this.base, start2.startsWith("/") ? start2 : `/${start2}`);
14806
14939
  const [file = ""] = argv;
14807
14940
  await this.base.mkdir(cwd, { recursive: true });
14808
- return { cwd, file, planned: await this.plan(file, argv, cwd, scope) };
14941
+ const planned = await this.plan(file, argv, cwd, scope);
14942
+ await this.mayExec(credOf(account), { file, cwd, pid: 0 }, planned);
14943
+ return { cwd, file, planned };
14809
14944
  }
14810
14945
  async openTerminal(argv, options) {
14811
14946
  const account = await this.account(options.user);