@ai-ecoverse/slicc-kernel 1.76.0 → 1.77.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/node.js CHANGED
@@ -3022,6 +3022,9 @@ function writerTags(protection) {
3022
3022
  return new Set(writers);
3023
3023
  }
3024
3024
  function protectedPrefixes(paths) {
3025
+ if (!Array.isArray(paths) || paths.some((p) => typeof p !== "string" || !p.startsWith("/"))) {
3026
+ throw new TypeError("protect.paths must be a list of absolute paths");
3027
+ }
3025
3028
  return [...new Set(paths.map((p) => normalizePath(p)))];
3026
3029
  }
3027
3030
  function within(path, prefix) {
@@ -3125,6 +3128,9 @@ var ProtectedFs = class {
3125
3128
  await check(path, true);
3126
3129
  return fs.utimes(path, atime, mtime);
3127
3130
  },
3131
+ async mayChange(path, follow2) {
3132
+ await check(path, follow2);
3133
+ },
3128
3134
  async stat(path) {
3129
3135
  return marked(path, await fs.stat(path));
3130
3136
  },
@@ -3800,6 +3806,10 @@ var OpfsFs = class {
3800
3806
  };
3801
3807
  }
3802
3808
  async describe(found) {
3809
+ const stat = await this.described(found);
3810
+ return isProtected(this.guarded, found.path) ? { ...stat, readonly: true } : stat;
3811
+ }
3812
+ async described(found) {
3803
3813
  if (found.link) return this.owned(this.linkStat(found.link), found.path, found.link);
3804
3814
  const touched = this.timesOf(found.path);
3805
3815
  if (!this.owners) {
@@ -5759,6 +5769,83 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
5759
5769
  };
5760
5770
  }
5761
5771
 
5772
+ // src/fs/fresh-modules.ts
5773
+ var MAGIC = [0, 97, 115, 109];
5774
+ var MAX_FRESH = 65536;
5775
+ function isModule(bytes) {
5776
+ return MAGIC.every((b, i) => bytes[i] === b);
5777
+ }
5778
+ var FreshModules = class {
5779
+ fresh = /* @__PURE__ */ new Map();
5780
+ add(path, umask, pid) {
5781
+ const key = normalizePath(path);
5782
+ this.fresh.delete(key);
5783
+ const oldest = this.fresh.size >= MAX_FRESH ? this.fresh.keys().next().value : void 0;
5784
+ if (oldest !== void 0) this.fresh.delete(oldest);
5785
+ this.fresh.set(key, { umask: umask & 511, pid, added: 0 });
5786
+ }
5787
+ forget(pid) {
5788
+ for (const [path, fresh2] of this.fresh) if (fresh2.pid === pid) this.fresh.delete(path);
5789
+ }
5790
+ wrap(fs) {
5791
+ const decide = async (given, bytes) => {
5792
+ const path = normalizePath(given);
5793
+ const fresh2 = this.fresh.get(path);
5794
+ if (!fresh2 || typeof bytes === "string" || bytes.length < MAGIC.length) return;
5795
+ const want = isModule(bytes) ? 73 & ~fresh2.umask : 0;
5796
+ if (want === fresh2.added) return;
5797
+ const mode = (await fs.stat(path)).mode & 4095;
5798
+ await fs.chmod(path, mode & ~fresh2.added | want);
5799
+ fresh2.added = want;
5800
+ };
5801
+ const overrides = {
5802
+ writeFile: async (path, content, owner) => {
5803
+ await fs.writeFile(path, content, owner);
5804
+ await decide(path, content);
5805
+ },
5806
+ chmod: async (path, mode) => {
5807
+ const fresh2 = this.fresh.get(normalizePath(path));
5808
+ const creating = fresh2?.added === 0 && (mode & 4095) === (438 & ~fresh2.umask);
5809
+ if (!creating) this.fresh.delete(normalizePath(path));
5810
+ await fs.chmod(path, mode);
5811
+ },
5812
+ rm: async (path, options) => {
5813
+ this.fresh.delete(normalizePath(path));
5814
+ await fs.rm(path, options);
5815
+ },
5816
+ rename: async (from, to) => {
5817
+ await fs.rename(from, to);
5818
+ const fresh2 = this.fresh.get(normalizePath(from));
5819
+ this.fresh.delete(normalizePath(from));
5820
+ this.fresh.delete(normalizePath(to));
5821
+ if (fresh2) this.fresh.set(normalizePath(to), fresh2);
5822
+ }
5823
+ };
5824
+ const ranged2 = rangedOps(fs);
5825
+ if (ranged2) {
5826
+ overrides.pwrite = async (path, at, bytes, transfer) => {
5827
+ const head = at === 0 && this.fresh.has(normalizePath(path)) ? bytes.slice(0, 4) : void 0;
5828
+ await ranged2.pwrite(path, at, bytes, transfer);
5829
+ if (head) await decide(path, head);
5830
+ };
5831
+ }
5832
+ const bound = /* @__PURE__ */ new WeakMap();
5833
+ return new Proxy(fs, {
5834
+ get(target, key, receiver) {
5835
+ if (Object.hasOwn(overrides, key)) return overrides[key];
5836
+ const value = Reflect.get(target, key, receiver);
5837
+ if (typeof value !== "function") return value;
5838
+ let method = bound.get(value);
5839
+ if (method === void 0) {
5840
+ method = value.bind(target);
5841
+ bound.set(value, method);
5842
+ }
5843
+ return method;
5844
+ }
5845
+ });
5846
+ }
5847
+ };
5848
+
5762
5849
  // src/fs/physical-path.ts
5763
5850
  var MAX_HOPS = 40;
5764
5851
  function start(path) {
@@ -7580,6 +7667,7 @@ async function run(entry, path, req) {
7580
7667
  case "create":
7581
7668
  await createExclusive(fs, path, ownerFor2(entry, req.mode ?? 438));
7582
7669
  entry.created?.add(path);
7670
+ entry.fresh?.(path);
7583
7671
  return done;
7584
7672
  case "readlink":
7585
7673
  return json2(await fs.readlink(path));
@@ -7678,11 +7766,39 @@ async function accessOp(entry, path, req) {
7678
7766
  await need({ ...g, cred }, open ? "open" : "access", path, req.mode ?? 0, options);
7679
7767
  return done;
7680
7768
  }
7769
+ var CHANGING = /* @__PURE__ */ new Set([
7770
+ "mkdir",
7771
+ "create",
7772
+ "symlink",
7773
+ "unlink",
7774
+ "rmdir",
7775
+ "rm",
7776
+ "rename",
7777
+ "link",
7778
+ "utimes",
7779
+ "lutimes",
7780
+ "chmod",
7781
+ "chown",
7782
+ "lchown"
7783
+ ]);
7784
+ var CONTENT = /* @__PURE__ */ new Set(["write", "pwrite", "truncate"]);
7785
+ async function mayChange(entry, path, req) {
7786
+ const { mayChange: mayChange2 } = entry.fs;
7787
+ if (!mayChange2) return;
7788
+ const { op } = req;
7789
+ if (CONTENT.has(op) && req.handle === void 0) return mayChange2.call(entry.fs, path, true);
7790
+ if (!CHANGING.has(op)) return;
7791
+ await mayChange2.call(entry.fs, path, op === "utimes" || op === "chmod" || op === "chown");
7792
+ if (op === "rename" || op === "link") {
7793
+ await mayChange2.call(entry.fs, entry.fs.resolvePath(entry.cwd, req.arg2 ?? ""), false);
7794
+ }
7795
+ }
7681
7796
  async function guarded(entry, path, req) {
7682
7797
  const g = guardOf(entry);
7683
7798
  if (!g) return void 0;
7684
7799
  if (LOOKS.has(req.op) && await entry.holdsKept?.(path)) return void 0;
7685
7800
  try {
7801
+ await mayChange(entry, path, req);
7686
7802
  await guard(g, path, req, entry.cwd);
7687
7803
  return void 0;
7688
7804
  } catch (err) {
@@ -7961,6 +8077,7 @@ var IDLE_OPS = /* @__PURE__ */ new Set([
7961
8077
  ]);
7962
8078
  var SYS_CAP_US = 2e4;
7963
8079
  var LOCAL = SAB_CLOCK_OFFSET;
8080
+ var BLOCKED = 0;
7964
8081
  var IDLE = 1;
7965
8082
  function epochMicros() {
7966
8083
  return Math.floor((performance.timeOrigin + performance.now()) * 1e3);
@@ -8035,6 +8152,7 @@ var CpuClock = class {
8035
8152
  const busy = this.kind === "sys" ? Math.min(spent, SYS_CAP_US) : 0;
8036
8153
  this.sys += busy;
8037
8154
  this.idle += spent - busy;
8155
+ Atomics.store(slots(this.sab), BLOCKED, BigInt(this.idle));
8038
8156
  }
8039
8157
  };
8040
8158
  var ProcessClocks = class {
@@ -8406,8 +8524,8 @@ function pidStat(info, boot) {
8406
8524
  0,
8407
8525
  ticks(info.utime ?? 0),
8408
8526
  ticks(info.stime ?? 0),
8409
- 0,
8410
- 0,
8527
+ ticks(info.cutime ?? 0),
8528
+ ticks(info.cstime ?? 0),
8411
8529
  20,
8412
8530
  0,
8413
8531
  1,
@@ -8431,8 +8549,9 @@ function pidStat(info, boot) {
8431
8549
  `;
8432
8550
  }
8433
8551
  var sigMask = (mask = 0) => (mask >>> 1).toString(16).padStart(16, "0");
8552
+ var STATES = { S: "S (sleeping)", T: "T (stopped)", Z: "Z (zombie)" };
8434
8553
  function pidStatus(info) {
8435
- const state = info.state === "Z" ? "Z (zombie)" : "S (sleeping)";
8554
+ const state = STATES[info.state];
8436
8555
  const c = info.cred ?? ROOT;
8437
8556
  const ids = (r, e, s) => `${r} ${e} ${s} ${e}`;
8438
8557
  return [
@@ -12042,6 +12161,16 @@ function words(value) {
12042
12161
  }
12043
12162
  return [...value];
12044
12163
  }
12164
+ var ESCAPES = { " ": "\\t", "\n": "\\n", "\r": "\\r" };
12165
+ function clipboardLine(text) {
12166
+ let out = "";
12167
+ for (const c of text) {
12168
+ const code = c.charCodeAt(0);
12169
+ const control = code < 32 || code === 127;
12170
+ out += control ? ESCAPES[c] ?? `\\x${code.toString(16).padStart(2, "0")}` : c;
12171
+ }
12172
+ return out.length > 200 ? `${out.slice(0, 200)}\u2026` : out;
12173
+ }
12045
12174
  var DYING = { ok: false, errno: "EINTR", message: "the process is being killed" };
12046
12175
  var HOSTS_ONLY = new Resolver({ routes: new Routes() });
12047
12176
  var WasmProcess = class {
@@ -12294,7 +12423,22 @@ var WasmProcess = class {
12294
12423
  if (req.max <= 0) return new Uint8Array(0);
12295
12424
  if (channel.unread) channel.stamp = await this.clipboardStamp();
12296
12425
  const signal = channel.settled ? this.interrupt.signal : this.blockingSignal(cancelled);
12297
- return channel.read(Math.min(req.max, MAX_READ), signal);
12426
+ try {
12427
+ return await channel.read(Math.min(req.max, MAX_READ), signal);
12428
+ } catch (err) {
12429
+ await this.clipboardComplaint(err);
12430
+ throw err;
12431
+ }
12432
+ }
12433
+ async clipboardComplaint(err) {
12434
+ const name2 = this.options.clipboardName;
12435
+ if (name2 === void 0 || !(err instanceof KernelError) || err.detail === void 0) return;
12436
+ if (err.code !== "EACCES" && err.code !== "EIO") return;
12437
+ const stderr = this.fds.has(2) ? this.fds.get(2).file : void 0;
12438
+ if (!stderr?.write || stderr.clipboard) return;
12439
+ const line = `${name2}: /dev/clipboard: ${clipboardLine(err.detail)}
12440
+ `;
12441
+ await stderr.write(new TextEncoder().encode(line), AbortSignal.timeout(1e3)).catch(() => 0);
12298
12442
  }
12299
12443
  async write(fd, body, nonblock = false, cancelled) {
12300
12444
  const file = this.fds.get(fd).file;
@@ -12319,6 +12463,7 @@ var WasmProcess = class {
12319
12463
  throw Object.assign(new Error(alias.refused), { code: alias.refused });
12320
12464
  if (alias !== void 0) return this.openProc(req, alias);
12321
12465
  await refuseReadonly(this.options.fs, req.path, req.flags, req);
12466
+ if (this.writesAt(req)) await this.options.fs.mayChange?.(req.path, !req.exclusive);
12322
12467
  await this.mayOpen(req);
12323
12468
  const fd = this.fds.install(heldFile(), 3);
12324
12469
  try {
@@ -12507,7 +12652,10 @@ var WasmProcess = class {
12507
12652
  this.options.locks?.closed(this.pid, req.fd);
12508
12653
  const channel = this.fds.get(req.fd).file.clipboard;
12509
12654
  if (channel) channel.stamp = await this.clipboardStamp();
12510
- await Promise.resolve(this.fds.close(req.fd));
12655
+ await Promise.resolve(this.fds.close(req.fd)).catch(async (err) => {
12656
+ if (channel) await this.clipboardComplaint(err);
12657
+ throw err;
12658
+ });
12511
12659
  return { ok: true, kind: "void" };
12512
12660
  }
12513
12661
  case "fd-pipe": {
@@ -13186,6 +13334,7 @@ function holdingToken(opts, holds, caller) {
13186
13334
  return mintSyncFsToken({
13187
13335
  ...opts.owners ? caller : {},
13188
13336
  ...opts.dac ? { dac: opts.dac, pid: opts.pid, created: caller.created } : {},
13337
+ ...opts.fresh ? { fresh: (path) => opts.fresh?.(path, caller.umask()) } : {},
13189
13338
  fs: opts.fs,
13190
13339
  cwd: opts.cwd,
13191
13340
  ...opts.statfs ? { statfs: opts.statfs } : {},
@@ -13198,12 +13347,15 @@ function holdingToken(opts, holds, caller) {
13198
13347
  renamed: (from, to) => holds.renamed(from, to)
13199
13348
  });
13200
13349
  }
13350
+ function clipboardName(opts) {
13351
+ return opts.program.abi === "js" ? {} : { clipboardName: opts.argv0 };
13352
+ }
13201
13353
  function processOptions(opts, header4, hooks, created3) {
13202
13354
  return {
13203
13355
  ...inherited(opts),
13204
13356
  ...opts.owners ? { owners: true } : {},
13205
13357
  ...opts.sealed ? { sealed: opts.sealed } : {},
13206
- ...opts.clipboard ? { clipboard: opts.clipboard } : {},
13358
+ ...opts.clipboard ? { clipboard: opts.clipboard, ...clipboardName(opts) } : {},
13207
13359
  ...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
13208
13360
  ...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3 } } : {},
13209
13361
  ...opts.identity ? { identity: opts.identity } : {},
@@ -16095,6 +16247,10 @@ function concat5(chunks2) {
16095
16247
  }
16096
16248
  return out;
16097
16249
  }
16250
+ function addTimes(into, pid, t) {
16251
+ const at = into.get(pid) ?? { utime: 0, stime: 0 };
16252
+ into.set(pid, { utime: at.utime + t.utime, stime: at.stime + t.stime });
16253
+ }
16098
16254
  var Launcher = class {
16099
16255
  fs;
16100
16256
  processFs;
@@ -16120,6 +16276,7 @@ var Launcher = class {
16120
16276
  described = /* @__PURE__ */ new Map();
16121
16277
  creds = /* @__PURE__ */ new Map();
16122
16278
  tags = /* @__PURE__ */ new Map();
16279
+ freshModules = new FreshModules();
16123
16280
  granted = /* @__PURE__ */ new Map();
16124
16281
  users;
16125
16282
  sudoer;
@@ -16190,7 +16347,8 @@ var Launcher = class {
16190
16347
  changed: this.watchers.changed.bind(this.watchers),
16191
16348
  owners: this.owners
16192
16349
  });
16193
- this.base = this.watchers.wrap(this.mounts.wrap(options.fs));
16350
+ const watched = this.watchers.wrap(this.mounts.wrap(options.fs));
16351
+ this.base = this.owners ? this.freshModules.wrap(watched) : watched;
16194
16352
  this.createWorker = options.createWorker;
16195
16353
  this.createDriverWorker = options.createDriverWorker;
16196
16354
  this.media = options.media ?? memoryMedia();
@@ -16593,6 +16751,7 @@ var Launcher = class {
16593
16751
  ...req.ignored ? { ignored: req.ignored } : {},
16594
16752
  ...req.umask !== void 0 ? { umask: req.umask } : {},
16595
16753
  cred: req.cred,
16754
+ ...this.owners && req.program.abi === "wasi" ? { fresh: (path, umask) => this.freshModules.add(path, umask, pid) } : {},
16596
16755
  ...this.owners ? { owners: true } : {},
16597
16756
  sealed: this.sealed,
16598
16757
  clipboard: this.clipboard,
@@ -16632,7 +16791,7 @@ var Launcher = class {
16632
16791
  http: new HttpHandles(this.transport, pid),
16633
16792
  locks: this.locks,
16634
16793
  resolver: this.resolver,
16635
- onReap: (child) => this.reaped(child),
16794
+ onReap: (child) => this.reaped(child, pid),
16636
16795
  ...req.fork ? { fork: req.fork } : {},
16637
16796
  ...this.shownIds(req)
16638
16797
  });
@@ -16651,9 +16810,15 @@ var Launcher = class {
16651
16810
  tty: terminal?.name ?? null,
16652
16811
  started: Date.now()
16653
16812
  });
16813
+ handle.onState(
16814
+ (state) => state === "stopped" ? this.stopped.add(pid) : this.stopped.delete(pid)
16815
+ );
16654
16816
  void handle.exited.then(() => {
16817
+ this.freshModules.forget(pid);
16655
16818
  const spent = handle.times?.();
16656
16819
  if (spent) this.spent.set(pid, spent);
16820
+ if (spent) addTimes(this.imageTimes, this.jobs.shown(pid), spent);
16821
+ this.stopped.delete(pid);
16657
16822
  this.processes.delete(pid);
16658
16823
  if (this.processes.size === 0) {
16659
16824
  clearInterval(this.sampler);
@@ -16681,11 +16846,20 @@ var Launcher = class {
16681
16846
  this.reapedTimes.stime += spent.stime;
16682
16847
  }
16683
16848
  this.spent.delete(pid);
16849
+ this.imageTimes.delete(pid);
16850
+ this.childTimes.delete(pid);
16684
16851
  this.creds.delete(pid);
16685
16852
  this.tags.delete(pid);
16686
16853
  this.granted.delete(pid);
16687
16854
  }
16688
- reaped(pid) {
16855
+ reaped(pid, parent) {
16856
+ const own = this.imageTimes.get(pid);
16857
+ const theirs = this.childTimes.get(pid);
16858
+ const into = this.jobs.shown(parent);
16859
+ if (own) addTimes(this.childTimes, into, own);
16860
+ if (theirs) addTimes(this.childTimes, into, theirs);
16861
+ this.imageTimes.delete(pid);
16862
+ this.childTimes.delete(pid);
16689
16863
  if (this.zombies.delete(pid)) this.forget(pid);
16690
16864
  else this.orphans.add(pid);
16691
16865
  }
@@ -16737,6 +16911,17 @@ var Launcher = class {
16737
16911
  this.sampler.unref?.();
16738
16912
  }
16739
16913
  spent = /* @__PURE__ */ new Map();
16914
+ imageTimes = /* @__PURE__ */ new Map();
16915
+ childTimes = /* @__PURE__ */ new Map();
16916
+ stopped = /* @__PURE__ */ new Set();
16917
+ stateOf(pid) {
16918
+ if (!this.processes.has(pid)) return "Z";
16919
+ return this.stopped.has(pid) ? "T" : "S";
16920
+ }
16921
+ childrenOf(pid) {
16922
+ const t = this.childTimes.get(pid);
16923
+ return t ? { cutime: t.utime, cstime: t.stime } : {};
16924
+ }
16740
16925
  timesAt(pid) {
16741
16926
  return this.processes.get(pid)?.times?.() ?? this.spent.get(pid);
16742
16927
  }
@@ -16782,9 +16967,10 @@ var Launcher = class {
16782
16967
  sid: member.sid,
16783
16968
  ...described2,
16784
16969
  started: (this.described.get(root.pid) ?? described2).started,
16785
- state: this.processes.has(member.pid) ? "S" : "Z",
16970
+ state: this.stateOf(member.pid),
16786
16971
  memory: this.processes.get(member.pid)?.memory() ?? 0,
16787
16972
  ...this.timesOf(member, members),
16973
+ ...this.childrenOf(root.pid),
16788
16974
  umask: this.umaskOf(member.pid),
16789
16975
  cred,
16790
16976
  ...this.signalMasksOf(member.pid)
@@ -17295,8 +17481,9 @@ var Launcher = class {
17295
17481
  return this.writers;
17296
17482
  }
17297
17483
  writerStack() {
17484
+ const watched = this.watchers.wrap(this.mounts.wrap(this.unguardedRoot));
17298
17485
  const fs = withCommandDirs(
17299
- this.watchers.wrap(this.mounts.wrap(this.unguardedRoot)),
17486
+ this.owners ? this.freshModules.wrap(watched) : watched,
17300
17487
  async () => new Set((await this.commands()).keys()),
17301
17488
  (dir) => this.userCommandDir(dir),
17302
17489
  this.owners