@ai-ecoverse/slicc-kernel 1.67.0 → 1.68.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.
package/dist/node.js CHANGED
@@ -866,6 +866,7 @@ function kernelFdKind(file) {
866
866
  if (file.held) return "held";
867
867
  if (file.heldMeta && "device" in file.heldMeta) return "device";
868
868
  if (file.tty) return "tty";
869
+ if (file.sealed) return "sealed";
869
870
  return file.seek ? "file" : "stream";
870
871
  }
871
872
  var FdTable = class _FdTable {
@@ -10318,8 +10319,8 @@ function ptySyscall(req, ctx) {
10318
10319
  var RUN_CAPTURE_CAP = 64 * 1024 * 1024;
10319
10320
  var RUN_MAX_BUFFER = 1024 * 1024;
10320
10321
  function capLimit(maxBuffer) {
10321
- const asked = maxBuffer === void 0 ? RUN_MAX_BUFFER : maxBuffer;
10322
- return Math.max(0, Math.min(asked, RUN_CAPTURE_CAP));
10322
+ const asked = typeof maxBuffer === "number" && maxBuffer >= 0 ? maxBuffer : RUN_MAX_BUFFER;
10323
+ return Math.min(asked, RUN_CAPTURE_CAP);
10323
10324
  }
10324
10325
  function slot(how, n, extra) {
10325
10326
  if (how === "inherit") return { fd: n };
@@ -10345,6 +10346,7 @@ async function reaped(deps, pid) {
10345
10346
  }
10346
10347
  }
10347
10348
  async function runChild(req, deps) {
10349
+ if (!isSignal(req.killSignal)) throw new KernelError("EINVAL");
10348
10350
  const max = capLimit(req.maxBuffer);
10349
10351
  let failure;
10350
10352
  let pid = 0;
@@ -11181,6 +11183,7 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
11181
11183
  "fd-open-tty",
11182
11184
  "fd-tty-names",
11183
11185
  "fd-tty-owner",
11186
+ "fd-open-sealed",
11184
11187
  "tty-get",
11185
11188
  "tty-set",
11186
11189
  "tty-winsz",
@@ -11236,6 +11239,7 @@ var WasmProcess = class {
11236
11239
  ignored = 0;
11237
11240
  inherited = 0;
11238
11241
  interrupt = new AbortController();
11242
+ gone = new AbortController();
11239
11243
  dying = false;
11240
11244
  umask = DEFAULT_UMASK;
11241
11245
  cred;
@@ -11458,6 +11462,7 @@ var WasmProcess = class {
11458
11462
  }
11459
11463
  async write(fd, body, nonblock = false, cancelled) {
11460
11464
  const file = this.fds.get(fd).file;
11465
+ if (file.sealed) return file.sealed.send(body, await this.sealedCaller(), this.gone.signal);
11461
11466
  if (!file.write) throw new KernelError("EBADF");
11462
11467
  if (!pollFile(file).writable) {
11463
11468
  if (nonblock) throw new KernelError("EAGAIN");
@@ -11539,6 +11544,24 @@ var WasmProcess = class {
11539
11544
  if (name2 !== void 0) ttyOpenable(this.guarded(), tty);
11540
11545
  return this.fds.install(tty.file(), 3);
11541
11546
  }
11547
+ openSealed() {
11548
+ const broker = this.options.sealed;
11549
+ if (!broker) throw new KernelError("ENOENT");
11550
+ return this.fds.install(broker.open(), 3);
11551
+ }
11552
+ async sealedCaller() {
11553
+ const shown = await this.options.identity?.();
11554
+ const broker = this.options.sealed;
11555
+ const tag = broker.tagOf(this.pid);
11556
+ return {
11557
+ pid: shown?.pid ?? this.pid,
11558
+ ppid: shown?.ppid ?? 0,
11559
+ uid: this.cred.euid,
11560
+ gid: this.cred.egid,
11561
+ ...tag !== void 0 ? { tag } : {},
11562
+ users: broker.users
11563
+ };
11564
+ }
11542
11565
  guarded() {
11543
11566
  const { guard: guard2 } = this.options;
11544
11567
  return guard2 && { fs: guard2.fs, dac: guard2.dac, cred: this.cred, pid: this.pid };
@@ -11659,6 +11682,8 @@ var WasmProcess = class {
11659
11682
  return { ok: true, kind: "json", json: this.openTty(req.name) };
11660
11683
  case "fd-tty-names":
11661
11684
  return { ok: true, kind: "json", json: this.options.jobs?.terminalNames() ?? [] };
11685
+ case "fd-open-sealed":
11686
+ return { ok: true, kind: "json", json: this.openSealed() };
11662
11687
  case "fd-tty-owner":
11663
11688
  return { ok: true, kind: "json", json: this.terminalByName(req.name).owner ?? null };
11664
11689
  case "fd-select": {
@@ -12094,6 +12119,7 @@ var WasmProcess = class {
12094
12119
  async exit() {
12095
12120
  if (this.exited) return;
12096
12121
  this.exited = true;
12122
+ this.gone.abort();
12097
12123
  this.die();
12098
12124
  this.clearAlarm();
12099
12125
  this.interrupt.abort();
@@ -12281,6 +12307,7 @@ function processOptions(opts, header4, hooks, created3) {
12281
12307
  return {
12282
12308
  ...inherited(opts),
12283
12309
  ...opts.owners ? { owners: true } : {},
12310
+ ...opts.sealed ? { sealed: opts.sealed } : {},
12284
12311
  ...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3 } } : {},
12285
12312
  ...opts.identity ? { identity: opts.identity } : {},
12286
12313
  ...opts.onSyscall ? { onSyscall: opts.onSyscall } : {},
@@ -12763,6 +12790,178 @@ function replayingHops(inner, heldMs = HELD_MS) {
12763
12790
  };
12764
12791
  }
12765
12792
 
12793
+ // src/kernel/sealed.ts
12794
+ var SEALED_MAX_BYTES = 64 * 1024;
12795
+ var SEALED_TIMEOUT_MS = 1e4;
12796
+ var SEALED_OPS = /* @__PURE__ */ new Set([
12797
+ "seal",
12798
+ "placeholder",
12799
+ "list",
12800
+ "test",
12801
+ "request",
12802
+ "status",
12803
+ "cancel",
12804
+ "materialize"
12805
+ ]);
12806
+ var encoder3 = new TextEncoder();
12807
+ var decoder2 = new TextDecoder("utf-8", { fatal: true });
12808
+ function parseSealed(bytes) {
12809
+ try {
12810
+ if (bytes.length > SEALED_MAX_BYTES) throw new KernelError("EMSGSIZE");
12811
+ let parsed2;
12812
+ try {
12813
+ parsed2 = JSON.parse(decoder2.decode(bytes));
12814
+ } catch {
12815
+ throw new KernelError("EINVAL");
12816
+ }
12817
+ if (typeof parsed2 !== "object" || parsed2 === null || Array.isArray(parsed2))
12818
+ throw new KernelError("EINVAL");
12819
+ const req = parsed2;
12820
+ if (req.v !== 1 || typeof req.op !== "string" || !SEALED_OPS.has(req.op))
12821
+ throw new KernelError("EINVAL");
12822
+ return req;
12823
+ } finally {
12824
+ bytes.fill(0);
12825
+ }
12826
+ }
12827
+ function errorReply(code, message3) {
12828
+ return { v: 1, error: { code, message: message3 } };
12829
+ }
12830
+ function isRecord(value) {
12831
+ return typeof value === "object" && value !== null && !Array.isArray(value);
12832
+ }
12833
+ function serialized(reply) {
12834
+ let plain;
12835
+ try {
12836
+ const text = JSON.stringify(reply);
12837
+ plain = text === void 0 ? void 0 : JSON.parse(text);
12838
+ } catch (err) {
12839
+ return JSON.stringify(errorReply("EIO", lineOf(err)));
12840
+ }
12841
+ if (!isRecord(plain)) return JSON.stringify(errorReply("EIO", "the vault gave no reply"));
12842
+ const { v: _, ...rest2 } = plain;
12843
+ return JSON.stringify({ v: 1, ...rest2 });
12844
+ }
12845
+ function lineOf(err) {
12846
+ const text = err instanceof Error ? err.message : String(err);
12847
+ return (text.split("\n")[0] ?? "").slice(0, 200);
12848
+ }
12849
+ var SealedChannel = class {
12850
+ reply;
12851
+ offset = 0;
12852
+ inflight;
12853
+ broker;
12854
+ constructor(broker) {
12855
+ this.broker = broker;
12856
+ }
12857
+ send(bytes, caller, gone) {
12858
+ const length2 = bytes.length;
12859
+ const fields = parseSealed(bytes);
12860
+ this.reply = void 0;
12861
+ this.offset = 0;
12862
+ this.inflight?.control.abort("replaced");
12863
+ const control = new AbortController();
12864
+ const withdraw = () => control.abort("withdrawn");
12865
+ if (gone.aborted) withdraw();
12866
+ else gone.addEventListener("abort", withdraw, { once: true });
12867
+ const timer = setTimeout(() => control.abort("timeout"), this.broker.timeoutMs);
12868
+ const req = { ...fields, v: 1, op: fields.op, caller };
12869
+ const flight = { control, done: Promise.resolve() };
12870
+ flight.done = this.answer(req, control.signal).then((reply) => {
12871
+ clearTimeout(timer);
12872
+ gone.removeEventListener("abort", withdraw);
12873
+ if (this.inflight !== flight) return;
12874
+ this.inflight = void 0;
12875
+ if (reply) this.reply = encoder3.encode(serialized(reply));
12876
+ });
12877
+ this.inflight = flight;
12878
+ return length2;
12879
+ }
12880
+ async answer(req, signal) {
12881
+ const hook = this.broker.hook;
12882
+ if (!hook) return errorReply("ENOTCONN", "no vault is connected");
12883
+ const stopped = new Promise((resolve) => {
12884
+ signal.addEventListener("abort", () => resolve(void 0), { once: true });
12885
+ });
12886
+ if (signal.aborted) return void 0;
12887
+ const asked = Promise.resolve().then(() => hook(req, { signal })).then(
12888
+ (reply2) => isRecord(reply2) ? reply2 : errorReply("EIO", "the vault gave no reply"),
12889
+ (err) => errorReply("EIO", lineOf(err))
12890
+ );
12891
+ const reply = await Promise.race([asked, stopped]);
12892
+ if (!signal.aborted) return reply;
12893
+ return signal.reason === "timeout" ? errorReply("ETIMEDOUT", "the vault did not answer within 10 s") : void 0;
12894
+ }
12895
+ async read(max, signal) {
12896
+ for (; ; ) {
12897
+ if (this.reply) {
12898
+ const out = this.reply.slice(this.offset, this.offset + max);
12899
+ this.offset += out.length;
12900
+ if (this.offset >= this.reply.length) {
12901
+ this.reply = void 0;
12902
+ this.offset = 0;
12903
+ }
12904
+ return out;
12905
+ }
12906
+ const flight = this.inflight;
12907
+ if (!flight) return new Uint8Array(0);
12908
+ await this.until(flight, signal);
12909
+ }
12910
+ }
12911
+ async until(flight, signal) {
12912
+ if (!signal) return flight.done;
12913
+ if (signal.aborted) throw new KernelError("EINTR");
12914
+ let stop = () => {
12915
+ };
12916
+ const interrupted2 = new Promise((_, reject) => {
12917
+ stop = () => reject(new KernelError("EINTR"));
12918
+ signal.addEventListener("abort", stop, { once: true });
12919
+ });
12920
+ try {
12921
+ await Promise.race([flight.done, interrupted2]);
12922
+ } finally {
12923
+ signal.removeEventListener("abort", stop);
12924
+ }
12925
+ }
12926
+ poll() {
12927
+ return { readable: this.reply !== void 0 || !this.inflight, writable: true, hangup: false };
12928
+ }
12929
+ async changed(signal) {
12930
+ const flight = this.inflight;
12931
+ if (flight) await this.until(flight, signal);
12932
+ }
12933
+ close() {
12934
+ this.inflight?.control.abort("withdrawn");
12935
+ this.inflight = void 0;
12936
+ this.reply = void 0;
12937
+ }
12938
+ };
12939
+ var SealedBroker = class {
12940
+ hook;
12941
+ users;
12942
+ timeoutMs;
12943
+ tagOf;
12944
+ constructor(hook, users, timeoutMs = SEALED_TIMEOUT_MS, tagOf = () => void 0) {
12945
+ this.hook = hook;
12946
+ this.users = users;
12947
+ this.timeoutMs = timeoutMs;
12948
+ this.tagOf = tagOf;
12949
+ }
12950
+ open() {
12951
+ const channel = new SealedChannel(this);
12952
+ return new OpenFile({
12953
+ read: (max, signal) => channel.read(max, signal),
12954
+ write: async () => {
12955
+ throw new KernelError("EINVAL");
12956
+ },
12957
+ poll: () => channel.poll(),
12958
+ changed: (signal) => channel.changed(signal),
12959
+ close: () => channel.close(),
12960
+ sealed: channel
12961
+ });
12962
+ }
12963
+ };
12964
+
12766
12965
  // src/kernel/settling.ts
12767
12966
  var SETUP_OPS = /* @__PURE__ */ new Set([
12768
12967
  "proc-list",
@@ -13410,13 +13609,13 @@ var HostfsClient = class {
13410
13609
  const response = await this.send("/api/hostfs/watch", { method: "POST", signal });
13411
13610
  onOpen();
13412
13611
  const reader = response.body.getReader();
13413
- const decoder2 = new TextDecoder();
13612
+ const decoder3 = new TextDecoder();
13414
13613
  let buffered = "";
13415
13614
  try {
13416
13615
  for (; ; ) {
13417
13616
  const { done: done4, value } = await reader.read();
13418
13617
  if (done4) return;
13419
- buffered += decoder2.decode(value, { stream: true });
13618
+ buffered += decoder3.decode(value, { stream: true });
13420
13619
  let end = buffered.indexOf("\n");
13421
13620
  while (end >= 0) {
13422
13621
  const line = buffered.slice(0, end).trim();
@@ -14897,7 +15096,7 @@ var LAYOUT = [
14897
15096
  ];
14898
15097
  var LOAD_SAMPLE_MS = 5e3;
14899
15098
  var LOAD_WINDOWS_MS = [6e4, 3e5, 9e5];
14900
- var encoder3 = new TextEncoder();
15099
+ var encoder4 = new TextEncoder();
14901
15100
  var USERS_MODES = ["off", "report", "enforce"];
14902
15101
  function usersMode(mode = "off") {
14903
15102
  if (USERS_MODES.includes(mode)) return mode;
@@ -15047,12 +15246,19 @@ var Launcher = class {
15047
15246
  fstab = Promise.resolve([]);
15048
15247
  usersMode;
15049
15248
  dac;
15249
+ sealed;
15050
15250
  nextPid = 1e3;
15051
15251
  boot = Date.now();
15052
15252
  terminals = 0;
15053
15253
  constructor(options) {
15054
15254
  this.usersMode = usersMode(options.users);
15055
15255
  this.dac = this.usersMode === "off" ? void 0 : new Dac(this.usersMode, options.onDenial);
15256
+ this.sealed = new SealedBroker(
15257
+ options.sealed,
15258
+ this.usersMode,
15259
+ void 0,
15260
+ (pid) => this.tagOf(pid)
15261
+ );
15056
15262
  if (this.owners) options.fs.trackOwners?.();
15057
15263
  this.mounts = new MountTable({
15058
15264
  open: (type, spec) => this.driver(type, spec),
@@ -15452,6 +15658,7 @@ var Launcher = class {
15452
15658
  ...req.umask !== void 0 ? { umask: req.umask } : {},
15453
15659
  cred: req.cred,
15454
15660
  ...this.owners ? { owners: true } : {},
15661
+ sealed: this.sealed,
15455
15662
  ...this.dac ? { dac: this.dac } : {},
15456
15663
  program: req.program,
15457
15664
  argv0: req.argv0,
@@ -16090,7 +16297,7 @@ var Launcher = class {
16090
16297
  },
16091
16298
  account
16092
16299
  );
16093
- const report = (message3) => options.onData(encoder3.encode(`${message3}\r
16300
+ const report = (message3) => options.onData(encoder4.encode(`${message3}\r
16094
16301
  `));
16095
16302
  const handle = await this.launch(planned, {
16096
16303
  env,
@@ -16127,7 +16334,7 @@ var Launcher = class {
16127
16334
  };
16128
16335
  const stdout = collect(out, options.onStdout);
16129
16336
  const stderr = collect(err, options.onStderr);
16130
- const report = (message3) => stderr(encoder3.encode(`${message3}
16337
+ const report = (message3) => stderr(encoder4.encode(`${message3}
16131
16338
  `));
16132
16339
  if (!planned) {
16133
16340
  report(`${file}: command not found`);
@@ -16664,7 +16871,7 @@ function dialStream(port, onClose) {
16664
16871
 
16665
16872
  // src/client/loopback-fetch.ts
16666
16873
  var OWN_HEADERS = /* @__PURE__ */ new Set(["host", "connection", "content-length", "transfer-encoding"]);
16667
- var encoder4 = new TextEncoder();
16874
+ var encoder5 = new TextEncoder();
16668
16875
  var latin12 = new TextDecoder("latin1");
16669
16876
  var ByteSource = class {
16670
16877
  buffered = new Uint8Array(0);
@@ -16682,7 +16889,7 @@ var ByteSource = class {
16682
16889
  return true;
16683
16890
  }
16684
16891
  async until(marker) {
16685
- const needle = encoder4.encode(marker);
16892
+ const needle = encoder5.encode(marker);
16686
16893
  for (; ; ) {
16687
16894
  const at = indexOf(this.buffered, needle);
16688
16895
  if (at >= 0) return this.take(at + needle.length);
@@ -16790,7 +16997,7 @@ async function requestBytes(request) {
16790
16997
  });
16791
16998
  if (body) lines2.push(`Content-Length: ${body.length}`);
16792
16999
  lines2.push("Connection: close", "", "");
16793
- const head = encoder4.encode(lines2.join("\r\n"));
17000
+ const head = encoder5.encode(lines2.join("\r\n"));
16794
17001
  const out = new Uint8Array(head.length + (body?.length ?? 0));
16795
17002
  out.set(head);
16796
17003
  if (body) out.set(body, head.length);
@@ -16844,8 +17051,8 @@ var KernelCallError = class extends Error {
16844
17051
  this.code = code;
16845
17052
  }
16846
17053
  };
16847
- var encoder5 = new TextEncoder();
16848
- var bytesOf2 = (data) => typeof data === "string" ? encoder5.encode(data) : data;
17054
+ var encoder6 = new TextEncoder();
17055
+ var bytesOf2 = (data) => typeof data === "string" ? encoder6.encode(data) : data;
16849
17056
  var UNAVAILABLE = {
16850
17057
  manualRedirects: false,
16851
17058
  encodedBodies: false,
@@ -17263,8 +17470,8 @@ function nodeWorker(file, live, closed) {
17263
17470
  }
17264
17471
  function textOf(on) {
17265
17472
  if (!on) return void 0;
17266
- const decoder2 = new TextDecoder();
17267
- return (bytes) => on(decoder2.decode(bytes, { stream: true }));
17473
+ const decoder3 = new TextDecoder();
17474
+ return (bytes) => on(decoder3.decode(bytes, { stream: true }));
17268
17475
  }
17269
17476
  async function createNodeKernel(options = {}) {
17270
17477
  const root = options.root ?? memoryRoot();
@@ -17297,6 +17504,7 @@ async function createNodeKernel(options = {}) {
17297
17504
  ...options.hostname !== void 0 ? { hostname: options.hostname } : {},
17298
17505
  ...options.users !== void 0 ? { users: options.users } : {},
17299
17506
  ...options.onDenial ? { onDenial: options.onDenial } : {},
17507
+ ...options.sealed ? { sealed: options.sealed } : {},
17300
17508
  ...options.approve ? { approve: options.approve } : {}
17301
17509
  });
17302
17510
  await launcher.prepare();
@@ -17305,7 +17513,7 @@ async function createNodeKernel(options = {}) {
17305
17513
  pending.add(reject);
17306
17514
  work().then(resolve, reject).finally(() => pending.delete(reject));
17307
17515
  });
17308
- const decoder2 = new TextDecoder();
17516
+ const decoder3 = new TextDecoder();
17309
17517
  return {
17310
17518
  root,
17311
17519
  async run(argv, opts = {}) {
@@ -17322,8 +17530,8 @@ async function createNodeKernel(options = {}) {
17322
17530
  );
17323
17531
  return {
17324
17532
  status: result.status,
17325
- stdout: decoder2.decode(result.stdout),
17326
- stderr: decoder2.decode(result.stderr)
17533
+ stdout: decoder3.decode(result.stdout),
17534
+ stderr: decoder3.decode(result.stderr)
17327
17535
  };
17328
17536
  },
17329
17537
  async openTerminal(argv, opts = {}) {