@ai-ecoverse/slicc-kernel 1.64.2 → 1.65.0

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.
@@ -6839,6 +6839,23 @@ var CONTINUED_STATUS = 65535;
6839
6839
  function waitStatus(code, termsig) {
6840
6840
  return termsig ? termsig & 127 : (code & 255) << 8;
6841
6841
  }
6842
+ function capturing(chunks, max = Number.POSITIVE_INFINITY, overflow) {
6843
+ let size = 0;
6844
+ let over = false;
6845
+ return (bytes) => {
6846
+ if (over) return;
6847
+ const room = max - size;
6848
+ if (bytes.length > room) {
6849
+ over = true;
6850
+ if (room > 0) chunks.push(bytes.slice(0, room));
6851
+ size = max;
6852
+ overflow?.();
6853
+ return;
6854
+ }
6855
+ chunks.push(bytes);
6856
+ size += bytes.length;
6857
+ };
6858
+ }
6842
6859
  function concat3(chunks) {
6843
6860
  const out = new Uint8Array(chunks.reduce((n, c) => n + c.length, 0));
6844
6861
  let offset = 0;
@@ -6872,15 +6889,15 @@ var ChildTable = class {
6872
6889
  const fds = new FdTable();
6873
6890
  const captured = /* @__PURE__ */ new Map();
6874
6891
  try {
6875
- for (const [n, slot] of stdio.entries()) fds.installAt(n, this.openSlot(slot, n, captured));
6876
- for (const slot of inherit2) {
6877
- if (slot.fd <= 2) continue;
6878
- if ("device" in slot) {
6879
- fds.installAt(slot.fd, deviceFile(slot.device, slot.access));
6892
+ for (const [n, slot2] of stdio.entries()) fds.installAt(n, this.openSlot(slot2, n, captured));
6893
+ for (const slot2 of inherit2) {
6894
+ if (slot2.fd <= 2) continue;
6895
+ if ("device" in slot2) {
6896
+ fds.installAt(slot2.fd, deviceFile(slot2.device, slot2.access));
6880
6897
  continue;
6881
6898
  }
6882
- fds.installAt(slot.fd, this.parentFds.get(slot.kernel).retain());
6883
- if (slot.flags !== void 0) fds.setStatusFlags(slot.fd, slot.flags);
6899
+ fds.installAt(slot2.fd, this.parentFds.get(slot2.kernel).retain());
6900
+ if (slot2.flags !== void 0) fds.setStatusFlags(slot2.fd, slot2.flags);
6884
6901
  }
6885
6902
  } catch (e) {
6886
6903
  await fds.closeAll();
@@ -6921,15 +6938,15 @@ var ChildTable = class {
6921
6938
  this.watchers.set(pid, listener);
6922
6939
  if (child.stoppedBy !== void 0) listener("stopped", child.stoppedBy);
6923
6940
  }
6924
- openSlot(slot, n, captured) {
6925
- if ("fd" in slot) return this.parentFds.get(slot.fd).retain();
6926
- if ("input" in slot) return bytesSource(slot.input);
6927
- if ("device" in slot) return deviceFile(slot.device, slot.access);
6928
- if ("dir" in slot) return heldFile({ dir: slot.dir });
6929
- if ("capture" in slot) {
6941
+ openSlot(slot2, n, captured) {
6942
+ if ("fd" in slot2) return this.parentFds.get(slot2.fd).retain();
6943
+ if ("input" in slot2) return bytesSource(slot2.input);
6944
+ if ("device" in slot2) return deviceFile(slot2.device, slot2.access);
6945
+ if ("dir" in slot2) return heldFile({ dir: slot2.dir });
6946
+ if ("capture" in slot2) {
6930
6947
  const chunks = [];
6931
6948
  captured.set(n, chunks);
6932
- return sinkFile((bytes) => chunks.push(bytes));
6949
+ return sinkFile(capturing(chunks, slot2.max, slot2.overflow));
6933
6950
  }
6934
6951
  return deviceFile("null");
6935
6952
  }
@@ -6996,11 +7013,11 @@ var ChildTable = class {
6996
7013
  pids() {
6997
7014
  return [...this.children.keys()];
6998
7015
  }
6999
- captured(pid, slot) {
7016
+ captured(pid, slot2) {
7000
7017
  const slots = this.leftovers.get(pid);
7001
- const chunks = slots?.get(slot);
7018
+ const chunks = slots?.get(slot2);
7002
7019
  if (!slots || !chunks) return new Uint8Array(0);
7003
- slots.delete(slot);
7020
+ slots.delete(slot2);
7004
7021
  if (slots.size === 0) this.leftovers.delete(pid);
7005
7022
  return concat3(chunks);
7006
7023
  }
@@ -9234,6 +9251,7 @@ var HOST_OPS = [
9234
9251
  "async-resolve",
9235
9252
  "async-hold",
9236
9253
  "async-cancel",
9254
+ "async-settle",
9237
9255
  "async-close"
9238
9256
  ];
9239
9257
  var json3 = (value) => ({ ok: true, kind: "json", json: value });
@@ -9265,12 +9283,16 @@ var LockTable = class {
9265
9283
  for (const path of [...this.held.keys()]) this.unlock(pid, path);
9266
9284
  }
9267
9285
  };
9286
+ var KEPT_OPS = /* @__PURE__ */ new Set(["fd-write"]);
9287
+ var unknownSettled = false;
9268
9288
  var AsyncOps = class {
9269
9289
  next = 1;
9270
9290
  results = /* @__PURE__ */ new Map();
9271
9291
  ready = [];
9272
9292
  running = /* @__PURE__ */ new Map();
9273
9293
  waiters = /* @__PURE__ */ new Set();
9294
+ taken = /* @__PURE__ */ new Map();
9295
+ kept = /* @__PURE__ */ new Set();
9274
9296
  closed = false;
9275
9297
  run;
9276
9298
  onReady;
@@ -9280,6 +9302,7 @@ var AsyncOps = class {
9280
9302
  }
9281
9303
  submit(req) {
9282
9304
  const id2 = this.hold();
9305
+ if (KEPT_OPS.has(req.op)) this.kept.add(id2);
9283
9306
  const running = new AbortController();
9284
9307
  this.running.set(id2, running);
9285
9308
  this.run(req, running.signal).then(
@@ -9303,9 +9326,26 @@ var AsyncOps = class {
9303
9326
  const result = this.results.get(id2);
9304
9327
  if (!result) throw new KernelError("EAGAIN");
9305
9328
  this.forget(id2);
9329
+ if (this.kept.delete(id2)) this.taken.set(id2, result);
9306
9330
  return result;
9307
9331
  }
9332
+ ack(ids) {
9333
+ for (const id2 of ids) this.taken.delete(id2);
9334
+ }
9335
+ async settle(ids) {
9336
+ const never = new AbortController().signal;
9337
+ while (ids.some((id2) => this.running.has(id2))) await this.sleep(void 0, never);
9338
+ return ids.map((id2) => {
9339
+ const result = this.results.get(id2) ?? this.taken.get(id2) ?? null;
9340
+ if (!result && !unknownSettled) {
9341
+ unknownSettled = true;
9342
+ console.warn(`async-settle: no result for op ${id2}; its write counts as made`);
9343
+ }
9344
+ return result;
9345
+ });
9346
+ }
9308
9347
  cancel(id2) {
9348
+ this.kept.delete(id2);
9309
9349
  this.running.get(id2)?.abort();
9310
9350
  this.forget(id2);
9311
9351
  }
@@ -9381,6 +9421,7 @@ async function hostSyscall(req, ctx) {
9381
9421
  return done2;
9382
9422
  case "async-submit":
9383
9423
  if (!ctx.valid(req.req)) throw new KernelError("EINVAL");
9424
+ if (req.ack) ctx.ops.ack(req.ack);
9384
9425
  return json3(ctx.ops.submit(req.req));
9385
9426
  case "async-wait":
9386
9427
  return json3(await ctx.ops.wait(req.timeoutMs, ctx.blocking));
@@ -9393,6 +9434,8 @@ async function hostSyscall(req, ctx) {
9393
9434
  case "async-cancel":
9394
9435
  ctx.ops.cancel(req.id);
9395
9436
  return done2;
9437
+ case "async-settle":
9438
+ return json3(await ctx.ops.settle(req.ids));
9396
9439
  case "async-close":
9397
9440
  ctx.ops.close();
9398
9441
  return done2;
@@ -9900,8 +9943,8 @@ var KernelTty = class {
9900
9943
  get canonical() {
9901
9944
  return (this.termios.c_lflag & ICANON) !== 0;
9902
9945
  }
9903
- cc(slot) {
9904
- return this.termios.c_cc[slot] ?? 0;
9946
+ cc(slot2) {
9947
+ return this.termios.c_cc[slot2] ?? 0;
9905
9948
  }
9906
9949
  key(input) {
9907
9950
  const { c_iflag, c_lflag } = this.termios;
@@ -10256,6 +10299,84 @@ function ptySyscall(req, ctx) {
10256
10299
  }
10257
10300
  }
10258
10301
 
10302
+ // src/kernel/run-child.ts
10303
+ var RUN_CAPTURE_CAP = 64 * 1024 * 1024;
10304
+ var RUN_MAX_BUFFER = 1024 * 1024;
10305
+ function capLimit(maxBuffer) {
10306
+ const asked = maxBuffer === void 0 ? RUN_MAX_BUFFER : maxBuffer;
10307
+ return Math.max(0, Math.min(asked, RUN_CAPTURE_CAP));
10308
+ }
10309
+ function slot(how, n, extra) {
10310
+ if (how === "inherit") return { fd: n };
10311
+ if (how === "ignore") return { none: true };
10312
+ return extra;
10313
+ }
10314
+ function frame2(outcome, out, err) {
10315
+ const head = new TextEncoder().encode(JSON.stringify(outcome));
10316
+ const bytes = new Uint8Array(4 + head.length + out.length + err.length);
10317
+ new DataView(bytes.buffer).setUint32(0, head.length, true);
10318
+ bytes.set(head, 4);
10319
+ bytes.set(out, 4 + head.length);
10320
+ bytes.set(err, 4 + head.length + out.length);
10321
+ return bytes;
10322
+ }
10323
+ async function reaped(deps, pid) {
10324
+ for (; ; ) {
10325
+ try {
10326
+ return (await deps.children.wait(pid, false, deps.interrupt()))[1];
10327
+ } catch (e) {
10328
+ if (!(e instanceof KernelError && e.code === "EINTR") || deps.dying()) throw e;
10329
+ }
10330
+ }
10331
+ }
10332
+ async function runChild(req, deps) {
10333
+ const max = capLimit(req.maxBuffer);
10334
+ let failure2;
10335
+ let pid = 0;
10336
+ const kill = () => void Promise.allSettled([deps.kill(pid, req.killSignal)]);
10337
+ const stop = (code) => {
10338
+ if (failure2 !== void 0) return;
10339
+ failure2 = code;
10340
+ if (pid !== 0) kill();
10341
+ };
10342
+ const capture = () => ({ capture: true, max, overflow: () => stop("ENOBUFS") });
10343
+ const stdio = [
10344
+ slot(req.stdin, 0, { input: req.input ?? new Uint8Array(0) }),
10345
+ slot(req.stdout, 1, capture()),
10346
+ slot(req.stderr, 2, capture())
10347
+ ];
10348
+ try {
10349
+ pid = await deps.children.spawn(
10350
+ { file: req.file, argv: req.argv, env: req.env, cwd: req.cwd },
10351
+ stdio
10352
+ );
10353
+ } catch (e) {
10354
+ if (!(e instanceof SpawnError)) throw e;
10355
+ const outcome2 = { pid: 0, status: null, error: { code: e.code }, out: 0, err: 0 };
10356
+ return frame2(outcome2, new Uint8Array(0), new Uint8Array(0));
10357
+ }
10358
+ if (failure2 !== void 0) kill();
10359
+ const timer = req.timeoutMs !== void 0 && req.timeoutMs > 0 ? setTimeout(() => stop("ETIMEDOUT"), req.timeoutMs) : void 0;
10360
+ let status;
10361
+ try {
10362
+ status = await reaped(deps, pid);
10363
+ } finally {
10364
+ clearTimeout(timer);
10365
+ }
10366
+ const out = deps.children.captured(pid, 1);
10367
+ const err = deps.children.captured(pid, 2);
10368
+ const termsig = status & 127;
10369
+ const outcome = {
10370
+ pid,
10371
+ status: termsig ? null : status >> 8 & 255,
10372
+ ...termsig ? { signal: termsig } : {},
10373
+ ...failure2 !== void 0 ? { error: { code: failure2 } } : {},
10374
+ out: out.length,
10375
+ err: err.length
10376
+ };
10377
+ return frame2(outcome, out, err);
10378
+ }
10379
+
10259
10380
  // src/kernel/select.ts
10260
10381
  function ready(fds, read, write) {
10261
10382
  const hangup = /* @__PURE__ */ new Set();
@@ -11052,6 +11173,8 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
11052
11173
  "proc-spawn",
11053
11174
  "proc-wait",
11054
11175
  "proc-captured",
11176
+ "proc-run",
11177
+ "proc-exit",
11055
11178
  "proc-fork",
11056
11179
  "proc-kill",
11057
11180
  "thread-kill",
@@ -11172,6 +11295,14 @@ var WasmProcess = class {
11172
11295
  else this.adoptExec(pid);
11173
11296
  return pid;
11174
11297
  }
11298
+ run(req) {
11299
+ return runChild(req, {
11300
+ children: this.children,
11301
+ kill: (pid, sig) => this.options.kill?.(pid, sig),
11302
+ interrupt: () => this.interrupt.signal,
11303
+ dying: () => this.dying
11304
+ });
11305
+ }
11175
11306
  adoptExec(pid) {
11176
11307
  this.execChild = pid;
11177
11308
  this.children.watch(pid, (state, sig) => state === "stopped" ? this.stop(sig) : this.cont());
@@ -11720,6 +11851,12 @@ var WasmProcess = class {
11720
11851
  return { ok: true, kind: "json", json: await this.spawn(req) };
11721
11852
  case "proc-wait":
11722
11853
  return { ok: true, kind: "json", json: await this.procWait(req, cancelled) };
11854
+ case "proc-run":
11855
+ return { ok: true, kind: "bytes", bytes: await this.run(req) };
11856
+ case "proc-exit":
11857
+ if (!this.options.exitNow) throw new KernelError("ENOSYS");
11858
+ this.options.exitNow(req.status & 255);
11859
+ return DYING;
11723
11860
  case "proc-identity":
11724
11861
  return { ok: true, kind: "json", json: await this.options.identity?.() ?? null };
11725
11862
  case "proc-exec": {
@@ -12126,6 +12263,7 @@ function processOptions(opts, header3, hooks, created3) {
12126
12263
  hasPending: () => Atomics.load(header3, SAB_I_SIGNALS) !== 0 || Atomics.load(header3, SAB_I_TIMERS) !== 0,
12127
12264
  raise: hooks.raise,
12128
12265
  threadKill: hooks.threadKill,
12266
+ ...hooks.exitNow ? { exitNow: hooks.exitNow } : {},
12129
12267
  ...opts.program.abi === "wasi" || opts.program.abi === "js" ? { pendingBits: () => Atomics.load(header3, SAB_I_SIGNALS) } : {}
12130
12268
  };
12131
12269
  }
@@ -12142,7 +12280,8 @@ function spawnWasmProcess(opts) {
12142
12280
  header3,
12143
12281
  {
12144
12282
  raise: (sig) => signal(sig),
12145
- threadKill: (tid, sig) => killThread(threadSignals.get(tid), process.signalMasks().caught, sig)
12283
+ threadKill: (tid, sig) => killThread(threadSignals.get(tid), process.signalMasks().caught, sig),
12284
+ ...opts.program.abi === "js" ? { exitNow: (code) => exitNow(code) } : {}
12146
12285
  },
12147
12286
  created3
12148
12287
  )
@@ -12183,6 +12322,9 @@ function spawnWasmProcess(opts) {
12183
12322
  () => settle3(code)
12184
12323
  );
12185
12324
  };
12325
+ const exitNow = (code) => {
12326
+ queueMicrotask(() => finish(...dying.status(code)));
12327
+ };
12186
12328
  const handle = (data) => {
12187
12329
  if (data?.type === WASM_PROCESS_EXIT) {
12188
12330
  finish(...dying.status(data.code));
@@ -12918,15 +13060,15 @@ function granted(handle) {
12918
13060
  );
12919
13061
  }
12920
13062
  function removableMedium(onLost, open = (handle) => new OpfsFs(handle)) {
12921
- const slot = mediumSlot(
13063
+ const slot2 = mediumSlot(
12922
13064
  async (err, handle) => denied(err) && !await granted(handle),
12923
13065
  onLost
12924
13066
  );
12925
13067
  return {
12926
- handlers: slot.handlers,
12927
- present: slot.present,
12928
- insert: (handle) => slot.insert(handle, kernelFsHandlers(open(handle))),
12929
- eject: slot.eject
13068
+ handlers: slot2.handlers,
13069
+ present: slot2.present,
13070
+ insert: (handle) => slot2.insert(handle, kernelFsHandlers(open(handle))),
13071
+ eject: slot2.eject
12930
13072
  };
12931
13073
  }
12932
13074
 
@@ -13710,7 +13852,7 @@ function grantConnect(hook, fetch2, liveness = {}) {
13710
13852
  async function openHostfs(spec, connect, timing = {}) {
13711
13853
  const readonly2 = spec.options?.ro !== void 0;
13712
13854
  const { port1, port2 } = new MessageChannel();
13713
- const slot = mediumSlot(
13855
+ const slot2 = mediumSlot(
13714
13856
  async (err) => unreachable(err),
13715
13857
  () => watching.kick()
13716
13858
  );
@@ -13723,7 +13865,7 @@ async function openHostfs(spec, connect, timing = {}) {
13723
13865
  });
13724
13866
  const served = serveFilesystem(
13725
13867
  port2,
13726
- { ...slot.handlers, mount: ranged2 },
13868
+ { ...slot2.handlers, mount: ranged2 },
13727
13869
  {
13728
13870
  symlinks: true,
13729
13871
  chmod: true,
@@ -13737,12 +13879,12 @@ async function openHostfs(spec, connect, timing = {}) {
13737
13879
  connect: () => connect(spec.source, { readonly: readonly2 }),
13738
13880
  up(client) {
13739
13881
  active = timed(client, timing.callMs ?? HOSTFS_CALL_MS);
13740
- slot.insert(client, handlers);
13882
+ slot2.insert(client, handlers);
13741
13883
  served.invalidate(true);
13742
13884
  },
13743
13885
  event: (event) => served.invalidate(invalidation(event)),
13744
13886
  down() {
13745
- slot.eject();
13887
+ slot2.eject();
13746
13888
  served.invalidate(true);
13747
13889
  },
13748
13890
  ...timing
@@ -13752,7 +13894,7 @@ async function openHostfs(spec, connect, timing = {}) {
13752
13894
  await watching.ready;
13753
13895
  return {
13754
13896
  port: port1,
13755
- present: slot.present,
13897
+ present: slot2.present,
13756
13898
  dispose() {
13757
13899
  stop.abort();
13758
13900
  port1.close();
@@ -15046,16 +15188,16 @@ var Launcher = class {
15046
15188
  }
15047
15189
  async insert(target, handle, source) {
15048
15190
  const at = normalizePath(target);
15049
- const slot = this.removable.get(at);
15050
- if (!slot) throw fsError("EINVAL", `${at} is not a removable mount`);
15051
- if (source !== void 0 && source !== `fsa:${slot.id}`) {
15191
+ const slot2 = this.removable.get(at);
15192
+ if (!slot2) throw fsError("EINVAL", `${at} is not a removable mount`);
15193
+ if (source !== void 0 && source !== `fsa:${slot2.id}`) {
15052
15194
  throw fsError("EINVAL", `${at} is no longer the drive ${source}`);
15053
15195
  }
15054
15196
  if (!await granted(handle)) throw fsError("EACCES", `${at}: no permission for this folder`);
15055
- this.inserted.set(slot.id, handle);
15056
- await this.media.put(slot.id, handle);
15057
- slot.medium.insert(handle);
15058
- slot.served.invalidate(true);
15197
+ this.inserted.set(slot2.id, handle);
15198
+ await this.media.put(slot2.id, handle);
15199
+ slot2.medium.insert(handle);
15200
+ slot2.served.invalidate(true);
15059
15201
  void this.unlinked.sweep(at);
15060
15202
  }
15061
15203
  umount(target, detach = false) {