@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.
@@ -373,6 +373,9 @@ function writerTags(protection) {
373
373
  return new Set(writers);
374
374
  }
375
375
  function protectedPrefixes(paths) {
376
+ if (!Array.isArray(paths) || paths.some((p) => typeof p !== "string" || !p.startsWith("/"))) {
377
+ throw new TypeError("protect.paths must be a list of absolute paths");
378
+ }
376
379
  return [...new Set(paths.map((p) => normalizePath(p)))];
377
380
  }
378
381
  function within(path, prefix) {
@@ -476,6 +479,9 @@ var ProtectedFs = class {
476
479
  await check(path, true);
477
480
  return fs.utimes(path, atime, mtime);
478
481
  },
482
+ async mayChange(path, follow2) {
483
+ await check(path, follow2);
484
+ },
479
485
  async stat(path) {
480
486
  return marked(path, await fs.stat(path));
481
487
  },
@@ -1155,6 +1161,10 @@ var OpfsFs = class {
1155
1161
  };
1156
1162
  }
1157
1163
  async describe(found) {
1164
+ const stat = await this.described(found);
1165
+ return isProtected(this.guarded, found.path) ? { ...stat, readonly: true } : stat;
1166
+ }
1167
+ async described(found) {
1158
1168
  if (found.link) return this.owned(this.linkStat(found.link), found.path, found.link);
1159
1169
  const touched = this.timesOf(found.path);
1160
1170
  if (!this.owners) {
@@ -7318,6 +7328,83 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
7318
7328
  };
7319
7329
  }
7320
7330
 
7331
+ // src/fs/fresh-modules.ts
7332
+ var MAGIC = [0, 97, 115, 109];
7333
+ var MAX_FRESH = 65536;
7334
+ function isModule(bytes) {
7335
+ return MAGIC.every((b, i) => bytes[i] === b);
7336
+ }
7337
+ var FreshModules = class {
7338
+ fresh = /* @__PURE__ */ new Map();
7339
+ add(path, umask, pid) {
7340
+ const key = normalizePath(path);
7341
+ this.fresh.delete(key);
7342
+ const oldest = this.fresh.size >= MAX_FRESH ? this.fresh.keys().next().value : void 0;
7343
+ if (oldest !== void 0) this.fresh.delete(oldest);
7344
+ this.fresh.set(key, { umask: umask & 511, pid, added: 0 });
7345
+ }
7346
+ forget(pid) {
7347
+ for (const [path, fresh2] of this.fresh) if (fresh2.pid === pid) this.fresh.delete(path);
7348
+ }
7349
+ wrap(fs) {
7350
+ const decide = async (given, bytes) => {
7351
+ const path = normalizePath(given);
7352
+ const fresh2 = this.fresh.get(path);
7353
+ if (!fresh2 || typeof bytes === "string" || bytes.length < MAGIC.length) return;
7354
+ const want = isModule(bytes) ? 73 & ~fresh2.umask : 0;
7355
+ if (want === fresh2.added) return;
7356
+ const mode = (await fs.stat(path)).mode & 4095;
7357
+ await fs.chmod(path, mode & ~fresh2.added | want);
7358
+ fresh2.added = want;
7359
+ };
7360
+ const overrides = {
7361
+ writeFile: async (path, content, owner) => {
7362
+ await fs.writeFile(path, content, owner);
7363
+ await decide(path, content);
7364
+ },
7365
+ chmod: async (path, mode) => {
7366
+ const fresh2 = this.fresh.get(normalizePath(path));
7367
+ const creating = fresh2?.added === 0 && (mode & 4095) === (438 & ~fresh2.umask);
7368
+ if (!creating) this.fresh.delete(normalizePath(path));
7369
+ await fs.chmod(path, mode);
7370
+ },
7371
+ rm: async (path, options) => {
7372
+ this.fresh.delete(normalizePath(path));
7373
+ await fs.rm(path, options);
7374
+ },
7375
+ rename: async (from, to) => {
7376
+ await fs.rename(from, to);
7377
+ const fresh2 = this.fresh.get(normalizePath(from));
7378
+ this.fresh.delete(normalizePath(from));
7379
+ this.fresh.delete(normalizePath(to));
7380
+ if (fresh2) this.fresh.set(normalizePath(to), fresh2);
7381
+ }
7382
+ };
7383
+ const ranged2 = rangedOps(fs);
7384
+ if (ranged2) {
7385
+ overrides.pwrite = async (path, at, bytes, transfer) => {
7386
+ const head = at === 0 && this.fresh.has(normalizePath(path)) ? bytes.slice(0, 4) : void 0;
7387
+ await ranged2.pwrite(path, at, bytes, transfer);
7388
+ if (head) await decide(path, head);
7389
+ };
7390
+ }
7391
+ const bound = /* @__PURE__ */ new WeakMap();
7392
+ return new Proxy(fs, {
7393
+ get(target, key, receiver) {
7394
+ if (Object.hasOwn(overrides, key)) return overrides[key];
7395
+ const value = Reflect.get(target, key, receiver);
7396
+ if (typeof value !== "function") return value;
7397
+ let method = bound.get(value);
7398
+ if (method === void 0) {
7399
+ method = value.bind(target);
7400
+ bound.set(value, method);
7401
+ }
7402
+ return method;
7403
+ }
7404
+ });
7405
+ }
7406
+ };
7407
+
7321
7408
  // src/fs/physical-path.ts
7322
7409
  var MAX_HOPS = 40;
7323
7410
  function start(path) {
@@ -9194,6 +9281,7 @@ async function run(entry, path, req) {
9194
9281
  case "create":
9195
9282
  await createExclusive(fs, path, ownerFor2(entry, req.mode ?? 438));
9196
9283
  entry.created?.add(path);
9284
+ entry.fresh?.(path);
9197
9285
  return done;
9198
9286
  case "readlink":
9199
9287
  return json2(await fs.readlink(path));
@@ -9292,11 +9380,39 @@ async function accessOp(entry, path, req) {
9292
9380
  await need({ ...g, cred }, open ? "open" : "access", path, req.mode ?? 0, options);
9293
9381
  return done;
9294
9382
  }
9383
+ var CHANGING = /* @__PURE__ */ new Set([
9384
+ "mkdir",
9385
+ "create",
9386
+ "symlink",
9387
+ "unlink",
9388
+ "rmdir",
9389
+ "rm",
9390
+ "rename",
9391
+ "link",
9392
+ "utimes",
9393
+ "lutimes",
9394
+ "chmod",
9395
+ "chown",
9396
+ "lchown"
9397
+ ]);
9398
+ var CONTENT = /* @__PURE__ */ new Set(["write", "pwrite", "truncate"]);
9399
+ async function mayChange(entry, path, req) {
9400
+ const { mayChange: mayChange2 } = entry.fs;
9401
+ if (!mayChange2) return;
9402
+ const { op } = req;
9403
+ if (CONTENT.has(op) && req.handle === void 0) return mayChange2.call(entry.fs, path, true);
9404
+ if (!CHANGING.has(op)) return;
9405
+ await mayChange2.call(entry.fs, path, op === "utimes" || op === "chmod" || op === "chown");
9406
+ if (op === "rename" || op === "link") {
9407
+ await mayChange2.call(entry.fs, entry.fs.resolvePath(entry.cwd, req.arg2 ?? ""), false);
9408
+ }
9409
+ }
9295
9410
  async function guarded(entry, path, req) {
9296
9411
  const g = guardOf(entry);
9297
9412
  if (!g) return void 0;
9298
9413
  if (LOOKS.has(req.op) && await entry.holdsKept?.(path)) return void 0;
9299
9414
  try {
9415
+ await mayChange(entry, path, req);
9300
9416
  await guard(g, path, req, entry.cwd);
9301
9417
  return void 0;
9302
9418
  } catch (err) {
@@ -9575,6 +9691,7 @@ var IDLE_OPS = /* @__PURE__ */ new Set([
9575
9691
  ]);
9576
9692
  var SYS_CAP_US = 2e4;
9577
9693
  var LOCAL = SAB_CLOCK_OFFSET;
9694
+ var BLOCKED = 0;
9578
9695
  var IDLE = 1;
9579
9696
  function epochMicros() {
9580
9697
  return Math.floor((performance.timeOrigin + performance.now()) * 1e3);
@@ -9649,6 +9766,7 @@ var CpuClock = class {
9649
9766
  const busy = this.kind === "sys" ? Math.min(spent, SYS_CAP_US) : 0;
9650
9767
  this.sys += busy;
9651
9768
  this.idle += spent - busy;
9769
+ Atomics.store(slots(this.sab), BLOCKED, BigInt(this.idle));
9652
9770
  }
9653
9771
  };
9654
9772
  var ProcessClocks = class {
@@ -9993,8 +10111,8 @@ function pidStat(info, boot) {
9993
10111
  0,
9994
10112
  ticks(info.utime ?? 0),
9995
10113
  ticks(info.stime ?? 0),
9996
- 0,
9997
- 0,
10114
+ ticks(info.cutime ?? 0),
10115
+ ticks(info.cstime ?? 0),
9998
10116
  20,
9999
10117
  0,
10000
10118
  1,
@@ -10018,8 +10136,9 @@ function pidStat(info, boot) {
10018
10136
  `;
10019
10137
  }
10020
10138
  var sigMask = (mask = 0) => (mask >>> 1).toString(16).padStart(16, "0");
10139
+ var STATES = { S: "S (sleeping)", T: "T (stopped)", Z: "Z (zombie)" };
10021
10140
  function pidStatus(info) {
10022
- const state = info.state === "Z" ? "Z (zombie)" : "S (sleeping)";
10141
+ const state = STATES[info.state];
10023
10142
  const c = info.cred ?? ROOT;
10024
10143
  const ids = (r, e, s) => `${r} ${e} ${s} ${e}`;
10025
10144
  return [
@@ -12308,6 +12427,16 @@ function words(value) {
12308
12427
  }
12309
12428
  return [...value];
12310
12429
  }
12430
+ var ESCAPES = { " ": "\\t", "\n": "\\n", "\r": "\\r" };
12431
+ function clipboardLine(text) {
12432
+ let out = "";
12433
+ for (const c of text) {
12434
+ const code = c.charCodeAt(0);
12435
+ const control = code < 32 || code === 127;
12436
+ out += control ? ESCAPES[c] ?? `\\x${code.toString(16).padStart(2, "0")}` : c;
12437
+ }
12438
+ return out.length > 200 ? `${out.slice(0, 200)}\u2026` : out;
12439
+ }
12311
12440
  var DYING = { ok: false, errno: "EINTR", message: "the process is being killed" };
12312
12441
  var HOSTS_ONLY = new Resolver({ routes: new Routes() });
12313
12442
  var WasmProcess = class {
@@ -12560,7 +12689,22 @@ var WasmProcess = class {
12560
12689
  if (req.max <= 0) return new Uint8Array(0);
12561
12690
  if (channel.unread) channel.stamp = await this.clipboardStamp();
12562
12691
  const signal = channel.settled ? this.interrupt.signal : this.blockingSignal(cancelled);
12563
- return channel.read(Math.min(req.max, MAX_READ), signal);
12692
+ try {
12693
+ return await channel.read(Math.min(req.max, MAX_READ), signal);
12694
+ } catch (err) {
12695
+ await this.clipboardComplaint(err);
12696
+ throw err;
12697
+ }
12698
+ }
12699
+ async clipboardComplaint(err) {
12700
+ const name2 = this.options.clipboardName;
12701
+ if (name2 === void 0 || !(err instanceof KernelError) || err.detail === void 0) return;
12702
+ if (err.code !== "EACCES" && err.code !== "EIO") return;
12703
+ const stderr = this.fds.has(2) ? this.fds.get(2).file : void 0;
12704
+ if (!stderr?.write || stderr.clipboard) return;
12705
+ const line = `${name2}: /dev/clipboard: ${clipboardLine(err.detail)}
12706
+ `;
12707
+ await stderr.write(new TextEncoder().encode(line), AbortSignal.timeout(1e3)).catch(() => 0);
12564
12708
  }
12565
12709
  async write(fd, body, nonblock = false, cancelled) {
12566
12710
  const file = this.fds.get(fd).file;
@@ -12585,6 +12729,7 @@ var WasmProcess = class {
12585
12729
  throw Object.assign(new Error(alias.refused), { code: alias.refused });
12586
12730
  if (alias !== void 0) return this.openProc(req, alias);
12587
12731
  await refuseReadonly(this.options.fs, req.path, req.flags, req);
12732
+ if (this.writesAt(req)) await this.options.fs.mayChange?.(req.path, !req.exclusive);
12588
12733
  await this.mayOpen(req);
12589
12734
  const fd = this.fds.install(heldFile(), 3);
12590
12735
  try {
@@ -12773,7 +12918,10 @@ var WasmProcess = class {
12773
12918
  this.options.locks?.closed(this.pid, req.fd);
12774
12919
  const channel = this.fds.get(req.fd).file.clipboard;
12775
12920
  if (channel) channel.stamp = await this.clipboardStamp();
12776
- await Promise.resolve(this.fds.close(req.fd));
12921
+ await Promise.resolve(this.fds.close(req.fd)).catch(async (err) => {
12922
+ if (channel) await this.clipboardComplaint(err);
12923
+ throw err;
12924
+ });
12777
12925
  return { ok: true, kind: "void" };
12778
12926
  }
12779
12927
  case "fd-pipe": {
@@ -13452,6 +13600,7 @@ function holdingToken(opts, holds, caller) {
13452
13600
  return mintSyncFsToken({
13453
13601
  ...opts.owners ? caller : {},
13454
13602
  ...opts.dac ? { dac: opts.dac, pid: opts.pid, created: caller.created } : {},
13603
+ ...opts.fresh ? { fresh: (path) => opts.fresh?.(path, caller.umask()) } : {},
13455
13604
  fs: opts.fs,
13456
13605
  cwd: opts.cwd,
13457
13606
  ...opts.statfs ? { statfs: opts.statfs } : {},
@@ -13464,12 +13613,15 @@ function holdingToken(opts, holds, caller) {
13464
13613
  renamed: (from, to) => holds.renamed(from, to)
13465
13614
  });
13466
13615
  }
13616
+ function clipboardName(opts) {
13617
+ return opts.program.abi === "js" ? {} : { clipboardName: opts.argv0 };
13618
+ }
13467
13619
  function processOptions(opts, header3, hooks, created3) {
13468
13620
  return {
13469
13621
  ...inherited(opts),
13470
13622
  ...opts.owners ? { owners: true } : {},
13471
13623
  ...opts.sealed ? { sealed: opts.sealed } : {},
13472
- ...opts.clipboard ? { clipboard: opts.clipboard } : {},
13624
+ ...opts.clipboard ? { clipboard: opts.clipboard, ...clipboardName(opts) } : {},
13473
13625
  ...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
13474
13626
  ...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3 } } : {},
13475
13627
  ...opts.identity ? { identity: opts.identity } : {},
@@ -16258,6 +16410,10 @@ function concat5(chunks) {
16258
16410
  }
16259
16411
  return out;
16260
16412
  }
16413
+ function addTimes(into, pid, t) {
16414
+ const at = into.get(pid) ?? { utime: 0, stime: 0 };
16415
+ into.set(pid, { utime: at.utime + t.utime, stime: at.stime + t.stime });
16416
+ }
16261
16417
  var Launcher = class {
16262
16418
  fs;
16263
16419
  processFs;
@@ -16283,6 +16439,7 @@ var Launcher = class {
16283
16439
  described = /* @__PURE__ */ new Map();
16284
16440
  creds = /* @__PURE__ */ new Map();
16285
16441
  tags = /* @__PURE__ */ new Map();
16442
+ freshModules = new FreshModules();
16286
16443
  granted = /* @__PURE__ */ new Map();
16287
16444
  users;
16288
16445
  sudoer;
@@ -16353,7 +16510,8 @@ var Launcher = class {
16353
16510
  changed: this.watchers.changed.bind(this.watchers),
16354
16511
  owners: this.owners
16355
16512
  });
16356
- this.base = this.watchers.wrap(this.mounts.wrap(options.fs));
16513
+ const watched = this.watchers.wrap(this.mounts.wrap(options.fs));
16514
+ this.base = this.owners ? this.freshModules.wrap(watched) : watched;
16357
16515
  this.createWorker = options.createWorker;
16358
16516
  this.createDriverWorker = options.createDriverWorker;
16359
16517
  this.media = options.media ?? memoryMedia();
@@ -16756,6 +16914,7 @@ var Launcher = class {
16756
16914
  ...req.ignored ? { ignored: req.ignored } : {},
16757
16915
  ...req.umask !== void 0 ? { umask: req.umask } : {},
16758
16916
  cred: req.cred,
16917
+ ...this.owners && req.program.abi === "wasi" ? { fresh: (path, umask) => this.freshModules.add(path, umask, pid) } : {},
16759
16918
  ...this.owners ? { owners: true } : {},
16760
16919
  sealed: this.sealed,
16761
16920
  clipboard: this.clipboard,
@@ -16795,7 +16954,7 @@ var Launcher = class {
16795
16954
  http: new HttpHandles(this.transport, pid),
16796
16955
  locks: this.locks,
16797
16956
  resolver: this.resolver,
16798
- onReap: (child) => this.reaped(child),
16957
+ onReap: (child) => this.reaped(child, pid),
16799
16958
  ...req.fork ? { fork: req.fork } : {},
16800
16959
  ...this.shownIds(req)
16801
16960
  });
@@ -16814,9 +16973,15 @@ var Launcher = class {
16814
16973
  tty: terminal?.name ?? null,
16815
16974
  started: Date.now()
16816
16975
  });
16976
+ handle.onState(
16977
+ (state) => state === "stopped" ? this.stopped.add(pid) : this.stopped.delete(pid)
16978
+ );
16817
16979
  void handle.exited.then(() => {
16980
+ this.freshModules.forget(pid);
16818
16981
  const spent = handle.times?.();
16819
16982
  if (spent) this.spent.set(pid, spent);
16983
+ if (spent) addTimes(this.imageTimes, this.jobs.shown(pid), spent);
16984
+ this.stopped.delete(pid);
16820
16985
  this.processes.delete(pid);
16821
16986
  if (this.processes.size === 0) {
16822
16987
  clearInterval(this.sampler);
@@ -16844,11 +17009,20 @@ var Launcher = class {
16844
17009
  this.reapedTimes.stime += spent.stime;
16845
17010
  }
16846
17011
  this.spent.delete(pid);
17012
+ this.imageTimes.delete(pid);
17013
+ this.childTimes.delete(pid);
16847
17014
  this.creds.delete(pid);
16848
17015
  this.tags.delete(pid);
16849
17016
  this.granted.delete(pid);
16850
17017
  }
16851
- reaped(pid) {
17018
+ reaped(pid, parent) {
17019
+ const own = this.imageTimes.get(pid);
17020
+ const theirs = this.childTimes.get(pid);
17021
+ const into = this.jobs.shown(parent);
17022
+ if (own) addTimes(this.childTimes, into, own);
17023
+ if (theirs) addTimes(this.childTimes, into, theirs);
17024
+ this.imageTimes.delete(pid);
17025
+ this.childTimes.delete(pid);
16852
17026
  if (this.zombies.delete(pid)) this.forget(pid);
16853
17027
  else this.orphans.add(pid);
16854
17028
  }
@@ -16900,6 +17074,17 @@ var Launcher = class {
16900
17074
  this.sampler.unref?.();
16901
17075
  }
16902
17076
  spent = /* @__PURE__ */ new Map();
17077
+ imageTimes = /* @__PURE__ */ new Map();
17078
+ childTimes = /* @__PURE__ */ new Map();
17079
+ stopped = /* @__PURE__ */ new Set();
17080
+ stateOf(pid) {
17081
+ if (!this.processes.has(pid)) return "Z";
17082
+ return this.stopped.has(pid) ? "T" : "S";
17083
+ }
17084
+ childrenOf(pid) {
17085
+ const t = this.childTimes.get(pid);
17086
+ return t ? { cutime: t.utime, cstime: t.stime } : {};
17087
+ }
16903
17088
  timesAt(pid) {
16904
17089
  return this.processes.get(pid)?.times?.() ?? this.spent.get(pid);
16905
17090
  }
@@ -16945,9 +17130,10 @@ var Launcher = class {
16945
17130
  sid: member.sid,
16946
17131
  ...described2,
16947
17132
  started: (this.described.get(root.pid) ?? described2).started,
16948
- state: this.processes.has(member.pid) ? "S" : "Z",
17133
+ state: this.stateOf(member.pid),
16949
17134
  memory: this.processes.get(member.pid)?.memory() ?? 0,
16950
17135
  ...this.timesOf(member, members),
17136
+ ...this.childrenOf(root.pid),
16951
17137
  umask: this.umaskOf(member.pid),
16952
17138
  cred,
16953
17139
  ...this.signalMasksOf(member.pid)
@@ -17458,8 +17644,9 @@ var Launcher = class {
17458
17644
  return this.writers;
17459
17645
  }
17460
17646
  writerStack() {
17647
+ const watched = this.watchers.wrap(this.mounts.wrap(this.unguardedRoot));
17461
17648
  const fs = withCommandDirs(
17462
- this.watchers.wrap(this.mounts.wrap(this.unguardedRoot)),
17649
+ this.owners ? this.freshModules.wrap(watched) : watched,
17463
17650
  async () => new Set((await this.commands()).keys()),
17464
17651
  (dir) => this.userCommandDir(dir),
17465
17652
  this.owners