@ai-ecoverse/slicc-kernel 1.64.2 → 1.65.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
@@ -2848,18 +2848,99 @@ function serveClient(port, host) {
2848
2848
  return { detach, closed: ended.promise };
2849
2849
  }
2850
2850
 
2851
- // src/fs/meta.ts
2851
+ // src/fs/path-map.ts
2852
2852
  function parentOf2(path) {
2853
+ if (path === "/" || path === "") return void 0;
2854
+ const at = path.lastIndexOf("/");
2855
+ return at <= 0 ? "/" : path.slice(0, at);
2856
+ }
2857
+ var PathMap = class {
2858
+ values = /* @__PURE__ */ new Map();
2859
+ children = /* @__PURE__ */ new Map();
2860
+ get size() {
2861
+ return this.values.size;
2862
+ }
2863
+ has(path) {
2864
+ return this.values.has(path);
2865
+ }
2866
+ get(path) {
2867
+ return this.values.get(path);
2868
+ }
2869
+ keys() {
2870
+ return this.values.keys();
2871
+ }
2872
+ set(path, value) {
2873
+ if (!this.values.has(path)) this.link(path);
2874
+ this.values.set(path, value);
2875
+ return this;
2876
+ }
2877
+ delete(path) {
2878
+ if (!this.values.delete(path)) return false;
2879
+ this.prune(path);
2880
+ return true;
2881
+ }
2882
+ subtree(root) {
2883
+ const out = [];
2884
+ const stack = [root];
2885
+ for (let path = stack.pop(); path !== void 0; path = stack.pop()) {
2886
+ if (this.values.has(path)) out.push(path);
2887
+ const below = this.children.get(path);
2888
+ if (below) for (const child of below) stack.push(child);
2889
+ }
2890
+ return out;
2891
+ }
2892
+ entries() {
2893
+ return this.values.entries();
2894
+ }
2895
+ deleteTree(root) {
2896
+ const stack = [root];
2897
+ for (let path = stack.pop(); path !== void 0; path = stack.pop()) {
2898
+ this.values.delete(path);
2899
+ const below = this.children.get(path);
2900
+ if (below) for (const child of below) stack.push(child);
2901
+ this.children.delete(path);
2902
+ }
2903
+ this.prune(root);
2904
+ }
2905
+ clear() {
2906
+ this.values.clear();
2907
+ this.children.clear();
2908
+ }
2909
+ link(path) {
2910
+ for (let child = path, parent = parentOf2(path); parent !== void 0; ) {
2911
+ const set2 = this.children.get(parent);
2912
+ if (set2?.has(child)) return;
2913
+ if (set2) set2.add(child);
2914
+ else this.children.set(parent, /* @__PURE__ */ new Set([child]));
2915
+ child = parent;
2916
+ parent = parentOf2(parent);
2917
+ }
2918
+ }
2919
+ prune(path) {
2920
+ for (let child = path, parent = parentOf2(path); parent !== void 0; ) {
2921
+ if (this.values.has(child) || this.children.has(child)) return;
2922
+ const set2 = this.children.get(parent);
2923
+ set2?.delete(child);
2924
+ if (set2 && set2.size > 0) return;
2925
+ this.children.delete(parent);
2926
+ child = parent;
2927
+ parent = parentOf2(parent);
2928
+ }
2929
+ }
2930
+ };
2931
+
2932
+ // src/fs/meta.ts
2933
+ function parentOf3(path) {
2853
2934
  return path.slice(0, path.lastIndexOf("/")) || "/";
2854
2935
  }
2855
2936
  function moved(entry, path) {
2856
- return entry.link === void 0 ? { ...entry, path } : { ...entry, path, dir: parentOf2(path) };
2937
+ return entry.link === void 0 ? { ...entry, path } : { ...entry, path, dir: parentOf3(path) };
2857
2938
  }
2858
2939
  function under(path, root) {
2859
2940
  return path === root || path.startsWith(`${root}/`);
2860
2941
  }
2861
2942
  var MemoryMeta = class {
2862
- entries = /* @__PURE__ */ new Map();
2943
+ entries = new PathMap();
2863
2944
  async get(paths) {
2864
2945
  return paths.map((path) => this.entries.get(path));
2865
2946
  }
@@ -2872,7 +2953,7 @@ var MemoryMeta = class {
2872
2953
  }
2873
2954
  async move(from, to, root) {
2874
2955
  await this.remove([to]);
2875
- const carried = [...this.entries.values()].filter((entry) => under(entry.path, from));
2956
+ const carried = this.entries.subtree(from).map((path) => this.entries.get(path));
2876
2957
  for (const entry of carried) this.entries.delete(entry.path);
2877
2958
  for (const entry of carried) {
2878
2959
  const path = to + entry.path.slice(from.length);
@@ -2881,18 +2962,16 @@ var MemoryMeta = class {
2881
2962
  this.entries.set(to, moved(root(carried.find((entry) => entry.path === from)), to));
2882
2963
  }
2883
2964
  async remove(paths) {
2884
- for (const key of [...this.entries.keys()]) {
2885
- if (paths.some((path) => under(key, path))) this.entries.delete(key);
2886
- }
2965
+ for (const path of paths) this.entries.deleteTree(path);
2887
2966
  }
2888
2967
  async links(dir) {
2889
- return [...this.entries.values()].filter((entry) => entry.dir === dir);
2968
+ return [...this.entries.entries()].map(([, e]) => e).filter((entry) => entry.dir === dir);
2890
2969
  }
2891
2970
  async under(path) {
2892
- return [...this.entries.values()].filter((entry) => under(entry.path, path));
2971
+ return [...this.entries.entries()].map(([, e]) => e).filter((entry) => under(entry.path, path));
2893
2972
  }
2894
2973
  async all() {
2895
- return [...this.entries.values()];
2974
+ return [...this.entries.entries()].map(([, e]) => e);
2896
2975
  }
2897
2976
  };
2898
2977
 
@@ -2947,7 +3026,7 @@ function missing(err) {
2947
3026
  function split(path) {
2948
3027
  return path.split("/").filter(Boolean);
2949
3028
  }
2950
- function parentOf3(path) {
3029
+ function parentOf4(path) {
2951
3030
  return path.slice(0, path.lastIndexOf("/")) || "/";
2952
3031
  }
2953
3032
  function baseOf(path) {
@@ -3016,7 +3095,7 @@ function isHandle(child) {
3016
3095
  var OpfsFs = class {
3017
3096
  root;
3018
3097
  meta;
3019
- dirs = /* @__PURE__ */ new Map();
3098
+ dirs = new PathMap();
3020
3099
  checked = /* @__PURE__ */ new WeakMap();
3021
3100
  channel;
3022
3101
  ranged;
@@ -3065,7 +3144,7 @@ var OpfsFs = class {
3065
3144
  async ownerAt(path, entry) {
3066
3145
  const own = ownOf(entry);
3067
3146
  if (own) return own;
3068
- return path === "/" ? { ...ROOT_OWNER } : this.handedTo(parentOf3(path));
3147
+ return path === "/" ? { ...ROOT_OWNER } : this.handedTo(parentOf4(path));
3069
3148
  }
3070
3149
  async owned(stat, path, entry) {
3071
3150
  return this.owners ? { ...stat, ...await this.ownerAt(path, entry) } : stat;
@@ -3095,7 +3174,7 @@ var OpfsFs = class {
3095
3174
  async chown(path, change) {
3096
3175
  if (!this.owners) return;
3097
3176
  if (change.kind === "new") {
3098
- const at = resolvePath((await this.locate(parentOf3(path), true)).path, baseOf(path));
3177
+ const at = resolvePath((await this.locate(parentOf4(path), true)).path, baseOf(path));
3099
3178
  const { uid = 0, gid = 0, mode } = change;
3100
3179
  return this.stampNew(at, { uid, gid, ...mode !== void 0 ? { mode } : {} }, false);
3101
3180
  }
@@ -3159,7 +3238,7 @@ var OpfsFs = class {
3159
3238
  return true;
3160
3239
  }
3161
3240
  forget(path) {
3162
- for (const key of [...this.dirs.keys()]) if (within(key, path)) this.dirs.delete(key);
3241
+ this.dirs.deleteTree(path);
3163
3242
  }
3164
3243
  async inDir(path, op) {
3165
3244
  const dir = await this.dir(path);
@@ -3206,7 +3285,7 @@ var OpfsFs = class {
3206
3285
  async linkAlong(path) {
3207
3286
  for (const entry of await this.meta.get(prefixes(path))) {
3208
3287
  if (entry?.link === void 0) continue;
3209
- if (!await this.real(entry.path) && await this.real(parentOf3(entry.path))) return entry;
3288
+ if (!await this.real(entry.path) && await this.real(parentOf4(entry.path))) return entry;
3210
3289
  }
3211
3290
  return void 0;
3212
3291
  }
@@ -3221,7 +3300,7 @@ var OpfsFs = class {
3221
3300
  if (!link2) throw err;
3222
3301
  if (link2.path === current && !follow2) return { path: current, link: link2 };
3223
3302
  const target = `${link2.link}${current.slice(link2.path.length)}`;
3224
- current = target.split("/").includes("..") ? await this.physical(parentOf3(link2.path), target, hops + 1) : resolvePath(parentOf3(link2.path), target);
3303
+ current = target.split("/").includes("..") ? await this.physical(parentOf4(link2.path), target, hops + 1) : resolvePath(parentOf4(link2.path), target);
3225
3304
  }
3226
3305
  }
3227
3306
  throw fsError("ELOOP", path);
@@ -3411,8 +3490,8 @@ var OpfsFs = class {
3411
3490
  if (!missing(err)) throw err;
3412
3491
  }
3413
3492
  const [entry] = await this.meta.get([path]);
3414
- const wanted = entry?.link === void 0 ? path : resolvePath(parentOf3(path), entry.link);
3415
- return [resolvePath((await this.locate(parentOf3(wanted), true)).path, baseOf(wanted)), false];
3493
+ const wanted = entry?.link === void 0 ? path : resolvePath(parentOf4(path), entry.link);
3494
+ return [resolvePath((await this.locate(parentOf4(wanted), true)).path, baseOf(wanted)), false];
3416
3495
  }
3417
3496
  async fileFor(path) {
3418
3497
  const [target, existed] = await this.writeTarget(path);
@@ -3422,7 +3501,7 @@ var OpfsFs = class {
3422
3501
  dir,
3423
3502
  (parent) => parent.getFileHandle(name2, { create: true })
3424
3503
  );
3425
- if (!existed) this.touch(parentOf3(target));
3504
+ if (!existed) this.touch(parentOf4(target));
3426
3505
  return [handle, existed, target];
3427
3506
  } catch (err) {
3428
3507
  const code = err?.name === "TypeMismatchError" ? "EISDIR" : null;
@@ -3492,9 +3571,9 @@ var OpfsFs = class {
3492
3571
  gid: owner.gid,
3493
3572
  ...mode ? { mode, parents: mode } : {}
3494
3573
  };
3495
- await this.mkdir(parentOf3(path), { recursive: true, ...above ? { owner: above } : {} });
3574
+ await this.mkdir(parentOf4(path), { recursive: true, ...above ? { owner: above } : {} });
3496
3575
  }
3497
- const parent = await this.locate(parentOf3(path), true);
3576
+ const parent = await this.locate(parentOf4(path), true);
3498
3577
  if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
3499
3578
  await parent.handle.getDirectoryHandle(baseOf(path), { create: true });
3500
3579
  const made2 = `${parent.path === "/" ? "" : parent.path}/${baseOf(path)}`;
@@ -3523,7 +3602,7 @@ var OpfsFs = class {
3523
3602
  this.changed(found.path);
3524
3603
  }
3525
3604
  await this.meta.remove([found.path]);
3526
- this.touch(parentOf3(found.path));
3605
+ this.touch(parentOf4(found.path));
3527
3606
  }
3528
3607
  links() {
3529
3608
  return false;
@@ -3535,7 +3614,7 @@ var OpfsFs = class {
3535
3614
  if (from === to) return;
3536
3615
  const source = await this.locate(from, false);
3537
3616
  this.forget(source.path);
3538
- const parent = await this.locate(parentOf3(to), true);
3617
+ const parent = await this.locate(parentOf4(to), true);
3539
3618
  const destination = resolvePath(parent.path, baseOf(to));
3540
3619
  const sourceStat = await this.describe(source);
3541
3620
  if (sourceStat.isDirectory && within(destination, source.path)) throw fsError("EINVAL", to);
@@ -3563,7 +3642,7 @@ var OpfsFs = class {
3563
3642
  if (source.link) {
3564
3643
  if (target?.handle) await this.rm(destination);
3565
3644
  await this.meta.move(source.path, destination, carry);
3566
- this.touch(parentOf3(source.path), parent.path);
3645
+ this.touch(parentOf4(source.path), parent.path);
3567
3646
  return;
3568
3647
  }
3569
3648
  const dir = parent.handle;
@@ -3585,7 +3664,7 @@ var OpfsFs = class {
3585
3664
  }
3586
3665
  if (copied) await this.restamp(destination, timed2);
3587
3666
  this.changed(source.path, destination);
3588
- this.touch(parentOf3(source.path), parent.path);
3667
+ this.touch(parentOf4(source.path), parent.path);
3589
3668
  }
3590
3669
  async modified(path) {
3591
3670
  const handle = await this.handle(path);
@@ -3631,7 +3710,7 @@ var OpfsFs = class {
3631
3710
  for await (const child of handle.values()) await this.copy(child, target, child.name);
3632
3711
  }
3633
3712
  async symlink(target, path, owner) {
3634
- const parent = await this.locate(parentOf3(path), true);
3713
+ const parent = await this.locate(parentOf4(path), true);
3635
3714
  if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
3636
3715
  const at = resolvePath(parent.path, baseOf(path));
3637
3716
  if (await this.locate(at, false).then(
@@ -3707,7 +3786,7 @@ var OpfsFs = class {
3707
3786
  let removed = 0;
3708
3787
  for (const entry of await this.meta.all()) {
3709
3788
  const real = await this.real(entry.path);
3710
- const stale = entry.link === void 0 ? !real : real || !await this.real(parentOf3(entry.path));
3789
+ const stale = entry.link === void 0 ? !real : real || !await this.real(parentOf4(entry.path));
3711
3790
  if (!stale) continue;
3712
3791
  await this.meta.update(entry.path, () => void 0);
3713
3792
  removed++;
@@ -3778,7 +3857,7 @@ function webLocks() {
3778
3857
  return globalThis.navigator?.locks;
3779
3858
  }
3780
3859
  var within2 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
3781
- var parentOf4 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
3860
+ var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
3782
3861
  var lockName = (instance) => `slicc-unlinked:${instance}`;
3783
3862
  var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
3784
3863
  var PathGate = class {
@@ -3911,7 +3990,7 @@ var UnlinkedKeeper = class {
3911
3990
  await this.serial(async () => {
3912
3991
  await Promise.allSettled([this.fs.rename(file.hidden, path)]);
3913
3992
  this.unlinkedName(file.hidden);
3914
- await this.tidy(parentOf4(file.hidden));
3993
+ await this.tidy(parentOf5(file.hidden));
3915
3994
  });
3916
3995
  }
3917
3996
  done4();
@@ -3952,7 +4031,7 @@ var UnlinkedKeeper = class {
3952
4031
  }
3953
4032
  await this.fs.rm(hidden).catch(() => void 0);
3954
4033
  this.unlinkedName(hidden);
3955
- await this.tidy(parentOf4(hidden));
4034
+ await this.tidy(parentOf5(hidden));
3956
4035
  }
3957
4036
  async linkedName(hidden) {
3958
4037
  if (!mayLink(this.fs, hidden)) return;
@@ -5148,10 +5227,10 @@ async function physicalPathAsync(fs, path) {
5148
5227
  // src/fs/watch.ts
5149
5228
  var WATCH_BATCH_LIMIT = 1e3;
5150
5229
  var under2 = (path, dir) => dir === "/" || path.startsWith(`${dir}/`);
5151
- var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
5230
+ var parentOf6 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
5152
5231
  function touches(watcher, changed) {
5153
5232
  return watcher.paths.some(
5154
- (watched) => changed === watched || under2(watched, changed) || (watcher.recursive ? under2(changed, watched) : parentOf5(changed) === watched)
5233
+ (watched) => changed === watched || under2(watched, changed) || (watcher.recursive ? under2(changed, watched) : parentOf6(changed) === watched)
5155
5234
  );
5156
5235
  }
5157
5236
  var FsWatchers = class {
@@ -5264,6 +5343,23 @@ var CONTINUED_STATUS = 65535;
5264
5343
  function waitStatus(code, termsig) {
5265
5344
  return termsig ? termsig & 127 : (code & 255) << 8;
5266
5345
  }
5346
+ function capturing(chunks2, max = Number.POSITIVE_INFINITY, overflow) {
5347
+ let size = 0;
5348
+ let over = false;
5349
+ return (bytes) => {
5350
+ if (over) return;
5351
+ const room = max - size;
5352
+ if (bytes.length > room) {
5353
+ over = true;
5354
+ if (room > 0) chunks2.push(bytes.slice(0, room));
5355
+ size = max;
5356
+ overflow?.();
5357
+ return;
5358
+ }
5359
+ chunks2.push(bytes);
5360
+ size += bytes.length;
5361
+ };
5362
+ }
5267
5363
  function concat2(chunks2) {
5268
5364
  const out = new Uint8Array(chunks2.reduce((n, c) => n + c.length, 0));
5269
5365
  let offset = 0;
@@ -5297,15 +5393,15 @@ var ChildTable = class {
5297
5393
  const fds = new FdTable();
5298
5394
  const captured = /* @__PURE__ */ new Map();
5299
5395
  try {
5300
- for (const [n, slot] of stdio.entries()) fds.installAt(n, this.openSlot(slot, n, captured));
5301
- for (const slot of inherit2) {
5302
- if (slot.fd <= 2) continue;
5303
- if ("device" in slot) {
5304
- fds.installAt(slot.fd, deviceFile(slot.device, slot.access));
5396
+ for (const [n, slot2] of stdio.entries()) fds.installAt(n, this.openSlot(slot2, n, captured));
5397
+ for (const slot2 of inherit2) {
5398
+ if (slot2.fd <= 2) continue;
5399
+ if ("device" in slot2) {
5400
+ fds.installAt(slot2.fd, deviceFile(slot2.device, slot2.access));
5305
5401
  continue;
5306
5402
  }
5307
- fds.installAt(slot.fd, this.parentFds.get(slot.kernel).retain());
5308
- if (slot.flags !== void 0) fds.setStatusFlags(slot.fd, slot.flags);
5403
+ fds.installAt(slot2.fd, this.parentFds.get(slot2.kernel).retain());
5404
+ if (slot2.flags !== void 0) fds.setStatusFlags(slot2.fd, slot2.flags);
5309
5405
  }
5310
5406
  } catch (e) {
5311
5407
  await fds.closeAll();
@@ -5346,15 +5442,15 @@ var ChildTable = class {
5346
5442
  this.watchers.set(pid, listener);
5347
5443
  if (child.stoppedBy !== void 0) listener("stopped", child.stoppedBy);
5348
5444
  }
5349
- openSlot(slot, n, captured) {
5350
- if ("fd" in slot) return this.parentFds.get(slot.fd).retain();
5351
- if ("input" in slot) return bytesSource(slot.input);
5352
- if ("device" in slot) return deviceFile(slot.device, slot.access);
5353
- if ("dir" in slot) return heldFile({ dir: slot.dir });
5354
- if ("capture" in slot) {
5445
+ openSlot(slot2, n, captured) {
5446
+ if ("fd" in slot2) return this.parentFds.get(slot2.fd).retain();
5447
+ if ("input" in slot2) return bytesSource(slot2.input);
5448
+ if ("device" in slot2) return deviceFile(slot2.device, slot2.access);
5449
+ if ("dir" in slot2) return heldFile({ dir: slot2.dir });
5450
+ if ("capture" in slot2) {
5355
5451
  const chunks2 = [];
5356
5452
  captured.set(n, chunks2);
5357
- return sinkFile((bytes) => chunks2.push(bytes));
5453
+ return sinkFile(capturing(chunks2, slot2.max, slot2.overflow));
5358
5454
  }
5359
5455
  return deviceFile("null");
5360
5456
  }
@@ -5421,11 +5517,11 @@ var ChildTable = class {
5421
5517
  pids() {
5422
5518
  return [...this.children.keys()];
5423
5519
  }
5424
- captured(pid, slot) {
5520
+ captured(pid, slot2) {
5425
5521
  const slots = this.leftovers.get(pid);
5426
- const chunks2 = slots?.get(slot);
5522
+ const chunks2 = slots?.get(slot2);
5427
5523
  if (!slots || !chunks2) return new Uint8Array(0);
5428
- slots.delete(slot);
5524
+ slots.delete(slot2);
5429
5525
  if (slots.size === 0) this.leftovers.delete(pid);
5430
5526
  return concat2(chunks2);
5431
5527
  }
@@ -5711,7 +5807,7 @@ var HeldPaths = class extends Map {
5711
5807
  return this.revoked.has(path);
5712
5808
  }
5713
5809
  };
5714
- var parentOf6 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
5810
+ var parentOf7 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
5715
5811
  var readonly = (mount) => mount.caps.readonly === true || mount.options.ro !== void 0;
5716
5812
  function inodeless(mount, attr2) {
5717
5813
  if (attr2.ino !== void 0) return false;
@@ -5991,7 +6087,7 @@ var MountTable = class {
5991
6087
  mount.epoch++;
5992
6088
  for (const key of [...mount.attrs.keys()]) if (within3(key, path)) mount.attrs.delete(key);
5993
6089
  mount.lists.delete(path);
5994
- mount.lists.delete(parentOf6(path));
6090
+ mount.lists.delete(parentOf7(path));
5995
6091
  for (const key of [...mount.lists.keys()]) if (within3(key, path)) mount.lists.delete(key);
5996
6092
  }
5997
6093
  async getattr(mount, path, rel2, settled = false) {
@@ -6067,7 +6163,7 @@ var MountFs = class {
6067
6163
  if (attr2.kind !== "symlink") return real;
6068
6164
  if (hops >= MAX_LINKS6) throw errnoError("ELOOP", path);
6069
6165
  const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
6070
- return this.follow(this.base.resolvePath(parentOf6(real), target), hops + 1, create);
6166
+ return this.follow(this.base.resolvePath(parentOf7(real), target), hops + 1, create);
6071
6167
  }
6072
6168
  async open(mount, rel2, write, owner) {
6073
6169
  return await mount.conn.call({
@@ -6083,8 +6179,8 @@ var MountFs = class {
6083
6179
  async driverOwner(found, real, owner, directory) {
6084
6180
  const { mount, rel: rel2 } = found;
6085
6181
  if (!owner || !mount.owners || !mount.caps.owners) return void 0;
6086
- const dir = parentOf6(real);
6087
- const above = statOf(await this.table.getattr(mount, dir, parentOf6(rel2)), dir, mount);
6182
+ const dir = parentOf7(real);
6183
+ const above = statOf(await this.table.getattr(mount, dir, parentOf7(rel2)), dir, mount);
6088
6184
  const setgid = (above.mode & S_ISGID4) !== 0;
6089
6185
  let mode = owner.mode;
6090
6186
  if (directory && setgid) mode = (mode ?? 493) | S_ISGID4;
@@ -6423,7 +6519,7 @@ var MountFs = class {
6423
6519
  }
6424
6520
  async named(path) {
6425
6521
  const at = normalizePath(path);
6426
- return joinName(await realDir(this, parentOf6(at)), at.slice(at.lastIndexOf("/") + 1));
6522
+ return joinName(await realDir(this, parentOf7(at)), at.slice(at.lastIndexOf("/") + 1));
6427
6523
  }
6428
6524
  async link(source, target, follow2 = false) {
6429
6525
  const from = follow2 ? await realTarget(this, normalizePath(source)) : await this.named(source);
@@ -7686,6 +7782,7 @@ var HOST_OPS = [
7686
7782
  "async-resolve",
7687
7783
  "async-hold",
7688
7784
  "async-cancel",
7785
+ "async-settle",
7689
7786
  "async-close"
7690
7787
  ];
7691
7788
  var json3 = (value) => ({ ok: true, kind: "json", json: value });
@@ -7717,12 +7814,16 @@ var LockTable = class {
7717
7814
  for (const path of [...this.held.keys()]) this.unlock(pid, path);
7718
7815
  }
7719
7816
  };
7817
+ var KEPT_OPS = /* @__PURE__ */ new Set(["fd-write"]);
7818
+ var unknownSettled = false;
7720
7819
  var AsyncOps = class {
7721
7820
  next = 1;
7722
7821
  results = /* @__PURE__ */ new Map();
7723
7822
  ready = [];
7724
7823
  running = /* @__PURE__ */ new Map();
7725
7824
  waiters = /* @__PURE__ */ new Set();
7825
+ taken = /* @__PURE__ */ new Map();
7826
+ kept = /* @__PURE__ */ new Set();
7726
7827
  closed = false;
7727
7828
  run;
7728
7829
  onReady;
@@ -7732,6 +7833,7 @@ var AsyncOps = class {
7732
7833
  }
7733
7834
  submit(req) {
7734
7835
  const id2 = this.hold();
7836
+ if (KEPT_OPS.has(req.op)) this.kept.add(id2);
7735
7837
  const running = new AbortController();
7736
7838
  this.running.set(id2, running);
7737
7839
  this.run(req, running.signal).then(
@@ -7755,9 +7857,26 @@ var AsyncOps = class {
7755
7857
  const result = this.results.get(id2);
7756
7858
  if (!result) throw new KernelError("EAGAIN");
7757
7859
  this.forget(id2);
7860
+ if (this.kept.delete(id2)) this.taken.set(id2, result);
7758
7861
  return result;
7759
7862
  }
7863
+ ack(ids) {
7864
+ for (const id2 of ids) this.taken.delete(id2);
7865
+ }
7866
+ async settle(ids) {
7867
+ const never = new AbortController().signal;
7868
+ while (ids.some((id2) => this.running.has(id2))) await this.sleep(void 0, never);
7869
+ return ids.map((id2) => {
7870
+ const result = this.results.get(id2) ?? this.taken.get(id2) ?? null;
7871
+ if (!result && !unknownSettled) {
7872
+ unknownSettled = true;
7873
+ console.warn(`async-settle: no result for op ${id2}; its write counts as made`);
7874
+ }
7875
+ return result;
7876
+ });
7877
+ }
7760
7878
  cancel(id2) {
7879
+ this.kept.delete(id2);
7761
7880
  this.running.get(id2)?.abort();
7762
7881
  this.forget(id2);
7763
7882
  }
@@ -7833,6 +7952,7 @@ async function hostSyscall(req, ctx) {
7833
7952
  return done2;
7834
7953
  case "async-submit":
7835
7954
  if (!ctx.valid(req.req)) throw new KernelError("EINVAL");
7955
+ if (req.ack) ctx.ops.ack(req.ack);
7836
7956
  return json3(ctx.ops.submit(req.req));
7837
7957
  case "async-wait":
7838
7958
  return json3(await ctx.ops.wait(req.timeoutMs, ctx.blocking));
@@ -7845,6 +7965,8 @@ async function hostSyscall(req, ctx) {
7845
7965
  case "async-cancel":
7846
7966
  ctx.ops.cancel(req.id);
7847
7967
  return done2;
7968
+ case "async-settle":
7969
+ return json3(await ctx.ops.settle(req.ids));
7848
7970
  case "async-close":
7849
7971
  ctx.ops.close();
7850
7972
  return done2;
@@ -9673,8 +9795,8 @@ var KernelTty = class {
9673
9795
  get canonical() {
9674
9796
  return (this.termios.c_lflag & ICANON) !== 0;
9675
9797
  }
9676
- cc(slot) {
9677
- return this.termios.c_cc[slot] ?? 0;
9798
+ cc(slot2) {
9799
+ return this.termios.c_cc[slot2] ?? 0;
9678
9800
  }
9679
9801
  key(input) {
9680
9802
  const { c_iflag, c_lflag } = this.termios;
@@ -10029,6 +10151,84 @@ function ptySyscall(req, ctx) {
10029
10151
  }
10030
10152
  }
10031
10153
 
10154
+ // src/kernel/run-child.ts
10155
+ var RUN_CAPTURE_CAP = 64 * 1024 * 1024;
10156
+ var RUN_MAX_BUFFER = 1024 * 1024;
10157
+ function capLimit(maxBuffer) {
10158
+ const asked = maxBuffer === void 0 ? RUN_MAX_BUFFER : maxBuffer;
10159
+ return Math.max(0, Math.min(asked, RUN_CAPTURE_CAP));
10160
+ }
10161
+ function slot(how, n, extra) {
10162
+ if (how === "inherit") return { fd: n };
10163
+ if (how === "ignore") return { none: true };
10164
+ return extra;
10165
+ }
10166
+ function frame2(outcome, out, err) {
10167
+ const head = new TextEncoder().encode(JSON.stringify(outcome));
10168
+ const bytes = new Uint8Array(4 + head.length + out.length + err.length);
10169
+ new DataView(bytes.buffer).setUint32(0, head.length, true);
10170
+ bytes.set(head, 4);
10171
+ bytes.set(out, 4 + head.length);
10172
+ bytes.set(err, 4 + head.length + out.length);
10173
+ return bytes;
10174
+ }
10175
+ async function reaped(deps, pid) {
10176
+ for (; ; ) {
10177
+ try {
10178
+ return (await deps.children.wait(pid, false, deps.interrupt()))[1];
10179
+ } catch (e) {
10180
+ if (!(e instanceof KernelError && e.code === "EINTR") || deps.dying()) throw e;
10181
+ }
10182
+ }
10183
+ }
10184
+ async function runChild(req, deps) {
10185
+ const max = capLimit(req.maxBuffer);
10186
+ let failure;
10187
+ let pid = 0;
10188
+ const kill = () => void Promise.allSettled([deps.kill(pid, req.killSignal)]);
10189
+ const stop = (code) => {
10190
+ if (failure !== void 0) return;
10191
+ failure = code;
10192
+ if (pid !== 0) kill();
10193
+ };
10194
+ const capture = () => ({ capture: true, max, overflow: () => stop("ENOBUFS") });
10195
+ const stdio = [
10196
+ slot(req.stdin, 0, { input: req.input ?? new Uint8Array(0) }),
10197
+ slot(req.stdout, 1, capture()),
10198
+ slot(req.stderr, 2, capture())
10199
+ ];
10200
+ try {
10201
+ pid = await deps.children.spawn(
10202
+ { file: req.file, argv: req.argv, env: req.env, cwd: req.cwd },
10203
+ stdio
10204
+ );
10205
+ } catch (e) {
10206
+ if (!(e instanceof SpawnError)) throw e;
10207
+ const outcome2 = { pid: 0, status: null, error: { code: e.code }, out: 0, err: 0 };
10208
+ return frame2(outcome2, new Uint8Array(0), new Uint8Array(0));
10209
+ }
10210
+ if (failure !== void 0) kill();
10211
+ const timer = req.timeoutMs !== void 0 && req.timeoutMs > 0 ? setTimeout(() => stop("ETIMEDOUT"), req.timeoutMs) : void 0;
10212
+ let status;
10213
+ try {
10214
+ status = await reaped(deps, pid);
10215
+ } finally {
10216
+ clearTimeout(timer);
10217
+ }
10218
+ const out = deps.children.captured(pid, 1);
10219
+ const err = deps.children.captured(pid, 2);
10220
+ const termsig = status & 127;
10221
+ const outcome = {
10222
+ pid,
10223
+ status: termsig ? null : status >> 8 & 255,
10224
+ ...termsig ? { signal: termsig } : {},
10225
+ ...failure !== void 0 ? { error: { code: failure } } : {},
10226
+ out: out.length,
10227
+ err: err.length
10228
+ };
10229
+ return frame2(outcome, out, err);
10230
+ }
10231
+
10032
10232
  // src/kernel/select.ts
10033
10233
  function ready(fds, read, write) {
10034
10234
  const hangup = /* @__PURE__ */ new Set();
@@ -10825,6 +11025,8 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
10825
11025
  "proc-spawn",
10826
11026
  "proc-wait",
10827
11027
  "proc-captured",
11028
+ "proc-run",
11029
+ "proc-exit",
10828
11030
  "proc-fork",
10829
11031
  "proc-kill",
10830
11032
  "thread-kill",
@@ -10945,6 +11147,14 @@ var WasmProcess = class {
10945
11147
  else this.adoptExec(pid);
10946
11148
  return pid;
10947
11149
  }
11150
+ run(req) {
11151
+ return runChild(req, {
11152
+ children: this.children,
11153
+ kill: (pid, sig) => this.options.kill?.(pid, sig),
11154
+ interrupt: () => this.interrupt.signal,
11155
+ dying: () => this.dying
11156
+ });
11157
+ }
10948
11158
  adoptExec(pid) {
10949
11159
  this.execChild = pid;
10950
11160
  this.children.watch(pid, (state, sig) => state === "stopped" ? this.stop(sig) : this.cont());
@@ -11493,6 +11703,12 @@ var WasmProcess = class {
11493
11703
  return { ok: true, kind: "json", json: await this.spawn(req) };
11494
11704
  case "proc-wait":
11495
11705
  return { ok: true, kind: "json", json: await this.procWait(req, cancelled) };
11706
+ case "proc-run":
11707
+ return { ok: true, kind: "bytes", bytes: await this.run(req) };
11708
+ case "proc-exit":
11709
+ if (!this.options.exitNow) throw new KernelError("ENOSYS");
11710
+ this.options.exitNow(req.status & 255);
11711
+ return DYING;
11496
11712
  case "proc-identity":
11497
11713
  return { ok: true, kind: "json", json: await this.options.identity?.() ?? null };
11498
11714
  case "proc-exec": {
@@ -11899,6 +12115,7 @@ function processOptions(opts, header4, hooks, created3) {
11899
12115
  hasPending: () => Atomics.load(header4, SAB_I_SIGNALS) !== 0 || Atomics.load(header4, SAB_I_TIMERS) !== 0,
11900
12116
  raise: hooks.raise,
11901
12117
  threadKill: hooks.threadKill,
12118
+ ...hooks.exitNow ? { exitNow: hooks.exitNow } : {},
11902
12119
  ...opts.program.abi === "wasi" || opts.program.abi === "js" ? { pendingBits: () => Atomics.load(header4, SAB_I_SIGNALS) } : {}
11903
12120
  };
11904
12121
  }
@@ -11915,7 +12132,8 @@ function spawnWasmProcess(opts) {
11915
12132
  header4,
11916
12133
  {
11917
12134
  raise: (sig) => signal(sig),
11918
- threadKill: (tid, sig) => killThread(threadSignals.get(tid), process2.signalMasks().caught, sig)
12135
+ threadKill: (tid, sig) => killThread(threadSignals.get(tid), process2.signalMasks().caught, sig),
12136
+ ...opts.program.abi === "js" ? { exitNow: (code) => exitNow(code) } : {}
11919
12137
  },
11920
12138
  created3
11921
12139
  )
@@ -11956,6 +12174,9 @@ function spawnWasmProcess(opts) {
11956
12174
  () => settle(code)
11957
12175
  );
11958
12176
  };
12177
+ const exitNow = (code) => {
12178
+ queueMicrotask(() => finish(...dying.status(code)));
12179
+ };
11959
12180
  const handle = (data) => {
11960
12181
  if (data?.type === WASM_PROCESS_EXIT) {
11961
12182
  finish(...dying.status(data.code));
@@ -12691,15 +12912,15 @@ function granted(handle) {
12691
12912
  );
12692
12913
  }
12693
12914
  function removableMedium(onLost, open = (handle) => new OpfsFs(handle)) {
12694
- const slot = mediumSlot(
12915
+ const slot2 = mediumSlot(
12695
12916
  async (err, handle) => denied(err) && !await granted(handle),
12696
12917
  onLost
12697
12918
  );
12698
12919
  return {
12699
- handlers: slot.handlers,
12700
- present: slot.present,
12701
- insert: (handle) => slot.insert(handle, kernelFsHandlers(open(handle))),
12702
- eject: slot.eject
12920
+ handlers: slot2.handlers,
12921
+ present: slot2.present,
12922
+ insert: (handle) => slot2.insert(handle, kernelFsHandlers(open(handle))),
12923
+ eject: slot2.eject
12703
12924
  };
12704
12925
  }
12705
12926
 
@@ -13577,7 +13798,7 @@ function grantConnect(hook, fetch2, liveness = {}) {
13577
13798
  async function openHostfs(spec, connect, timing = {}) {
13578
13799
  const readonly2 = spec.options?.ro !== void 0;
13579
13800
  const { port1, port2 } = new MessageChannel();
13580
- const slot = mediumSlot(
13801
+ const slot2 = mediumSlot(
13581
13802
  async (err) => unreachable(err),
13582
13803
  () => watching.kick()
13583
13804
  );
@@ -13590,7 +13811,7 @@ async function openHostfs(spec, connect, timing = {}) {
13590
13811
  });
13591
13812
  const served = serveFilesystem(
13592
13813
  port2,
13593
- { ...slot.handlers, mount: ranged2 },
13814
+ { ...slot2.handlers, mount: ranged2 },
13594
13815
  {
13595
13816
  symlinks: true,
13596
13817
  chmod: true,
@@ -13604,12 +13825,12 @@ async function openHostfs(spec, connect, timing = {}) {
13604
13825
  connect: () => connect(spec.source, { readonly: readonly2 }),
13605
13826
  up(client) {
13606
13827
  active = timed(client, timing.callMs ?? HOSTFS_CALL_MS);
13607
- slot.insert(client, handlers);
13828
+ slot2.insert(client, handlers);
13608
13829
  served.invalidate(true);
13609
13830
  },
13610
13831
  event: (event) => served.invalidate(invalidation(event)),
13611
13832
  down() {
13612
- slot.eject();
13833
+ slot2.eject();
13613
13834
  served.invalidate(true);
13614
13835
  },
13615
13836
  ...timing
@@ -13619,7 +13840,7 @@ async function openHostfs(spec, connect, timing = {}) {
13619
13840
  await watching.ready;
13620
13841
  return {
13621
13842
  port: port1,
13622
- present: slot.present,
13843
+ present: slot2.present,
13623
13844
  dispose() {
13624
13845
  stop.abort();
13625
13846
  port1.close();
@@ -14922,16 +15143,16 @@ var Launcher = class {
14922
15143
  }
14923
15144
  async insert(target, handle, source) {
14924
15145
  const at = normalizePath(target);
14925
- const slot = this.removable.get(at);
14926
- if (!slot) throw fsError("EINVAL", `${at} is not a removable mount`);
14927
- if (source !== void 0 && source !== `fsa:${slot.id}`) {
15146
+ const slot2 = this.removable.get(at);
15147
+ if (!slot2) throw fsError("EINVAL", `${at} is not a removable mount`);
15148
+ if (source !== void 0 && source !== `fsa:${slot2.id}`) {
14928
15149
  throw fsError("EINVAL", `${at} is no longer the drive ${source}`);
14929
15150
  }
14930
15151
  if (!await granted(handle)) throw fsError("EACCES", `${at}: no permission for this folder`);
14931
- this.inserted.set(slot.id, handle);
14932
- await this.media.put(slot.id, handle);
14933
- slot.medium.insert(handle);
14934
- slot.served.invalidate(true);
15152
+ this.inserted.set(slot2.id, handle);
15153
+ await this.media.put(slot2.id, handle);
15154
+ slot2.medium.insert(handle);
15155
+ slot2.served.invalidate(true);
14935
15156
  void this.unlinked.sweep(at);
14936
15157
  }
14937
15158
  umount(target, detach = false) {