@ai-ecoverse/slicc-kernel 1.85.0 → 1.86.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
@@ -107,6 +107,12 @@ async function lutimesOf(fs, path, atime, mtime) {
107
107
  function mayLink(fs, path) {
108
108
  return fs.links?.(path) ?? true;
109
109
  }
110
+ function modeOption(mode) {
111
+ if (!Number.isInteger(mode) || mode < 0 || mode > 4095) {
112
+ throw fsError("EINVAL", `not a mode: ${String(mode)}`);
113
+ }
114
+ return mode;
115
+ }
110
116
  function fsError(code, path) {
111
117
  return Object.assign(new Error(`${code}: ${path}`), { code });
112
118
  }
@@ -570,7 +576,9 @@ function id(value) {
570
576
  return value;
571
577
  }
572
578
  function changeCred(cur, change) {
573
- if (typeof change !== "object" || change === null) throw fsError("EINVAL", "not a change");
579
+ if (typeof change !== "object" || change === null || Array.isArray(change)) {
580
+ throw fsError("EINVAL", "not a change");
581
+ }
574
582
  const privileged = cur.euid === 0;
575
583
  const next2 = copyCred(cur);
576
584
  for (const keys of [UIDS, GIDS]) {
@@ -3002,6 +3010,9 @@ var MemoryMeta = class {
3002
3010
  async get(paths) {
3003
3011
  return paths.map((path) => this.entries.get(path));
3004
3012
  }
3013
+ async fresh(path) {
3014
+ return this.entries.get(path);
3015
+ }
3005
3016
  async update(path, change) {
3006
3017
  this.set(path, change(this.entries.get(path)));
3007
3018
  }
@@ -3473,7 +3484,8 @@ function sidecar(meta) {
3473
3484
  const guarded3 = (op) => (...args) => meta[op].apply(meta, args).catch((err) => {
3474
3485
  throw translate(err, pathOf(args), "sidecar", false);
3475
3486
  });
3476
- const ops = Object.fromEntries(META_OPS.map((op) => [op, guarded3(op)]));
3487
+ const names = meta.fresh ? [...META_OPS, "fresh"] : META_OPS;
3488
+ const ops = Object.fromEntries(names.map((op) => [op, guarded3(op)]));
3477
3489
  if (meta.absent) ops.absent = meta.absent.bind(meta);
3478
3490
  const { removeFile } = meta;
3479
3491
  if (removeFile)
@@ -3597,6 +3609,24 @@ var OpfsFs = class {
3597
3609
  committed(target) {
3598
3610
  if (this.remote?.watched()) this.remote.wrote(target);
3599
3611
  }
3612
+ counted = [];
3613
+ async stamp(path) {
3614
+ const entry = this.meta.fresh ? await this.meta.fresh(path) : (await this.meta.get([path]))[0];
3615
+ return String(entry?.writes ?? 0);
3616
+ }
3617
+ async counting(target) {
3618
+ if (!isProtected(this.counted, target)) return;
3619
+ await this.meta.update(target, (entry) => ({
3620
+ ...entry,
3621
+ path: target,
3622
+ writes: (entry?.writes ?? 0) + 1
3623
+ }));
3624
+ }
3625
+ async recounting(handle, target) {
3626
+ if (this.counted.length === 0) return;
3627
+ const where = await this.root.resolve(handle).catch(() => null);
3628
+ await this.counting(where ? `/${where.join("/")}` : target);
3629
+ }
3600
3630
  resolvePath(base, path) {
3601
3631
  return resolvePath(base, path);
3602
3632
  }
@@ -3655,6 +3685,7 @@ var OpfsFs = class {
3655
3685
  guarded = [];
3656
3686
  protect(prefixes2) {
3657
3687
  this.guarded = prefixes2;
3688
+ this.counted = prefixes2;
3658
3689
  }
3659
3690
  unguarded() {
3660
3691
  return Object.create(this, { guarded: { value: [] } });
@@ -3991,9 +4022,10 @@ var OpfsFs = class {
3991
4022
  return this.retrying(
3992
4023
  path,
3993
4024
  async () => {
4025
+ const stamp = await this.stamp(path);
3994
4026
  const file = await handle.getFile();
3995
4027
  const bytes = new Uint8Array(await file.arrayBuffer());
3996
- return { bytes, version: `${file.lastModified}:${file.size}` };
4028
+ return { bytes, version: `${file.lastModified}:${file.size}:${stamp}` };
3997
4029
  },
3998
4030
  false
3999
4031
  );
@@ -4003,8 +4035,9 @@ var OpfsFs = class {
4003
4035
  return this.retrying(
4004
4036
  path,
4005
4037
  async () => {
4038
+ const stamp = await this.stamp(path);
4006
4039
  const file = await handle.getFile();
4007
- return `${file.lastModified}:${file.size}`;
4040
+ return `${file.lastModified}:${file.size}:${stamp}`;
4008
4041
  },
4009
4042
  false
4010
4043
  );
@@ -4019,14 +4052,24 @@ var OpfsFs = class {
4019
4052
  }
4020
4053
  async pwrite(path, offset, bytes) {
4021
4054
  const [handle, , target] = await this.fileFor(path);
4022
- await this.retrying(path, () => positioned(handle, (io) => io.write(offset, bytes)), true);
4055
+ await this.counting(target);
4056
+ try {
4057
+ await this.retrying(path, () => positioned(handle, (io) => io.write(offset, bytes)), true);
4058
+ } finally {
4059
+ await this.recounting(handle, target);
4060
+ }
4023
4061
  this.written(target);
4024
4062
  }
4025
4063
  async truncate(path, size) {
4026
4064
  const handle = await this.existing(path, true);
4027
4065
  const resolve2 = this.ranged || this.remote?.watched();
4028
- const target = resolve2 ? (await this.locate(path, true)).path : path;
4029
- await this.retrying(path, () => positioned(handle, (io) => io.truncate(size)), true);
4066
+ const target = resolve2 || this.counted.length > 0 ? (await this.locate(path, true)).path : path;
4067
+ await this.counting(target);
4068
+ try {
4069
+ await this.retrying(path, () => positioned(handle, (io) => io.truncate(size)), true);
4070
+ } finally {
4071
+ await this.recounting(handle, target);
4072
+ }
4030
4073
  this.written(target);
4031
4074
  }
4032
4075
  async writeSession(path, truncate) {
@@ -11508,7 +11551,7 @@ function ptySyscall(req, ctx) {
11508
11551
  const pair = ctx.ptys.get(req.n);
11509
11552
  ttyOpenable(ctx.guard, pair.slave);
11510
11553
  const slave = pair.openSlave();
11511
- const fd = ctx.fds.install(ctx.ttyFilter?.(slave) ?? slave, 3);
11554
+ const fd = ctx.fds.install(ctx.outputFilter?.(slave) ?? slave, 3);
11512
11555
  if (!req.noctty) ctx.jobs?.acquireTerminal(ctx.pid, pair.slave);
11513
11556
  return json4(fd);
11514
11557
  }
@@ -12674,8 +12717,8 @@ var WasmProcess = class {
12674
12717
  const { ptys, jobs } = this.options;
12675
12718
  const owner = this.options.owners ? this.ttyOwner() : void 0;
12676
12719
  const guard2 = this.guarded();
12677
- const { ttyFilter } = this.options;
12678
- const ctx = { pid: this.pid, fds: this.fds, ptys, jobs, guard: guard2, owner, ttyFilter };
12720
+ const { outputFilter } = this.options;
12721
+ const ctx = { pid: this.pid, fds: this.fds, ptys, jobs, guard: guard2, owner, outputFilter };
12679
12722
  return ptySyscall(req, ctx);
12680
12723
  }
12681
12724
  return await this.procSyscall(req, cancelled);
@@ -12852,7 +12895,7 @@ var WasmProcess = class {
12852
12895
  if (!tty) throw new KernelError("ENXIO");
12853
12896
  if (name2 !== void 0) ttyOpenable(this.guarded(), tty);
12854
12897
  const file = tty.file();
12855
- return this.fds.install(this.options.ttyFilter?.(file) ?? file, 3);
12898
+ return this.fds.install(this.options.outputFilter?.(file) ?? file, 3);
12856
12899
  }
12857
12900
  openSealed() {
12858
12901
  const broker = this.options.sealed;
@@ -12864,7 +12907,8 @@ var WasmProcess = class {
12864
12907
  if (!broker) throw new KernelError("ENOENT");
12865
12908
  const mode = flags2 & WRITE_ACCESS;
12866
12909
  const access2 = mode === 0 ? "read" : mode === 1 ? "write" : "both";
12867
- return this.fds.install(broker.open(access2, await this.clipboardStamp()), 3);
12910
+ const file = broker.open(access2, await this.clipboardStamp());
12911
+ return this.fds.install(this.options.outputFilter?.(file) ?? file, 3);
12868
12912
  }
12869
12913
  async clipboardStamp() {
12870
12914
  const shown = await this.options.identity?.();
@@ -13663,7 +13707,7 @@ function processOptions(opts, header4, hooks, created3) {
13663
13707
  ...inherited(opts),
13664
13708
  ...opts.owners ? { owners: true } : {},
13665
13709
  ...opts.sealed ? { sealed: opts.sealed } : {},
13666
- ...opts.ttyFilter ? { ttyFilter: opts.ttyFilter } : {},
13710
+ ...opts.outputFilter ? { outputFilter: opts.outputFilter } : {},
13667
13711
  ...opts.clipboard ? { clipboard: opts.clipboard, ...clipboardName(opts) } : {},
13668
13712
  ...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
13669
13713
  ...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3 } } : {},
@@ -14665,7 +14709,7 @@ function scrubStdio(fds, scrubber) {
14665
14709
  const stdio = stdioFiles(fds);
14666
14710
  for (const fd of fds.numbers()) {
14667
14711
  const original = fds.get(fd);
14668
- if (!stdio.has(original) && !original.file.tty) continue;
14712
+ if (!stdio.has(original) && !original.file.tty && !original.file.clipboard) continue;
14669
14713
  const known = wrapped.get(original);
14670
14714
  const flags2 = fds.statusFlags(fd);
14671
14715
  const wrapper = known ? known.retain() : scrubbed(original.retain(), scrubber);
@@ -17847,7 +17891,7 @@ var Launcher = class {
17847
17891
  ...this.owners && req.program.abi === "wasi" ? { fresh: (path, umask) => this.freshModules.add(path, umask, pid) } : {},
17848
17892
  ...this.owners ? { owners: true } : {},
17849
17893
  sealed: this.sealed,
17850
- ...scrubs ? { ttyFilter: (file) => scrubbedAll(file, scrubs) } : {},
17894
+ ...scrubs ? { outputFilter: (file) => scrubbedAll(file, scrubs) } : {},
17851
17895
  clipboard: this.clipboard,
17852
17896
  ...this.dac ? { dac: this.dac } : {},
17853
17897
  program: req.program,
@@ -18380,17 +18424,21 @@ var Launcher = class {
18380
18424
  const real = await this.realLayoutPath("/tmp");
18381
18425
  const st = real === void 0 ? void 0 : await this.base.lstat(real).catch(() => void 0);
18382
18426
  if (real !== void 0 && st?.isDirectory && this.base.hasOwner && !await this.base.hasOwner(real)) {
18427
+ const names = await this.base.readdir(real).catch(() => void 0);
18383
18428
  let removed = 0;
18384
- for (const name2 of await this.base.readdir(real).catch(() => [])) {
18429
+ let left = names === void 0 ? 1 : 0;
18430
+ for (const name2 of names ?? []) {
18385
18431
  const path = joinName(real, name2);
18386
- if (await this.base.hasOwner(path) || this.inUse(path)) continue;
18387
- const failed3 = await this.base.rm(path, { recursive: true, force: true }).catch(String);
18432
+ if (await this.base.hasOwner(path)) continue;
18433
+ const failed3 = this.inUse(path) ? "busy" : await this.base.rm(path, { recursive: true, force: true }).catch(String);
18388
18434
  if (failed3 === void 0) removed++;
18435
+ else left++;
18389
18436
  }
18390
18437
  if (removed > 0) {
18391
18438
  console.warn(`slicc-kernel users: removed ${removed} entries from /tmp that had no owner`);
18392
18439
  }
18393
- await this.base.chown?.(real, { uid: 0, gid: 0, mode: 1023, follow: false });
18440
+ if (left === 0)
18441
+ await this.base.chown?.(real, { uid: 0, gid: 0, mode: 1023, follow: false });
18394
18442
  }
18395
18443
  if (st?.isDirectory) await this.base.chmod(real, 1023);
18396
18444
  }
@@ -20059,11 +20107,17 @@ async function createNodeKernel(options = {}) {
20059
20107
  close: () => session.close()
20060
20108
  };
20061
20109
  },
20062
- async writeFile(path, data) {
20110
+ async writeFile(path, data, options2 = {}) {
20111
+ const mode = options2.mode === void 0 ? void 0 : modeOption(options2.mode);
20063
20112
  const parent = path.slice(0, path.lastIndexOf("/")) || "/";
20064
20113
  await launcher.fs.mkdir(parent, { recursive: true });
20114
+ if (mode !== void 0) {
20115
+ await launcher.fs.writeFile(path, "");
20116
+ await launcher.fs.chmod(path, mode);
20117
+ }
20065
20118
  await launcher.fs.writeFile(path, data);
20066
20119
  },
20120
+ chmod: async (path, mode) => launcher.fs.chmod(path, modeOption(mode)),
20067
20121
  readFile: async (path) => launcher.fs.readFileBuffer(path),
20068
20122
  mount: (spec) => guard2(() => launcher.mount(spec)),
20069
20123
  umount: (target) => guard2(async () => launcher.umount(target)),