@ai-ecoverse/slicc-kernel 1.152.2 → 1.153.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.
@@ -1922,9 +1922,9 @@ var OpfsFs = class {
1922
1922
  this.lookups++;
1923
1923
  if (split(path).length === 0) return this.root;
1924
1924
  const cached = this.files.get(path);
1925
- const fresh2 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
1926
- if (cached && !fresh2) this.files.delete(path);
1927
- if (cached && fresh2) {
1925
+ const fresh3 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
1926
+ if (cached && !fresh3) this.files.delete(path);
1927
+ if (cached && fresh3) {
1928
1928
  if (this.glanced(cached)) return this.remember(path, cached);
1929
1929
  try {
1930
1930
  await this.glance(cached);
@@ -2142,8 +2142,8 @@ var OpfsFs = class {
2142
2142
  glanced(handle) {
2143
2143
  if (this.exact > 0) return void 0;
2144
2144
  const seen = this.glances.get(handle);
2145
- const fresh2 = seen && seen.changes === this.changes && this.recent(seen, GLANCE_LOOKUPS);
2146
- return fresh2 ? seen.file : void 0;
2145
+ const fresh3 = seen && seen.changes === this.changes && this.recent(seen, GLANCE_LOOKUPS);
2146
+ return fresh3 ? seen.file : void 0;
2147
2147
  }
2148
2148
  async glance(handle) {
2149
2149
  const at = performance.now();
@@ -2333,13 +2333,13 @@ var OpfsFs = class {
2333
2333
  async keptFor(path) {
2334
2334
  const at = normalizePath(path);
2335
2335
  const cached = this.files.get(at);
2336
- const fresh2 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
2337
- const [target, existed] = fresh2 ? [at, true] : await this.writeTarget(path);
2336
+ const fresh3 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
2337
+ const [target, existed] = fresh3 ? [at, true] : await this.writeTarget(path);
2338
2338
  if (!existed) return [void 0, target, false];
2339
2339
  this.refuse(target);
2340
2340
  const [entry] = await this.meta.get([target]);
2341
2341
  if (entry?.fifo || entry?.socket) throw fsError("ENXIO", path);
2342
- const kept = fresh2 ? cached : this.files.get(target);
2342
+ const kept = fresh3 ? cached : this.files.get(target);
2343
2343
  const usable2 = kept && performance.now() - (this.found.get(kept) ?? -CHECK_MS) < CHECK_MS;
2344
2344
  return [usable2 ? kept : void 0, target, true];
2345
2345
  }
@@ -4520,6 +4520,102 @@ function portCdpHook(ask, gone) {
4520
4520
  });
4521
4521
  }
4522
4522
 
4523
+ // src/embedder-relay.ts
4524
+ function fresh2(bytes) {
4525
+ return bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength ? bytes : bytes.slice();
4526
+ }
4527
+ function embedderAsker(port) {
4528
+ const asks = /* @__PURE__ */ new Map();
4529
+ let next3 = 0;
4530
+ const post = (message3, transfer = []) => port.postMessage({ embedder: message3 }, transfer);
4531
+ const push = (asked, item) => {
4532
+ asked.chunks.push(item);
4533
+ asked.wake?.();
4534
+ };
4535
+ const reply = (id2, asked) => new ReadableStream(
4536
+ {
4537
+ pull: async (controller) => {
4538
+ if (asked.chunks.length === 0) {
4539
+ post({ id: id2, pull: true });
4540
+ await new Promise((resolve2) => {
4541
+ asked.wake = resolve2;
4542
+ });
4543
+ }
4544
+ const item = asked.chunks.shift();
4545
+ if (item instanceof Uint8Array) controller.enqueue(item);
4546
+ else if (item === null || item === void 0) {
4547
+ asks.delete(id2);
4548
+ controller.close();
4549
+ } else {
4550
+ asks.delete(id2);
4551
+ controller.error(Object.assign(new Error(item.message), { name: item.name }));
4552
+ }
4553
+ },
4554
+ cancel: (reason) => {
4555
+ asks.delete(id2);
4556
+ post({ id: id2, cancel: String(reason ?? "closed") });
4557
+ }
4558
+ },
4559
+ { highWaterMark: 0 }
4560
+ );
4561
+ const sendBody = async (id2, asked) => {
4562
+ const got = await asked.body.read().catch(() => ({ done: true, value: void 0 }));
4563
+ if (got.done) post({ id: id2, bodyEnd: true });
4564
+ else {
4565
+ const bytes = fresh2(got.value);
4566
+ post({ id: id2, body: bytes }, [bytes.buffer]);
4567
+ }
4568
+ };
4569
+ const hookFor = (client, kinds) => ({
4570
+ kinds,
4571
+ handle: async (request3) => {
4572
+ const id2 = ++next3;
4573
+ const asked = {
4574
+ ready: Promise.withResolvers(),
4575
+ chunks: [],
4576
+ wake: void 0,
4577
+ body: request3.body.getReader(),
4578
+ progress: request3.progress
4579
+ };
4580
+ asks.set(id2, asked);
4581
+ const cancel = () => {
4582
+ asks.delete(id2);
4583
+ post({
4584
+ id: id2,
4585
+ cancel: String(request3.signal.reason?.message ?? "closed")
4586
+ });
4587
+ asked.ready.reject(request3.signal.reason);
4588
+ push(asked, { name: "AbortError", message: "cancelled" });
4589
+ };
4590
+ request3.signal.addEventListener("abort", cancel, { once: true });
4591
+ const { kind, options, stamp } = request3;
4592
+ post({ ask: { id: id2, client, kind, options, stamp } });
4593
+ await asked.ready.promise;
4594
+ return reply(id2, asked);
4595
+ }
4596
+ });
4597
+ const answer2 = (message3) => {
4598
+ const asked = asks.get(message3.id);
4599
+ if (!asked) return;
4600
+ if ("ready" in message3) asked.ready.resolve();
4601
+ else if ("chunk" in message3) push(asked, message3.chunk);
4602
+ else if ("done" in message3) push(asked, null);
4603
+ else if ("error" in message3) {
4604
+ asked.ready.reject(
4605
+ Object.assign(new Error(message3.error.message), { name: message3.error.name })
4606
+ );
4607
+ asks.delete(message3.id);
4608
+ void asked.body.cancel(message3.error.message).catch(() => void 0);
4609
+ push(asked, message3.error);
4610
+ } else if ("progress" in message3) asked.progress(message3.progress);
4611
+ else void sendBody(message3.id, asked);
4612
+ };
4613
+ return { hookFor, answer: answer2 };
4614
+ }
4615
+ function embedderReplyOf(data) {
4616
+ return data?.embedderReply;
4617
+ }
4618
+
4523
4619
  // src/facade/port.ts
4524
4620
  var FACADE_GONE = "the facade host is gone";
4525
4621
  function portFacadeHook(ask, gone) {
@@ -5347,6 +5443,7 @@ function kernelFdKind(file) {
5347
5443
  if (file.tty) return "tty";
5348
5444
  if (file.sealed) return "sealed";
5349
5445
  if (file.clipboard) return "clipboard";
5446
+ if (file.embedder) return "embedder";
5350
5447
  return file.seek ? "file" : "stream";
5351
5448
  }
5352
5449
  var FdTable = class _FdTable {
@@ -7231,7 +7328,7 @@ var UserDb = class {
7231
7328
  joined(name2, extra);
7232
7329
  group.push({ name: name2, gid: uid, members: [] });
7233
7330
  passwd.push(row);
7234
- const fresh2 = !await this.fs.exists(home);
7331
+ const fresh3 = !await this.fs.exists(home);
7235
7332
  await this.fs.mkdir(home, { recursive: true });
7236
7333
  try {
7237
7334
  await this.provision(row, this.umaskFor(options), shared);
@@ -7239,7 +7336,7 @@ var UserDb = class {
7239
7336
  await this.fs.mkdir("/etc", { recursive: true });
7240
7337
  await this.commit(formatPasswd(passwd), formatGroup(group));
7241
7338
  } catch (err) {
7242
- if (fresh2) await this.fs.rm(home, { recursive: true, force: true });
7339
+ if (fresh3) await this.fs.rm(home, { recursive: true, force: true });
7243
7340
  throw err;
7244
7341
  }
7245
7342
  remember();
@@ -7303,7 +7400,7 @@ var UserDb = class {
7303
7400
  };
7304
7401
 
7305
7402
  // src/client/protocol.ts
7306
- var PROTOCOL = [1, 12];
7403
+ var PROTOCOL = [1, 13];
7307
7404
  var FS_METHODS = [
7308
7405
  "readFile",
7309
7406
  "writeFile",
@@ -7470,14 +7567,15 @@ async function runAs(l, self, requested) {
7470
7567
  function clientCred(self) {
7471
7568
  return self ? credOf(self) : copyCred(ROOT);
7472
7569
  }
7473
- async function runTagged(l, self, options, tag) {
7570
+ async function runTagged(l, self, options, tag, session = -1) {
7474
7571
  tagOption(options.tag);
7475
7572
  const tags = withTag(withTag(void 0, tag), options.tag);
7476
7573
  return {
7477
7574
  user: await runAs(l, self, options.user),
7478
7575
  tag,
7479
7576
  tags,
7480
- tagBase: tag === void 0 ? 0 : 1
7577
+ tagBase: tag === void 0 ? 0 : 1,
7578
+ session
7481
7579
  };
7482
7580
  }
7483
7581
  function processes(l, self) {
@@ -7558,10 +7656,12 @@ function facadeRegistry(send2, gone) {
7558
7656
  }
7559
7657
  };
7560
7658
  }
7561
- function hostRegistries(send2, gone) {
7659
+ function hostRegistries(send2, gone, port) {
7562
7660
  const cdps = cdpRegistry(send2, gone);
7563
7661
  const facades = facadeRegistry(send2, gone);
7662
+ const requests = requestBinding(send2, port);
7564
7663
  return {
7664
+ requests,
7565
7665
  answer(l, req) {
7566
7666
  if (req.op === "serve-cdp") return cdps.serve(l, req);
7567
7667
  if (req.op === "unserve-cdp") return cdps.drop(req.registration);
@@ -7569,6 +7669,7 @@ function hostRegistries(send2, gone) {
7569
7669
  return facades.drop(req.registration);
7570
7670
  },
7571
7671
  close() {
7672
+ requests.close();
7572
7673
  cdps.close();
7573
7674
  facades.close();
7574
7675
  }
@@ -7603,6 +7704,28 @@ var TRAITS_WATCH = -1;
7603
7704
  function traitsWatch(launcher, transport, send2) {
7604
7705
  return launcher.onTraits(() => send2({ traits: transport.traits }));
7605
7706
  }
7707
+ function requestBinding(send2, port) {
7708
+ const asker = embedderAsker({ postMessage: (m, t) => send2(m, t) });
7709
+ let unbind;
7710
+ port.addEventListener("message", (event) => {
7711
+ const reply = embedderReplyOf(event.data);
7712
+ if (reply) asker.answer(reply);
7713
+ });
7714
+ return {
7715
+ async accept(host, hello) {
7716
+ if (!hello.requests) return void 0;
7717
+ if (!host.attachRequests || host.session === void 0)
7718
+ return "EPERM: this port may not answer requests";
7719
+ const launcher = await host.launcher();
7720
+ unbind = launcher.embedder.register(
7721
+ host.session,
7722
+ asker.hookFor(host.session, hello.requests)
7723
+ );
7724
+ return void 0;
7725
+ },
7726
+ close: () => unbind?.()
7727
+ };
7728
+ }
7606
7729
  function serveClient(port, host) {
7607
7730
  let state = "new";
7608
7731
  let self;
@@ -7617,7 +7740,7 @@ function serveClient(port, host) {
7617
7740
  if (state !== "closed") port.postMessage(message3, transfer);
7618
7741
  };
7619
7742
  const reply = (id2, body, transfer) => send2({ id: id2, ...body }, transfer);
7620
- const hosts = hostRegistries(send2, ended.promise);
7743
+ const hosts = hostRegistries(send2, ended.promise, port);
7621
7744
  function detach(kill = false, bye = "the slicc-kernel detached this client") {
7622
7745
  if (state === "closed") return;
7623
7746
  send2({ bye });
@@ -7634,7 +7757,7 @@ function serveClient(port, host) {
7634
7757
  ended.resolve();
7635
7758
  }
7636
7759
  async function hello(message3) {
7637
- const refused3 = versionError(message3.protocol);
7760
+ const refused3 = versionError(message3.protocol) ?? await hosts.requests.accept(host, message3);
7638
7761
  if (refused3) {
7639
7762
  send2({ hello: { protocol: PROTOCOL, error: refused3 } });
7640
7763
  detach();
@@ -7659,7 +7782,7 @@ function serveClient(port, host) {
7659
7782
  async function terminal(req, l) {
7660
7783
  const session = await l.openTerminal(req.argv, {
7661
7784
  ...req.options,
7662
- ...await runTagged(l, self, req.options, host.tag),
7785
+ ...await runTagged(l, self, req.options, host.tag, host.session),
7663
7786
  onData: (bytes) => reply(req.id, { fd: 1, bytes: bytes.slice() })
7664
7787
  });
7665
7788
  if (state === "closed") {
@@ -7682,7 +7805,7 @@ function serveClient(port, host) {
7682
7805
  let leader;
7683
7806
  const result = await l.run(req.argv, {
7684
7807
  ...req.options,
7685
- ...await runTagged(l, self, req.options, host.tag),
7808
+ ...await runTagged(l, self, req.options, host.tag, host.session),
7686
7809
  collect: false,
7687
7810
  onStdout: out(1),
7688
7811
  onStderr: out(2),
@@ -7926,7 +8049,7 @@ function satisfies(version, range2) {
7926
8049
  }
7927
8050
 
7928
8051
  // src/version.ts
7929
- var KERNEL_VERSION = true ? "1.152.2" : "0.0.0-dev";
8052
+ var KERNEL_VERSION = true ? "1.153.0" : "0.0.0-dev";
7930
8053
  var OS_RELEASE = "6.0.0-slicc";
7931
8054
  function procVersion() {
7932
8055
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -8771,9 +8894,9 @@ var Crontabs = class {
8771
8894
  const json5 = JSON.stringify({ v: TAGS_VERSION, ...kept });
8772
8895
  if (json5 === await fs.readFile(TAGS_PATH).catch(() => "")) return;
8773
8896
  const owner = owners ? { uid: 0, gid: 0, mode: 384 } : void 0;
8774
- const fresh2 = `${TAGS_PATH}.${crypto.randomUUID()}.new`;
8775
- await fs.writeFile(fresh2, json5, owner);
8776
- await fs.rename(fresh2, TAGS_PATH);
8897
+ const fresh3 = `${TAGS_PATH}.${crypto.randomUUID()}.new`;
8898
+ await fs.writeFile(fresh3, json5, owner);
8899
+ await fs.rename(fresh3, TAGS_PATH);
8777
8900
  }
8778
8901
  async install(path, target, text, caller) {
8779
8902
  if (text.length > CRONTAB_MAX) return { error: "crontab: the table is too long" };
@@ -8787,11 +8910,11 @@ var Crontabs = class {
8787
8910
  await fs.mkdir(SPOOL_DIR, { recursive: true });
8788
8911
  if (owners) await fs.chown?.(SPOOL_DIR, { uid: 0, gid: 0, mode: 448, follow: false });
8789
8912
  }
8790
- const fresh2 = `${SPOOL_DIR}/.${target.name}.${crypto.randomUUID()}.new`;
8913
+ const fresh3 = `${SPOOL_DIR}/.${target.name}.${crypto.randomUUID()}.new`;
8791
8914
  const owner = owners ? { uid: target.uid, gid: target.gid, mode: 384 } : void 0;
8792
8915
  await this.retag(path, text, caller);
8793
- await fs.writeFile(fresh2, text, owner);
8794
- await fs.rename(fresh2, path);
8916
+ await fs.writeFile(fresh3, text, owner);
8917
+ await fs.rename(fresh3, path);
8795
8918
  return { text: "" };
8796
8919
  }
8797
8920
  };
@@ -9067,15 +9190,15 @@ var UnlinkedKeeper = class {
9067
9190
  const releases = [];
9068
9191
  const take = async () => {
9069
9192
  for (; ; ) {
9070
- const fresh2 = [...this.heldWithin(target).keys()].filter((path) => !paths.includes(path));
9071
- if (fresh2.length === 0) return;
9072
- fresh2.sort();
9073
- if (paths.some((path) => path > fresh2[0])) {
9193
+ const fresh3 = [...this.heldWithin(target).keys()].filter((path) => !paths.includes(path));
9194
+ if (fresh3.length === 0) return;
9195
+ fresh3.sort();
9196
+ if (paths.some((path) => path > fresh3[0])) {
9074
9197
  for (const release of releases.splice(0)) release();
9075
- fresh2.push(...paths.splice(0));
9076
- fresh2.sort();
9198
+ fresh3.push(...paths.splice(0));
9199
+ fresh3.sort();
9077
9200
  }
9078
- for (const path of fresh2) {
9201
+ for (const path of fresh3) {
9079
9202
  paths.push(path);
9080
9203
  releases.push(await this.gate.acquire(path));
9081
9204
  }
@@ -9410,10 +9533,10 @@ var CronService = class {
9410
9533
  const text = JSON.stringify(stamp);
9411
9534
  const old = await fs.readFile(STAMP_PATH).catch(() => void 0);
9412
9535
  if (old === text || old === void 0 && text === EMPTY_STAMP) return;
9413
- const fresh2 = `${STAMP_PATH}.new`;
9536
+ const fresh3 = `${STAMP_PATH}.new`;
9414
9537
  const owner = owners ? { uid: 0, gid: 0, mode: 384 } : void 0;
9415
- await fs.writeFile(fresh2, text, owner);
9416
- await fs.rename(fresh2, STAMP_PATH);
9538
+ await fs.writeFile(fresh3, text, owner);
9539
+ await fs.rename(fresh3, STAMP_PATH);
9417
9540
  }
9418
9541
  async load() {
9419
9542
  const { fs } = this.host;
@@ -10763,10 +10886,10 @@ var FreshModules = class {
10763
10886
  this.fresh.set(key, { umask: mask, pid, added: 0, setup: mask !== DEFAULT_UMASK });
10764
10887
  }
10765
10888
  forget(pid) {
10766
- for (const [path, fresh2] of this.fresh) if (fresh2.pid === pid) this.fresh.delete(path);
10889
+ for (const [path, fresh3] of this.fresh) if (fresh3.pid === pid) this.fresh.delete(path);
10767
10890
  }
10768
- drop(fresh2) {
10769
- for (const [key, note] of [...this.fresh]) if (note === fresh2) this.fresh.delete(key);
10891
+ drop(fresh3) {
10892
+ for (const [key, note] of [...this.fresh]) if (note === fresh3) this.fresh.delete(key);
10770
10893
  }
10771
10894
  moved(from, to) {
10772
10895
  const source = normalizePath(from);
@@ -10775,10 +10898,10 @@ var FreshModules = class {
10775
10898
  for (const key of [...this.fresh.keys()]) {
10776
10899
  if (key === target || within5(key, target)) this.fresh.delete(key);
10777
10900
  }
10778
- for (const [key, fresh2] of [...this.fresh]) {
10901
+ for (const [key, fresh3] of [...this.fresh]) {
10779
10902
  if (key !== source && !within5(key, source)) continue;
10780
10903
  this.fresh.delete(key);
10781
- this.fresh.set(target + key.slice(source.length), fresh2);
10904
+ this.fresh.set(target + key.slice(source.length), fresh3);
10782
10905
  }
10783
10906
  }
10784
10907
  wrap(fs) {
@@ -10788,15 +10911,15 @@ var FreshModules = class {
10788
10911
  };
10789
10912
  const decide = async (given, known) => {
10790
10913
  const path = normalizePath(given);
10791
- const fresh2 = this.fresh.get(path);
10792
- if (!fresh2) return;
10914
+ const fresh3 = this.fresh.get(path);
10915
+ if (!fresh3) return;
10793
10916
  const bytes = known && known.length >= MAGIC.length ? known : await head(path);
10794
- const want = isModule(bytes) ? 73 & ~fresh2.umask : 0;
10795
- fresh2.setup = false;
10796
- if (want === fresh2.added) return;
10917
+ const want = isModule(bytes) ? 73 & ~fresh3.umask : 0;
10918
+ fresh3.setup = false;
10919
+ if (want === fresh3.added) return;
10797
10920
  const mode = (await fs.stat(path)).mode & 4095;
10798
- await fs.chmod(path, mode & ~fresh2.added | want);
10799
- fresh2.added = want;
10921
+ await fs.chmod(path, mode & ~fresh3.added | want);
10922
+ fresh3.added = want;
10800
10923
  };
10801
10924
  const overrides = {
10802
10925
  writeFile: async (path, content, owner) => {
@@ -10809,9 +10932,9 @@ var FreshModules = class {
10809
10932
  }
10810
10933
  },
10811
10934
  chmod: async (path, mode) => {
10812
- const fresh2 = this.fresh.get(normalizePath(path));
10813
- if (fresh2?.setup && (mode & 4095) === (438 & ~fresh2.umask)) fresh2.setup = false;
10814
- else if (fresh2) this.drop(fresh2);
10935
+ const fresh3 = this.fresh.get(normalizePath(path));
10936
+ if (fresh3?.setup && (mode & 4095) === (438 & ~fresh3.umask)) fresh3.setup = false;
10937
+ else if (fresh3) this.drop(fresh3);
10815
10938
  await fs.chmod(path, mode);
10816
10939
  },
10817
10940
  rm: async (path, options) => {
@@ -10828,8 +10951,8 @@ var FreshModules = class {
10828
10951
  link: async (from, to, follow2) => {
10829
10952
  await fs.link(from, to, follow2);
10830
10953
  const source = follow2 && fs.realpath ? await fs.realpath(from, true).catch(() => from) : from;
10831
- const fresh2 = this.fresh.get(normalizePath(source));
10832
- if (fresh2) this.fresh.set(normalizePath(to), fresh2);
10954
+ const fresh3 = this.fresh.get(normalizePath(source));
10955
+ if (fresh3) this.fresh.set(normalizePath(to), fresh3);
10833
10956
  }
10834
10957
  };
10835
10958
  const ranged2 = rangedOps(fs);
@@ -11147,7 +11270,7 @@ var FsWatchers = class {
11147
11270
  };
11148
11271
  const content = (run2, types) => async (...args) => {
11149
11272
  const at = normalizePath(args[0]);
11150
- const fresh2 = this.covers(at) && !await fs.exists(at).catch(() => true);
11273
+ const fresh3 = this.covers(at) && !await fs.exists(at).catch(() => true);
11151
11274
  let done5 = false;
11152
11275
  try {
11153
11276
  await run2(...args);
@@ -11156,11 +11279,11 @@ var FsWatchers = class {
11156
11279
  const real = await realOf(at);
11157
11280
  const paths = real === at ? [at] : [at, real];
11158
11281
  this.report(paths);
11159
- if (done5) for (const type of types(fresh2, args)) this.emit(type, paths);
11282
+ if (done5) for (const type of types(fresh3, args)) this.emit(type, paths);
11160
11283
  }
11161
11284
  };
11162
- const written2 = (fresh2) => fresh2 ? ["rename", "change"] : ["change"];
11163
- const overwritten = (fresh2, args) => fresh2 || args[1].length === 0 ? written2(fresh2) : ["change", "change"];
11285
+ const written2 = (fresh3) => fresh3 ? ["rename", "change"] : ["change"];
11286
+ const overwritten = (fresh3, args) => fresh3 || args[1].length === 0 ? written2(fresh3) : ["change", "change"];
11164
11287
  const reshaping = (run2) => async (...args) => {
11165
11288
  this.reshaped();
11166
11289
  try {
@@ -11187,14 +11310,14 @@ var FsWatchers = class {
11187
11310
  if (created4.length > 0 || !await fs.exists(ancestor)) created4.push(ancestor);
11188
11311
  }
11189
11312
  }
11190
- const fresh2 = !options?.recursive || this.typed.size > 0 && !await fs.exists(path);
11313
+ const fresh3 = !options?.recursive || this.typed.size > 0 && !await fs.exists(path);
11191
11314
  let done5 = false;
11192
11315
  try {
11193
11316
  await fs.mkdir(path, options);
11194
11317
  done5 = true;
11195
11318
  } finally {
11196
11319
  this.report([...created4, path]);
11197
- if (done5) this.emit("rename", fresh2 ? [...created4, path] : created4);
11320
+ if (done5) this.emit("rename", fresh3 ? [...created4, path] : created4);
11198
11321
  }
11199
11322
  },
11200
11323
  rm: reshaping(after(fs.rm.bind(fs), (path) => [path], "rename")),
@@ -11650,6 +11773,403 @@ var ClipboardBroker = class {
11650
11773
  }
11651
11774
  };
11652
11775
 
11776
+ // src/kernel/embedder.ts
11777
+ var EMBEDDER_PATH = "/dev/embedder";
11778
+ var EMBEDDER_HEADER_MAX = 64 * 1024;
11779
+ var EMBEDDER_WINDOW = 1024 * 1024;
11780
+ var EMBEDDER_BUDGET = 256 * 1024 * 1024;
11781
+ var EMBEDDER_PER_PROCESS = 16;
11782
+ var EMBEDDER_PROGRESS_PER_SECOND = 4;
11783
+ var KIND = /^[a-z][a-z0-9-]*(\.[a-z0-9-]+)*$/;
11784
+ var NL = 10;
11785
+ var READY2 = { readable: true, writable: true, hangup: false };
11786
+ function lineOf2(err) {
11787
+ const text = err instanceof Error ? err.message : String(err);
11788
+ return (text.split("\n")[0] ?? "").slice(0, 200);
11789
+ }
11790
+ var ERRNOS = {
11791
+ NotAllowedError: "EACCES",
11792
+ AbortError: "EACCES",
11793
+ NotSupportedError: "ENOTSUP",
11794
+ NotFoundError: "ENOENT"
11795
+ };
11796
+ function embedderFailure(err) {
11797
+ if (err instanceof KernelError) return err;
11798
+ const name2 = err?.name;
11799
+ return new KernelError(ERRNOS[String(name2)] ?? "EIO", lineOf2(err));
11800
+ }
11801
+ function headerOf(bytes) {
11802
+ let parsed2;
11803
+ try {
11804
+ parsed2 = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
11805
+ } catch {
11806
+ throw new KernelError("EINVAL", "the request header is not JSON");
11807
+ }
11808
+ if (typeof parsed2 !== "object" || parsed2 === null || Array.isArray(parsed2))
11809
+ throw new KernelError("EINVAL", "the request header is not an object");
11810
+ const { kind, ...options } = parsed2;
11811
+ if (typeof kind !== "string" || kind.length > 64 || !KIND.test(kind))
11812
+ throw new KernelError("EINVAL", "the request has no valid kind");
11813
+ return { kind, options };
11814
+ }
11815
+ function joined3(a, b) {
11816
+ const out = new Uint8Array(a.length + b.length);
11817
+ out.set(a);
11818
+ out.set(b, a.length);
11819
+ return out;
11820
+ }
11821
+ function aborted(signal) {
11822
+ if (!signal) return void 0;
11823
+ return new Promise((_, reject) => {
11824
+ const stop = () => reject(new KernelError("EINTR"));
11825
+ if (signal.aborted) stop();
11826
+ else signal.addEventListener("abort", stop, { once: true });
11827
+ });
11828
+ }
11829
+ async function interruptible2(work, signal) {
11830
+ const stop = aborted(signal);
11831
+ if (!stop) return work;
11832
+ stop.catch(() => void 0);
11833
+ return Promise.race([work, stop]);
11834
+ }
11835
+ var Budget2 = class {
11836
+ used = 0;
11837
+ limit;
11838
+ constructor(limit) {
11839
+ this.limit = limit;
11840
+ }
11841
+ take(n) {
11842
+ if (this.used + n > this.limit) return false;
11843
+ this.used += n;
11844
+ return true;
11845
+ }
11846
+ give(n) {
11847
+ this.used = Math.max(0, this.used - n);
11848
+ }
11849
+ };
11850
+ var BodyPipe = class {
11851
+ queue = [];
11852
+ size = 0;
11853
+ ended = false;
11854
+ wake;
11855
+ room;
11856
+ stream;
11857
+ constructor() {
11858
+ this.stream = new ReadableStream(
11859
+ {
11860
+ pull: async (controller) => {
11861
+ while (this.queue.length === 0 && !this.ended) {
11862
+ await new Promise((resolve2) => {
11863
+ this.wake = resolve2;
11864
+ });
11865
+ }
11866
+ const next3 = this.queue.shift();
11867
+ if (next3) {
11868
+ this.size -= next3.length;
11869
+ this.room?.();
11870
+ controller.enqueue(next3);
11871
+ } else controller.close();
11872
+ }
11873
+ },
11874
+ { highWaterMark: 0 }
11875
+ );
11876
+ }
11877
+ get full() {
11878
+ return this.size >= EMBEDDER_WINDOW;
11879
+ }
11880
+ push(bytes) {
11881
+ if (bytes.length === 0) return;
11882
+ this.queue.push(bytes.slice());
11883
+ this.size += bytes.length;
11884
+ this.wake?.();
11885
+ }
11886
+ waitRoom() {
11887
+ return new Promise((resolve2) => {
11888
+ this.room = resolve2;
11889
+ });
11890
+ }
11891
+ end() {
11892
+ this.ended = true;
11893
+ this.wake?.();
11894
+ this.room?.();
11895
+ }
11896
+ };
11897
+ function replyStream(reply) {
11898
+ if (reply instanceof ReadableStream) return reply;
11899
+ const bytes = typeof reply === "string" ? new TextEncoder().encode(reply) : reply instanceof Uint8Array ? reply : new Uint8Array(0);
11900
+ return new ReadableStream({
11901
+ start(controller) {
11902
+ if (bytes.length > 0) controller.enqueue(bytes);
11903
+ controller.close();
11904
+ }
11905
+ });
11906
+ }
11907
+ var Throttle = class {
11908
+ window = 0;
11909
+ count = 0;
11910
+ allow(now) {
11911
+ if (now - this.window >= 1e3) {
11912
+ this.window = now;
11913
+ this.count = 0;
11914
+ }
11915
+ this.count += 1;
11916
+ return this.count <= EMBEDDER_PROGRESS_PER_SECOND;
11917
+ }
11918
+ };
11919
+ var EmbedderChannel = class {
11920
+ access;
11921
+ owner;
11922
+ caller;
11923
+ broker;
11924
+ header = new Uint8Array(0);
11925
+ body;
11926
+ reply;
11927
+ replied;
11928
+ rest;
11929
+ done = false;
11930
+ failure;
11931
+ reported = false;
11932
+ abort = new AbortController();
11933
+ closed = false;
11934
+ throttle = new Throttle();
11935
+ constructor(broker, access2, caller) {
11936
+ this.broker = broker;
11937
+ this.access = access2;
11938
+ this.caller = caller;
11939
+ this.owner = caller.pid;
11940
+ }
11941
+ get dispatched() {
11942
+ return this.body !== void 0;
11943
+ }
11944
+ fail(err) {
11945
+ this.failure ??= embedderFailure(err);
11946
+ return this.failure;
11947
+ }
11948
+ report(err) {
11949
+ this.reported = true;
11950
+ return this.fail(err);
11951
+ }
11952
+ async dispatch(bytes) {
11953
+ const { kind, options } = headerOf(bytes);
11954
+ const stamp = await this.broker.admit(kind, this.caller);
11955
+ const hook = this.broker.hookFor(this.caller.client);
11956
+ const body = new BodyPipe();
11957
+ this.body = body;
11958
+ const caller = this.caller;
11959
+ const progress = (line) => {
11960
+ if (this.throttle.allow(Date.now())) caller.progress(lineOf2(line));
11961
+ };
11962
+ const asked = Promise.resolve().then(
11963
+ () => hook.handle({
11964
+ kind,
11965
+ options,
11966
+ body: body.stream,
11967
+ stamp,
11968
+ signal: this.abort.signal,
11969
+ progress
11970
+ })
11971
+ ).then((reply) => replyStream(reply).getReader());
11972
+ this.replied = asked.catch((err) => {
11973
+ body.end();
11974
+ throw this.fail(err);
11975
+ });
11976
+ this.replied.catch(() => void 0);
11977
+ }
11978
+ async write(bytes, signal) {
11979
+ if (this.access === "read") throw new KernelError("EBADF");
11980
+ if (this.failure) throw this.report(this.failure);
11981
+ if (this.reply || this.done) throw new KernelError("EBADF");
11982
+ if (this.body) return this.writeBody(bytes, signal);
11983
+ const at = bytes.indexOf(NL);
11984
+ const head = at < 0 ? bytes : bytes.subarray(0, at);
11985
+ if (this.header.length + head.length > EMBEDDER_HEADER_MAX)
11986
+ throw this.report(new KernelError("EINVAL", "the request header is too long"));
11987
+ this.header = joined3(this.header, head);
11988
+ if (at < 0) return bytes.length;
11989
+ try {
11990
+ await this.dispatch(this.header);
11991
+ } catch (err) {
11992
+ throw this.report(err);
11993
+ }
11994
+ this.body?.push(bytes.subarray(at + 1));
11995
+ return bytes.length;
11996
+ }
11997
+ async writeBody(bytes, signal) {
11998
+ const body = this.body;
11999
+ while (body.full && !this.failure) {
12000
+ await interruptible2(body.waitRoom(), signal).catch((err) => {
12001
+ this.cancel("interrupted");
12002
+ throw err;
12003
+ });
12004
+ }
12005
+ if (this.failure) throw this.report(this.failure);
12006
+ body.push(bytes);
12007
+ return bytes.length;
12008
+ }
12009
+ async readerOf(signal) {
12010
+ if (this.reply) return this.reply;
12011
+ if (!this.body) {
12012
+ try {
12013
+ await this.dispatch(this.header);
12014
+ } catch (err) {
12015
+ throw this.fail(err);
12016
+ }
12017
+ }
12018
+ this.body?.end();
12019
+ this.reply = await interruptible2(
12020
+ this.replied,
12021
+ signal
12022
+ );
12023
+ return this.reply;
12024
+ }
12025
+ async read(max, signal) {
12026
+ if (this.access === "write") throw new KernelError("EBADF");
12027
+ if (this.failure) throw this.report(this.failure);
12028
+ if (this.done || max <= 0) return new Uint8Array(0);
12029
+ try {
12030
+ return await this.next(max, signal);
12031
+ } catch (err) {
12032
+ if (err instanceof KernelError && err.code === "EINTR") {
12033
+ this.cancel("interrupted");
12034
+ throw err;
12035
+ }
12036
+ throw this.report(err);
12037
+ }
12038
+ }
12039
+ async next(max, signal) {
12040
+ const reader = await this.readerOf(signal);
12041
+ let chunk2 = this.rest;
12042
+ this.rest = void 0;
12043
+ if (!chunk2) {
12044
+ const got = await interruptible2(reader.read(), signal);
12045
+ if (got.done) {
12046
+ this.done = true;
12047
+ return new Uint8Array(0);
12048
+ }
12049
+ chunk2 = got.value;
12050
+ }
12051
+ if (chunk2.length > max) this.rest = chunk2.subarray(max);
12052
+ return chunk2.subarray(0, max);
12053
+ }
12054
+ poll() {
12055
+ return this.body?.full ? { ...READY2, writable: false } : READY2;
12056
+ }
12057
+ cancel(reason) {
12058
+ if (this.abort.signal.aborted) return;
12059
+ this.abort.abort(new DOMException(reason, "AbortError"));
12060
+ this.body?.end();
12061
+ void this.reply?.cancel(reason).catch(() => void 0);
12062
+ }
12063
+ async close() {
12064
+ if (this.closed) return;
12065
+ const signal = this.caller.closing();
12066
+ this.closed = true;
12067
+ try {
12068
+ if (this.access === "read") return;
12069
+ if (this.failure) {
12070
+ if (this.reported) return;
12071
+ throw this.report(this.failure);
12072
+ }
12073
+ if (this.access === "both" && this.dispatched) return;
12074
+ if (!this.dispatched && this.header.length === 0) return;
12075
+ await this.drain(signal);
12076
+ } finally {
12077
+ if (!this.done) this.cancel("closed");
12078
+ this.broker.release(this);
12079
+ }
12080
+ }
12081
+ async drain(signal) {
12082
+ try {
12083
+ const reader = await this.readerOf(signal);
12084
+ for (; ; ) {
12085
+ const got = await interruptible2(reader.read(), signal);
12086
+ if (got.done) break;
12087
+ }
12088
+ this.done = true;
12089
+ } catch (err) {
12090
+ if (err instanceof KernelError && err.code === "EINTR") this.cancel("interrupted");
12091
+ throw this.fail(err);
12092
+ }
12093
+ }
12094
+ };
12095
+ var EmbedderBroker = class {
12096
+ users;
12097
+ lookup;
12098
+ hooks = /* @__PURE__ */ new Map();
12099
+ budget;
12100
+ open = /* @__PURE__ */ new Map();
12101
+ tagsOf;
12102
+ constructor(users, lookup2, tagsOf = () => void 0, budget = EMBEDDER_BUDGET) {
12103
+ this.users = users;
12104
+ this.tagsOf = tagsOf;
12105
+ this.lookup = lookup2;
12106
+ this.budget = new Budget2(budget);
12107
+ }
12108
+ register(client, hook) {
12109
+ if (!hook) return () => void 0;
12110
+ this.hooks.set(client, hook);
12111
+ return () => {
12112
+ if (this.hooks.get(client) === hook) this.hooks.delete(client);
12113
+ };
12114
+ }
12115
+ hookFor(client) {
12116
+ return this.hooks.get(client);
12117
+ }
12118
+ async allowed(policy, caller) {
12119
+ if (caller.euid === 0 || caller.euid === caller.sessionUid) return true;
12120
+ if (policy.group !== void 0) {
12121
+ const gid = await this.lookup.groupId(policy.group);
12122
+ if (gid !== void 0 && (caller.egid === gid || caller.groups.includes(gid))) return true;
12123
+ }
12124
+ for (const user of policy.users ?? []) {
12125
+ const uid = typeof user === "number" ? user : await this.lookup.userId(user);
12126
+ if (uid === caller.euid) return true;
12127
+ }
12128
+ return false;
12129
+ }
12130
+ async admit(kind, caller) {
12131
+ const hook = this.hooks.get(caller.client);
12132
+ if (!hook) throw new KernelError("ENOTSUP", "no page answers requests for this session");
12133
+ const policy = Object.hasOwn(hook.kinds, kind) ? hook.kinds[kind] : void 0;
12134
+ if (!policy) throw new KernelError("ENOTSUP", `this embedder does not answer ${kind}`);
12135
+ if (this.users !== "off" && !await this.allowed(policy, caller))
12136
+ throw new KernelError("EACCES", `${kind} is not allowed for this user`);
12137
+ const tags = caller.tags;
12138
+ return {
12139
+ pid: caller.shownPid,
12140
+ uid: caller.euid,
12141
+ gid: caller.egid,
12142
+ groups: [...caller.groups],
12143
+ users: this.users,
12144
+ kind,
12145
+ ...tags?.length ? { tag: tags[0], tags: [...tags] } : {}
12146
+ };
12147
+ }
12148
+ channel(access2, caller) {
12149
+ const mine = this.open.get(caller.pid) ?? /* @__PURE__ */ new Set();
12150
+ if (mine.size >= EMBEDDER_PER_PROCESS)
12151
+ throw new KernelError("EMFILE", `at most ${EMBEDDER_PER_PROCESS} requests per process`);
12152
+ if (!this.budget.take(2 * EMBEDDER_WINDOW))
12153
+ throw new KernelError("EAGAIN", "the embedder request budget is spent");
12154
+ const channel = new EmbedderChannel(this, access2, caller);
12155
+ mine.add(channel);
12156
+ this.open.set(caller.pid, mine);
12157
+ return new OpenFile({
12158
+ read: channel.read.bind(channel),
12159
+ write: (bytes, signal) => channel.write(bytes, signal),
12160
+ poll: () => channel.poll(),
12161
+ close: () => channel.close(),
12162
+ embedder: channel
12163
+ });
12164
+ }
12165
+ release(channel) {
12166
+ const mine = this.open.get(channel.owner);
12167
+ if (!mine?.delete(channel)) return;
12168
+ if (mine.size === 0) this.open.delete(channel.owner);
12169
+ this.budget.give(2 * EMBEDDER_WINDOW);
12170
+ }
12171
+ };
12172
+
11653
12173
  // src/kernel/exec-ticket.ts
11654
12174
  var TICKET_ENV = "SLICC_SEALED_TICKET";
11655
12175
  var TICKET_TTL_MS = 3e4;
@@ -12201,9 +12721,9 @@ var HeldPaths = class extends Map {
12201
12721
  return false;
12202
12722
  }
12203
12723
  keep(path, file) {
12204
- const fresh2 = this.handlesAt(path).filter((id2) => !this.keptHandles.has(id2));
12205
- for (const id2 of fresh2) this.keptHandles.set(id2, file);
12206
- this.uses.set(file, Math.max(fresh2.length, 1));
12724
+ const fresh3 = this.handlesAt(path).filter((id2) => !this.keptHandles.has(id2));
12725
+ for (const id2 of fresh3) this.keptHandles.set(id2, file);
12726
+ this.uses.set(file, Math.max(fresh3.length, 1));
12207
12727
  this.unlinked.set(path, file);
12208
12728
  }
12209
12729
  unkeep(path, file) {
@@ -13399,8 +13919,8 @@ var MountFs = class _MountFs {
13399
13919
  }
13400
13920
  this.writable(mount, real);
13401
13921
  const { uid, gid, mode } = change;
13402
- const fresh2 = change.kind === "new" && { uid, gid, mode };
13403
- const set3 = fresh2 ? await this.driverOwner(found, real, fresh2, false) : change;
13922
+ const fresh3 = change.kind === "new" && { uid, gid, mode };
13923
+ const set3 = fresh3 ? await this.driverOwner(found, real, fresh3, false) : change;
13404
13924
  this.table.forget(mount, real);
13405
13925
  const attrs = Object.entries({ uid: set3.uid, gid: set3.gid, mode: set3.mode });
13406
13926
  await mount.conn.call({
@@ -13672,10 +14192,10 @@ async function runMkdir(entry, path, req) {
13672
14192
  const plain3 = plainDirMode(entry, req.mode);
13673
14193
  const parentsMode = !req.exclusive ? plainParentsMode(entry) : void 0;
13674
14194
  const fixParents = parentsMode !== void 0 ? await missingParents(fs, path, parentsMode) : void 0;
13675
- const fresh2 = plain3 !== void 0 && !await fs.exists(path);
14195
+ const fresh3 = plain3 !== void 0 && !await fs.exists(path);
13676
14196
  await (req.exclusive ? mkdirExclusive(entry, path, req.mode) : fs.mkdir(path, mkdirOptions(entry, req.mode)));
13677
14197
  await fixParents?.();
13678
- if (fresh2) await fs.chmod(path, plain3);
14198
+ if (fresh3) await fs.chmod(path, plain3);
13679
14199
  return done2;
13680
14200
  }
13681
14201
  async function shownAs(entry, path, st) {
@@ -14679,9 +15199,9 @@ var Pages = class {
14679
15199
  const page = (start2 + at) / PAGE;
14680
15200
  if (this.pages.has(page)) continue;
14681
15201
  const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
14682
- const fresh2 = this.take(part.length);
14683
- fresh2.set(part);
14684
- this.pages.set(page, fresh2);
15202
+ const fresh3 = this.take(part.length);
15203
+ fresh3.set(part);
15204
+ this.pages.set(page, fresh3);
14685
15205
  }
14686
15206
  }
14687
15207
  read(at, length2) {
@@ -16987,18 +17507,18 @@ var TlsEngine = class {
16987
17507
  chainFlags(store, chain2, hostname) {
16988
17508
  const verify = this.m._tls_chain_verify;
16989
17509
  if (!verify || chain2.length === 0) return void 0;
16990
- const joined4 = new Uint8Array(chain2.reduce((n, c) => n + c.length, 0));
17510
+ const joined5 = new Uint8Array(chain2.reduce((n, c) => n + c.length, 0));
16991
17511
  let at = 0;
16992
17512
  for (const der2 of chain2) {
16993
- joined4.set(der2, at);
17513
+ joined5.set(der2, at);
16994
17514
  at += der2.length;
16995
17515
  }
16996
17516
  const name2 = this.put(new TextEncoder().encode(hostname));
16997
17517
  try {
16998
- return this.withBytes(joined4, (p) => {
17518
+ return this.withBytes(joined5, (p) => {
16999
17519
  const out = this.scratch;
17000
17520
  this.m.HEAPU8.fill(255, out, out + 4);
17001
- const ret = verify(store.ptr, p, joined4.length, name2, out);
17521
+ const ret = verify(store.ptr, p, joined5.length, name2, out);
17002
17522
  const flags2 = new DataView(this.m.HEAPU8.buffer, out, 4).getUint32(0, true);
17003
17523
  if (ret === 0) return flags2 === 0 ? 0 : void 0;
17004
17524
  return ret === 1 && flags2 !== 0 ? flags2 : void 0;
@@ -19848,6 +20368,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
19848
20368
  ...file.tty ? { tty: file.tty } : {},
19849
20369
  identity: file.identity ?? file,
19850
20370
  ...file.clipboard ? { clipboard: file.clipboard } : {},
20371
+ ...file.embedder ? { embedder: file.embedder } : {},
19851
20372
  ...file.write ? {
19852
20373
  write: async (bytes, signal) => {
19853
20374
  await turn(signal);
@@ -19904,7 +20425,8 @@ function scrubStdio(fds, scrubber) {
19904
20425
  const stdio = stdioFiles(fds);
19905
20426
  for (const fd of fds.numbers()) {
19906
20427
  const original = fds.get(fd);
19907
- if (!stdio.has(original) && !original.file.tty && !original.file.clipboard) continue;
20428
+ const named = original.file.tty || original.file.clipboard || original.file.embedder;
20429
+ if (!stdio.has(original) && !named) continue;
19908
20430
  const known = wrapped.get(original);
19909
20431
  const flags2 = fds.statusFlags(fd);
19910
20432
  const wrapper = known ? known.retain() : scrubbed(original.retain(), scrubber);
@@ -19917,9 +20439,9 @@ function scrubStdio(fds, scrubber) {
19917
20439
  // src/kernel/net/sealed-scrubs.ts
19918
20440
  function addTo(tree2, host, secrets) {
19919
20441
  const entry = tree2.get(host) ?? { secrets: [], version: 0, scrubber: void 0 };
19920
- const fresh2 = secrets.filter((s) => !entry.secrets.some((e) => e.value === s.value));
19921
- if (fresh2.length === 0) return;
19922
- entry.secrets = [...entry.secrets, ...fresh2];
20442
+ const fresh3 = secrets.filter((s) => !entry.secrets.some((e) => e.value === s.value));
20443
+ if (fresh3.length === 0) return;
20444
+ entry.secrets = [...entry.secrets, ...fresh3];
19923
20445
  entry.version += 1;
19924
20446
  entry.scrubber = void 0;
19925
20447
  tree2.set(host, entry);
@@ -20133,13 +20655,13 @@ function serialized(reply) {
20133
20655
  const text = JSON.stringify(reply);
20134
20656
  plain3 = text === void 0 ? void 0 : JSON.parse(text);
20135
20657
  } catch (err) {
20136
- return JSON.stringify(errorReply("EIO", lineOf2(err)));
20658
+ return JSON.stringify(errorReply("EIO", lineOf3(err)));
20137
20659
  }
20138
20660
  if (!isRecord(plain3)) return JSON.stringify(errorReply("EIO", "the vault gave no reply"));
20139
20661
  const { v: _, ...rest2 } = plain3;
20140
20662
  return JSON.stringify({ v: 1, ...rest2 });
20141
20663
  }
20142
- function lineOf2(err) {
20664
+ function lineOf3(err) {
20143
20665
  const text = err instanceof Error ? err.message : String(err);
20144
20666
  return (text.split("\n")[0] ?? "").slice(0, 200);
20145
20667
  }
@@ -20190,7 +20712,7 @@ var SealedChannel = class {
20190
20712
  if (signal.aborted) return void 0;
20191
20713
  const asked = Promise.resolve().then(() => hook(req, { signal })).then(
20192
20714
  (reply2) => isRecord(reply2) ? reply2 : errorReply("EIO", "the vault gave no reply"),
20193
- (err) => errorReply("EIO", lineOf2(err))
20715
+ (err) => errorReply("EIO", lineOf3(err))
20194
20716
  );
20195
20717
  const reply = await Promise.race([asked, stopped]);
20196
20718
  if (!signal.aborted) return reply;
@@ -21307,13 +21829,13 @@ var PgServer = class {
21307
21829
  at += whole;
21308
21830
  }
21309
21831
  this.buffer = data.slice(at);
21310
- const joined4 = new Uint8Array(out.reduce((n, b) => n + b.length, 0));
21832
+ const joined5 = new Uint8Array(out.reduce((n, b) => n + b.length, 0));
21311
21833
  let to = 0;
21312
21834
  for (const b of out) {
21313
- joined4.set(b, to);
21835
+ joined5.set(b, to);
21314
21836
  to += b.length;
21315
21837
  }
21316
- return joined4;
21838
+ return joined5;
21317
21839
  }
21318
21840
  };
21319
21841
  function frame3(type, body) {
@@ -21655,12 +22177,12 @@ var SessionWriter = class {
21655
22177
  if (this.failed !== void 0) throw this.failed;
21656
22178
  if (this.mode === "pass") return this.conn.write(bytes);
21657
22179
  clearTimeout(this.timer);
21658
- const joined4 = new Uint8Array(this.buffer.length + bytes.length);
21659
- joined4.set(this.buffer);
21660
- joined4.set(bytes, this.buffer.length);
22180
+ const joined5 = new Uint8Array(this.buffer.length + bytes.length);
22181
+ joined5.set(this.buffer);
22182
+ joined5.set(bytes, this.buffer.length);
21661
22183
  this.buffer.fill(0);
21662
- this.buffer = joined4;
21663
- if (this.mode === "sniff") this.mode = modeOf(latin12(joined4.subarray(0, 8))) ?? "sniff";
22184
+ this.buffer = joined5;
22185
+ if (this.mode === "sniff") this.mode = modeOf(latin12(joined5.subarray(0, 8))) ?? "sniff";
21664
22186
  if (this.mode === "pg" && !this.sawPg) {
21665
22187
  this.sawPg = true;
21666
22188
  if (!this.s.verified) void this.proof("pg");
@@ -21808,7 +22330,7 @@ var ScrubReader = class {
21808
22330
  const before = this.current();
21809
22331
  const out = this.through(got);
21810
22332
  if (before.length === 0 && out !== void 0) return out;
21811
- if (before.length > 0) return joined3(before, out);
22333
+ if (before.length > 0) return joined4(before, out);
21812
22334
  }
21813
22335
  }
21814
22336
  through(got) {
@@ -21824,7 +22346,7 @@ var ScrubReader = class {
21824
22346
  return out.length > 0 ? out : void 0;
21825
22347
  }
21826
22348
  };
21827
- function joined3(a, b) {
22349
+ function joined4(a, b) {
21828
22350
  if (!b) return a;
21829
22351
  const out = new Uint8Array(a.length + b.length);
21830
22352
  out.set(a);
@@ -22732,7 +23254,7 @@ async function connect(req, proc) {
22732
23254
  dialing?.catch(() => void 0);
22733
23255
  throw new KernelError("EINPROGRESS");
22734
23256
  }
22735
- if (dialing !== void 0) await interruptible2(dialing, proc.blocking());
23257
+ if (dialing !== void 0) await interruptible3(dialing, proc.blocking());
22736
23258
  return ok3();
22737
23259
  }
22738
23260
  async function bindUnixDgram(proc, socket, addr) {
@@ -22797,7 +23319,7 @@ async function unixDatagram(req, dgram, proc) {
22797
23319
  function domainOf(socket) {
22798
23320
  return socket instanceof UnixDgram ? "unix" : "inet";
22799
23321
  }
22800
- function interruptible2(dialing, signal) {
23322
+ function interruptible3(dialing, signal) {
22801
23323
  return new Promise((resolve2, reject) => {
22802
23324
  const abort = () => reject(new KernelError("EINTR"));
22803
23325
  signal.addEventListener("abort", abort, { once: true });
@@ -23801,7 +24323,7 @@ var ProcessWatches = class {
23801
24323
  };
23802
24324
 
23803
24325
  // src/kernel/process.ts
23804
- function aborted(signal) {
24326
+ function aborted2(signal) {
23805
24327
  return new Promise((_, reject) => {
23806
24328
  signal.addEventListener("abort", () => reject(new KernelError("EINTR")), { once: true });
23807
24329
  });
@@ -23891,6 +24413,7 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
23891
24413
  "fd-open-sealed",
23892
24414
  "fd-open-clipboard",
23893
24415
  "fd-clipboard-abort",
24416
+ "fd-open-embedder",
23894
24417
  "tty-get",
23895
24418
  "tty-set",
23896
24419
  "tty-winsz",
@@ -24444,6 +24967,7 @@ var WasmProcess = class {
24444
24967
  if (!read) throw new KernelError(req.peek && file.read ? "EOPNOTSUPP" : "EBADF");
24445
24968
  if (file.tty) this.checkForeground(file.tty);
24446
24969
  if (file.clipboard) return this.readClipboard(file.clipboard, req, cancelled);
24970
+ if (file.embedder) return this.readEmbedder(file, req, cancelled);
24447
24971
  if (file instanceof EventFd && req.max < EventFd.SIZE) throw new KernelError("EINVAL");
24448
24972
  if (req.max <= 0) return new Uint8Array(0);
24449
24973
  if (file instanceof KernelSocket && file.unconnected) throw new KernelError("ENOTCONN");
@@ -24464,6 +24988,81 @@ var WasmProcess = class {
24464
24988
  throw err;
24465
24989
  }
24466
24990
  }
24991
+ async readEmbedder(file, req, cancelled) {
24992
+ const channel = file.embedder;
24993
+ if (req.max <= 0) return new Uint8Array(0);
24994
+ channel.caller = await this.embedderCaller();
24995
+ try {
24996
+ return await channel.read(Math.min(req.max, MAX_READ), this.blockingSignal(cancelled));
24997
+ } catch (err) {
24998
+ await this.deviceComplaint(EMBEDDER_PATH, err);
24999
+ throw err;
25000
+ }
25001
+ }
25002
+ async writeEmbedder(file, body, nonblock, cancelled) {
25003
+ const channel = file.embedder;
25004
+ channel.caller = await this.embedderCaller();
25005
+ const write = file.write;
25006
+ if (!pollFile(file).writable && nonblock) throw new KernelError("EAGAIN");
25007
+ try {
25008
+ return await write.call(file, body, this.blockingSignal(cancelled));
25009
+ } catch (err) {
25010
+ await this.deviceComplaint(EMBEDDER_PATH, err);
25011
+ throw err;
25012
+ }
25013
+ }
25014
+ async deviceComplaint(path, err) {
25015
+ const name2 = this.options.clipboardName;
25016
+ if (name2 === void 0 || !(err instanceof KernelError) || err.detail === void 0) return;
25017
+ if (err.code === "EINTR") return;
25018
+ await this.complainLine(`${name2}: ${path}: ${clipboardLine(err.detail)}`);
25019
+ }
25020
+ async complainLine(line) {
25021
+ const stderr = this.fds.has(2) ? this.fds.get(2).file : void 0;
25022
+ if (!stderr?.write || stderr.clipboard || stderr.embedder || stderr.sealed) return;
25023
+ const timeout = new Promise((r) => setTimeout(() => r(0), 1e3));
25024
+ await Promise.race([
25025
+ stderr.write(new TextEncoder().encode(`${line}
25026
+ `), AbortSignal.timeout(1e3)).catch(() => 0),
25027
+ timeout
25028
+ ]);
25029
+ }
25030
+ async embedderCaller() {
25031
+ const shown = await this.options.identity?.();
25032
+ const session = this.options.embedderSession?.() ?? { client: 0, uid: 0 };
25033
+ const name2 = this.options.clipboardName ?? this.options.embedderName ?? "program";
25034
+ return {
25035
+ pid: this.pid,
25036
+ shownPid: shown?.pid ?? this.pid,
25037
+ euid: this.cred.euid,
25038
+ egid: this.cred.egid,
25039
+ groups: [...this.cred.groups],
25040
+ sessionUid: session.uid,
25041
+ client: session.client,
25042
+ tags: this.options.embedder?.tagsOf(this.pid),
25043
+ progress: (line) => void this.complainLine(`${name2}: ${line}`),
25044
+ closing: () => AbortSignal.any([this.interrupt.signal, this.gone.signal])
25045
+ };
25046
+ }
25047
+ async closeFd(fd) {
25048
+ this.options.locks?.closed(this.pid, fd);
25049
+ await this.dropRecordLocks(fd);
25050
+ const closing2 = this.fds.get(fd).file;
25051
+ const channel = closing2.clipboard;
25052
+ if (channel) channel.stamp = await this.clipboardStamp();
25053
+ if (closing2.embedder) closing2.embedder.caller = await this.embedderCaller();
25054
+ await Promise.resolve(this.fds.close(fd)).catch(async (err) => {
25055
+ if (channel) await this.clipboardComplaint(err);
25056
+ if (closing2.embedder) await this.deviceComplaint(EMBEDDER_PATH, err);
25057
+ throw err;
25058
+ });
25059
+ }
25060
+ async openEmbedder(flags2) {
25061
+ const broker = this.options.embedder;
25062
+ if (!broker) throw new KernelError("ENOENT");
25063
+ const file = broker.channel(openAccess(flags2), await this.embedderCaller());
25064
+ return this.fds.install(this.options.outputFilter?.(file) ?? file, 3);
25065
+ }
24467
25066
  async complain(line) {
24468
25067
  const stderr = this.fds.has(2) ? this.fds.get(2).file : void 0;
24469
25068
  if (!stderr?.write || stderr.sealed || stderr.clipboard) return false;
@@ -24494,6 +25093,7 @@ var WasmProcess = class {
24494
25093
  const file = this.fds.get(fd).file;
24495
25094
  if (file.sealed) return file.sealed.send(body, await this.sealedCaller(), this.gone.signal);
24496
25095
  if (!file.write) throw new KernelError("EBADF");
25096
+ if (file.embedder) return this.writeEmbedder(file, body, nonblock, cancelled);
24497
25097
  if (!pollFile(file).writable) {
24498
25098
  if (nonblock) throw new KernelError("EAGAIN");
24499
25099
  return file.write(body, this.blockingSignal(cancelled));
@@ -24541,11 +25141,11 @@ var WasmProcess = class {
24541
25141
  } else if (req.create) {
24542
25142
  await nodes.onEntry(path, async () => {
24543
25143
  const marks = this.options.owners || plain3 !== void 0;
24544
- const fresh2 = marks && !await present3(fs, path, true);
25144
+ const fresh3 = marks && !await present3(fs, path, true);
24545
25145
  this.fds.installAt(fd, this.openVfsFile(open2, await nodes.identify(path), fs));
24546
25146
  await this.fds.get(fd).file.stat?.();
24547
- if (fresh2 && plain3 !== void 0) await fs.chmod?.(path, plain3);
24548
- else if (fresh2) {
25147
+ if (fresh3 && plain3 !== void 0) await fs.chmod?.(path, plain3);
25148
+ else if (fresh3) {
24549
25149
  await fs.chown?.(path, { ...this.newOwner(req.mode), kind: "new" });
24550
25150
  }
24551
25151
  });
@@ -24763,17 +25363,9 @@ var WasmProcess = class {
24763
25363
  kind: "json",
24764
25364
  json: await this.write(req.fd, req.body, req.nonblock, cancelled)
24765
25365
  };
24766
- case "fd-close": {
24767
- this.options.locks?.closed(this.pid, req.fd);
24768
- await this.dropRecordLocks(req.fd);
24769
- const channel = this.fds.get(req.fd).file.clipboard;
24770
- if (channel) channel.stamp = await this.clipboardStamp();
24771
- await Promise.resolve(this.fds.close(req.fd)).catch(async (err) => {
24772
- if (channel) await this.clipboardComplaint(err);
24773
- throw err;
24774
- });
25366
+ case "fd-close":
25367
+ await this.closeFd(req.fd);
24775
25368
  return { ok: true, kind: "void" };
24776
- }
24777
25369
  case "fd-pipe": {
24778
25370
  const pipe = openPipe();
24779
25371
  const read = this.fds.install(pipe.read, 3);
@@ -24833,6 +25425,8 @@ var WasmProcess = class {
24833
25425
  return { ok: true, kind: "json", json: this.openSealed(req.flags) };
24834
25426
  case "fd-open-clipboard":
24835
25427
  return { ok: true, kind: "json", json: await this.openClipboard(req.flags) };
25428
+ case "fd-open-embedder":
25429
+ return { ok: true, kind: "json", json: await this.openEmbedder(req.flags) };
24836
25430
  case "fd-clipboard-abort": {
24837
25431
  const file = this.fds.get(req.fd);
24838
25432
  file.file.clipboard?.abort();
@@ -25327,7 +25921,7 @@ var WasmProcess = class {
25327
25921
  else {
25328
25922
  const { source, target, type, flags: flags2, data } = req;
25329
25923
  const signal = this.blockingSignal(cancelled);
25330
- await Promise.race([mount({ source, target, type, flags: flags2, data }, signal), aborted(signal)]);
25924
+ await Promise.race([mount({ source, target, type, flags: flags2, data }, signal), aborted2(signal)]);
25331
25925
  }
25332
25926
  } catch (err) {
25333
25927
  const code = err?.code;
@@ -25337,7 +25931,7 @@ var WasmProcess = class {
25337
25931
  return { ok: true, kind: "void" };
25338
25932
  }
25339
25933
  pause(cancelled) {
25340
- return aborted(this.blockingSignal(cancelled));
25934
+ return aborted2(this.blockingSignal(cancelled));
25341
25935
  }
25342
25936
  alarm;
25343
25937
  dlLog = [];
@@ -25887,6 +26481,15 @@ function linksOf(opts) {
25887
26481
  function clipboardName(opts) {
25888
26482
  return opts.program.abi === "js" ? {} : { clipboardName: opts.argv0 };
25889
26483
  }
26484
+ function embedderOptions(opts) {
26485
+ if (!opts.embedder) return {};
26486
+ return {
26487
+ embedder: opts.embedder.broker,
26488
+ embedderSession: opts.embedder.session,
26489
+ embedderName: opts.argv0,
26490
+ ...clipboardName(opts)
26491
+ };
26492
+ }
25890
26493
  function processOptions(opts, header4, hooks, created4) {
25891
26494
  return {
25892
26495
  ...inherited(opts),
@@ -25894,6 +26497,7 @@ function processOptions(opts, header4, hooks, created4) {
25894
26497
  ...opts.sealed ? { sealed: opts.sealed } : {},
25895
26498
  ...opts.outputFilter ? { outputFilter: opts.outputFilter } : {},
25896
26499
  ...opts.clipboard ? { clipboard: opts.clipboard, ...clipboardName(opts) } : {},
26500
+ ...embedderOptions(opts),
25897
26501
  ...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
25898
26502
  ...opts.source ? { source: opts.source } : {},
25899
26503
  noLinks: opts.noLinks,
@@ -28044,14 +28648,14 @@ var RESTORE_AGAIN_MS = 3e4;
28044
28648
  function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
28045
28649
  const handles = /* @__PURE__ */ new Map();
28046
28650
  let nextFh = 0;
28047
- const fresh2 = (remote) => {
28651
+ const fresh3 = (remote) => {
28048
28652
  if (remote.client.token === remote.token && !remote.client.gone) return remote;
28049
28653
  throw fsError2("ESTALE", "the folder was granted again, so this open file is gone");
28050
28654
  };
28051
28655
  const live = (fh) => {
28052
28656
  const remote = handles.get(fh);
28053
28657
  if (!remote) throw fsError2("EBADF", `handle ${fh}`);
28054
- return fresh2(remote);
28658
+ return fresh3(remote);
28055
28659
  };
28056
28660
  const call = (op, body = {}) => current().call(op, body);
28057
28661
  const same3 = (was, mode, seen) => seen?.mode === (mode | 128) && (was.ino !== void 0 && seen.ino !== void 0 ? seen.ino === was.ino : seen.size === was.size && seen.mtime === was.mtime);
@@ -28133,7 +28737,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
28133
28737
  if (!remote) throw fsError2("EBADF", `handle ${fh}`);
28134
28738
  handles.delete(fh);
28135
28739
  try {
28136
- await fresh2(remote).client.call("release", { fh: remote.fh }, true);
28740
+ await fresh3(remote).client.call("release", { fh: remote.fh }, true);
28137
28741
  } finally {
28138
28742
  if (remote.restore) await settle3(current(), remote.restore);
28139
28743
  }
@@ -29365,12 +29969,12 @@ ${lines2}
29365
29969
  const timer = setTimeout(() => control.abort("expired"), timeout);
29366
29970
  const withdraw = () => control.abort("withdrawn");
29367
29971
  requester.addEventListener("abort", withdraw, { once: true });
29368
- const aborted2 = new Promise(
29972
+ const aborted3 = new Promise(
29369
29973
  (resolve2) => control.signal.addEventListener("abort", () => resolve2("aborted"), { once: true })
29370
29974
  );
29371
29975
  try {
29372
29976
  const answered2 = Promise.resolve().then(() => approve(req, { signal: control.signal })).catch(() => "deny");
29373
- const answer2 = await Promise.race([answered2, aborted2]);
29977
+ const answer2 = await Promise.race([answered2, aborted3]);
29374
29978
  if (answer2 === "aborted" || control.signal.aborted) {
29375
29979
  return control.signal.reason === "expired" ? failed2("ETIMEDOUT", "sudo: the request expired") : failed2("EINTR", "sudo: the request was withdrawn");
29376
29980
  }
@@ -29496,6 +30100,7 @@ var SUDO_SOURCE = [
29496
30100
  ].join("\n");
29497
30101
 
29498
30102
  // src/launcher.ts
30103
+ var NO_SESSION = { client: -1, uid: -1 };
29499
30104
  var MODULE_URLS = 16;
29500
30105
  function dataModuleUrl(source) {
29501
30106
  const bytes = new TextEncoder().encode(source);
@@ -29758,6 +30363,7 @@ var Launcher = class {
29758
30363
  described = /* @__PURE__ */ new Map();
29759
30364
  creds = /* @__PURE__ */ new Map();
29760
30365
  tags = /* @__PURE__ */ new Map();
30366
+ sessions = /* @__PURE__ */ new Map();
29761
30367
  tagBases = /* @__PURE__ */ new Map();
29762
30368
  links = /* @__PURE__ */ new Map();
29763
30369
  freshModules = new FreshModules();
@@ -29818,6 +30424,7 @@ var Launcher = class {
29818
30424
  sealed;
29819
30425
  scrubs = /* @__PURE__ */ new Map();
29820
30426
  clipboard;
30427
+ embedder;
29821
30428
  rootFs;
29822
30429
  fileWatches;
29823
30430
  nextPid = 1e3;
@@ -29839,14 +30446,14 @@ var Launcher = class {
29839
30446
  void 0,
29840
30447
  (pid) => this.tagsOf(pid)
29841
30448
  );
30449
+ this.embedder = this.embedderBroker(options.embedder);
29842
30450
  this.sealed = new SealedBroker(
29843
30451
  options.sealed,
29844
30452
  this.usersMode,
29845
30453
  void 0,
29846
30454
  (pid) => this.tagsOf(pid)
29847
30455
  );
29848
- this.sealed.tickets.normalise = (path) => physicalPathAsync(this.base, path);
29849
- this.sealed.userOf = async (uid) => (await this.users.lookup(uid))?.name;
30456
+ this.wireSealed();
29850
30457
  if (this.owners) options.fs.trackOwners?.();
29851
30458
  this.watchers.tap((paths) => {
29852
30459
  this.changes.publish(...paths);
@@ -30907,6 +31514,8 @@ var Launcher = class {
30907
31514
  }
30908
31515
  marks(pid, req) {
30909
31516
  if (req.tags?.length) this.tags.set(pid, req.tags);
31517
+ const session = req.session ?? this.sessions.get(req.ppid ?? -1);
31518
+ if (session) this.sessions.set(pid, session);
30910
31519
  if (req.tags?.length) this.tagBases.set(pid, req.tagBase ?? req.tags.length);
30911
31520
  if (req.links !== void 0) this.links.set(pid, req.links);
30912
31521
  }
@@ -30986,6 +31595,7 @@ var Launcher = class {
30986
31595
  sealed: this.sealed,
30987
31596
  ...scrubs ? { outputFilter: (file) => scrubbedAll(file, scrubs) } : {},
30988
31597
  clipboard: this.clipboard,
31598
+ embedder: { broker: this.embedder, session: () => this.sessionOf(pid) },
30989
31599
  ...this.dac ? { dac: this.dac } : {},
30990
31600
  program: req.program,
30991
31601
  changes: this.changes.sab,
@@ -31114,6 +31724,7 @@ var Launcher = class {
31114
31724
  this.childTimes.delete(pid);
31115
31725
  this.creds.delete(pid);
31116
31726
  this.tags.delete(pid);
31727
+ this.sessions.delete(pid);
31117
31728
  this.tagBases.delete(pid);
31118
31729
  this.links.delete(pid);
31119
31730
  this.granted.delete(pid);
@@ -31159,8 +31770,8 @@ var Launcher = class {
31159
31770
  this.sampledAt = Math.max(this.sampledAt, now);
31160
31771
  this.load = this.load.map((avg, i) => {
31161
31772
  const window = LOAD_WINDOWS_MS[i];
31162
- const fresh2 = 1 - Math.exp(-LOAD_SAMPLE_MS / window);
31163
- return avg * Math.exp(-elapsed / window) + running * fresh2;
31773
+ const fresh3 = 1 - Math.exp(-LOAD_SAMPLE_MS / window);
31774
+ return avg * Math.exp(-elapsed / window) + running * fresh3;
31164
31775
  });
31165
31776
  }
31166
31777
  idleSince;
@@ -31390,7 +32001,7 @@ var Launcher = class {
31390
32001
  forker(ppid, parent) {
31391
32002
  const { exec: _exec, pgid: _pgid, group: _group, pid: _pid, scrub: _scrub, ...image } = parent;
31392
32003
  return async (state, given) => {
31393
- const fresh2 = (fds, quietOom) => this.start({
32004
+ const fresh3 = (fds, quietOom) => this.start({
31394
32005
  ...image,
31395
32006
  quietOom,
31396
32007
  cwd: state.cwd ?? parent.cwd,
@@ -31406,7 +32017,7 @@ var Launcher = class {
31406
32017
  tagBase: this.tagBases.get(ppid),
31407
32018
  links: this.links.get(ppid)
31408
32019
  });
31409
- return childHandle(await this.admitted(parent.program.abi !== "js", given, fresh2));
32020
+ return childHandle(await this.admitted(parent.program.abi !== "js", given, fresh3));
31410
32021
  };
31411
32022
  }
31412
32023
  keySignal(tty, fallback, sig, send2) {
@@ -31442,6 +32053,25 @@ var Launcher = class {
31442
32053
  credOf(pid) {
31443
32054
  return this.creds.get(pid)?.() ?? copyCred(ROOT);
31444
32055
  }
32056
+ wireSealed() {
32057
+ this.sealed.tickets.normalise = (path) => physicalPathAsync(this.base, path);
32058
+ this.sealed.userOf = async (uid) => (await this.users.lookup(uid))?.name;
32059
+ }
32060
+ embedderBroker(hook) {
32061
+ const broker = new EmbedderBroker(
32062
+ this.usersMode,
32063
+ {
32064
+ groupId: async (name2) => (await this.users.groups()).find((g) => g.name === name2)?.gid,
32065
+ userId: async (name2) => (await this.users.lookup(name2))?.uid
32066
+ },
32067
+ (pid) => this.tagsOf(pid)
32068
+ );
32069
+ broker.register(0, hook);
32070
+ return broker;
32071
+ }
32072
+ sessionOf(pid) {
32073
+ return this.sessions.get(pid) ?? NO_SESSION;
32074
+ }
31445
32075
  tagOf(pid) {
31446
32076
  return this.tags.get(pid)?.[0];
31447
32077
  }
@@ -31713,8 +32343,8 @@ var Launcher = class {
31713
32343
  for (const [path, mode] of layout) {
31714
32344
  if (isProtected(this.prefixes, normalizePath(path))) continue;
31715
32345
  const real = await this.realLayoutPath(path);
31716
- const fresh2 = real !== void 0 && !await this.base.hasOwner?.(real);
31717
- if (fresh2) await this.base.chown?.(real, { uid: 0, gid: 0, mode, follow: false });
32346
+ const fresh3 = real !== void 0 && !await this.base.hasOwner?.(real);
32347
+ if (fresh3) await this.base.chown?.(real, { uid: 0, gid: 0, mode, follow: false });
31718
32348
  }
31719
32349
  }
31720
32350
  async realLayoutPath(given) {
@@ -31887,9 +32517,10 @@ var Launcher = class {
31887
32517
  if (!found) throw fsError("ENOENT", `no user ${String(user)}`);
31888
32518
  return found;
31889
32519
  }
31890
- identity(account, given) {
32520
+ identity(account, given, session = 0) {
31891
32521
  return {
31892
32522
  ...given,
32523
+ session: { client: session, uid: account.uid },
31893
32524
  cred: credOf(account),
31894
32525
  ...account.umask !== void 0 ? { umask: account.umask } : {}
31895
32526
  };
@@ -32076,7 +32707,7 @@ var Launcher = class {
32076
32707
  cwd,
32077
32708
  fds,
32078
32709
  report,
32079
- ...this.identity(account, tagged)
32710
+ ...this.identity(account, tagged, options.session)
32080
32711
  });
32081
32712
  leader = handle.pid;
32082
32713
  void handle.exited.then(() => tty.hangup());
@@ -32133,7 +32764,7 @@ var Launcher = class {
32133
32764
  fds,
32134
32765
  report,
32135
32766
  ...group,
32136
- ...this.identity(account, tagged)
32767
+ ...this.identity(account, tagged, options.session)
32137
32768
  });
32138
32769
  options.onStarted?.(handle.pid);
32139
32770
  const status = await handle.exited;
@@ -32599,6 +33230,8 @@ function isUsersRequest(req) {
32599
33230
  }
32600
33231
  function transferOf2(req, result) {
32601
33232
  if (result instanceof MessagePort) return [result];
33233
+ const bound3 = result?.port;
33234
+ if (req.op === "connect" && bound3 instanceof MessagePort) return [bound3];
32602
33235
  if (req.op === "attest") return [result.bytes.buffer];
32603
33236
  return void 0;
32604
33237
  }
@@ -32632,7 +33265,35 @@ function usersOp(l, req) {
32632
33265
  if (req.op === "groups-remove-member") return l.users.removeMember(req.group, req.user);
32633
33266
  return l.users.list();
32634
33267
  }
32635
- function hostOptions(req, port, sealed, clipboard, lookup2) {
33268
+ var clientIds = 0;
33269
+ async function connectClient(deps, { user, tag, bind: bind2 }) {
33270
+ const l = await deps.ready();
33271
+ tagOption(tag);
33272
+ if (user !== void 0 && !await l.users.lookup(user)) {
33273
+ throw fsError("ENOENT", `no user ${String(user)}`);
33274
+ }
33275
+ const { port1, port2 } = new MessageChannel();
33276
+ const held = deps.held ? await deps.held() : void 0;
33277
+ const client = ++clientIds;
33278
+ const served2 = serveClient(port1, {
33279
+ launcher: deps.ready,
33280
+ signal: signalNumber,
33281
+ session: client,
33282
+ ...bind2 === "attach" ? { attachRequests: true } : {},
33283
+ ...user !== void 0 ? { user } : {},
33284
+ ...tag !== void 0 ? { tag } : {},
33285
+ ...deps.locks ? { locks: deps.locks } : {},
33286
+ ...held ? { lock: held } : {}
33287
+ });
33288
+ deps.clients.add(served2);
33289
+ const unbind = bind2 && bind2 !== "attach" ? l.embedder.register(client, deps.requests.hookFor(client, bind2)) : void 0;
33290
+ void served2.closed.then(() => {
33291
+ deps.clients.delete(served2);
33292
+ unbind?.();
33293
+ });
33294
+ return unbind ? { port: port2, client } : port2;
33295
+ }
33296
+ function hostOptions(req, port, sealed, clipboard, lookup2, requests) {
32636
33297
  return {
32637
33298
  cron: { lock: `slicc-kernel-cron:${req.metadata ?? META_DB}` },
32638
33299
  ...req.hostname !== void 0 ? { hostname: req.hostname } : {},
@@ -32641,7 +33302,8 @@ function hostOptions(req, port, sealed, clipboard, lookup2) {
32641
33302
  ...req.denials ? { onDenial: (denial) => port.postMessage({ denial }) } : {},
32642
33303
  ...req.protect ? { protect: hostProtect(req.protect, lookup2) } : {},
32643
33304
  ...req.sealed ? { sealed } : {},
32644
- ...req.clipboard ? { clipboard } : {}
33305
+ ...req.clipboard ? { clipboard } : {},
33306
+ ...req.requests ? { embedder: requests.hookFor(0, req.requests) } : {}
32645
33307
  };
32646
33308
  }
32647
33309
  function hostProtect(init, lookup2) {
@@ -32676,6 +33338,7 @@ function serveKernel(port, deps) {
32676
33338
  const clients = /* @__PURE__ */ new Set();
32677
33339
  const sealed = sealedAsker(port);
32678
33340
  const clipboard = clipboardAsker(port);
33341
+ const requests = embedderAsker(port);
32679
33342
  const integrity = integrityAsker(port);
32680
33343
  const approvals = sudoApprovals(port);
32681
33344
  const locks = deps.locks === null ? void 0 : deps.locks ?? locksOf();
@@ -32691,26 +33354,10 @@ function serveKernel(port, deps) {
32691
33354
  });
32692
33355
  return holding;
32693
33356
  }
32694
- async function connect2(user, tag) {
32695
- const l = await ready2();
32696
- tagOption(tag);
32697
- if (user !== void 0 && !await l.users.lookup(user)) {
32698
- throw fsError("ENOENT", `no user ${String(user)}`);
32699
- }
32700
- const { port1, port2 } = new MessageChannel();
32701
- const held = locks ? await holdLock(locks) : void 0;
32702
- const served2 = serveClient(port1, {
32703
- launcher: ready2,
32704
- signal: signalNumber,
32705
- ...user !== void 0 ? { user } : {},
32706
- ...tag !== void 0 ? { tag } : {},
32707
- ...locks ? { locks } : {},
32708
- ...held ? { lock: held } : {}
32709
- });
32710
- clients.add(served2);
32711
- void served2.closed.then(() => clients.delete(served2));
32712
- return port2;
32713
- }
33357
+ const connect2 = (user, tag, bind2) => connectClient(
33358
+ { ready: ready2, clients, requests, held: locks ? () => holdLock(locks) : void 0, locks },
33359
+ { user, tag, bind: bind2 }
33360
+ );
32714
33361
  async function ready2() {
32715
33362
  if (!launcher) throw new Error("the kernel is not initialized");
32716
33363
  return launcher;
@@ -32744,7 +33391,7 @@ function serveKernel(port, deps) {
32744
33391
  ...req.modules ? { modules: req.modules } : {},
32745
33392
  ...req.commands ? { commands: req.commands } : {},
32746
33393
  ...req.env ? { env: req.env } : {},
32747
- ...hostOptions(req, port, sealed.hook, clipboard.hook, integrity.lookup),
33394
+ ...hostOptions(req, port, sealed.hook, clipboard.hook, integrity.lookup, requests),
32748
33395
  ...req.approve ? { approve: approvals.ask } : {},
32749
33396
  ...net,
32750
33397
  ...media ? { media } : {},
@@ -32772,7 +33419,7 @@ function serveKernel(port, deps) {
32772
33419
  if (req.op === "init") return init(req);
32773
33420
  if (isAttestRequest(req)) return attestOp(await ready2(), req);
32774
33421
  if (req.op === "open-terminal") return terminal(req);
32775
- if (req.op === "connect") return connect2(req.user, req.tag);
33422
+ if (req.op === "connect") return connect2(req.user, req.tag, req.requests);
32776
33423
  if (isUsersRequest(req)) return usersOp(await ready2(), req);
32777
33424
  if (req.op === "routes" || req.op === "transport-traits") {
32778
33425
  return networkOp(await ready2(), req, remote);
@@ -32814,6 +33461,8 @@ function serveKernel(port, deps) {
32814
33461
  if (sealedReply) return sealed.answer(sealedReply);
32815
33462
  const clipboardReply = event.data.clipboardReply;
32816
33463
  if (clipboardReply) return clipboard.answer(clipboardReply);
33464
+ const embedderReply = embedderReplyOf(event.data);
33465
+ if (embedderReply) return requests.answer(embedderReply);
32817
33466
  const integrityReply = event.data.integrityReply;
32818
33467
  if (integrityReply) return integrity.answer(integrityReply);
32819
33468
  const grant = event.data.hostfsGrant;