@ai-ecoverse/slicc-kernel 1.161.0 → 1.162.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.
package/dist/node.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/node.ts
2
2
  import { createHash } from "node:crypto";
3
- import { lstatSync, mkdtempSync, readdirSync, rmSync } from "node:fs";
3
+ import { lstatSync, mkdirSync, mkdtempSync, readdirSync, rmSync } from "node:fs";
4
4
  import { tmpdir } from "node:os";
5
5
  import { isAbsolute, join } from "node:path";
6
6
  import { MessageChannel as MessageChannel2, Worker } from "node:worker_threads";
@@ -2212,7 +2212,9 @@ var KernelSocket = class _KernelSocket {
2212
2212
  throw new KernelError("ECONNRESET", "read ECONNRESET");
2213
2213
  return bytes.length > 0 ? bytes : null;
2214
2214
  },
2215
- write: (bytes) => raw2.tx.write(bytes),
2215
+ write: (bytes) => raw2.tx.write(bytes).catch((err) => {
2216
+ throw this.refused ? new KernelError("ECONNRESET", "read ECONNRESET") : err;
2217
+ }),
2216
2218
  closeWrite: () => raw2.tx.closeWrite(),
2217
2219
  close: () => {
2218
2220
  raw2.rx.closeRead();
@@ -3675,7 +3677,7 @@ function satisfies(version, range2) {
3675
3677
  }
3676
3678
 
3677
3679
  // src/version.ts
3678
- var KERNEL_VERSION = true ? "1.161.0" : "0.0.0-dev";
3680
+ var KERNEL_VERSION = true ? "1.162.1" : "0.0.0-dev";
3679
3681
  var OS_RELEASE = "6.0.0-slicc";
3680
3682
  function procVersion() {
3681
3683
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -4638,6 +4640,9 @@ var LOOKUP_OPS = [
4638
4640
  var within2 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
4639
4641
  var TOUCHED_MAX = 4096;
4640
4642
  var parentOf2 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
4643
+ var PEER_RM_MS = 1e3;
4644
+ var PEER_RENAME_MS = 1e4;
4645
+ var PEER_GATE_MS = PEER_RENAME_MS + 5e3;
4641
4646
  var lockName = (instance) => `slicc-unlinked:${instance}`;
4642
4647
  var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
4643
4648
  var PathGate = class {
@@ -4680,7 +4685,9 @@ var UnlinkedKeeper = class {
4680
4685
  links = /* @__PURE__ */ new Map();
4681
4686
  linked = /* @__PURE__ */ new Map();
4682
4687
  dirOwner;
4688
+ peers;
4683
4689
  constructor(fs, holders, rootOf, options = {}) {
4690
+ this.peers = options.peers;
4684
4691
  this.marks = options.marks ?? new PathMarks();
4685
4692
  const { locks, owner } = options;
4686
4693
  this.dirOwner = options.dirOwner;
@@ -4756,22 +4763,29 @@ var UnlinkedKeeper = class {
4756
4763
  lstat: (path) => unreplaced(path, async () => counted3(await fs.lstat(path))),
4757
4764
  async rm(path, options) {
4758
4765
  const target = fs.resolvePath("/", path);
4759
- await replace(target, async (gated, sealed) => {
4760
- const pinned = await keeper.pin(target, gated, sealed);
4761
- if (pinned.moved.has(target)) pinned.commit();
4762
- else await keeper.settle(pinned, () => fs.rm(path, options));
4763
- });
4766
+ const kept = await keeper.peerKeep(target, false, () => keeper.removable(target, options));
4767
+ if (kept.whole) return;
4768
+ await kept.undoing(
4769
+ () => replace(target, async (gated, sealed) => {
4770
+ const pinned = await keeper.pin(target, gated, sealed);
4771
+ if (pinned.moved.has(target)) pinned.commit();
4772
+ else await keeper.settle(pinned, () => fs.rm(path, options));
4773
+ })
4774
+ );
4764
4775
  },
4765
4776
  async rename(from, to) {
4766
4777
  const target = fs.resolvePath("/", to);
4767
4778
  const source = fs.resolvePath("/", from);
4768
4779
  if (target === source) return fs.rename(from, to);
4769
- await keeper.marks.around(
4770
- [source],
4771
- () => replace(target, async (gated, sealed) => {
4772
- const pinned = await keeper.pin(target, gated, sealed);
4773
- await keeper.settle(pinned, () => fs.rename(from, to));
4774
- })
4780
+ const kept = await keeper.peerKeep(target, true, () => keeper.renamable(source, target));
4781
+ await kept.undoing(
4782
+ () => keeper.marks.around(
4783
+ [source],
4784
+ () => replace(target, async (gated, sealed) => {
4785
+ const pinned = await keeper.pin(target, gated, sealed);
4786
+ await keeper.settle(pinned, () => fs.rename(from, to));
4787
+ })
4788
+ )
4775
4789
  );
4776
4790
  },
4777
4791
  symlink: (target, path, owner) => keeper.marks.around([fs.resolvePath("/", path)], () => fs.symlink(target, path, owner)),
@@ -4916,6 +4930,114 @@ var UnlinkedKeeper = class {
4916
4930
  (err) => err?.code !== "ENOENT" && err?.code !== "ENOTDIR"
4917
4931
  );
4918
4932
  }
4933
+ get id() {
4934
+ return this.instance;
4935
+ }
4936
+ adopt(hidden, refs) {
4937
+ return this.keptFile(hidden, { refs });
4938
+ }
4939
+ async peerKeep(target, over, valid2 = async () => true) {
4940
+ const none = { whole: false, undoing: (op) => op() };
4941
+ const peers = this.peers;
4942
+ if (!peers?.shared() || this.rootOf(target) !== "/") return none;
4943
+ const held = peers.heldWithin(target).filter((holder) => holder.instance !== void 0);
4944
+ if (held.length === 0 || !await valid2()) return none;
4945
+ const tabs = /* @__PURE__ */ new Map();
4946
+ for (const holder of held) tabs.set(holder.tab, [...tabs.get(holder.tab) ?? [], holder]);
4947
+ const asked = [...tabs].map(([tab, holders]) => {
4948
+ const paths = holders.map((holder) => holder.path);
4949
+ return { tab, holders, paths };
4950
+ });
4951
+ const heard = await Promise.all(
4952
+ asked.map(
4953
+ ({ tab, paths }) => peers.ask(tab, { keeping: { paths } }, over ? PEER_RENAME_MS : PEER_RM_MS)
4954
+ )
4955
+ );
4956
+ if (over && heard.includes(false)) {
4957
+ for (const { tab, paths } of asked)
4958
+ void peers.ask(tab, { kept: { items: [], paths } }, PEER_RM_MS);
4959
+ throw fsError("EBUSY", `${target}: open in another tab that did not answer`);
4960
+ }
4961
+ const { whole, moved: moved2 } = await this.setAside(peers, target, asked, heard);
4962
+ return { whole, undoing: (op) => this.undoing(op, moved2) };
4963
+ }
4964
+ async setAside(peers, target, asked, heard) {
4965
+ const mine = this.heldWithin(target);
4966
+ const taken = /* @__PURE__ */ new Set();
4967
+ let whole = false;
4968
+ const told = [];
4969
+ const moved2 = [];
4970
+ for (const [i, { tab, holders, paths }] of asked.entries()) {
4971
+ const items = [];
4972
+ for (const holder of holders) {
4973
+ const free = !mine.has(holder.path) && !taken.has(holder.path);
4974
+ const hidden = free ? await this.asideFor(holder.path, holder.instance) : void 0;
4975
+ if (hidden !== void 0) {
4976
+ taken.add(holder.path);
4977
+ moved2.push({ tab, path: holder.path, hidden, fid: holder.fid });
4978
+ if (holder.path === target) whole = true;
4979
+ }
4980
+ items.push({ path: holder.path, hidden, fid: holder.fid });
4981
+ }
4982
+ const answer2 = peers.ask(tab, { kept: { items, paths } }, PEER_RM_MS);
4983
+ if (heard[i]) told.push(answer2);
4984
+ }
4985
+ await Promise.all(told);
4986
+ return { whole, moved: moved2 };
4987
+ }
4988
+ async undoing(op, moved2) {
4989
+ try {
4990
+ return await op();
4991
+ } catch (err) {
4992
+ for (const { tab, path, hidden, fid } of moved2) {
4993
+ const back = await this.serial(async () => {
4994
+ if (await this.present(path)) return false;
4995
+ return this.fs.rename(hidden, path).then(
4996
+ () => true,
4997
+ () => false
4998
+ );
4999
+ });
5000
+ if (!back) continue;
5001
+ void this.peers?.ask(
5002
+ tab,
5003
+ { kept: { items: [{ path, hidden: void 0, fid, undo: hidden }], paths: [] } },
5004
+ PEER_RM_MS
5005
+ );
5006
+ }
5007
+ throw err;
5008
+ }
5009
+ }
5010
+ async removable(target, options) {
5011
+ const st = await this.fs.lstat(target).catch(() => void 0);
5012
+ return st !== void 0 && (!st.isDirectory || options?.recursive === true);
5013
+ }
5014
+ async renamable(source, target) {
5015
+ const from = await this.fs.lstat(source);
5016
+ const to = await this.fs.lstat(target).catch(() => void 0);
5017
+ if (!to) return true;
5018
+ if (to.isDirectory && !from.isDirectory) throw fsError("EISDIR", target);
5019
+ if (!to.isDirectory && from.isDirectory) throw fsError("ENOTDIR", target);
5020
+ if (to.isDirectory && (await this.fs.readdir(target)).length > 0)
5021
+ throw fsError("ENOTEMPTY", target);
5022
+ return true;
5023
+ }
5024
+ async asideFor(path, instance) {
5025
+ return await this.hideFor(path, instance) ?? this.hideFor(path, instance);
5026
+ }
5027
+ hideFor(path, instance) {
5028
+ return this.serial(async () => {
5029
+ const dir = dirOf("/");
5030
+ const hidden = `${dir}/${instance}.p${crypto.randomUUID().replaceAll("-", "")}`;
5031
+ try {
5032
+ const owner = this.dirOwner;
5033
+ await this.fs.mkdir(dir, { recursive: true, ...owner ? { owner } : {} });
5034
+ await this.fs.rename(path, hidden);
5035
+ return hidden;
5036
+ } catch {
5037
+ return void 0;
5038
+ }
5039
+ });
5040
+ }
4919
5041
  keptFile(hidden, entry) {
4920
5042
  return {
4921
5043
  hidden,
@@ -4970,7 +5092,7 @@ var UnlinkedKeeper = class {
4970
5092
  }
4971
5093
  async tidy(dir) {
4972
5094
  const left = await this.fs.readdir(dir).catch(() => [UNLINKED_DIR]);
4973
- if (left.length === 0) await this.fs.rm(dir, { recursive: true }).catch(() => void 0);
5095
+ if (left.length === 0) await this.fs.rm(dir).catch(() => void 0);
4974
5096
  }
4975
5097
  sweep(root) {
4976
5098
  const query = this.locks?.query?.bind(this.locks);
@@ -6380,6 +6502,11 @@ async function shownTarget(fs, target) {
6380
6502
  const st = await quiet2(fs.stat(target));
6381
6503
  return st?.isFile && await readsAs2(fs, NOBODY, target) ? st : void 0;
6382
6504
  }
6505
+ async function madeFor(owner, virtual) {
6506
+ if (!owner?.existing || await virtual() === void 0) return owner;
6507
+ const { existing: _, ...made2 } = owner;
6508
+ return made2;
6509
+ }
6383
6510
  function withCommandDirs(fs, names2, userDir = async () => void 0, owners = false, targetOf2 = async () => void 0) {
6384
6511
  async function nearest(path) {
6385
6512
  const st = await fs.stat(path).catch(() => void 0);
@@ -6477,7 +6604,7 @@ function withCommandDirs(fs, names2, userDir = async () => void 0, owners = fals
6477
6604
  }),
6478
6605
  readFileBuffer: (path) => fs.readFileBuffer(path).catch((err) => content(path, err)),
6479
6606
  commandTarget: async (path) => (await entryTarget(path))?.path,
6480
- writeFile: (path, content2, owner) => fs.writeFile(path, content2, owner),
6607
+ writeFile: async (path, content2, owner) => fs.writeFile(path, content2, await madeFor(owner, () => entryTarget(path))),
6481
6608
  link: (from, to, follow2) => fs.link(from, to, follow2),
6482
6609
  links: (path) => mayLink(fs, path),
6483
6610
  async exists(path) {
@@ -6642,6 +6769,10 @@ function withEtcFiles(fs, files) {
6642
6769
  }
6643
6770
  } : {},
6644
6771
  readFile: (path) => fs.readFile(path).catch(fallback(path, (b) => new TextDecoder().decode(b))),
6772
+ writeFile: async (path, content, owner) => {
6773
+ if (owner?.existing && files.has(path)) await materialize(path);
6774
+ return fs.writeFile(path, content, owner);
6775
+ },
6645
6776
  ...fs.append ? {
6646
6777
  append: async (path, bytes, options) => {
6647
6778
  if (files.has(path)) await materialize(path);
@@ -9022,12 +9153,25 @@ var HeldPaths = class extends Map {
9022
9153
  for (const file of this.unlinked.values()) if (file.hidden === hidden) return true;
9023
9154
  return false;
9024
9155
  }
9156
+ boundAt(path, fid) {
9157
+ return this.handlesAt(path).filter(
9158
+ (id2) => !this.keptHandles.has(id2) && this.bound.get(id2)?.fid === fid
9159
+ );
9160
+ }
9161
+ keepBound(ids, path, file) {
9162
+ for (const id2 of ids) this.keptHandles.set(id2, file);
9163
+ this.uses.set(file, ids.length);
9164
+ this.unlinked.set(path, file);
9165
+ }
9025
9166
  keep(path, file) {
9026
9167
  const fresh3 = this.handlesAt(path).filter((id2) => !this.keptHandles.has(id2));
9027
9168
  for (const id2 of fresh3) this.keptHandles.set(id2, file);
9028
9169
  this.uses.set(file, Math.max(fresh3.length, 1));
9029
9170
  this.unlinked.set(path, file);
9030
9171
  }
9172
+ unkeepAt(path, hidden) {
9173
+ for (const file of [...this.uses.keys()]) if (file.hidden === hidden) this.unkeep(path, file);
9174
+ }
9031
9175
  unkeep(path, file) {
9032
9176
  for (const [id2, kept] of this.keptHandles) if (kept === file) this.keptHandles.delete(id2);
9033
9177
  this.uses.delete(file);
@@ -9596,12 +9740,12 @@ var MountFs = class _MountFs {
9596
9740
  const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
9597
9741
  return this.follow(this.base.resolvePath(parentOf5(real), target), hops + 1, create);
9598
9742
  }
9599
- async open(mount, rel2, write, owner) {
9743
+ async open(mount, rel2, write, owner, create = write) {
9600
9744
  return await mount.conn.call({
9601
9745
  op: "open",
9602
9746
  path: rel2,
9603
9747
  write,
9604
- create: write,
9748
+ create,
9605
9749
  truncate: write,
9606
9750
  exclusive: false,
9607
9751
  ...owner
@@ -9698,10 +9842,10 @@ var MountFs = class _MountFs {
9698
9842
  `${real} would be larger than this mount's maxfile (${mount.maxFile})`
9699
9843
  );
9700
9844
  }
9701
- const made2 = await this.driverOwner(found, real, owner, false);
9845
+ const made2 = owner?.existing ? void 0 : await this.driverOwner(found, real, owner, false);
9702
9846
  for (let attempt = 1; ; attempt++) {
9703
9847
  try {
9704
- return await this.writeWhole(mount, real, rel2, bytes, made2);
9848
+ return await this.writeWhole(mount, real, rel2, bytes, made2, owner?.existing !== true);
9705
9849
  } catch (err) {
9706
9850
  if (err.code !== "ESTALE" || attempt >= STALE_RETRIES) throw err;
9707
9851
  }
@@ -9719,10 +9863,10 @@ var MountFs = class _MountFs {
9719
9863
  this.table.forget(mount, real);
9720
9864
  }
9721
9865
  }
9722
- async writeWhole(mount, real, rel2, bytes, owner) {
9866
+ async writeWhole(mount, real, rel2, bytes, owner, create = true) {
9723
9867
  await this.table.commit(real);
9724
9868
  this.table.forget(mount, real);
9725
- const fh = await this.open(mount, rel2, true, owner);
9869
+ const fh = await this.open(mount, rel2, true, owner, create);
9726
9870
  const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
9727
9871
  try {
9728
9872
  for (let offset = 0; offset < bytes.length; offset += chunk2) {
@@ -10459,6 +10603,11 @@ function ownerFor(entry, mode) {
10459
10603
  if (!cred) return void 0;
10460
10604
  return ownerOf(cred, mode === void 0 ? void 0 : mode & ~(entry.umask?.() ?? 18));
10461
10605
  }
10606
+ function writeOwner(entry, req) {
10607
+ const owner = ownerFor(entry, 438);
10608
+ if (req.handle === void 0 || req.mode !== 1) return owner;
10609
+ return { ...owner ?? {}, existing: true };
10610
+ }
10462
10611
  function plainDirMode(entry, mode) {
10463
10612
  if (entry.cred || mode === void 0) return void 0;
10464
10613
  const wanted = mode & 4095 & ~(entry.umask?.() ?? 18);
@@ -10529,7 +10678,7 @@ async function run(entry, path, req) {
10529
10678
  case "read":
10530
10679
  return { ok: true, kind: "bytes", bytes: await readSource(entry, path) };
10531
10680
  case "write":
10532
- await fs.writeFile(path, req.body ?? new Uint8Array(0), ownerFor(entry, 438));
10681
+ await fs.writeFile(path, req.body ?? new Uint8Array(0), writeOwner(entry, req));
10533
10682
  return done;
10534
10683
  case "exists":
10535
10684
  return json2(await fs.exists(path));
@@ -23425,6 +23574,9 @@ var RemoteWatch = class {
23425
23574
  holdsSent = 0;
23426
23575
  silent = /* @__PURE__ */ new Set();
23427
23576
  holders = /* @__PURE__ */ new Set();
23577
+ instances = /* @__PURE__ */ new Map();
23578
+ instance;
23579
+ keeps;
23428
23580
  listeners = /* @__PURE__ */ new Set();
23429
23581
  waiting = /* @__PURE__ */ new Map();
23430
23582
  locks;
@@ -23544,8 +23696,9 @@ var RemoteWatch = class {
23544
23696
  this.met?.();
23545
23697
  }
23546
23698
  if (data.tab !== void 0 && data.paths !== void 0) this.announced(data);
23547
- if (data.tab !== void 0 && data.holds !== void 0) this.heldBy(data);
23699
+ this.holdsHeard(data);
23548
23700
  if (data.ack !== void 0 && data.to === this.tab && data.from !== void 0) {
23701
+ if (data.instance !== void 0) this.instances.set(data.from, data.instance);
23549
23702
  this.acked(data.ack, data.from);
23550
23703
  }
23551
23704
  if (data.changed !== void 0) this.emit({ paths: data.changed, trees: [], content: true });
@@ -23572,6 +23725,7 @@ var RemoteWatch = class {
23572
23725
  this.mourned.delete(peer);
23573
23726
  this.others.delete(peer);
23574
23727
  this.peerHolds.delete(peer);
23728
+ this.keeps?.left(peer);
23575
23729
  this.peers.delete(peer);
23576
23730
  this.ackers.delete(peer);
23577
23731
  this.holders.delete(peer);
@@ -23612,9 +23766,38 @@ var RemoteWatch = class {
23612
23766
  this.rebuild();
23613
23767
  this.channel.postMessage({ ack: seq2, from: this.tab, to: from });
23614
23768
  }
23769
+ holdsHeard(data) {
23770
+ if (data.tab === void 0) return;
23771
+ if (data.holds !== void 0) this.heldBy(data);
23772
+ if (data.to === this.tab && (data.keeping || data.kept)) void this.keepAsked(data);
23773
+ }
23774
+ keepChains = /* @__PURE__ */ new Map();
23775
+ keepAsked(data) {
23776
+ const from = data.tab;
23777
+ const next3 = (this.keepChains.get(from) ?? Promise.resolve()).then(() => this.keepOne(data));
23778
+ this.keepChains.set(from, next3);
23779
+ return next3;
23780
+ }
23781
+ async keepOne({ tab, seq: seq2, keeping, kept }) {
23782
+ const from = tab;
23783
+ const keeps = this.keeps;
23784
+ try {
23785
+ if (keeping) await keeps?.keeping(from, keeping.paths);
23786
+ if (kept)
23787
+ await keeps?.kept(from, kept.items, kept.paths ?? kept.items.map((item) => item.path));
23788
+ } finally {
23789
+ this.channel.postMessage({
23790
+ ack: seq2,
23791
+ from: this.tab,
23792
+ to: from,
23793
+ ...this.instance === void 0 ? {} : { instance: this.instance }
23794
+ });
23795
+ }
23796
+ }
23615
23797
  heldBy({ tab, holds: holds2, seq: seq2 }) {
23616
23798
  const from = tab;
23617
23799
  const { add, drop } = holds2;
23800
+ if (holds2?.instance !== void 0) this.instances.set(from, holds2.instance);
23618
23801
  const held = this.peerHolds.get(from) ?? /* @__PURE__ */ new Map();
23619
23802
  for (const path of drop) held.delete(path);
23620
23803
  for (const [path, fid] of add) held.set(path, fid);
@@ -23656,9 +23839,38 @@ var RemoteWatch = class {
23656
23839
  sendHolds(add, drop) {
23657
23840
  const seq2 = ++this.seq;
23658
23841
  this.holdsSent++;
23659
- this.channel.postMessage({ tab: this.tab, holds: { add: [...add], drop: [...drop] }, seq: seq2 });
23842
+ const instance = this.instance === void 0 ? {} : { instance: this.instance };
23843
+ this.channel.postMessage({
23844
+ tab: this.tab,
23845
+ holds: { add: [...add], drop: [...drop], ...instance },
23846
+ seq: seq2
23847
+ });
23660
23848
  return seq2;
23661
23849
  }
23850
+ heldWithin(target) {
23851
+ const out = [];
23852
+ for (const [tab, held] of this.peerHolds) {
23853
+ for (const [path, fid] of held) {
23854
+ if (path !== target && !path.startsWith(`${target}/`)) continue;
23855
+ out.push({ tab, path, fid, instance: this.instances.get(tab) });
23856
+ }
23857
+ }
23858
+ return out;
23859
+ }
23860
+ ask(to, body, cap) {
23861
+ const seq2 = ++this.seq;
23862
+ this.channel.postMessage({ tab: this.tab, to, seq: seq2, ...body });
23863
+ return new Promise((resolve2) => {
23864
+ const done4 = () => {
23865
+ clearTimeout(timer);
23866
+ this.waiting.delete(seq2);
23867
+ resolve2(left.size === 0);
23868
+ };
23869
+ const left = /* @__PURE__ */ new Set([to]);
23870
+ const timer = setTimeout(done4, cap);
23871
+ this.waiting.set(seq2, { left, done: done4 });
23872
+ });
23873
+ }
23662
23874
  peerHeld(path) {
23663
23875
  const out = [];
23664
23876
  for (const [tab, held] of this.peerHolds)
@@ -23694,6 +23906,7 @@ var RemoteWatch = class {
23694
23906
  if (this.peers.delete(tab)) left = true;
23695
23907
  this.others.delete(tab);
23696
23908
  this.peerHolds.delete(tab);
23909
+ this.keeps?.left(tab);
23697
23910
  this.ackers.delete(tab);
23698
23911
  this.holders.delete(tab);
23699
23912
  }
@@ -25169,6 +25382,7 @@ function translate(err, path, op = "lookup", mapped = true) {
25169
25382
  return error;
25170
25383
  }
25171
25384
  var META_OPS = ["get", "update", "move", "remove", "links", "under", "all"];
25385
+ var FILE_ID_TRIES = 3;
25172
25386
  function sidecar(meta) {
25173
25387
  const pathOf = ([first]) => [first, "/"].flat().find((p) => typeof p === "string");
25174
25388
  const guarded3 = (op) => (...args) => meta[op].apply(meta, args).catch((err) => {
@@ -25408,18 +25622,48 @@ var OpfsFs = class {
25408
25622
  this.channel?.postMessage(paths);
25409
25623
  }
25410
25624
  async fileId(path) {
25625
+ for (let tries = 0; tries < FILE_ID_TRIES; tries++) {
25626
+ const fid = await this.assignId(path);
25627
+ if (fid !== void 0) return fid;
25628
+ }
25629
+ throw fsError("EAGAIN", `${path}: moved while its file id was assigned`);
25630
+ }
25631
+ bindId(path) {
25632
+ return this.assignId(path);
25633
+ }
25634
+ async assignId(path) {
25411
25635
  const found = await this.locate(path, true);
25412
25636
  const [entry] = await (this.meta.fresh ?? this.meta.get).call(this.meta, [found.path]);
25413
25637
  if (entry?.fid !== void 0) return entry.fid;
25414
25638
  const fid = crypto.randomUUID();
25639
+ let moved2 = false;
25640
+ await this.meta.update(found.path, (now) => {
25641
+ const vacatedSince = now?.vacated && (!entry?.vacated || now.ino !== entry.ino);
25642
+ if (vacatedSince || entry !== void 0 && now === void 0) {
25643
+ moved2 = true;
25644
+ return now;
25645
+ }
25646
+ return { ...occupied(now), path: found.path, fid: now?.fid ?? fid };
25647
+ });
25648
+ if (moved2) return void 0;
25415
25649
  this.idWrites++;
25416
- await this.meta.update(found.path, (now) => ({
25417
- ...occupied(now),
25418
- path: found.path,
25419
- fid: now?.fid ?? fid
25420
- }));
25421
- const [stored] = await this.meta.get([found.path]);
25422
- return stored?.fid ?? fid;
25650
+ const there = await this.locate(path, true).then(
25651
+ (at) => at.path === found.path,
25652
+ () => false
25653
+ );
25654
+ if (there) return (await this.meta.get([found.path]))[0]?.fid ?? fid;
25655
+ const gone = await this.locate(found.path, true).then(
25656
+ () => false,
25657
+ (err) => missing4(err)
25658
+ );
25659
+ if (gone) {
25660
+ await this.meta.update(found.path, (now) => {
25661
+ if (now?.fid !== fid || now.vacated) return now;
25662
+ const { fid: _, ...rest2 } = now;
25663
+ return rest2;
25664
+ });
25665
+ }
25666
+ return void 0;
25423
25667
  }
25424
25668
  heldUnder = () => [];
25425
25669
  heldBy(held) {
@@ -25445,6 +25689,20 @@ var OpfsFs = class {
25445
25689
  const remote = this.remote;
25446
25690
  return remote && { ...remote.holdCounts(), held: remote.peerHeldPaths() };
25447
25691
  }
25692
+ peerKeeps() {
25693
+ const remote = this.remote;
25694
+ if (!remote) return void 0;
25695
+ return {
25696
+ shared: () => this.shared(),
25697
+ heldWithin: (path) => remote.heldWithin(path),
25698
+ ask: (to, body, cap) => remote.ask(to, body, cap)
25699
+ };
25700
+ }
25701
+ servePeerKeeps(instance, handlers) {
25702
+ if (!this.remote) return;
25703
+ this.remote.instance = instance;
25704
+ this.remote.keeps = handlers;
25705
+ }
25448
25706
  rehold(from, to) {
25449
25707
  this.remote?.rehold(from, to);
25450
25708
  }
@@ -26392,7 +26650,7 @@ var OpfsFs = class {
26392
26650
  }
26393
26651
  }
26394
26652
  async writeFile(path, content, owner) {
26395
- const [file, existed, target] = await this.fileFor(path);
26653
+ const [file, existed, target] = await this.fileFor(path, owner?.existing !== true);
26396
26654
  if (!existed) await this.stampNew(target, owner, false);
26397
26655
  if (!existed && content.length === 0) return;
26398
26656
  let writable2;
@@ -30283,7 +30541,7 @@ var Launcher = class {
30283
30541
  busy: (path) => [...this.openFiles].some((nodes) => nodes.holds(path)),
30284
30542
  owner: options.unlinkOwner,
30285
30543
  marks: this.pathMarks,
30286
- ...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
30544
+ ...this.peerOptions(options.fs)
30287
30545
  }
30288
30546
  );
30289
30547
  this.fs = this.processChain(fs, this.nodes, this.unlinked);
@@ -31117,7 +31375,7 @@ var Launcher = class {
31117
31375
  const assign = this.mounts.locate(target.wasm) === void 0 ? this.rootFs.fileId : void 0;
31118
31376
  return this.base.stat(target.wasm).then(
31119
31377
  async (st) => {
31120
- const fid = st.fid ?? await assign?.call(this.rootFs, target.wasm);
31378
+ const fid = st.fid ?? await assign?.call(this.rootFs, target.wasm)?.catch(() => void 0);
31121
31379
  if (fid === void 0) return exeIdOf(fileIdentity(st.ino, st.mtime.getTime()));
31122
31380
  return { exeId: `fid:${fid}:${st.mtime.getTime()}` };
31123
31381
  },
@@ -32279,8 +32537,82 @@ var Launcher = class {
32279
32537
  this.writers ??= this.writerStack();
32280
32538
  return this.writers;
32281
32539
  }
32540
+ peerGates = /* @__PURE__ */ new Map();
32541
+ peerOptions(fs) {
32542
+ const peers = fs.peerKeeps?.();
32543
+ queueMicrotask(() => fs.servePeerKeeps?.(this.unlinked.id, this.peerKept()));
32544
+ return {
32545
+ ...peers ? { peers } : {},
32546
+ ...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
32547
+ };
32548
+ }
32549
+ peerKept() {
32550
+ const gates = this.peerGates;
32551
+ const gone = /* @__PURE__ */ new Set();
32552
+ const instance = this.unlinked.id;
32553
+ const ours = (hidden) => hidden.startsWith(`/${UNLINKED_DIR}/${instance}.p`) && /^[0-9a-f]+$/.test(hidden.slice(`/${UNLINKED_DIR}/${instance}.p`.length));
32554
+ const open = (tab, paths) => {
32555
+ for (const [key, release] of [...gates]) {
32556
+ const [owner, path] = key.split("\0");
32557
+ if (owner !== tab || paths && !paths.includes(path)) continue;
32558
+ gates.delete(key);
32559
+ release();
32560
+ }
32561
+ };
32562
+ return {
32563
+ keeping: async (tab, paths) => {
32564
+ for (const path of paths) {
32565
+ if (gone.has(tab)) return;
32566
+ const release = await this.unlinked.gate.acquire(path);
32567
+ const key = `${tab}\0${path}`;
32568
+ let done4 = false;
32569
+ const once2 = () => {
32570
+ if (done4) return;
32571
+ done4 = true;
32572
+ clearTimeout(timer);
32573
+ release();
32574
+ };
32575
+ const timer = setTimeout(() => {
32576
+ if (gates.get(key) === once2) gates.delete(key);
32577
+ once2();
32578
+ }, PEER_GATE_MS);
32579
+ if (gone.has(tab)) once2();
32580
+ else gates.set(key, once2);
32581
+ }
32582
+ },
32583
+ kept: async (tab, items, paths) => {
32584
+ for (const { path, hidden, fid, undo } of items) {
32585
+ if (undo !== void 0) this.unadopt(path, undo, fid);
32586
+ else this.adoptKept(path, hidden, fid, ours);
32587
+ }
32588
+ open(tab, paths);
32589
+ },
32590
+ left: (tab) => {
32591
+ gone.add(tab);
32592
+ open(tab);
32593
+ }
32594
+ };
32595
+ }
32596
+ unadopt(path, undo, fid) {
32597
+ for (const holds2 of this.held) holds2.unkeepAt(path, undo);
32598
+ void this.rootFs.announceHolds?.([[path, fid]], [], false);
32599
+ }
32600
+ adoptKept(path, hidden, fid, ours) {
32601
+ if (hidden === void 0 || !ours(hidden)) {
32602
+ this.held.doubt([path]);
32603
+ return;
32604
+ }
32605
+ const bound3 = [...this.held].map(
32606
+ (holds2) => [holds2, [...holds2.boundAt(path, fid), ...holds2.boundAt(hidden, fid)]]
32607
+ );
32608
+ const keeping = bound3.filter(([, ids]) => ids.length > 0);
32609
+ const file = this.unlinked.adopt(hidden, Math.max(keeping.length, 1));
32610
+ if (keeping.length === 0) file.release();
32611
+ for (const [holds2, ids] of keeping) holds2.keepBound(ids, path, file);
32612
+ void this.rootFs.announceHolds?.([], [path, hidden], false);
32613
+ }
32282
32614
  rootFileId(path) {
32283
- return this.mounts.locate(path) === void 0 ? this.rootFs.fileId?.(path) : void 0;
32615
+ return this.mounts.locate(path) === void 0 ? this.rootFs.bindId?.(path) : void 0;
32284
32616
  }
32285
32617
  flushingPeers(fs) {
32286
32618
  const peers = fs.peers?.();
@@ -32326,6 +32658,7 @@ var Launcher = class {
32326
32658
  busy: (path) => [...this.openFiles].some((open) => open.holds(path)),
32327
32659
  owner: this.unlinkOwnerOption,
32328
32660
  marks: this.pathMarks,
32661
+ peers: this.rootFs.peerKeeps?.(),
32329
32662
  ...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
32330
32663
  }
32331
32664
  );
@@ -35024,25 +35357,38 @@ function ownCacheDir(make = privateCacheDir) {
35024
35357
  }
35025
35358
  }
35026
35359
  var swept = false;
35027
- function privateCacheDir(base = tmpdir(), alive = pidAlive) {
35360
+ function privateCacheDir(base = tmpdir(), alive = pidAlive, uid = process.getuid?.()) {
35361
+ const parent = cacheParent(base, uid);
35028
35362
  if (!swept) {
35029
35363
  swept = true;
35030
- sweepCacheDirs(base, alive);
35364
+ sweepCacheDirs(parent, alive, uid);
35365
+ }
35366
+ return mkdtempSync(join(parent, `${process.pid}-`));
35367
+ }
35368
+ function cacheParent(base, uid) {
35369
+ const parent = join(base, uid === void 0 ? "slicc-kernel-cc" : `${CACHE_PREFIX}u${uid}`);
35370
+ try {
35371
+ mkdirSync(parent, { mode: 448 });
35372
+ } catch (err) {
35373
+ if (err.code !== "EEXIST") throw err;
35374
+ }
35375
+ const st = lstatSync(parent);
35376
+ if (!st.isDirectory() || uid !== void 0 && (st.uid !== uid || (st.mode & 63) !== 0)) {
35377
+ throw new Error(`slicc-kernel: ${parent} is not a private directory of this user`);
35031
35378
  }
35032
- return mkdtempSync(join(base, `${CACHE_PREFIX}${process.pid}-`));
35379
+ return parent;
35033
35380
  }
35034
- function sweepCacheDirs(base, alive = pidAlive, uid = process.getuid?.()) {
35381
+ function sweepCacheDirs(parent, alive = pidAlive, uid = process.getuid?.()) {
35035
35382
  let names2;
35036
35383
  try {
35037
- names2 = readdirSync(base);
35384
+ names2 = readdirSync(parent);
35038
35385
  } catch {
35039
35386
  return;
35040
35387
  }
35041
35388
  for (const name2 of names2) {
35042
- if (!name2.startsWith(CACHE_PREFIX)) continue;
35043
- const pid = Number(name2.slice(CACHE_PREFIX.length).split("-")[0]);
35389
+ const pid = Number(name2.split("-")[0]);
35044
35390
  if (!Number.isInteger(pid) || pid <= 0 || alive(pid)) continue;
35045
- removeOurs(join(base, name2), uid);
35391
+ removeOurs(join(parent, name2), uid);
35046
35392
  }
35047
35393
  }
35048
35394
  function removeOurs(path, uid) {