@ai-ecoverse/slicc-kernel 1.41.0 → 1.41.3

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.
@@ -106,15 +106,15 @@ var IndexedDbMeta = class {
106
106
  async update(path, change) {
107
107
  const [transaction, store] = this.transaction("readwrite");
108
108
  const request2 = store.get(path);
109
- let next;
109
+ let next2;
110
110
  request2.onsuccess = () => {
111
- next = change(request2.result);
112
- if (next) store.put(next);
111
+ next2 = change(request2.result);
112
+ if (next2) store.put(next2);
113
113
  else store.delete(path);
114
114
  };
115
115
  await settle(transaction, () => void 0);
116
116
  this.changed({ paths: [path] });
117
- this.cache.set(path, next ?? null);
117
+ this.cache.set(path, next2 ?? null);
118
118
  }
119
119
  async move(from, to, root) {
120
120
  const [transaction, store] = this.transaction("readwrite");
@@ -341,8 +341,8 @@ var OpfsFs = class {
341
341
  resolvePath(base, path) {
342
342
  return resolvePath(base, path);
343
343
  }
344
- async walk(parts, from, start) {
345
- let dir = start;
344
+ async walk(parts, from, start2) {
345
+ let dir = start2;
346
346
  for (let at = from; at < parts.length; at++) {
347
347
  dir = await dir.getDirectoryHandle(parts[at]);
348
348
  this.dirs.set(`/${parts.slice(0, at + 1).join("/")}`, dir);
@@ -428,7 +428,7 @@ var OpfsFs = class {
428
428
  }
429
429
  return void 0;
430
430
  }
431
- async locate(path, follow) {
431
+ async locate(path, follow2) {
432
432
  let current = path;
433
433
  for (let hops = 0; hops <= MAX_LINKS; hops++) {
434
434
  try {
@@ -437,9 +437,9 @@ var OpfsFs = class {
437
437
  if (!missing(err)) throw err;
438
438
  const link2 = await this.linkAlong(current);
439
439
  if (!link2) throw err;
440
- if (link2.path === current && !follow) return { path: current, link: link2 };
441
- const rest = current.slice(link2.path.length);
442
- current = resolvePath(parentOf2(link2.path), `${link2.link}${rest}`);
440
+ if (link2.path === current && !follow2) return { path: current, link: link2 };
441
+ const rest2 = current.slice(link2.path.length);
442
+ current = resolvePath(parentOf2(link2.path), `${link2.link}${rest2}`);
443
443
  }
444
444
  }
445
445
  throw fsError("ELOOP", path);
@@ -755,9 +755,9 @@ var OpfsFs = class {
755
755
  return timed;
756
756
  }
757
757
  async restamp(path, timed) {
758
- for (const rest of timed) {
759
- const mtimeFor = await this.modified(path + rest);
760
- await this.meta.update(path + rest, (entry) => entry && { ...entry, mtimeFor });
758
+ for (const rest2 of timed) {
759
+ const mtimeFor = await this.modified(path + rest2);
760
+ await this.meta.update(path + rest2, (entry) => entry && { ...entry, mtimeFor });
761
761
  }
762
762
  }
763
763
  async place(handle, dir, name2) {
@@ -805,8 +805,8 @@ var OpfsFs = class {
805
805
  }));
806
806
  this.touch(parent.path);
807
807
  }
808
- async realpath(path, follow) {
809
- return (await this.locate(path, follow)).path;
808
+ async realpath(path, follow2) {
809
+ return (await this.locate(path, follow2)).path;
810
810
  }
811
811
  async readlink(path) {
812
812
  const found = await this.locate(path, false);
@@ -1659,8 +1659,8 @@ var KernelSocket = class _KernelSocket {
1659
1659
  async accept(signal, nonblock = false) {
1660
1660
  for (; ; ) {
1661
1661
  if (this.state !== "listening") throw new KernelError("EINVAL");
1662
- const next = this.queue.shift();
1663
- if (next) return next;
1662
+ const next2 = this.queue.shift();
1663
+ if (next2) return next2;
1664
1664
  if (nonblock) throw new KernelError("EAGAIN");
1665
1665
  await this.changes.wait(signal);
1666
1666
  }
@@ -1841,8 +1841,8 @@ var LoopbackNet = class {
1841
1841
  if (reserve) this.internal.add(port);
1842
1842
  return this.internal.has(port);
1843
1843
  }
1844
- activate(addr, start) {
1845
- this.activators.set(keyOf(addr), start);
1844
+ activate(addr, start2) {
1845
+ this.activators.set(keyOf(addr), start2);
1846
1846
  }
1847
1847
  socket(domain) {
1848
1848
  return new KernelSocket(this, domain);
@@ -2084,12 +2084,12 @@ var MessageQueue = class {
2084
2084
  }
2085
2085
  async *[Symbol.asyncIterator]() {
2086
2086
  for (; ; ) {
2087
- const next = this.items.shift();
2088
- if (next !== void 0) {
2089
- const n = payloadSize(next);
2087
+ const next2 = this.items.shift();
2088
+ if (next2 !== void 0) {
2089
+ const n = payloadSize(next2);
2090
2090
  this.size -= n;
2091
2091
  this.taken?.(n);
2092
- yield next;
2092
+ yield next2;
2093
2093
  continue;
2094
2094
  }
2095
2095
  if (this.ended) return;
@@ -2118,7 +2118,7 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
2118
2118
  ...typeof code === "string" ? { code } : {}
2119
2119
  });
2120
2120
  };
2121
- const start = async (call) => {
2121
+ const start2 = async (call) => {
2122
2122
  const abort = new AbortController();
2123
2123
  const entry = { abort };
2124
2124
  open.set(call.nid, entry);
@@ -2197,9 +2197,9 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
2197
2197
  try {
2198
2198
  const messages = socket.messages[Symbol.asyncIterator]();
2199
2199
  for (; ; ) {
2200
- const next = await Promise.race([messages.next(), unanswered]);
2201
- if (next === "late" || next.done) break;
2202
- const data = next.value;
2200
+ const next2 = await Promise.race([messages.next(), unanswered]);
2201
+ if (next2 === "late" || next2.done) break;
2202
+ const data = next2.value;
2203
2203
  if (flow.closing) continue;
2204
2204
  if (payloadSize(data) > MESSAGE_LIMIT) {
2205
2205
  oversize = true;
@@ -2240,12 +2240,12 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
2240
2240
  const body = open.get(nid)?.body;
2241
2241
  if (!body) return;
2242
2242
  try {
2243
- const next = await body.next();
2244
- if (next.done) {
2243
+ const next2 = await body.next();
2244
+ if (next2.done) {
2245
2245
  open.delete(nid);
2246
2246
  peer.postMessage({ net: "end", nid });
2247
2247
  } else {
2248
- const bytes = next.value.slice();
2248
+ const bytes = next2.value.slice();
2249
2249
  peer.postMessage({ net: "chunk", nid, bytes }, [bytes.buffer]);
2250
2250
  }
2251
2251
  } catch (e) {
@@ -2262,7 +2262,7 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
2262
2262
  };
2263
2263
  return {
2264
2264
  answer(call) {
2265
- if (call.net === "fetch") void start(call);
2265
+ if (call.net === "fetch") void start2(call);
2266
2266
  else if (call.net === "read") void read(call.nid);
2267
2267
  else if (call.net === "ws-open") void openSocket(call);
2268
2268
  else if (call.net === "ws-send") void sendOn(call);
@@ -2290,17 +2290,17 @@ async function realpath(fs, path) {
2290
2290
  let parts = fs.resolvePath("/", path).split("/").filter(Boolean);
2291
2291
  let resolved = "";
2292
2292
  for (let hops = 0; parts.length > 0; ) {
2293
- const [name2, ...rest] = parts;
2294
- const next = `${resolved}/${name2}`;
2295
- if (!(await fs.lstat(next)).isSymbolicLink) {
2296
- resolved = next;
2297
- parts = rest;
2293
+ const [name2, ...rest2] = parts;
2294
+ const next2 = `${resolved}/${name2}`;
2295
+ if (!(await fs.lstat(next2)).isSymbolicLink) {
2296
+ resolved = next2;
2297
+ parts = rest2;
2298
2298
  continue;
2299
2299
  }
2300
2300
  if (++hops > MAX_LINKS2) throw fsError("ELOOP", path);
2301
- const target = await fs.readlink(next);
2301
+ const target = await fs.readlink(next2);
2302
2302
  const base = target.startsWith("/") ? "/" : resolved || "/";
2303
- parts = [...fs.resolvePath(base, target).split("/").filter(Boolean), ...rest];
2303
+ parts = [...fs.resolvePath(base, target).split("/").filter(Boolean), ...rest2];
2304
2304
  resolved = "";
2305
2305
  }
2306
2306
  return resolved || "/";
@@ -2652,10 +2652,10 @@ var Incoming = class {
2652
2652
  this.eof = true;
2653
2653
  return false;
2654
2654
  }
2655
- const next = new Uint8Array(this.buf.length + chunk2.length);
2656
- next.set(this.buf);
2657
- next.set(chunk2, this.buf.length);
2658
- this.buf = next;
2655
+ const next2 = new Uint8Array(this.buf.length + chunk2.length);
2656
+ next2.set(this.buf);
2657
+ next2.set(chunk2, this.buf.length);
2658
+ this.buf = next2;
2659
2659
  return true;
2660
2660
  }
2661
2661
  take(n) {
@@ -3629,15 +3629,15 @@ var octets = (bytes) => tlv(4, bytes);
3629
3629
  var bits = (bytes, unused = 0) => tlv(3, Uint8Array.of(unused), bytes);
3630
3630
  var TRUE = tlv(1, Uint8Array.of(255));
3631
3631
  function integer(magnitude) {
3632
- let start = 0;
3633
- while (start < magnitude.length - 1 && magnitude[start] === 0) start++;
3634
- const trimmed = magnitude.subarray(start);
3632
+ let start2 = 0;
3633
+ while (start2 < magnitude.length - 1 && magnitude[start2] === 0) start2++;
3634
+ const trimmed = magnitude.subarray(start2);
3635
3635
  return tlv(2, trimmed[0] & 128 ? concat2([Uint8Array.of(0), trimmed]) : trimmed);
3636
3636
  }
3637
3637
  function oid(dotted) {
3638
- const [a, b, ...rest] = dotted.split(".").map(Number);
3638
+ const [a, b, ...rest2] = dotted.split(".").map(Number);
3639
3639
  const out = [40 * a + b];
3640
- for (const arc of rest) {
3640
+ for (const arc of rest2) {
3641
3641
  const groups = [];
3642
3642
  for (let v = arc; ; v = Math.floor(v / 128)) {
3643
3643
  groups.unshift(v & 127);
@@ -3677,19 +3677,19 @@ function children(der) {
3677
3677
  function header(der, at) {
3678
3678
  const tag = der[at];
3679
3679
  let len = der[at + 1];
3680
- let start = at + 2;
3680
+ let start2 = at + 2;
3681
3681
  if (len & 128) {
3682
3682
  const count = len & 127;
3683
3683
  len = 0;
3684
- for (let i = 0; i < count; i++) len = len * 256 + der[start + i];
3685
- start += count;
3684
+ for (let i = 0; i < count; i++) len = len * 256 + der[start2 + i];
3685
+ start2 += count;
3686
3686
  }
3687
- if (start + len > der.length) throw new Error("truncated DER");
3688
- return [tag, start, start + len];
3687
+ if (start2 + len > der.length) throw new Error("truncated DER");
3688
+ return [tag, start2, start2 + len];
3689
3689
  }
3690
3690
  function contents(element) {
3691
- const [, start, end] = header(element, 0);
3692
- return element.subarray(start, end);
3691
+ const [, start2, end] = header(element, 0);
3692
+ return element.subarray(start2, end);
3693
3693
  }
3694
3694
  function publicKeyBits(spki) {
3695
3695
  const key = children(spki)[1];
@@ -5288,6 +5288,53 @@ function withCommandDirs(fs, names) {
5288
5288
  };
5289
5289
  }
5290
5290
 
5291
+ // src/fs/physical-path.ts
5292
+ var MAX_HOPS = 40;
5293
+ function start(path) {
5294
+ return { done: [], todo: path.split("/").filter((p) => p !== ""), hops: 0 };
5295
+ }
5296
+ function next(walk) {
5297
+ while (walk.todo.length > 0) {
5298
+ const part = walk.todo.shift();
5299
+ if (part === ".") continue;
5300
+ if (part === "..") {
5301
+ walk.done.pop();
5302
+ continue;
5303
+ }
5304
+ return part;
5305
+ }
5306
+ return void 0;
5307
+ }
5308
+ function lexical(path) {
5309
+ const walk = start(path);
5310
+ for (let part = next(walk); part !== void 0; part = next(walk)) walk.done.push(part);
5311
+ return `/${walk.done.join("/")}`;
5312
+ }
5313
+ function rest(walk, part) {
5314
+ return lexical(`/${[...walk.done, part, ...walk.todo].join("/")}`);
5315
+ }
5316
+ function follow(walk, target) {
5317
+ if (++walk.hops > MAX_HOPS) return false;
5318
+ if (target.startsWith("/")) walk.done.length = 0;
5319
+ walk.todo = [...target.split("/").filter((p) => p !== ""), ...walk.todo];
5320
+ return true;
5321
+ }
5322
+ async function physicalPathAsync(fs, path) {
5323
+ const walk = start(path);
5324
+ for (let part = next(walk); part !== void 0; part = next(walk)) {
5325
+ const at = `/${[...walk.done, part].join("/")}`;
5326
+ let target;
5327
+ try {
5328
+ target = (await fs.lstat(at)).isSymbolicLink ? await fs.readlink(at) : void 0;
5329
+ } catch {
5330
+ return rest(walk, part);
5331
+ }
5332
+ if (target === void 0) walk.done.push(part);
5333
+ else if (!follow(walk, target)) return lexical(path);
5334
+ }
5335
+ return `/${walk.done.join("/")}`;
5336
+ }
5337
+
5291
5338
  // src/fs/unlinked.ts
5292
5339
  var UNLINKED_DIR = ".slicc-unlinked";
5293
5340
  function webLocks() {
@@ -5343,9 +5390,9 @@ var UnlinkedKeeper = class {
5343
5390
  void locks?.request(lockName(this.instance), () => new Promise(() => void 0));
5344
5391
  }
5345
5392
  serial(op) {
5346
- const next = this.queue.then(op);
5347
- this.queue = next.catch(() => void 0);
5348
- return next;
5393
+ const next2 = this.queue.then(op);
5394
+ this.queue = next2.catch(() => void 0);
5395
+ return next2;
5349
5396
  }
5350
5397
  wrap(fs = this.fs) {
5351
5398
  const keeper = this;
@@ -5642,10 +5689,10 @@ var ChildTable = class {
5642
5689
  }
5643
5690
  return this.track(req, fds, captured, this.spawner);
5644
5691
  }
5645
- async track(req, fds, captured, start) {
5692
+ async track(req, fds, captured, start2) {
5646
5693
  let handle;
5647
5694
  try {
5648
- handle = await start(req, fds);
5695
+ handle = await start2(req, fds);
5649
5696
  } catch (e) {
5650
5697
  await fds.closeAll();
5651
5698
  throw e;
@@ -5658,6 +5705,7 @@ var ChildTable = class {
5658
5705
  });
5659
5706
  handle.onState?.((state, sig) => {
5660
5707
  child.stopReport = state === "stopped" ? sig : void 0;
5708
+ child.stoppedBy = child.stopReport;
5661
5709
  child.continueReport = state === "continued";
5662
5710
  this.watchers.get(handle.pid)?.(state, sig);
5663
5711
  const waiters = this.stateChanged;
@@ -5669,7 +5717,10 @@ var ChildTable = class {
5669
5717
  return handle.pid;
5670
5718
  }
5671
5719
  watch(pid, listener) {
5672
- if (this.children.has(pid)) this.watchers.set(pid, listener);
5720
+ const child = this.children.get(pid);
5721
+ if (!child) return;
5722
+ this.watchers.set(pid, listener);
5723
+ if (child.stoppedBy !== void 0) listener("stopped", child.stoppedBy);
5673
5724
  }
5674
5725
  openSlot(slot, n, captured) {
5675
5726
  if ("fd" in slot) return this.parentFds.get(slot.fd).retain();
@@ -5882,8 +5933,7 @@ var HeldSet = class extends Set {
5882
5933
  delete(holds) {
5883
5934
  const removed = super.delete(holds);
5884
5935
  for (const path of /* @__PURE__ */ new Set([...holds.keys(), ...holds.opens.keys()])) this.idle(path);
5885
- for (const kept of holds.unlinked.values()) kept.release();
5886
- holds.unlinked.clear();
5936
+ holds.releaseAll();
5887
5937
  return removed;
5888
5938
  }
5889
5939
  };
@@ -5896,6 +5946,10 @@ var HeldPaths = class extends Map {
5896
5946
  opens = /* @__PURE__ */ new Map();
5897
5947
  unlinked = /* @__PURE__ */ new Map();
5898
5948
  aliases = /* @__PURE__ */ new Map();
5949
+ handles = /* @__PURE__ */ new Map();
5950
+ keptHandles = /* @__PURE__ */ new Map();
5951
+ uses = /* @__PURE__ */ new Map();
5952
+ lastHandle = 0;
5899
5953
  owning;
5900
5954
  onIdle;
5901
5955
  keyOf(path) {
@@ -5911,29 +5965,70 @@ var HeldPaths = class extends Map {
5911
5965
  return this.opens.keys();
5912
5966
  }
5913
5967
  isKept(path) {
5914
- return this.unlinked.has(path);
5968
+ const ids = this.handlesAt(path);
5969
+ return ids.length === 0 ? this.unlinked.has(path) : ids.every((id) => this.keptHandles.has(id));
5970
+ }
5971
+ handlesAt(path) {
5972
+ return [...this.handles].filter(([, key]) => key === path).map(([id]) => id);
5973
+ }
5974
+ keptFor(handle) {
5975
+ return this.keptHandles.get(handle);
5915
5976
  }
5916
5977
  keep(path, file) {
5978
+ const fresh = this.handlesAt(path).filter((id) => !this.keptHandles.has(id));
5979
+ for (const id of fresh) this.keptHandles.set(id, file);
5980
+ this.uses.set(file, Math.max(fresh.length, 1));
5917
5981
  this.unlinked.set(path, file);
5918
5982
  }
5919
- unkeep(path) {
5920
- this.unlinked.delete(path);
5921
- }
5922
- hold(path, on, open = false, real) {
5923
- if (open) {
5924
- const key = real ?? this.keyOf(path);
5925
- if (key !== path) this.aliases.set(path, key);
5926
- const count2 = (this.opens.get(key) ?? 0) + (on ? 1 : -1);
5927
- if (count2 > 0) this.opens.set(key, count2);
5928
- else {
5929
- this.opens.delete(key);
5930
- this.unlinked.get(key)?.release();
5931
- this.unlinked.delete(key);
5932
- for (const [alias, target] of this.aliases) if (target === key) this.aliases.delete(alias);
5933
- this.onIdle?.(key);
5934
- }
5983
+ unkeep(path, file) {
5984
+ for (const [id, kept] of this.keptHandles) if (kept === file) this.keptHandles.delete(id);
5985
+ this.uses.delete(file);
5986
+ if (this.unlinked.get(path) === file) this.unlinked.delete(path);
5987
+ }
5988
+ letGo(file, all = false) {
5989
+ const left = all ? 0 : (this.uses.get(file) ?? 0) - 1;
5990
+ if (left > 0) {
5991
+ this.uses.set(file, left);
5935
5992
  return;
5936
5993
  }
5994
+ if (!this.uses.delete(file)) return;
5995
+ file.release();
5996
+ for (const [path, kept] of this.unlinked) if (kept === file) this.unlinked.delete(path);
5997
+ }
5998
+ releaseAll() {
5999
+ for (const file of [...this.uses.keys()]) this.letGo(file, true);
6000
+ this.keptHandles.clear();
6001
+ }
6002
+ hold(path, on, open = false, real, handle) {
6003
+ if (open) return this.holdOpen(path, on, real, handle);
6004
+ this.holdWrite(path, on);
6005
+ return void 0;
6006
+ }
6007
+ holdOpen(path, on, real, handle) {
6008
+ const known = handle === void 0 ? void 0 : this.handles.get(handle);
6009
+ const key = known ?? real ?? this.keyOf(path);
6010
+ if (key !== path) this.aliases.set(path, key);
6011
+ if (handle !== void 0) {
6012
+ const kept = this.keptHandles.get(handle);
6013
+ this.handles.delete(handle);
6014
+ this.keptHandles.delete(handle);
6015
+ if (kept) this.letGo(kept);
6016
+ }
6017
+ const count = (this.opens.get(key) ?? 0) + (on ? 1 : -1);
6018
+ if (count > 0) this.opens.set(key, count);
6019
+ else {
6020
+ this.opens.delete(key);
6021
+ for (const id of this.handlesAt(key)) this.handles.delete(id);
6022
+ const kept = this.unlinked.get(key);
6023
+ if (kept) this.letGo(kept, true);
6024
+ for (const [alias, target] of this.aliases) if (target === key) this.aliases.delete(alias);
6025
+ this.onIdle?.(key);
6026
+ }
6027
+ if (!on) return void 0;
6028
+ this.handles.set(++this.lastHandle, key);
6029
+ return this.lastHandle;
6030
+ }
6031
+ holdWrite(path, on) {
5937
6032
  const count = (this.get(path) ?? 0) + (on ? 1 : -1);
5938
6033
  if (count > 0) this.set(path, count);
5939
6034
  else {
@@ -5970,6 +6065,9 @@ var HeldPaths = class extends Map {
5970
6065
  this.opens.delete(path);
5971
6066
  this.opens.set(moved2, (this.opens.get(moved2) ?? 0) + count);
5972
6067
  }
6068
+ for (const [id, key] of this.handles) {
6069
+ if (within4(key, from)) this.handles.set(id, to + key.slice(from.length));
6070
+ }
5973
6071
  }
5974
6072
  isRevoked(path) {
5975
6073
  return this.revoked.has(path);
@@ -6644,9 +6742,9 @@ var MountFs = class {
6644
6742
  this.table.forget(found.mount, normalizePath(path));
6645
6743
  await found.mount.conn.call({ op: "symlink", target, path: found.rel });
6646
6744
  }
6647
- async realpath(path, follow) {
6745
+ async realpath(path, follow2) {
6648
6746
  if (this.at(path) || !this.base.realpath) throw errnoError("ENOSYS", path);
6649
- const real = await this.base.realpath(path, follow);
6747
+ const real = await this.base.realpath(path, follow2);
6650
6748
  if (this.at(real)) throw errnoError("ENOSYS", path);
6651
6749
  return real;
6652
6750
  }
@@ -6833,25 +6931,31 @@ async function ranged(fs, path, req) {
6833
6931
  }
6834
6932
  var UNLINKED_OPS = /* @__PURE__ */ new Set(["read", "pread", "pwrite", "truncate"]);
6835
6933
  var OWN_OPS = /* @__PURE__ */ new Set(["rm", "unlink", "rmdir", "rename"]);
6934
+ async function keptOr(entry, path, handle) {
6935
+ if (handle !== void 0) return entry.keptFor?.(handle)?.hidden ?? path;
6936
+ const kept = entry.unlinked?.(path);
6937
+ return kept && !await entry.fs.exists(path) ? kept.hidden : path;
6938
+ }
6836
6939
  async function dispatchSyncFs(req) {
6837
6940
  const entry = resolveSyncFsToken(req.token);
6838
6941
  if (!entry) return { ok: false, errno: "EACCES", message: "sync-fs: unknown or revoked token" };
6839
6942
  const { fs, cwd } = entry;
6840
6943
  try {
6841
6944
  const path = fs.resolvePath(cwd, req.path);
6945
+ if (req.op === "kept") return json2(entry.keptFor?.(req.handle ?? -1) !== void 0);
6842
6946
  if (req.op === "statfs") return json2(await entry.statfs?.(path) ?? await storageUsage());
6843
6947
  if (req.op === "hold") {
6844
6948
  const [on, open] = [((req.mode ?? 0) & 1) === 1, ((req.mode ?? 0) & 2) === 2];
6845
- const real = on && open ? await fs.realpath?.(path, true).catch(() => path) : void 0;
6846
- entry.hold?.(path, on, open, real);
6949
+ if (on && open) {
6950
+ const real = await fs.realpath?.(path, true).catch(() => path);
6951
+ return json2(entry.hold?.(path, on, open, real) ?? null);
6952
+ }
6953
+ entry.hold?.(path, on, open, void 0, req.handle);
6847
6954
  return done;
6848
6955
  }
6849
6956
  if (entry.revoked?.(path)) return { ok: false, errno: "EIO", message: `${path} was unmounted` };
6850
6957
  if (req.op === "write" && req.mode === 1 || UNLINKED_OPS.has(req.op)) {
6851
- const serve = async () => {
6852
- const kept = entry.unlinked?.(path);
6853
- return run(entry, kept && !await fs.exists(path) ? kept.hidden : path, req);
6854
- };
6958
+ const serve = async () => run(entry, await keptOr(entry, path, req.handle), req);
6855
6959
  return await (entry.gate ? entry.gate(path, serve) : serve());
6856
6960
  }
6857
6961
  if (!entry.own || !OWN_OPS.has(req.op)) return await run(entry, path, req);
@@ -7210,9 +7314,9 @@ var HttpHandles = class {
7210
7314
  return this.close(req.handle);
7211
7315
  }
7212
7316
  traits() {
7213
- const { unavailable, crossOrigin = "any", ...rest } = this.transport.traits;
7317
+ const { unavailable, crossOrigin = "any", ...rest2 } = this.transport.traits;
7214
7318
  if (unavailable) return { ok: false, errno: "ENETUNREACH", message: NO_TRANSPORT };
7215
- return { ok: true, kind: "json", json: { ...rest, crossOrigin } };
7319
+ return { ok: true, kind: "json", json: { ...rest2, crossOrigin } };
7216
7320
  }
7217
7321
  add(abort, body, cancel) {
7218
7322
  const handle = ++this.nextHandle;
@@ -7274,9 +7378,9 @@ var HttpHandles = class {
7274
7378
  async take(entry, max) {
7275
7379
  if (entry.left.length === 0 && !entry.done) {
7276
7380
  try {
7277
- const next = await entry.body.next();
7278
- if (next.done) entry.done = true;
7279
- else entry.left = next.value;
7381
+ const next2 = await entry.body.next();
7382
+ if (next2.done) entry.done = true;
7383
+ else entry.left = next2.value;
7280
7384
  } catch (e) {
7281
7385
  entry.done = true;
7282
7386
  const timedOut = e?.code === "ETIMEDOUT";
@@ -7638,12 +7742,12 @@ var KernelTty = class {
7638
7742
  const chunks = [];
7639
7743
  let n = 0;
7640
7744
  while (this.readable.length > 0 && n < max) {
7641
- const next = this.readable[0];
7642
- if (next === null || next === void 0) break;
7643
- const take = next.subarray(0, max - n);
7745
+ const next2 = this.readable[0];
7746
+ if (next2 === null || next2 === void 0) break;
7747
+ const take = next2.subarray(0, max - n);
7644
7748
  chunks.push(take);
7645
7749
  n += take.length;
7646
- if (take.length < next.length) this.readable[0] = next.subarray(take.length);
7750
+ if (take.length < next2.length) this.readable[0] = next2.subarray(take.length);
7647
7751
  else this.readable.shift();
7648
7752
  if (this.canonical) break;
7649
7753
  }
@@ -7740,11 +7844,11 @@ var PtyPair = class {
7740
7844
  const chunks = [];
7741
7845
  let n = 0;
7742
7846
  while (this.queue.length > 0 && n < max - status) {
7743
- const next = this.queue[0];
7744
- const take = next.subarray(0, max - status - n);
7847
+ const next2 = this.queue[0];
7848
+ const take = next2.subarray(0, max - status - n);
7745
7849
  chunks.push(take);
7746
7850
  n += take.length;
7747
- if (take.length < next.length) this.queue[0] = next.subarray(take.length);
7851
+ if (take.length < next2.length) this.queue[0] = next2.subarray(take.length);
7748
7852
  else this.queue.shift();
7749
7853
  }
7750
7854
  const out = new Uint8Array(status + n);
@@ -8050,7 +8154,7 @@ var Pages = class {
8050
8154
  }
8051
8155
  const last = runs[runs.length - 1];
8052
8156
  if (last) last[1] = Math.max(last[1], Math.min(READAHEAD, this.stored - last[0]));
8053
- return runs.map(([start, n]) => [start, Math.min(n, this.stored - start)]);
8157
+ return runs.map(([start2, n]) => [start2, Math.min(n, this.stored - start2)]);
8054
8158
  }
8055
8159
  partial(at, length2) {
8056
8160
  if (length2 === 0) return [];
@@ -8058,16 +8162,16 @@ var Pages = class {
8058
8162
  const edges = /* @__PURE__ */ new Set([Math.floor(at / PAGE), Math.floor((at + length2 - 1) / PAGE)]);
8059
8163
  const runs = [];
8060
8164
  for (const page of edges) {
8061
- const start = page * PAGE;
8062
- const covered = at <= start && at + length2 >= start + PAGE;
8063
- if (covered || this.pages.has(page) || start >= this.stored) continue;
8064
- runs.push([start, Math.min(PAGE, this.stored - start)]);
8165
+ const start2 = page * PAGE;
8166
+ const covered = at <= start2 && at + length2 >= start2 + PAGE;
8167
+ if (covered || this.pages.has(page) || start2 >= this.stored) continue;
8168
+ runs.push([start2, Math.min(PAGE, this.stored - start2)]);
8065
8169
  }
8066
8170
  return runs;
8067
8171
  }
8068
- fill(start, bytes) {
8172
+ fill(start2, bytes) {
8069
8173
  for (let at = 0; at < bytes.length; at += PAGE) {
8070
- const page = (start + at) / PAGE;
8174
+ const page = (start2 + at) / PAGE;
8071
8175
  if (this.pages.has(page)) continue;
8072
8176
  const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
8073
8177
  const fresh = this.take(part.length);
@@ -8133,14 +8237,14 @@ var Pages = class {
8133
8237
  while (j < sorted.length && sorted[j] === first + (j - i) && sorted[j] * PAGE % RUN !== 0) {
8134
8238
  j++;
8135
8239
  }
8136
- const start = first * PAGE;
8240
+ const start2 = first * PAGE;
8137
8241
  const end = Math.min(this.length, (first + (j - i)) * PAGE);
8138
- const bytes = new Uint8Array(end - start);
8242
+ const bytes = new Uint8Array(end - start2);
8139
8243
  for (let k = i; k < j; k++) {
8140
8244
  const page = this.pages.get(sorted[k]);
8141
- bytes.set(page.subarray(0, Math.min(PAGE, end - (k - i) * PAGE - start)), (k - i) * PAGE);
8245
+ bytes.set(page.subarray(0, Math.min(PAGE, end - (k - i) * PAGE - start2)), (k - i) * PAGE);
8142
8246
  }
8143
- runs.push({ offset: start, bytes });
8247
+ runs.push({ offset: start2, bytes });
8144
8248
  i = j;
8145
8249
  }
8146
8250
  const written2 = Math.max(this.stored, ...runs.map((r) => r.offset + r.bytes.length));
@@ -8207,8 +8311,8 @@ var AsyncRangedFile = class {
8207
8311
  return this.pages.length;
8208
8312
  }
8209
8313
  async fetch(runs) {
8210
- for (const [start, n] of runs) {
8211
- this.pages.fill(start, await this.io.pread(this.path, start, n, this.version));
8314
+ for (const [start2, n] of runs) {
8315
+ this.pages.fill(start2, await this.io.pread(this.path, start2, n, this.version));
8212
8316
  }
8213
8317
  }
8214
8318
  async read(at, length2) {
@@ -8283,9 +8387,9 @@ var VfsNode = class {
8283
8387
  if (dirty && contents2 !== void 0) this.markDirty();
8284
8388
  }
8285
8389
  serial(op) {
8286
- const next = this.queue.then(op);
8287
- this.queue = next.catch(() => void 0);
8288
- return next;
8390
+ const next2 = this.queue.then(op);
8391
+ this.queue = next2.catch(() => void 0);
8392
+ return next2;
8289
8393
  }
8290
8394
  revoke() {
8291
8395
  this.revoked = true;
@@ -8467,10 +8571,10 @@ var VfsNodes = class {
8467
8571
  targetKey(path) {
8468
8572
  return this.real(path, true, () => realTarget(this.fs, path));
8469
8573
  }
8470
- async real(path, follow, walk) {
8574
+ async real(path, follow2, walk) {
8471
8575
  if (this.fs.realpath) {
8472
8576
  try {
8473
- return await this.fs.realpath(path, follow);
8577
+ return await this.fs.realpath(path, follow2);
8474
8578
  } catch (err) {
8475
8579
  if (errCode(err) === "ELOOP") throw err;
8476
8580
  }
@@ -8598,8 +8702,8 @@ var VfsNodes = class {
8598
8702
  }
8599
8703
  }
8600
8704
  };
8601
- async function present(fs, path, follow = false) {
8602
- const look = (follow ? void 0 : fs.lstat) ?? fs.stat;
8705
+ async function present(fs, path, follow2 = false) {
8706
+ const look = (follow2 ? void 0 : fs.lstat) ?? fs.stat;
8603
8707
  try {
8604
8708
  await (look ? look.call(fs, path) : fs.readFileBuffer(path));
8605
8709
  return true;
@@ -8625,10 +8729,10 @@ async function realDir(fs, dir, budget = { hops: 0 }) {
8625
8729
  at = parentOf6(at);
8626
8730
  continue;
8627
8731
  }
8628
- const next = at === "/" ? `/${part}` : `${at}/${part}`;
8629
- const link2 = await linkOf(fs, next);
8732
+ const next2 = at === "/" ? `/${part}` : `${at}/${part}`;
8733
+ const link2 = await linkOf(fs, next2);
8630
8734
  if (link2 === void 0) {
8631
- at = next;
8735
+ at = next2;
8632
8736
  continue;
8633
8737
  }
8634
8738
  if (budget.hops >= MAX_LINKS5) throw fsError("ELOOP", dir);
@@ -8885,6 +8989,7 @@ var WasmProcess = class {
8885
8989
  dying = false;
8886
8990
  umask = DEFAULT_UMASK;
8887
8991
  execChild;
8992
+ killed = false;
8888
8993
  execTermsig;
8889
8994
  stopped = 0;
8890
8995
  stops = 0;
@@ -8922,7 +9027,10 @@ var WasmProcess = class {
8922
9027
  this.wake?.();
8923
9028
  return "forward";
8924
9029
  }
8925
- if (sig === SIG.KILL) return "terminate";
9030
+ if (sig === SIG.KILL) {
9031
+ this.killed = true;
9032
+ return "terminate";
9033
+ }
8926
9034
  if (sig === SIG.CONT) this.cont();
8927
9035
  if (sig === SIG.STOP) return this.stop(sig);
8928
9036
  const bit = sigbit(sig);
@@ -8940,6 +9048,19 @@ var WasmProcess = class {
8940
9048
  blocked.abort();
8941
9049
  return "deliver";
8942
9050
  }
9051
+ async spawn(req) {
9052
+ const { file, argv, env, cwd, stdio, inherit, exec } = req;
9053
+ const spawned = { file, argv, env, cwd, ...exec ? { exec } : {} };
9054
+ const pid = await this.children.spawn(spawned, stdio, inherit);
9055
+ if (!exec) return pid;
9056
+ if (this.killed) void Promise.allSettled([this.options.kill?.(pid, SIG.KILL)]);
9057
+ else this.adoptExec(pid);
9058
+ return pid;
9059
+ }
9060
+ adoptExec(pid) {
9061
+ this.execChild = pid;
9062
+ this.children.watch(pid, (state, sig) => state === "stopped" ? this.stop(sig) : this.cont());
9063
+ }
8943
9064
  onState(listener) {
8944
9065
  this.stateListeners.push(listener);
8945
9066
  }
@@ -9345,12 +9466,8 @@ var WasmProcess = class {
9345
9466
  switch (req.op) {
9346
9467
  case "proc-fork":
9347
9468
  return { ok: true, kind: "json", json: await this.children.fork(req.state) };
9348
- case "proc-spawn": {
9349
- const { file, argv, env, cwd, stdio, inherit, exec } = req;
9350
- const spawned = { file, argv, env, cwd, ...exec ? { exec } : {} };
9351
- const pid = await this.children.spawn(spawned, stdio, inherit);
9352
- return { ok: true, kind: "json", json: pid };
9353
- }
9469
+ case "proc-spawn":
9470
+ return { ok: true, kind: "json", json: await this.spawn(req) };
9354
9471
  case "proc-wait": {
9355
9472
  const signal = req.nohang ? this.interrupt.signal : this.blockingSignal();
9356
9473
  const flags = {
@@ -9364,12 +9481,8 @@ var WasmProcess = class {
9364
9481
  case "proc-identity":
9365
9482
  return { ok: true, kind: "json", json: await this.options.identity?.() ?? null };
9366
9483
  case "proc-exec": {
9367
- this.execChild = req.pid;
9484
+ this.adoptExec(req.pid);
9368
9485
  this.options.jobs?.exec(this.pid, req.pid);
9369
- this.children.watch(
9370
- req.pid,
9371
- (state, sig) => state === "stopped" ? this.stop(sig) : this.cont()
9372
- );
9373
9486
  await this.fds.closeAll();
9374
9487
  try {
9375
9488
  const waited = await this.children.wait(req.pid, false);
@@ -9623,7 +9736,8 @@ function holdingToken(opts, holds) {
9623
9736
  fs: opts.fs,
9624
9737
  cwd: opts.cwd,
9625
9738
  ...opts.statfs ? { statfs: opts.statfs } : {},
9626
- hold: (path, on, open, real) => holds.hold(path, on, open, real),
9739
+ hold: (path, on, open, real, handle) => holds.hold(path, on, open, real, handle),
9740
+ keptFor: (handle) => holds.keptFor(handle),
9627
9741
  unlinked: (path) => holds.keptAt(path),
9628
9742
  gate: (path, op) => opts.unlinkGate && holds.holding(path) ? opts.unlinkGate.run(holds.keyOf(path), op) : op(),
9629
9743
  own: (path, op) => holds.own(path, op),
@@ -10880,9 +10994,9 @@ function tmpfs() {
10880
10994
  let node = root;
10881
10995
  for (const name2 of parts(path)) {
10882
10996
  if (node.kind !== "directory") throw fsError2("ENOTDIR", path);
10883
- const next = node.children.get(name2);
10884
- if (!next) throw fsError2("ENOENT", path);
10885
- node = next;
10997
+ const next2 = node.children.get(name2);
10998
+ if (!next2) throw fsError2("ENOENT", path);
10999
+ node = next2;
10886
11000
  }
10887
11001
  return node;
10888
11002
  }
@@ -11446,8 +11560,8 @@ var Launcher = class {
11446
11560
  const words = shebang(head) ?? (binfmt ? [binfmt] : void 0);
11447
11561
  if (!words) return void 0;
11448
11562
  if (baseName(words[0]) === "env") words.shift();
11449
- const [interp, ...rest] = words;
11450
- const arg = rest.join(" ");
11563
+ const [interp, ...rest2] = words;
11564
+ const arg = rest2.join(" ");
11451
11565
  const passed = [...arg ? [arg] : [], command ? script : file, ...argv.slice(1)];
11452
11566
  const found = interp ? await this.resolve(interp, interp, cwd) : void 0;
11453
11567
  if (!found) {
@@ -11528,7 +11642,8 @@ var Launcher = class {
11528
11642
  }
11529
11643
  async launch(planned, req) {
11530
11644
  const { target, args } = planned;
11531
- const seen = { ...req.env, HOSTNAME: this.hostname, cwd: req.cwd };
11645
+ const cwd = await physicalPathAsync(this.base, req.cwd);
11646
+ const seen = { ...req.env, HOSTNAME: this.hostname, cwd };
11532
11647
  const env = { ...expandDefaults(target.env, seen), ...req.env };
11533
11648
  for (const key of target.unset ?? []) delete env[key];
11534
11649
  env.HOSTNAME = this.hostname;
@@ -11539,7 +11654,7 @@ var Launcher = class {
11539
11654
  await req.fds.closeAll();
11540
11655
  throw err;
11541
11656
  }
11542
- return this.start({ ...req, program, argv0: target.argv0, args, env });
11657
+ return this.start({ ...req, cwd, program, argv0: target.argv0, args, env });
11543
11658
  }
11544
11659
  groupSession(pgid) {
11545
11660
  return this.jobs.sessionOf(pgid);
@@ -11946,7 +12061,8 @@ var Launcher = class {
11946
12061
  async starting(argv, dir) {
11947
12062
  this.catalog = void 0;
11948
12063
  this.binfmts = void 0;
11949
- const cwd = this.fs.resolvePath("/", dir ?? "/");
12064
+ const start2 = dir ?? "/";
12065
+ const cwd = await physicalPathAsync(this.base, start2.startsWith("/") ? start2 : `/${start2}`);
11950
12066
  const [file = ""] = argv;
11951
12067
  await this.base.mkdir(cwd, { recursive: true });
11952
12068
  return { cwd, file, planned: await this.plan(file, argv, cwd) };