@ai-ecoverse/slicc-kernel 1.143.4 → 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.
@@ -262,7 +262,7 @@ var IndexedDbMeta = class {
262
262
  if (left) store.put(left);
263
263
  };
264
264
  await settle(transaction, () => void 0);
265
- this.changed({ trees: [from, to] });
265
+ this.changed({ trees: [from, to], moved: { from, to } });
266
266
  }
267
267
  absent(path) {
268
268
  return this.cache.has(path) && this.cache.get(path) === null;
@@ -364,7 +364,8 @@ var SLOT = 2;
364
364
  var SHARED_TTL_MS = 100;
365
365
  var DIRTY = RING + RING_SIZE * SLOT;
366
366
  var FOREIGN = DIRTY + 1;
367
- var CHANGES_BYTES = (FOREIGN + 1) * 4;
367
+ var RENAMES = FOREIGN + 1;
368
+ var CHANGES_BYTES = (RENAMES + 1) * 4;
368
369
  function changeHash(path) {
369
370
  let h = 2166136261;
370
371
  for (let i = 0; i < path.length; i++) {
@@ -398,6 +399,11 @@ var FileChanges = class {
398
399
  flush() {
399
400
  Atomics.add(this.view, GEN, RING_SIZE + 1);
400
401
  }
402
+ renamed() {
403
+ const seq2 = Atomics.add(this.view, RENAMES, 1) + 1 | 0;
404
+ Atomics.notify(this.view, RENAMES);
405
+ return seq2;
406
+ }
401
407
  publish(...paths) {
402
408
  for (const path of paths) {
403
409
  const g = Atomics.add(this.view, GEN, 1) + 1 | 0;
@@ -422,6 +428,13 @@ var ChangeView = class {
422
428
  foreign() {
423
429
  return this.view.length > FOREIGN && Atomics.load(this.view, FOREIGN) === 1;
424
430
  }
431
+ renames() {
432
+ return this.view.length > RENAMES ? Atomics.load(this.view, RENAMES) : 0;
433
+ }
434
+ awaitRename(seen, ms) {
435
+ if (this.view.length > RENAMES) Atomics.wait(this.view, RENAMES, seen, ms);
436
+ return this.renames();
437
+ }
425
438
  changedSince(seen, path) {
426
439
  const now = Atomics.load(this.view, GEN);
427
440
  const distance = now - seen >>> 0;
@@ -1272,7 +1285,7 @@ var STALE = /* @__PURE__ */ new Set(["NotFoundError", "TypeMismatchError"]);
1272
1285
  var REPLACED_ATTEMPTS = 4;
1273
1286
  var RECONCILE_LISTINGS = 8;
1274
1287
  var EXECUTABLE = /\/node_modules\/(?:@[^/]+\/)?[^/]+\/bin\//;
1275
- var RENAMES = "/.slicc-renames";
1288
+ var RENAMES2 = "/.slicc-renames";
1276
1289
  var RENAME_LOCK = "slicc-rename:";
1277
1290
  var RENAME_ADMIT = "slicc-rename-admit:";
1278
1291
  var escapeRoot = (name2) => name2.replace(/[%:]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
@@ -1471,7 +1484,8 @@ var OpfsFs = class {
1471
1484
  const remote = this.remote;
1472
1485
  meta.listen?.((c) => {
1473
1486
  for (const path of [...c.paths ?? [], ...c.trees ?? []]) this.forget(path);
1474
- remote?.emit({ paths: c.paths ?? [], trees: c.trees ?? [] });
1487
+ const moved2 = movedOf(c.moved);
1488
+ remote?.emit({ paths: c.paths ?? [], trees: c.trees ?? [], ...moved2 ? { moved: moved2 } : {} });
1475
1489
  });
1476
1490
  remote?.listen((c) => {
1477
1491
  for (const path of [...c.paths, ...c.trees]) this.forget(path);
@@ -2193,31 +2207,38 @@ var OpfsFs = class {
2193
2207
  });
2194
2208
  }
2195
2209
  async pwrite(path, offset, bytes) {
2196
- const [kept, target] = await this.keptFor(path);
2197
- if (kept) {
2198
- try {
2199
- await this.writeAt(kept, target, path, offset, bytes);
2200
- return;
2201
- } catch (err) {
2202
- if (err.code !== "ENOENT") throw err;
2203
- this.files.delete(target);
2210
+ const at = normalizePath(path);
2211
+ const movedAway = this.renamesInFlight() ? await this.writeWaits(at) : false;
2212
+ this.fileWrites.set(at, (this.fileWrites.get(at) ?? 0) + 1);
2213
+ try {
2214
+ const [kept, target, existed] = await this.keptFor(path);
2215
+ if (kept) {
2216
+ try {
2217
+ await this.writeAt(kept, target, path, offset, bytes);
2218
+ return;
2219
+ } catch (err) {
2220
+ if (err.code !== "ENOENT") throw err;
2221
+ this.files.delete(target);
2222
+ }
2204
2223
  }
2224
+ const [handle] = await this.fileFor(path, !existed && !movedAway);
2225
+ await this.writeAt(handle, target, path, offset, bytes);
2226
+ } finally {
2227
+ this.writeEnded(at);
2205
2228
  }
2206
- const [handle] = await this.fileFor(path);
2207
- await this.writeAt(handle, target, path, offset, bytes);
2208
2229
  }
2209
2230
  async keptFor(path) {
2210
2231
  const at = normalizePath(path);
2211
2232
  const cached = this.files.get(at);
2212
2233
  const fresh2 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
2213
2234
  const [target, existed] = fresh2 ? [at, true] : await this.writeTarget(path);
2214
- if (!existed) return [void 0, target];
2235
+ if (!existed) return [void 0, target, false];
2215
2236
  this.refuse(target);
2216
2237
  const [entry] = await this.meta.get([target]);
2217
2238
  if (entry?.fifo || entry?.socket) throw fsError("ENXIO", path);
2218
2239
  const kept = fresh2 ? cached : this.files.get(target);
2219
2240
  const usable2 = kept && performance.now() - (this.found.get(kept) ?? -CHECK_MS) < CHECK_MS;
2220
- return [usable2 ? kept : void 0, target];
2241
+ return [usable2 ? kept : void 0, target, true];
2221
2242
  }
2222
2243
  async writeAt(handle, target, path, offset, bytes) {
2223
2244
  await this.counting(target);
@@ -2234,7 +2255,67 @@ var OpfsFs = class {
2234
2255
  }
2235
2256
  this.written(target);
2236
2257
  }
2258
+ fileWrites = /* @__PURE__ */ new Map();
2259
+ movingFiles = /* @__PURE__ */ new Map();
2260
+ writesIdle;
2261
+ writeBlocked(at) {
2262
+ const file = this.movingFiles.get(at);
2263
+ if (file !== void 0) return { done: file, away: true };
2264
+ for (const m of this.renamingDirs) {
2265
+ if (within2(at, m.from)) return { done: m.done, away: true };
2266
+ if (within2(at, m.to)) return { done: m.done, away: false };
2267
+ }
2268
+ return void 0;
2269
+ }
2270
+ renamesInFlight() {
2271
+ return this.movingFiles.size > 0 || this.renamingDirs.size > 0;
2272
+ }
2273
+ async writeWaits(at) {
2274
+ let away = false;
2275
+ for (let busy = this.writeBlocked(at); busy !== void 0; busy = this.writeBlocked(at)) {
2276
+ away ||= busy.away;
2277
+ await busy.done;
2278
+ }
2279
+ return away;
2280
+ }
2281
+ writeEnded(at) {
2282
+ const left = (this.fileWrites.get(at) ?? 1) - 1;
2283
+ if (left > 0) this.fileWrites.set(at, left);
2284
+ else this.fileWrites.delete(at);
2285
+ const idle2 = this.writesIdle;
2286
+ if (idle2 === void 0) return;
2287
+ this.writesIdle = void 0;
2288
+ idle2.resolve();
2289
+ }
2290
+ async writesDrained(tree2) {
2291
+ while ([...this.fileWrites.keys()].some((at) => within2(at, tree2))) {
2292
+ this.writesIdle ??= Promise.withResolvers();
2293
+ await this.writesIdle.promise;
2294
+ }
2295
+ }
2296
+ async movingFile(path, op) {
2297
+ const { promise, resolve: resolve2 } = Promise.withResolvers();
2298
+ while (this.movingFiles.has(path)) await this.movingFiles.get(path);
2299
+ this.movingFiles.set(path, promise);
2300
+ try {
2301
+ await this.writesDrained(path);
2302
+ return await op();
2303
+ } finally {
2304
+ this.movingFiles.delete(path);
2305
+ resolve2();
2306
+ }
2307
+ }
2237
2308
  async truncate(path, size) {
2309
+ const at = normalizePath(path);
2310
+ if (this.renamesInFlight()) await this.writeWaits(at);
2311
+ this.fileWrites.set(at, (this.fileWrites.get(at) ?? 0) + 1);
2312
+ try {
2313
+ await this.truncated(path, size);
2314
+ } finally {
2315
+ this.writeEnded(at);
2316
+ }
2317
+ }
2318
+ async truncated(path, size) {
2238
2319
  const handle = await this.existing(path, true);
2239
2320
  await this.refuseNode(path, (await this.io(handle.getFile())).size === 0);
2240
2321
  const resolve2 = this.ranged || this.remote?.watched();
@@ -2292,8 +2373,9 @@ var OpfsFs = class {
2292
2373
  const wanted = entry?.link === void 0 ? path : resolvePath(parentOf3(path), entry.link);
2293
2374
  return [resolvePath((await this.locate(parentOf3(wanted), true)).path, baseOf(wanted)), false];
2294
2375
  }
2295
- async fileFor(path) {
2376
+ async fileFor(path, create = true) {
2296
2377
  const [target, existed] = await this.writeTarget(path);
2378
+ if (!existed && !create) throw fsError("ENOENT", path);
2297
2379
  if (existed) {
2298
2380
  const [entry] = await this.meta.get([target]);
2299
2381
  if (entry?.fifo || entry?.socket) throw fsError("ENXIO", path);
@@ -2302,7 +2384,7 @@ var OpfsFs = class {
2302
2384
  try {
2303
2385
  const handle = await this.inDir(
2304
2386
  dir,
2305
- (parent) => this.io(parent.getFileHandle(name2, { create: true }))
2387
+ (parent) => this.io(parent.getFileHandle(name2, { create }))
2306
2388
  );
2307
2389
  if (!existed) this.touch(Date.now(), parentOf3(target));
2308
2390
  return [handle, existed, target];
@@ -2608,11 +2690,12 @@ var OpfsFs = class {
2608
2690
  this.changed(source.path, destination);
2609
2691
  this.touch(Date.now(), parentOf3(source.path), parent.path);
2610
2692
  };
2611
- if (!copying) return place();
2693
+ if (!copying) return sourceStat.isDirectory ? place() : this.movingFile(source.path, place);
2612
2694
  const { promise, resolve: resolve2 } = Promise.withResolvers();
2613
2695
  const entry = { from: source.path, to: destination, done: promise };
2614
2696
  this.renamingDirs.add(entry);
2615
2697
  try {
2698
+ await this.writesDrained(source.path);
2616
2699
  const locks = this.locks;
2617
2700
  if (locks) {
2618
2701
  lock = `${this.renameLocks()}${crypto.randomUUID()}`;
@@ -2715,7 +2798,7 @@ var OpfsFs = class {
2715
2798
  return name2.startsWith(this.renameLocks()) || LEGACY_RENAME_LOCK.test(name2);
2716
2799
  }
2717
2800
  async learnRenames() {
2718
- for (const { rename } of await this.meta.under(RENAMES)) {
2801
+ for (const { rename } of await this.meta.under(RENAMES2)) {
2719
2802
  if (rename?.lock === void 0 || this.ownLocks.has(rename.lock)) continue;
2720
2803
  this.notePeer(rename.lock, [rename.from, rename.to]);
2721
2804
  }
@@ -2804,7 +2887,7 @@ var OpfsFs = class {
2804
2887
  if (!move.done)
2805
2888
  await locks.request(move.lock, async () => {
2806
2889
  if (move.done) return;
2807
- const left = (await this.meta.under(RENAMES)).filter((entry) => entry.rename?.lock === move.lock).sort(byIntent);
2890
+ const left = (await this.meta.under(RENAMES2)).filter((entry) => entry.rename?.lock === move.lock).sort(byIntent);
2808
2891
  for (const entry of left) await this.finishRename(entry);
2809
2892
  });
2810
2893
  this.peerMoves.delete(move.lock);
@@ -2883,7 +2966,7 @@ var OpfsFs = class {
2883
2966
  }
2884
2967
  renamingDirs = /* @__PURE__ */ new Set();
2885
2968
  intents = 0;
2886
- async intend(rename, at = `${RENAMES}/${crypto.randomUUID()}`) {
2969
+ async intend(rename, at = `${RENAMES2}/${crypto.randomUUID()}`) {
2887
2970
  const record2 = { ...rename, at: Date.now() * 1e3 + this.intents++ % 1e3 };
2888
2971
  await this.meta.update(at, () => ({ path: at, rename: record2 }));
2889
2972
  return at;
@@ -2919,7 +3002,7 @@ var OpfsFs = class {
2919
3002
  const { held: h = [], pending = [] } = await locks.query();
2920
3003
  for (const lock of [...h, ...pending]) held.add(lock.name);
2921
3004
  }
2922
- const intents = (await this.meta.under(RENAMES)).filter(
3005
+ const intents = (await this.meta.under(RENAMES2)).filter(
2923
3006
  (entry) => entry.rename !== void 0 && (!orphaned || locks !== void 0 && entry.rename.lock !== void 0 && !held.has(entry.rename.lock))
2924
3007
  ).sort(
2925
3008
  (a, b) => a.rename.at - b.rename.at
@@ -2971,6 +3054,7 @@ var OpfsFs = class {
2971
3054
  );
2972
3055
  }
2973
3056
  await this.meta.update(entry.path, () => void 0);
3057
+ this.remote?.emit({ paths: [], trees: [], moved: { from, to } });
2974
3058
  } finally {
2975
3059
  this.renamingDirs.delete(barrier);
2976
3060
  resolve2();
@@ -3288,6 +3372,10 @@ var OpfsFs = class {
3288
3372
  return removed;
3289
3373
  }
3290
3374
  };
3375
+ function movedOf(moved2) {
3376
+ const m = moved2;
3377
+ return typeof m?.from === "string" && typeof m.to === "string" ? { from: m.from, to: m.to } : void 0;
3378
+ }
3291
3379
  function nothing() {
3292
3380
  return void 0;
3293
3381
  }
@@ -7598,7 +7686,7 @@ function satisfies(version, range2) {
7598
7686
  }
7599
7687
 
7600
7688
  // src/version.ts
7601
- var KERNEL_VERSION = true ? "1.143.4" : "0.0.0-dev";
7689
+ var KERNEL_VERSION = true ? "1.143.6" : "0.0.0-dev";
7602
7690
  var OS_RELEASE = "6.0.0-slicc";
7603
7691
  function procVersion() {
7604
7692
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -13033,7 +13121,8 @@ var SYNC_FS_OPS = [
13033
13121
  "truncate",
13034
13122
  "chown",
13035
13123
  "lchown",
13036
- "access"
13124
+ "access",
13125
+ "renames"
13037
13126
  ];
13038
13127
  var HOLD_READ = 4;
13039
13128
  var HOLD_WRITE = 8;
@@ -13081,6 +13170,30 @@ async function readdirStat(fs, dir, g) {
13081
13170
  const shown = async (name2, stat) => stat && statJson(await callerStat(g, joinName(dir, name2), stat));
13082
13171
  return Promise.all(listed.map(async ([name2, stat]) => [name2, await shown(name2, stat)]));
13083
13172
  }
13173
+ async function visibleRenames(entry, req) {
13174
+ const answer2 = entry.renames?.(req.offset ?? 0, heldPaths(req.arg2)) ?? { now: 0, renames: [] };
13175
+ const g = guardOf(entry);
13176
+ if (!g?.dac.enforcing) return answer2;
13177
+ const searchable2 = async (path) => {
13178
+ const dir = path.slice(0, Math.max(1, path.lastIndexOf("/")));
13179
+ const w = await walk(g.fs, g.cred, dir, true, g.links).catch(nothing2);
13180
+ if (w === false || w.denied || w.perm === void 0 || !permits(g.cred, w.perm, X_OK))
13181
+ return false;
13182
+ return g.fs.stat(w.path).then((st) => st.isDirectory, nothing2);
13183
+ };
13184
+ const shown = [];
13185
+ for (const [from, to] of answer2.renames) {
13186
+ if (await searchable2(from) && await searchable2(to)) shown.push([from, to]);
13187
+ }
13188
+ return { now: answer2.now, renames: shown };
13189
+ }
13190
+ function nothing2() {
13191
+ return false;
13192
+ }
13193
+ function heldPaths(arg) {
13194
+ const held = JSON.parse(arg ?? "[]");
13195
+ return Array.isArray(held) ? held.filter((path) => typeof path === "string") : [];
13196
+ }
13084
13197
  async function usageAt(entry, path) {
13085
13198
  if (!entry.statfs) return void 0;
13086
13199
  const real = await realTarget(entry.fs, path).catch(() => path);
@@ -13198,7 +13311,8 @@ async function run(entry, path, req) {
13198
13311
  const to = fs.resolvePath(cwd, req.arg2 ?? "");
13199
13312
  await fs.rename(path, to);
13200
13313
  entry.renamed?.(path, to);
13201
- return done2;
13314
+ const seq2 = entry.renameSeq?.(path, to);
13315
+ return seq2 === void 0 ? done2 : json2({ seq: seq2 });
13202
13316
  }
13203
13317
  case "link":
13204
13318
  await fs.link(path, fs.resolvePath(cwd, req.arg2 ?? ""), req.mode === 1);
@@ -13670,6 +13784,7 @@ async function linkFree(fs, path) {
13670
13784
  }
13671
13785
  }
13672
13786
  async function dispatchOne(entry, req) {
13787
+ if (req.op === "renames") return json2(await visibleRenames(entry, req));
13673
13788
  const result = await dispatchPath(entry, req);
13674
13789
  if (result.ok || result.errno !== "ENOENT" || !req.direct || !MISS_OPS.has(req.op) || req.handle !== void 0) {
13675
13790
  return result;
@@ -21871,6 +21986,7 @@ var VfsNodes = class {
21871
21986
  byId = /* @__PURE__ */ new Map();
21872
21987
  keepsOnDisk = false;
21873
21988
  changed;
21989
+ movedAway;
21874
21990
  dirtied;
21875
21991
  fs;
21876
21992
  entries;
@@ -22300,6 +22416,7 @@ function keepingOpen(fs, nodes, changed = () => {
22300
22416
  await nodes.unlinking(b);
22301
22417
  await fs.rename(from, to);
22302
22418
  nodes.renamed(a, b);
22419
+ nodes.movedAway?.(a, b);
22303
22420
  });
22304
22421
  },
22305
22422
  writeFile: async (path, content, owner) => {
@@ -24523,6 +24640,8 @@ function holdingToken(opts, holds2, caller) {
24523
24640
  noLinks: opts.noLinks,
24524
24641
  cwd: opts.cwd,
24525
24642
  ...opts.statfs ? { statfs: opts.statfs } : {},
24643
+ ...opts.renames ? { renames: opts.renames } : {},
24644
+ ...opts.renameSeq ? { renameSeq: opts.renameSeq } : {},
24526
24645
  hold: (path, on, open2, real, handle) => holds2.hold(path, on, open2, real, handle),
24527
24646
  keptFor: (handle) => holds2.keptFor(handle),
24528
24647
  unlinked: (path) => holds2.keptAt(path),
@@ -25892,6 +26011,43 @@ function replayingHops(inner, heldMs = HELD_MS) {
25892
26011
  };
25893
26012
  }
25894
26013
 
26014
+ // src/kernel/rename-log.ts
26015
+ var RENAME_LOG_SIZE = 4096;
26016
+ function within9(path, root) {
26017
+ return path === root || path.startsWith(root === "/" ? "/" : `${root}/`);
26018
+ }
26019
+ var RenameLog = class {
26020
+ changes;
26021
+ entries = [];
26022
+ constructor(changes) {
26023
+ this.changes = changes;
26024
+ }
26025
+ add(from, to) {
26026
+ const seq2 = this.changes.renamed();
26027
+ this.entries.push({ seq: seq2, from, to });
26028
+ if (this.entries.length > RENAME_LOG_SIZE) this.entries.shift();
26029
+ return seq2;
26030
+ }
26031
+ seqOf(from, to) {
26032
+ for (let i = this.entries.length - 1; i >= 0; i--) {
26033
+ const entry = this.entries[i];
26034
+ if (entry.from === from && entry.to === to) return entry.seq;
26035
+ }
26036
+ return void 0;
26037
+ }
26038
+ since(seen, held) {
26039
+ const now = this.entries.at(-1)?.seq ?? seen;
26040
+ const renames = [];
26041
+ let paths = [...held];
26042
+ for (const { seq: seq2, from, to } of this.entries) {
26043
+ if (seq2 <= seen || !paths.some((path) => within9(path, from))) continue;
26044
+ renames.push([from, to]);
26045
+ paths = paths.map((path) => within9(path, from) ? to + path.slice(from.length) : path);
26046
+ }
26047
+ return { now, renames };
26048
+ }
26049
+ };
26050
+
25895
26051
  // src/kernel/net/sealed-scrubs.ts
25896
26052
  function addTo(tree2, host, secrets) {
25897
26053
  const entry = tree2.get(host) ?? { secrets: [], version: 0, scrubber: void 0 };
@@ -28649,6 +28805,7 @@ var Launcher = class {
28649
28805
  hostname;
28650
28806
  watchers = new FsWatchers();
28651
28807
  changes = new FileChanges();
28808
+ renameLog = new RenameLog(this.changes);
28652
28809
  cwds = new CwdTable(async (path) => {
28653
28810
  const { dev, ino } = await this.base.stat(path);
28654
28811
  return `${dev ?? 0}:${ino}`;
@@ -28787,6 +28944,7 @@ var Launcher = class {
28787
28944
  this.openFiles.add(this.nodes);
28788
28945
  options.fs.onPeer?.(() => this.flushOpen());
28789
28946
  options.fs.remoteChanges?.((change) => {
28947
+ if (change.moved) this.moved(change.moved.from, change.moved.to);
28790
28948
  this.changes.flush();
28791
28949
  this.cwds.touched(change.paths, change.trees);
28792
28950
  this.rewritten(change.paths, change.trees);
@@ -29803,6 +29961,8 @@ var Launcher = class {
29803
29961
  unlinkGate: stack.unlinked.gate,
29804
29962
  replacements: stack.unlinked,
29805
29963
  statfs: (path) => this.mounts.statfs(path),
29964
+ renames: (since, held) => this.renameLog.since(since, held),
29965
+ renameSeq: (from, to) => this.renameLog.seqOf(from, to),
29806
29966
  mounts: () => this.mounts.list(),
29807
29967
  mount: (call, signal) => this.processMount(pid, call, signal),
29808
29968
  umount: (target, flags2) => this.processUmount(pid, target, flags2),
@@ -30750,8 +30910,15 @@ var Launcher = class {
30750
30910
  const target = command && (command.shows ?? command.script ?? command.wasm);
30751
30911
  return target === void 0 ? void 0 : { path: target, ...owner ? { owner } : {} };
30752
30912
  }
30913
+ moved(from, to, source) {
30914
+ this.renameLog.add(from, to);
30915
+ if (source) return;
30916
+ for (const nodes of this.openFiles) nodes.renamed(from, to);
30917
+ this.watchers.moved(from, to);
30918
+ }
30753
30919
  processChain(fs, nodes, unlinked) {
30754
30920
  nodes.changed = (path) => this.changes.publish(path);
30921
+ nodes.movedAway = (from, to) => this.moved(from, to, nodes);
30755
30922
  nodes.dirtied = (delta) => this.changes.dirtied(delta);
30756
30923
  return unlinked.wrap(keepingOpen(fs, nodes, (path) => this.changes.publish(path)));
30757
30924
  }