@ai-ecoverse/slicc-kernel 1.143.5 → 1.143.6

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
@@ -3632,7 +3632,8 @@ var SLOT = 2;
3632
3632
  var SHARED_TTL_MS = 100;
3633
3633
  var DIRTY = RING + RING_SIZE * SLOT;
3634
3634
  var FOREIGN = DIRTY + 1;
3635
- var CHANGES_BYTES = (FOREIGN + 1) * 4;
3635
+ var RENAMES = FOREIGN + 1;
3636
+ var CHANGES_BYTES = (RENAMES + 1) * 4;
3636
3637
  function changeHash(path) {
3637
3638
  let h = 2166136261;
3638
3639
  for (let i = 0; i < path.length; i++) {
@@ -3666,6 +3667,11 @@ var FileChanges = class {
3666
3667
  flush() {
3667
3668
  Atomics.add(this.view, GEN, RING_SIZE + 1);
3668
3669
  }
3670
+ renamed() {
3671
+ const seq2 = Atomics.add(this.view, RENAMES, 1) + 1 | 0;
3672
+ Atomics.notify(this.view, RENAMES);
3673
+ return seq2;
3674
+ }
3669
3675
  publish(...paths) {
3670
3676
  for (const path of paths) {
3671
3677
  const g = Atomics.add(this.view, GEN, 1) + 1 | 0;
@@ -3690,6 +3696,13 @@ var ChangeView = class {
3690
3696
  foreign() {
3691
3697
  return this.view.length > FOREIGN && Atomics.load(this.view, FOREIGN) === 1;
3692
3698
  }
3699
+ renames() {
3700
+ return this.view.length > RENAMES ? Atomics.load(this.view, RENAMES) : 0;
3701
+ }
3702
+ awaitRename(seen, ms) {
3703
+ if (this.view.length > RENAMES) Atomics.wait(this.view, RENAMES, seen, ms);
3704
+ return this.renames();
3705
+ }
3693
3706
  changedSince(seen, path) {
3694
3707
  const now = Atomics.load(this.view, GEN);
3695
3708
  const distance = now - seen >>> 0;
@@ -4546,7 +4559,7 @@ var STALE = /* @__PURE__ */ new Set(["NotFoundError", "TypeMismatchError"]);
4546
4559
  var REPLACED_ATTEMPTS = 4;
4547
4560
  var RECONCILE_LISTINGS = 8;
4548
4561
  var EXECUTABLE = /\/node_modules\/(?:@[^/]+\/)?[^/]+\/bin\//;
4549
- var RENAMES = "/.slicc-renames";
4562
+ var RENAMES2 = "/.slicc-renames";
4550
4563
  var RENAME_LOCK = "slicc-rename:";
4551
4564
  var RENAME_ADMIT = "slicc-rename-admit:";
4552
4565
  var escapeRoot = (name2) => name2.replace(/[%:]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
@@ -4741,7 +4754,8 @@ var OpfsFs = class {
4741
4754
  const remote = this.remote;
4742
4755
  meta.listen?.((c) => {
4743
4756
  for (const path of [...c.paths ?? [], ...c.trees ?? []]) this.forget(path);
4744
- remote?.emit({ paths: c.paths ?? [], trees: c.trees ?? [] });
4757
+ const moved2 = movedOf(c.moved);
4758
+ remote?.emit({ paths: c.paths ?? [], trees: c.trees ?? [], ...moved2 ? { moved: moved2 } : {} });
4745
4759
  });
4746
4760
  remote?.listen((c) => {
4747
4761
  for (const path of [...c.paths, ...c.trees]) this.forget(path);
@@ -5463,31 +5477,38 @@ var OpfsFs = class {
5463
5477
  });
5464
5478
  }
5465
5479
  async pwrite(path, offset, bytes) {
5466
- const [kept, target] = await this.keptFor(path);
5467
- if (kept) {
5468
- try {
5469
- await this.writeAt(kept, target, path, offset, bytes);
5470
- return;
5471
- } catch (err) {
5472
- if (err.code !== "ENOENT") throw err;
5473
- this.files.delete(target);
5480
+ const at = normalizePath(path);
5481
+ const movedAway = this.renamesInFlight() ? await this.writeWaits(at) : false;
5482
+ this.fileWrites.set(at, (this.fileWrites.get(at) ?? 0) + 1);
5483
+ try {
5484
+ const [kept, target, existed] = await this.keptFor(path);
5485
+ if (kept) {
5486
+ try {
5487
+ await this.writeAt(kept, target, path, offset, bytes);
5488
+ return;
5489
+ } catch (err) {
5490
+ if (err.code !== "ENOENT") throw err;
5491
+ this.files.delete(target);
5492
+ }
5474
5493
  }
5494
+ const [handle] = await this.fileFor(path, !existed && !movedAway);
5495
+ await this.writeAt(handle, target, path, offset, bytes);
5496
+ } finally {
5497
+ this.writeEnded(at);
5475
5498
  }
5476
- const [handle] = await this.fileFor(path);
5477
- await this.writeAt(handle, target, path, offset, bytes);
5478
5499
  }
5479
5500
  async keptFor(path) {
5480
5501
  const at = normalizePath(path);
5481
5502
  const cached = this.files.get(at);
5482
5503
  const fresh2 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
5483
5504
  const [target, existed] = fresh2 ? [at, true] : await this.writeTarget(path);
5484
- if (!existed) return [void 0, target];
5505
+ if (!existed) return [void 0, target, false];
5485
5506
  this.refuse(target);
5486
5507
  const [entry] = await this.meta.get([target]);
5487
5508
  if (entry?.fifo || entry?.socket) throw fsError("ENXIO", path);
5488
5509
  const kept = fresh2 ? cached : this.files.get(target);
5489
5510
  const usable2 = kept && performance.now() - (this.found.get(kept) ?? -CHECK_MS) < CHECK_MS;
5490
- return [usable2 ? kept : void 0, target];
5511
+ return [usable2 ? kept : void 0, target, true];
5491
5512
  }
5492
5513
  async writeAt(handle, target, path, offset, bytes) {
5493
5514
  await this.counting(target);
@@ -5504,7 +5525,67 @@ var OpfsFs = class {
5504
5525
  }
5505
5526
  this.written(target);
5506
5527
  }
5528
+ fileWrites = /* @__PURE__ */ new Map();
5529
+ movingFiles = /* @__PURE__ */ new Map();
5530
+ writesIdle;
5531
+ writeBlocked(at) {
5532
+ const file = this.movingFiles.get(at);
5533
+ if (file !== void 0) return { done: file, away: true };
5534
+ for (const m of this.renamingDirs) {
5535
+ if (within3(at, m.from)) return { done: m.done, away: true };
5536
+ if (within3(at, m.to)) return { done: m.done, away: false };
5537
+ }
5538
+ return void 0;
5539
+ }
5540
+ renamesInFlight() {
5541
+ return this.movingFiles.size > 0 || this.renamingDirs.size > 0;
5542
+ }
5543
+ async writeWaits(at) {
5544
+ let away = false;
5545
+ for (let busy = this.writeBlocked(at); busy !== void 0; busy = this.writeBlocked(at)) {
5546
+ away ||= busy.away;
5547
+ await busy.done;
5548
+ }
5549
+ return away;
5550
+ }
5551
+ writeEnded(at) {
5552
+ const left = (this.fileWrites.get(at) ?? 1) - 1;
5553
+ if (left > 0) this.fileWrites.set(at, left);
5554
+ else this.fileWrites.delete(at);
5555
+ const idle2 = this.writesIdle;
5556
+ if (idle2 === void 0) return;
5557
+ this.writesIdle = void 0;
5558
+ idle2.resolve();
5559
+ }
5560
+ async writesDrained(tree2) {
5561
+ while ([...this.fileWrites.keys()].some((at) => within3(at, tree2))) {
5562
+ this.writesIdle ??= Promise.withResolvers();
5563
+ await this.writesIdle.promise;
5564
+ }
5565
+ }
5566
+ async movingFile(path, op) {
5567
+ const { promise, resolve: resolve2 } = Promise.withResolvers();
5568
+ while (this.movingFiles.has(path)) await this.movingFiles.get(path);
5569
+ this.movingFiles.set(path, promise);
5570
+ try {
5571
+ await this.writesDrained(path);
5572
+ return await op();
5573
+ } finally {
5574
+ this.movingFiles.delete(path);
5575
+ resolve2();
5576
+ }
5577
+ }
5507
5578
  async truncate(path, size) {
5579
+ const at = normalizePath(path);
5580
+ if (this.renamesInFlight()) await this.writeWaits(at);
5581
+ this.fileWrites.set(at, (this.fileWrites.get(at) ?? 0) + 1);
5582
+ try {
5583
+ await this.truncated(path, size);
5584
+ } finally {
5585
+ this.writeEnded(at);
5586
+ }
5587
+ }
5588
+ async truncated(path, size) {
5508
5589
  const handle = await this.existing(path, true);
5509
5590
  await this.refuseNode(path, (await this.io(handle.getFile())).size === 0);
5510
5591
  const resolve2 = this.ranged || this.remote?.watched();
@@ -5562,8 +5643,9 @@ var OpfsFs = class {
5562
5643
  const wanted = entry?.link === void 0 ? path : resolvePath(parentOf4(path), entry.link);
5563
5644
  return [resolvePath((await this.locate(parentOf4(wanted), true)).path, baseOf(wanted)), false];
5564
5645
  }
5565
- async fileFor(path) {
5646
+ async fileFor(path, create = true) {
5566
5647
  const [target, existed] = await this.writeTarget(path);
5648
+ if (!existed && !create) throw fsError("ENOENT", path);
5567
5649
  if (existed) {
5568
5650
  const [entry] = await this.meta.get([target]);
5569
5651
  if (entry?.fifo || entry?.socket) throw fsError("ENXIO", path);
@@ -5572,7 +5654,7 @@ var OpfsFs = class {
5572
5654
  try {
5573
5655
  const handle = await this.inDir(
5574
5656
  dir,
5575
- (parent) => this.io(parent.getFileHandle(name2, { create: true }))
5657
+ (parent) => this.io(parent.getFileHandle(name2, { create }))
5576
5658
  );
5577
5659
  if (!existed) this.touch(Date.now(), parentOf4(target));
5578
5660
  return [handle, existed, target];
@@ -5878,11 +5960,12 @@ var OpfsFs = class {
5878
5960
  this.changed(source.path, destination);
5879
5961
  this.touch(Date.now(), parentOf4(source.path), parent.path);
5880
5962
  };
5881
- if (!copying) return place();
5963
+ if (!copying) return sourceStat.isDirectory ? place() : this.movingFile(source.path, place);
5882
5964
  const { promise, resolve: resolve2 } = Promise.withResolvers();
5883
5965
  const entry = { from: source.path, to: destination, done: promise };
5884
5966
  this.renamingDirs.add(entry);
5885
5967
  try {
5968
+ await this.writesDrained(source.path);
5886
5969
  const locks = this.locks;
5887
5970
  if (locks) {
5888
5971
  lock = `${this.renameLocks()}${crypto.randomUUID()}`;
@@ -5985,7 +6068,7 @@ var OpfsFs = class {
5985
6068
  return name2.startsWith(this.renameLocks()) || LEGACY_RENAME_LOCK.test(name2);
5986
6069
  }
5987
6070
  async learnRenames() {
5988
- for (const { rename } of await this.meta.under(RENAMES)) {
6071
+ for (const { rename } of await this.meta.under(RENAMES2)) {
5989
6072
  if (rename?.lock === void 0 || this.ownLocks.has(rename.lock)) continue;
5990
6073
  this.notePeer(rename.lock, [rename.from, rename.to]);
5991
6074
  }
@@ -6074,7 +6157,7 @@ var OpfsFs = class {
6074
6157
  if (!move.done)
6075
6158
  await locks.request(move.lock, async () => {
6076
6159
  if (move.done) return;
6077
- const left = (await this.meta.under(RENAMES)).filter((entry) => entry.rename?.lock === move.lock).sort(byIntent);
6160
+ const left = (await this.meta.under(RENAMES2)).filter((entry) => entry.rename?.lock === move.lock).sort(byIntent);
6078
6161
  for (const entry of left) await this.finishRename(entry);
6079
6162
  });
6080
6163
  this.peerMoves.delete(move.lock);
@@ -6153,7 +6236,7 @@ var OpfsFs = class {
6153
6236
  }
6154
6237
  renamingDirs = /* @__PURE__ */ new Set();
6155
6238
  intents = 0;
6156
- async intend(rename, at = `${RENAMES}/${crypto.randomUUID()}`) {
6239
+ async intend(rename, at = `${RENAMES2}/${crypto.randomUUID()}`) {
6157
6240
  const record2 = { ...rename, at: Date.now() * 1e3 + this.intents++ % 1e3 };
6158
6241
  await this.meta.update(at, () => ({ path: at, rename: record2 }));
6159
6242
  return at;
@@ -6189,7 +6272,7 @@ var OpfsFs = class {
6189
6272
  const { held: h = [], pending = [] } = await locks.query();
6190
6273
  for (const lock of [...h, ...pending]) held.add(lock.name);
6191
6274
  }
6192
- const intents = (await this.meta.under(RENAMES)).filter(
6275
+ const intents = (await this.meta.under(RENAMES2)).filter(
6193
6276
  (entry) => entry.rename !== void 0 && (!orphaned || locks !== void 0 && entry.rename.lock !== void 0 && !held.has(entry.rename.lock))
6194
6277
  ).sort(
6195
6278
  (a, b) => a.rename.at - b.rename.at
@@ -6241,6 +6324,7 @@ var OpfsFs = class {
6241
6324
  );
6242
6325
  }
6243
6326
  await this.meta.update(entry.path, () => void 0);
6327
+ this.remote?.emit({ paths: [], trees: [], moved: { from, to } });
6244
6328
  } finally {
6245
6329
  this.renamingDirs.delete(barrier);
6246
6330
  resolve2();
@@ -6558,6 +6642,10 @@ var OpfsFs = class {
6558
6642
  return removed;
6559
6643
  }
6560
6644
  };
6645
+ function movedOf(moved2) {
6646
+ const m = moved2;
6647
+ return typeof m?.from === "string" && typeof m.to === "string" ? { from: m.from, to: m.to } : void 0;
6648
+ }
6561
6649
  function nothing() {
6562
6650
  return void 0;
6563
6651
  }
@@ -7982,7 +8070,7 @@ function satisfies(version, range2) {
7982
8070
  }
7983
8071
 
7984
8072
  // src/version.ts
7985
- var KERNEL_VERSION = true ? "1.143.5" : "0.0.0-dev";
8073
+ var KERNEL_VERSION = true ? "1.143.6" : "0.0.0-dev";
7986
8074
  var OS_RELEASE = "6.0.0-slicc";
7987
8075
  function procVersion() {
7988
8076
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -13039,7 +13127,8 @@ var SYNC_FS_OPS = [
13039
13127
  "truncate",
13040
13128
  "chown",
13041
13129
  "lchown",
13042
- "access"
13130
+ "access",
13131
+ "renames"
13043
13132
  ];
13044
13133
  var HOLD_READ = 4;
13045
13134
  var HOLD_WRITE = 8;
@@ -13087,6 +13176,30 @@ async function readdirStat(fs, dir, g) {
13087
13176
  const shown = async (name2, stat) => stat && statJson(await callerStat(g, joinName(dir, name2), stat));
13088
13177
  return Promise.all(listed.map(async ([name2, stat]) => [name2, await shown(name2, stat)]));
13089
13178
  }
13179
+ async function visibleRenames(entry, req) {
13180
+ const answer2 = entry.renames?.(req.offset ?? 0, heldPaths(req.arg2)) ?? { now: 0, renames: [] };
13181
+ const g = guardOf(entry);
13182
+ if (!g?.dac.enforcing) return answer2;
13183
+ const searchable2 = async (path) => {
13184
+ const dir = path.slice(0, Math.max(1, path.lastIndexOf("/")));
13185
+ const w = await walk(g.fs, g.cred, dir, true, g.links).catch(nothing2);
13186
+ if (w === false || w.denied || w.perm === void 0 || !permits(g.cred, w.perm, X_OK))
13187
+ return false;
13188
+ return g.fs.stat(w.path).then((st) => st.isDirectory, nothing2);
13189
+ };
13190
+ const shown = [];
13191
+ for (const [from, to] of answer2.renames) {
13192
+ if (await searchable2(from) && await searchable2(to)) shown.push([from, to]);
13193
+ }
13194
+ return { now: answer2.now, renames: shown };
13195
+ }
13196
+ function nothing2() {
13197
+ return false;
13198
+ }
13199
+ function heldPaths(arg) {
13200
+ const held = JSON.parse(arg ?? "[]");
13201
+ return Array.isArray(held) ? held.filter((path) => typeof path === "string") : [];
13202
+ }
13090
13203
  async function usageAt(entry, path) {
13091
13204
  if (!entry.statfs) return void 0;
13092
13205
  const real = await realTarget(entry.fs, path).catch(() => path);
@@ -13204,7 +13317,8 @@ async function run(entry, path, req) {
13204
13317
  const to = fs.resolvePath(cwd, req.arg2 ?? "");
13205
13318
  await fs.rename(path, to);
13206
13319
  entry.renamed?.(path, to);
13207
- return done;
13320
+ const seq2 = entry.renameSeq?.(path, to);
13321
+ return seq2 === void 0 ? done : json2({ seq: seq2 });
13208
13322
  }
13209
13323
  case "link":
13210
13324
  await fs.link(path, fs.resolvePath(cwd, req.arg2 ?? ""), req.mode === 1);
@@ -13676,6 +13790,7 @@ async function linkFree(fs, path) {
13676
13790
  }
13677
13791
  }
13678
13792
  async function dispatchOne(entry, req) {
13793
+ if (req.op === "renames") return json2(await visibleRenames(entry, req));
13679
13794
  const result = await dispatchPath(entry, req);
13680
13795
  if (result.ok || result.errno !== "ENOENT" || !req.direct || !MISS_OPS.has(req.op) || req.handle !== void 0) {
13681
13796
  return result;
@@ -21717,6 +21832,7 @@ var VfsNodes = class {
21717
21832
  byId = /* @__PURE__ */ new Map();
21718
21833
  keepsOnDisk = false;
21719
21834
  changed;
21835
+ movedAway;
21720
21836
  dirtied;
21721
21837
  fs;
21722
21838
  entries;
@@ -22146,6 +22262,7 @@ function keepingOpen(fs, nodes, changed = () => {
22146
22262
  await nodes.unlinking(b);
22147
22263
  await fs.rename(from, to);
22148
22264
  nodes.renamed(a, b);
22265
+ nodes.movedAway?.(a, b);
22149
22266
  });
22150
22267
  },
22151
22268
  writeFile: async (path, content, owner) => {
@@ -24369,6 +24486,8 @@ function holdingToken(opts, holds2, caller) {
24369
24486
  noLinks: opts.noLinks,
24370
24487
  cwd: opts.cwd,
24371
24488
  ...opts.statfs ? { statfs: opts.statfs } : {},
24489
+ ...opts.renames ? { renames: opts.renames } : {},
24490
+ ...opts.renameSeq ? { renameSeq: opts.renameSeq } : {},
24372
24491
  hold: (path, on, open, real, handle) => holds2.hold(path, on, open, real, handle),
24373
24492
  keptFor: (handle) => holds2.keptFor(handle),
24374
24493
  unlinked: (path) => holds2.keptAt(path),
@@ -25738,6 +25857,43 @@ function replayingHops(inner, heldMs = HELD_MS) {
25738
25857
  };
25739
25858
  }
25740
25859
 
25860
+ // src/kernel/rename-log.ts
25861
+ var RENAME_LOG_SIZE = 4096;
25862
+ function within9(path, root) {
25863
+ return path === root || path.startsWith(root === "/" ? "/" : `${root}/`);
25864
+ }
25865
+ var RenameLog = class {
25866
+ changes;
25867
+ entries = [];
25868
+ constructor(changes) {
25869
+ this.changes = changes;
25870
+ }
25871
+ add(from, to) {
25872
+ const seq2 = this.changes.renamed();
25873
+ this.entries.push({ seq: seq2, from, to });
25874
+ if (this.entries.length > RENAME_LOG_SIZE) this.entries.shift();
25875
+ return seq2;
25876
+ }
25877
+ seqOf(from, to) {
25878
+ for (let i = this.entries.length - 1; i >= 0; i--) {
25879
+ const entry = this.entries[i];
25880
+ if (entry.from === from && entry.to === to) return entry.seq;
25881
+ }
25882
+ return void 0;
25883
+ }
25884
+ since(seen, held) {
25885
+ const now = this.entries.at(-1)?.seq ?? seen;
25886
+ const renames = [];
25887
+ let paths = [...held];
25888
+ for (const { seq: seq2, from, to } of this.entries) {
25889
+ if (seq2 <= seen || !paths.some((path) => within9(path, from))) continue;
25890
+ renames.push([from, to]);
25891
+ paths = paths.map((path) => within9(path, from) ? to + path.slice(from.length) : path);
25892
+ }
25893
+ return { now, renames };
25894
+ }
25895
+ };
25896
+
25741
25897
  // src/kernel/net/sealed-scrubs.ts
25742
25898
  function addTo(tree2, host, secrets) {
25743
25899
  const entry = tree2.get(host) ?? { secrets: [], version: 0, scrubber: void 0 };
@@ -28563,6 +28719,7 @@ var Launcher = class {
28563
28719
  hostname;
28564
28720
  watchers = new FsWatchers();
28565
28721
  changes = new FileChanges();
28722
+ renameLog = new RenameLog(this.changes);
28566
28723
  cwds = new CwdTable(async (path) => {
28567
28724
  const { dev, ino } = await this.base.stat(path);
28568
28725
  return `${dev ?? 0}:${ino}`;
@@ -28701,6 +28858,7 @@ var Launcher = class {
28701
28858
  this.openFiles.add(this.nodes);
28702
28859
  options.fs.onPeer?.(() => this.flushOpen());
28703
28860
  options.fs.remoteChanges?.((change) => {
28861
+ if (change.moved) this.moved(change.moved.from, change.moved.to);
28704
28862
  this.changes.flush();
28705
28863
  this.cwds.touched(change.paths, change.trees);
28706
28864
  this.rewritten(change.paths, change.trees);
@@ -29717,6 +29875,8 @@ var Launcher = class {
29717
29875
  unlinkGate: stack.unlinked.gate,
29718
29876
  replacements: stack.unlinked,
29719
29877
  statfs: (path) => this.mounts.statfs(path),
29878
+ renames: (since2, held) => this.renameLog.since(since2, held),
29879
+ renameSeq: (from, to) => this.renameLog.seqOf(from, to),
29720
29880
  mounts: () => this.mounts.list(),
29721
29881
  mount: (call, signal) => this.processMount(pid, call, signal),
29722
29882
  umount: (target, flags2) => this.processUmount(pid, target, flags2),
@@ -30664,8 +30824,15 @@ var Launcher = class {
30664
30824
  const target = command && (command.shows ?? command.script ?? command.wasm);
30665
30825
  return target === void 0 ? void 0 : { path: target, ...owner ? { owner } : {} };
30666
30826
  }
30827
+ moved(from, to, source) {
30828
+ this.renameLog.add(from, to);
30829
+ if (source) return;
30830
+ for (const nodes of this.openFiles) nodes.renamed(from, to);
30831
+ this.watchers.moved(from, to);
30832
+ }
30667
30833
  processChain(fs, nodes, unlinked) {
30668
30834
  nodes.changed = (path) => this.changes.publish(path);
30835
+ nodes.movedAway = (from, to) => this.moved(from, to, nodes);
30669
30836
  nodes.dirtied = (delta) => this.changes.dirtied(delta);
30670
30837
  return unlinked.wrap(keepingOpen(fs, nodes, (path) => this.changes.publish(path)));
30671
30838
  }