@ai-ecoverse/slicc-kernel 1.72.0 → 1.73.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
@@ -2525,6 +2525,12 @@ var CLIENT_CHECKS = {
2525
2525
  readlink: (g, at) => searchable(g, "readlink", at(0), false),
2526
2526
  exists: (g, at) => searchable(g, "exists", at(0), true)
2527
2527
  };
2528
+ function clientFs(l, tag, self, req) {
2529
+ if (!FS_METHODS.includes(req.method)) {
2530
+ throw new Error(`unknown file system call ${req.method}`);
2531
+ }
2532
+ return fsCall(l.fsFor(tag), req.method, req.args, ownerFor(l, self), guardFor(l, self, tag));
2533
+ }
2528
2534
  async function fsCall(fs, method, args, owner, g) {
2529
2535
  const path = (i) => fs.resolvePath("/", String(args[i]));
2530
2536
  if (g) {
@@ -2812,10 +2818,7 @@ function serveClient(port, host) {
2812
2818
  case "ps":
2813
2819
  return { result: processes(l, self) };
2814
2820
  case "fs":
2815
- if (!FS_METHODS.includes(req.method)) {
2816
- throw new Error(`unknown file system call ${req.method}`);
2817
- }
2818
- return fsCall(l.processFs, req.method, req.args, ownerFor(l, self), guard2(l));
2821
+ return clientFs(l, host.tag, self, req);
2819
2822
  case "watch":
2820
2823
  case "unwatch":
2821
2824
  return watchOp(l, req, watches, send, guard2(l));
@@ -3003,7 +3006,13 @@ var MemoryMeta = class {
3003
3006
  };
3004
3007
 
3005
3008
  // src/fs/protect.ts
3006
- var PROTECT_WRITER = "slicc-kernel: protect.writer needs the process tag (#405), which this kernel does not have yet";
3009
+ function writerTags(protection) {
3010
+ const writers = protection?.writers ?? [];
3011
+ if (!Array.isArray(writers) || writers.some((tag) => typeof tag !== "string" || tag === "")) {
3012
+ throw new TypeError("protect.writers must be a list of process tags");
3013
+ }
3014
+ return new Set(writers);
3015
+ }
3007
3016
  function protectedPrefixes(paths) {
3008
3017
  return [...new Set(paths.map((p) => normalizePath(p)))];
3009
3018
  }
@@ -3511,6 +3520,9 @@ var OpfsFs = class {
3511
3520
  protect(prefixes2) {
3512
3521
  this.guarded = prefixes2;
3513
3522
  }
3523
+ unguarded() {
3524
+ return Object.create(this, { guarded: { value: [] } });
3525
+ }
3514
3526
  refuse(path, holding = false) {
3515
3527
  if (isProtected(this.guarded, path) || holding && holdsProtected(this.guarded, path)) {
3516
3528
  throw fsError("EROFS", path);
@@ -3680,15 +3692,15 @@ var OpfsFs = class {
3680
3692
  }
3681
3693
  dirTimes = /* @__PURE__ */ new Map();
3682
3694
  dirDirty = /* @__PURE__ */ new Set();
3683
- dirFlush;
3695
+ flushing = {};
3684
3696
  touch(...dirs) {
3685
3697
  const now = Date.now();
3686
3698
  for (const dir of dirs) {
3687
3699
  this.dirTimes.set(dir, now);
3688
3700
  this.dirDirty.add(dir);
3689
3701
  }
3690
- this.dirFlush ??= Promise.resolve().then(() => this.flushDirTimes()).catch(() => void 0).finally(() => {
3691
- this.dirFlush = void 0;
3702
+ this.flushing.dir ??= Promise.resolve().then(() => this.flushDirTimes()).catch(() => void 0).finally(() => {
3703
+ this.flushing.dir = void 0;
3692
3704
  });
3693
3705
  }
3694
3706
  async flushDirTimes() {
@@ -15751,6 +15763,10 @@ var Launcher = class {
15751
15763
  fs;
15752
15764
  processFs;
15753
15765
  prefixes;
15766
+ writerTags;
15767
+ unguardedRoot;
15768
+ writers;
15769
+ unlinkOwnerOption;
15754
15770
  base;
15755
15771
  createWorker;
15756
15772
  modulesDir;
@@ -15868,7 +15884,7 @@ var Launcher = class {
15868
15884
  this.nodes = new VfsNodes(fs, (path) => this.released(path));
15869
15885
  this.nodes.keepsOnDisk = true;
15870
15886
  this.openFiles.add(this.nodes);
15871
- const holders = [this.held, [this.nodes]];
15887
+ const holders = [this.held, this.openFiles];
15872
15888
  this.unlinked = new UnlinkedKeeper(
15873
15889
  fs,
15874
15890
  { [Symbol.iterator]: () => holders.flatMap((set2) => [...set2]).values() },
@@ -15881,7 +15897,12 @@ var Launcher = class {
15881
15897
  }
15882
15898
  );
15883
15899
  this.fs = this.unlinked.wrap(keepingOpen(fs, this.nodes));
15884
- if (options.protect?.writer) throw new Error(PROTECT_WRITER);
15900
+ this.writerTags = writerTags(options.protect);
15901
+ this.unlinkOwnerOption = options.unlinkOwner;
15902
+ if (this.writerTags.size > 0 && options.fs.protect && !options.fs.unguarded) {
15903
+ throw new TypeError("protect.writers needs a root file system with an unguarded view");
15904
+ }
15905
+ this.unguardedRoot = options.fs.unguarded?.() ?? options.fs;
15885
15906
  this.prefixes = protectedPrefixes(options.protect?.paths ?? []);
15886
15907
  this.processFs = this.prefixes.length > 0 ? new ProtectedFs(this.prefixes).view(this.fs) : this.fs;
15887
15908
  options.fs.protect?.(this.prefixes);
@@ -16205,6 +16226,7 @@ var Launcher = class {
16205
16226
  throw fsError("ESRCH", `no process group ${req.pgid}`);
16206
16227
  }
16207
16228
  const pid = this.nextPid++;
16229
+ const stack = this.stackFor(req.tag);
16208
16230
  const terminal = req.fds.stdioTerminal();
16209
16231
  const decided = req.decided ?? Promise.resolve(true);
16210
16232
  const count = (byParent) => {
@@ -16235,8 +16257,8 @@ var Launcher = class {
16235
16257
  env: req.env,
16236
16258
  cwd: req.cwd,
16237
16259
  fds: req.fds,
16238
- fs: this.processFs,
16239
- ...this.prefixes.length > 0 ? { readonlyAt: (path) => isProtected(this.prefixes, path) } : {},
16260
+ fs: stack.fs,
16261
+ ...this.prefixes.length > 0 && !stack.writer ? { readonlyAt: (path) => isProtected(this.prefixes, path) } : {},
16240
16262
  createWorker: this.createWorker,
16241
16263
  onError: req.report,
16242
16264
  spawner: this.spawner(pid, req.report),
@@ -16251,9 +16273,9 @@ var Launcher = class {
16251
16273
  }),
16252
16274
  sudo: (call, signal) => this.sudo(pid, call, signal),
16253
16275
  openFiles: this.openFiles,
16254
- nodes: this.nodes,
16276
+ nodes: stack.nodes,
16255
16277
  held: this.held,
16256
- unlinkGate: this.unlinked.gate,
16278
+ unlinkGate: stack.unlinked.gate,
16257
16279
  statfs: (path) => this.mounts.statfs(path),
16258
16280
  mounts: () => this.mounts.list(),
16259
16281
  mount: (call, signal) => this.processMount(pid, call, signal),
@@ -16554,6 +16576,7 @@ var Launcher = class {
16554
16576
  released(path) {
16555
16577
  const at = normalizePath(path);
16556
16578
  this.unlinked.closed(at);
16579
+ this.writers?.unlinked.closed(at);
16557
16580
  const open = [...this.openFiles].some((nodes) => nodes.writes(at)) || heldUnder(this.held, at);
16558
16581
  if (!open) void this.mounts.commit(at).catch(() => void 0);
16559
16582
  }
@@ -16872,13 +16895,49 @@ var Launcher = class {
16872
16895
  ...account.umask !== void 0 ? { umask: account.umask } : {}
16873
16896
  };
16874
16897
  }
16898
+ fsFor(tag) {
16899
+ return this.stackFor(tag).fs;
16900
+ }
16901
+ isWriter(tag) {
16902
+ return tag !== void 0 && this.prefixes.length > 0 && this.writerTags.has(tag);
16903
+ }
16904
+ stackFor(tag) {
16905
+ if (!this.isWriter(tag)) {
16906
+ return { fs: this.processFs, nodes: this.nodes, unlinked: this.unlinked, writer: false };
16907
+ }
16908
+ this.writers ??= this.writerStack();
16909
+ return this.writers;
16910
+ }
16911
+ writerStack() {
16912
+ const fs = withCommandDirs(
16913
+ this.watchers.wrap(this.mounts.wrap(this.unguardedRoot)),
16914
+ async () => new Set((await this.commands()).keys()),
16915
+ (dir) => this.userCommandDir(dir),
16916
+ this.owners
16917
+ );
16918
+ const nodes = new VfsNodes(fs, (path) => this.released(path));
16919
+ nodes.keepsOnDisk = true;
16920
+ this.openFiles.add(nodes);
16921
+ const unlinked = new UnlinkedKeeper(
16922
+ fs,
16923
+ { [Symbol.iterator]: () => [this.held, this.openFiles].flatMap((set2) => [...set2]).values() },
16924
+ (path) => this.mountRoot(path),
16925
+ {
16926
+ locks: webLocks(),
16927
+ busy: (path) => [...this.openFiles].some((open) => open.holds(path)),
16928
+ owner: this.unlinkOwnerOption,
16929
+ ...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
16930
+ }
16931
+ );
16932
+ return { fs: unlinked.wrap(keepingOpen(fs, nodes)), nodes, unlinked, writer: true };
16933
+ }
16875
16934
  async starting(argv, dir, account, tag) {
16876
16935
  this.resetCatalogs();
16877
16936
  const scope = account.uid === 0 ? "" : pnpmHomeOf(account, this.pnpmHome);
16878
16937
  const start2 = dir ?? "/";
16879
16938
  const cwd = await physicalPathAsync(this.base, start2.startsWith("/") ? start2 : `/${start2}`);
16880
16939
  const [file = ""] = argv;
16881
- if (!await this.base.exists(cwd)) await this.processFs.mkdir(cwd, { recursive: true });
16940
+ if (!await this.base.exists(cwd)) await this.stackFor(tag).fs.mkdir(cwd, { recursive: true });
16882
16941
  const planned = await this.plan(file, argv, cwd, scope);
16883
16942
  await this.mayExec(credOf(account), { file, cwd, pid: 0, tag }, planned);
16884
16943
  return { cwd, file, planned };