@ai-ecoverse/slicc-kernel 1.174.3 → 1.175.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7514,7 +7514,7 @@ var init_version = __esm({
7514
7514
  "src/version.ts"() {
7515
7515
  "use strict";
7516
7516
  init_captured_globals();
7517
- KERNEL_VERSION = true ? "1.174.3" : "0.0.0-dev";
7517
+ KERNEL_VERSION = true ? "1.175.1" : "0.0.0-dev";
7518
7518
  OS_RELEASE = "6.0.0-slicc";
7519
7519
  }
7520
7520
  });
@@ -7513,7 +7513,7 @@ var init_version = __esm({
7513
7513
  "src/version.ts"() {
7514
7514
  "use strict";
7515
7515
  init_captured_globals();
7516
- KERNEL_VERSION = true ? "1.174.3" : "0.0.0-dev";
7516
+ KERNEL_VERSION = true ? "1.175.1" : "0.0.0-dev";
7517
7517
  OS_RELEASE = "6.0.0-slicc";
7518
7518
  }
7519
7519
  });
package/dist/node.js CHANGED
@@ -3702,7 +3702,7 @@ function satisfies(version, range2) {
3702
3702
  }
3703
3703
 
3704
3704
  // src/version.ts
3705
- var KERNEL_VERSION = true ? "1.174.3" : "0.0.0-dev";
3705
+ var KERNEL_VERSION = true ? "1.175.1" : "0.0.0-dev";
3706
3706
  var OS_RELEASE = "6.0.0-slicc";
3707
3707
  function procVersion() {
3708
3708
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -14776,7 +14776,12 @@ function sessionValues(reply, asked) {
14776
14776
  // src/kernel/sealed.ts
14777
14777
  var SEALED_MAX_BYTES = 64 * 1024;
14778
14778
  var SEALED_TIMEOUT_MS = 1e4;
14779
+ var SEALED_ASK_TIMEOUT_MS = 305e3;
14780
+ var SEALED_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
14781
+ var SEALED_NAME_MAX = 128;
14782
+ var SEALED_PROMPT_MAX = 200;
14779
14783
  var SEALED_OPS = /* @__PURE__ */ new Set([
14784
+ "ask",
14780
14785
  "seal",
14781
14786
  "placeholder",
14782
14787
  "list",
@@ -14791,6 +14796,16 @@ function tagFields(tags) {
14791
14796
  if (tag === void 0) return {};
14792
14797
  return tags.length > 1 ? { tag, tags: [...tags] } : { tag };
14793
14798
  }
14799
+ var ASK_FIELDS = /* @__PURE__ */ new Set([
14800
+ "v",
14801
+ "op",
14802
+ "name",
14803
+ "prompt",
14804
+ "reveal",
14805
+ "replace",
14806
+ "caller",
14807
+ "requester"
14808
+ ]);
14794
14809
  var encoder6 = new TextEncoder();
14795
14810
  var decoder3 = new TextDecoder("utf-8", { fatal: true });
14796
14811
  function parseSealed(bytes) {
@@ -14808,6 +14823,7 @@ function parseSealed(bytes) {
14808
14823
  if (req.v !== 1 || typeof req.op !== "string" || !SEALED_OPS.has(req.op))
14809
14824
  throw new KernelError("EINVAL");
14810
14825
  if (req.op === "materialize" && !materializable(req)) throw new KernelError("EINVAL");
14826
+ if (req.op === "ask" && !askable(req)) throw new KernelError("EINVAL");
14811
14827
  return req;
14812
14828
  } finally {
14813
14829
  bytes.fill(0);
@@ -14817,6 +14833,27 @@ function materializable(req) {
14817
14833
  if (req.shellGrants !== true && !isStrings(req.names)) return false;
14818
14834
  return isStrings(req.argv) && typeof req.path === "string" && typeof req.cwd === "string";
14819
14835
  }
14836
+ function askable(req) {
14837
+ if (Object.keys(req).some((key) => !ASK_FIELDS.has(key))) return false;
14838
+ const { name: name2, prompt, reveal, replace } = req;
14839
+ if (typeof name2 !== "string" || name2.length > SEALED_NAME_MAX || !SEALED_NAME.test(name2))
14840
+ return false;
14841
+ if (prompt !== void 0 && (typeof prompt !== "string" || [...prompt].length > SEALED_PROMPT_MAX))
14842
+ return false;
14843
+ return [reveal, replace].every((flag) => flag === void 0 || typeof flag === "boolean");
14844
+ }
14845
+ function askRequest(fields, caller, requester) {
14846
+ return {
14847
+ v: 1,
14848
+ op: "ask",
14849
+ name: fields.name,
14850
+ ...fields.prompt !== void 0 ? { prompt: fields.prompt } : {},
14851
+ reveal: fields.reveal === true,
14852
+ replace: fields.replace === true,
14853
+ caller,
14854
+ requester
14855
+ };
14856
+ }
14820
14857
  function errorReply(code, message3) {
14821
14858
  return { v: 1, error: { code, message: message3 } };
14822
14859
  }
@@ -14849,7 +14886,7 @@ var SealedChannel = class {
14849
14886
  this.broker = broker;
14850
14887
  this.access = access2;
14851
14888
  }
14852
- send(bytes, caller, gone) {
14889
+ send(bytes, caller, gone, requester = async () => void 0) {
14853
14890
  if (this.access === "read") {
14854
14891
  bytes.fill(0);
14855
14892
  throw new KernelError("EBADF");
@@ -14862,10 +14899,15 @@ var SealedChannel = class {
14862
14899
  const withdraw = () => control.abort("withdrawn");
14863
14900
  if (gone.aborted) withdraw();
14864
14901
  else gone.addEventListener("abort", withdraw, { once: true });
14865
- const timer = setTimeout(() => control.abort("timeout"), this.broker.timeoutMs);
14902
+ const ask = fields.op === "ask";
14903
+ const timer = setTimeout(
14904
+ () => control.abort(ask ? "expired" : "timeout"),
14905
+ ask ? this.broker.askTimeoutMs : this.broker.timeoutMs
14906
+ );
14866
14907
  const req = { ...fields, v: 1, op: fields.op, caller };
14908
+ const asked = ask ? this.asking(fields, caller, requester, control.signal) : void 0;
14867
14909
  const flight = { control, done: Promise.resolve() };
14868
- flight.done = this.answer(req, control.signal).then(async (reply) => {
14910
+ flight.done = (asked ?? this.answer(req, control.signal)).then(async (reply) => {
14869
14911
  clearTimeout(timer);
14870
14912
  gone.removeEventListener("abort", withdraw);
14871
14913
  if (reply && req.op === "materialize")
@@ -14877,6 +14919,17 @@ var SealedChannel = class {
14877
14919
  this.inflight = flight;
14878
14920
  return length2;
14879
14921
  }
14922
+ async asking(fields, caller, requester, signal) {
14923
+ const facts = await requester().catch(() => void 0);
14924
+ if (signal.aborted) return this.stopped(signal);
14925
+ if (!facts) return errorReply("EIO", "the kernel has no record of this process");
14926
+ return this.answer(askRequest(fields, caller, facts), signal);
14927
+ }
14928
+ stopped(signal) {
14929
+ if (signal.reason === "timeout")
14930
+ return errorReply("ETIMEDOUT", "the vault did not answer within 10 s");
14931
+ return signal.reason === "expired" ? { v: 1, state: "expired" } : void 0;
14932
+ }
14880
14933
  async answer(req, signal) {
14881
14934
  const hook = this.broker.hook;
14882
14935
  if (!hook) return errorReply("ENOTCONN", "no vault is connected");
@@ -14889,8 +14942,7 @@ var SealedChannel = class {
14889
14942
  (err) => errorReply("EIO", lineOf3(err))
14890
14943
  );
14891
14944
  const reply = await Promise.race([asked, stopped]);
14892
- if (!signal.aborted) return reply;
14893
- return signal.reason === "timeout" ? errorReply("ETIMEDOUT", "the vault did not answer within 10 s") : void 0;
14945
+ return signal.aborted ? this.stopped(signal) : reply;
14894
14946
  }
14895
14947
  drop() {
14896
14948
  this.reply?.fill(0);
@@ -14952,7 +15004,9 @@ var SealedBroker = class {
14952
15004
  tagOf;
14953
15005
  tickets = new TicketBook();
14954
15006
  sessions = new SessionScrubs();
15007
+ askTimeoutMs = SEALED_ASK_TIMEOUT_MS;
14955
15008
  userOf = async () => void 0;
15009
+ factsOf = async () => void 0;
14956
15010
  constructor(hook, users, timeoutMs = SEALED_TIMEOUT_MS, tagOf = () => void 0) {
14957
15011
  this.hook = hook;
14958
15012
  this.users = users;
@@ -23535,7 +23589,10 @@ var WasmProcess = class {
23535
23589
  }
23536
23590
  async write(fd, body, nonblock = false, cancelled) {
23537
23591
  const file = this.fds.get(fd).file;
23538
- if (file.sealed) return file.sealed.send(body, await this.sealedCaller(), this.gone.signal);
23592
+ if (file.sealed) {
23593
+ const caller = await this.sealedCaller();
23594
+ return file.sealed.send(body, caller, this.gone.signal, () => this.sealedRequester());
23595
+ }
23539
23596
  if (!file.write) throw new KernelError("EBADF");
23540
23597
  if (file.embedder) return this.writeEmbedder(file, body, nonblock, cancelled);
23541
23598
  if (!pollFile(file).writable) {
@@ -23740,6 +23797,10 @@ var WasmProcess = class {
23740
23797
  const ids = { pid: shown2?.pid ?? this.pid, ppid: shown2?.ppid ?? 0 };
23741
23798
  return broker.stamp(ids, this.cred, broker.tagOf(this.pid));
23742
23799
  }
23800
+ async sealedRequester() {
23801
+ const facts = await this.options.sealed.factsOf(this.pid);
23802
+ return facts && { ...facts, stdin: this.stdinKind() };
23803
+ }
23743
23804
  guarded() {
23744
23805
  const { guard: guard2 } = this.options;
23745
23806
  return guard2 && { fs: guard2.fs, dac: guard2.dac, cred: this.cred, pid: this.pid, links: guard2.links };
@@ -28243,8 +28304,10 @@ var OpfsFs = class {
28243
28304
  for (let at = from; at < parts2.length; at++) {
28244
28305
  this.calls++;
28245
28306
  dir = await dir.getDirectoryHandle(parts2[at]);
28246
- this.dirs.set(`/${parts2.slice(0, at + 1).join("/")}`, dir);
28307
+ const path = `/${parts2.slice(0, at + 1).join("/")}`;
28308
+ this.dirs.set(path, dir);
28247
28309
  this.checked.set(dir, performance.now());
28310
+ this.absent.delete(path);
28248
28311
  }
28249
28312
  return dir;
28250
28313
  }
@@ -28326,6 +28389,7 @@ var OpfsFs = class {
28326
28389
  if (looked) {
28327
28390
  this.dirs.set(path, handle);
28328
28391
  this.checked.set(handle, performance.now());
28392
+ this.absent.delete(path);
28329
28393
  }
28330
28394
  return handle;
28331
28395
  }
@@ -28976,7 +29040,8 @@ var OpfsFs = class {
28976
29040
  }
28977
29041
  const [entry] = await this.meta.get([path]);
28978
29042
  const wanted = entry?.link === void 0 ? path : resolvePath(parentOf9(path), entry.link);
28979
- return [resolvePath((await this.locate(parentOf9(wanted), true)).path, baseOf3(wanted)), false];
29043
+ const parent = await this.exactly(() => this.locate(parentOf9(wanted), true));
29044
+ return [resolvePath(parent.path, baseOf3(wanted)), false];
28980
29045
  }
28981
29046
  async fileFor(path, create = true) {
28982
29047
  const [target, existed] = await this.writeTarget(path);
@@ -29083,10 +29148,12 @@ var OpfsFs = class {
29083
29148
  await this.mkdir(parentOf9(path), { recursive: true, ...above ? { owner: above } : {} });
29084
29149
  const parent = await this.locate(parentOf9(path), true);
29085
29150
  if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
29086
- this.refuse(resolvePath(parent.path, baseOf3(path)));
29087
- await this.io(parent.handle.getDirectoryHandle(baseOf3(path), { create: true }));
29151
+ const made2 = resolvePath(parent.path, baseOf3(path));
29152
+ this.forget(made2);
29153
+ this.refuse(made2);
29154
+ const handle = await this.io(parent.handle.getDirectoryHandle(baseOf3(path), { create: true }));
29155
+ this.remember(made2, handle, true);
29088
29156
  const now = Date.now();
29089
- const made2 = `${parent.path === "/" ? "" : parent.path}/${baseOf3(path)}`;
29090
29157
  await this.stampNew(made2, owner, true);
29091
29158
  this.touch(now, parent.path, made2);
29092
29159
  }
@@ -31638,6 +31705,122 @@ function refusing(command) {
31638
31705
  };
31639
31706
  }
31640
31707
 
31708
+ // src/sealed-command.ts
31709
+ var SEALED_BIN = "/usr/bin/sealed";
31710
+ var SEALED_COMMAND = {
31711
+ name: "sealed",
31712
+ abi: "js",
31713
+ glue: SEALED_BIN,
31714
+ wasm: SEALED_BIN,
31715
+ argv0: "sealed"
31716
+ };
31717
+ var SEALED_SOURCE = `export default async function sealed(ctx) {
31718
+ const USAGE = "usage: sealed ask NAME [--prompt TEXT] [--reveal] [--replace]\\n sealed placeholder NAME\\n sealed list [--json]\\n";
31719
+ const NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
31720
+ const EXIT = { EINVAL: 2, EACCES: 3, EAGAIN: 4, ETIMEDOUT: 124 };
31721
+ const say = (fd, text) => ctx.write(fd, text).catch(() => undefined);
31722
+ const fail = async (status, text) => {
31723
+ await say(2, "sealed: " + text + "\\n");
31724
+ return status;
31725
+ };
31726
+ const usage = async (text) => {
31727
+ if (text !== undefined) await say(2, "sealed: " + text + "\\n");
31728
+ await say(2, USAGE);
31729
+ return 2;
31730
+ };
31731
+ const [action, ...args] = ctx.argv.slice(1);
31732
+ if (action === undefined || action === "-h" || action === "--help") return usage();
31733
+ let name;
31734
+ let prompt;
31735
+ let reveal = false;
31736
+ let replace = false;
31737
+ let asJson = false;
31738
+ for (let i = 0; i < args.length; i++) {
31739
+ const arg = args[i];
31740
+ if (action === "ask" && arg === "--prompt") {
31741
+ prompt = args[++i];
31742
+ if (prompt === undefined) return usage("--prompt needs a text");
31743
+ } else if (action === "ask" && arg.startsWith("--prompt=")) prompt = arg.slice(9);
31744
+ else if (action === "ask" && arg === "--reveal") reveal = true;
31745
+ else if (action === "ask" && arg === "--replace") replace = true;
31746
+ else if (action === "list" && arg === "--json") asJson = true;
31747
+ else if (action !== "list" && name === undefined && !arg.startsWith("-")) name = arg;
31748
+ else return usage("unexpected argument " + JSON.stringify(arg));
31749
+ }
31750
+ let request;
31751
+ if (action === "ask" || action === "placeholder") {
31752
+ if (name === undefined) return usage("a NAME is needed");
31753
+ if (name.length > 128 || !NAME.test(name)) return fail(2, JSON.stringify(name) + " is not a valid name");
31754
+ if (prompt !== undefined && [...prompt].length > 200) return fail(2, "the prompt is longer than 200 characters");
31755
+ request = action === "ask"
31756
+ ? { v: 1, op: "ask", name, ...(prompt !== undefined ? { prompt } : {}), reveal, replace }
31757
+ : { v: 1, op: "placeholder", name };
31758
+ } else if (action === "list") request = { v: 1, op: "list" };
31759
+ else return usage("unknown command " + JSON.stringify(action));
31760
+ let reply;
31761
+ try {
31762
+ reply = await exchange(request);
31763
+ } catch (err) {
31764
+ const code = err && err.code ? err.code : String(err);
31765
+ return fail(EXIT[code] ?? 1, "/dev/sealed: " + code);
31766
+ }
31767
+ if (reply.error !== undefined) {
31768
+ const code = String(reply.error && reply.error.code);
31769
+ const message = reply.error && reply.error.message ? reply.error.message : code;
31770
+ return fail(EXIT[code] ?? 1, (name !== undefined ? name + ": " : "") + message);
31771
+ }
31772
+ const newline = (await ctx.isatty(1).catch(() => false)) ? "\\n" : "";
31773
+ if (action === "list") {
31774
+ const secrets = Array.isArray(reply.secrets) ? reply.secrets : [];
31775
+ if (asJson) {
31776
+ await say(1, JSON.stringify(secrets) + "\\n");
31777
+ return 0;
31778
+ }
31779
+ await say(1, secrets.map((s) => s.name + "\\t" + s.placeholder + "\\t" + s.state + "\\n").join(""));
31780
+ return 0;
31781
+ }
31782
+ if (action === "placeholder") {
31783
+ if (typeof reply.placeholder !== "string") return fail(1, name + ": the vault gave no placeholder");
31784
+ await say(1, reply.placeholder + newline);
31785
+ return 0;
31786
+ }
31787
+ if (reply.state === "cancelled") return fail(1, name + ": cancelled");
31788
+ if (reply.state === "expired") return fail(124, name + ": the request expired");
31789
+ if (reply.state !== "done") return fail(1, name + ": the vault gave no answer");
31790
+ const out = reveal ? reply.value : reply.placeholder;
31791
+ if (typeof out !== "string") return fail(1, name + ": the vault gave no " + (reveal ? "value" : "placeholder"));
31792
+ await say(1, out + newline);
31793
+ return 0;
31794
+
31795
+ async function exchange(req) {
31796
+ const file = await ctx.open("/dev/sealed", { read: true, write: true });
31797
+ try {
31798
+ await ctx.write(file.fd, JSON.stringify(req));
31799
+ const parts = [];
31800
+ for (;;) {
31801
+ const chunk = await ctx.read(file.fd, 65536);
31802
+ if (chunk.length === 0) break;
31803
+ parts.push(chunk);
31804
+ }
31805
+ const bytes = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
31806
+ let at = 0;
31807
+ for (const part of parts) {
31808
+ bytes.set(part, at);
31809
+ at += part.length;
31810
+ part.fill(0);
31811
+ }
31812
+ const text = new TextDecoder().decode(bytes);
31813
+ bytes.fill(0);
31814
+ const parsed = JSON.parse(text);
31815
+ if (typeof parsed !== "object" || parsed === null) throw Object.assign(new Error("EIO"), { code: "EIO" });
31816
+ return parsed;
31817
+ } finally {
31818
+ await file.close().catch(() => undefined);
31819
+ }
31820
+ }
31821
+ }
31822
+ `;
31823
+
31641
31824
  // src/sudo-command.ts
31642
31825
  var SUDO_PATH = "/usr/bin/sudo";
31643
31826
  var SUDO_COMMAND = {
@@ -31847,7 +32030,7 @@ var LAYOUT = [
31847
32030
  ["/usr", 493],
31848
32031
  ["/usr/local", 493]
31849
32032
  ];
31850
- var IN_TREE = /* @__PURE__ */ new Set([SUDO_PATH, VISUDO_PATH, CRONTAB_PATH]);
32033
+ var IN_TREE = /* @__PURE__ */ new Set([SUDO_PATH, VISUDO_PATH, CRONTAB_PATH, SEALED_BIN]);
31851
32034
  var LOAD_SAMPLE_MS = 5e3;
31852
32035
  var LOAD_WINDOWS_MS = [6e4, 3e5, 9e5];
31853
32036
  var encoder10 = new TextEncoder();
@@ -31976,6 +32159,7 @@ function expandDefaults(defaults2, env) {
31976
32159
  function targetOf(command) {
31977
32160
  return {
31978
32161
  abi: command.abi,
32162
+ command: command.name,
31979
32163
  glue: command.glue,
31980
32164
  wasm: command.wasm,
31981
32165
  argv0: command.argv0,
@@ -32072,6 +32256,8 @@ var Launcher = class {
32072
32256
  orphans = /* @__PURE__ */ new Set();
32073
32257
  described = /* @__PURE__ */ new Map();
32074
32258
  creds = /* @__PURE__ */ new Map();
32259
+ programs = /* @__PURE__ */ new Map();
32260
+ cwdPaths = /* @__PURE__ */ new Map();
32075
32261
  tags = /* @__PURE__ */ new Map();
32076
32262
  sessions = /* @__PURE__ */ new Map();
32077
32263
  tagBases = /* @__PURE__ */ new Map();
@@ -32399,7 +32585,7 @@ var Launcher = class {
32399
32585
  this.refusedOf.set(commands, refused4.map(refusing));
32400
32586
  return commands;
32401
32587
  }).then(
32402
- (commands) => this.refusals(commands).set(SUDO_COMMAND.name, SUDO_COMMAND).set(VISUDO_COMMAND.name, VISUDO_COMMAND).set(CRONTAB_COMMAND.name, CRONTAB_COMMAND)
32588
+ (commands) => this.refusals(commands).set(SUDO_COMMAND.name, SUDO_COMMAND).set(VISUDO_COMMAND.name, VISUDO_COMMAND).set(CRONTAB_COMMAND.name, CRONTAB_COMMAND).set(SEALED_COMMAND.name, SEALED_COMMAND)
32403
32589
  );
32404
32590
  this.catalogs.set(scope, found);
32405
32591
  }
@@ -33172,6 +33358,9 @@ var Launcher = class {
33172
33358
  if (target.abi === "js" && target.wasm === CRONTAB_PATH) {
33173
33359
  return { abi: "js", glue: CRONTAB_SOURCE, path: CRONTAB_PATH };
33174
33360
  }
33361
+ if (target.abi === "js" && target.wasm === SEALED_BIN) {
33362
+ return { abi: "js", glue: SEALED_SOURCE, path: SEALED_BIN };
33363
+ }
33175
33364
  if (target.abi === "js" && target.wasm === REFUSED_PATH) {
33176
33365
  return { abi: "js", glue: REFUSED_SOURCE, path: REFUSED_PATH };
33177
33366
  }
@@ -33230,6 +33419,7 @@ var Launcher = class {
33230
33419
  argv0: target.argv0,
33231
33420
  exe: target.copy ?? target.glue,
33232
33421
  ...exeId,
33422
+ ...target.command !== void 0 && target.copy === void 0 ? { command: target.command } : {},
33233
33423
  args,
33234
33424
  env,
33235
33425
  cwdSlot
@@ -33346,6 +33536,8 @@ var Launcher = class {
33346
33536
  if (session) this.sessions.set(pid, session);
33347
33537
  if (req.tags?.length) this.tagBases.set(pid, req.tagBase ?? req.tags.length);
33348
33538
  if (req.links !== void 0) this.links.set(pid, req.links);
33539
+ if (req.command !== void 0) this.programs.set(pid, req.command);
33540
+ this.cwdPaths.set(pid, req.cwd);
33349
33541
  }
33350
33542
  async admitted(wasm, fds, start2) {
33351
33543
  for (; ; ) {
@@ -33514,6 +33706,7 @@ var Launcher = class {
33514
33706
  void handle.exited.then(() => {
33515
33707
  this.cwds.release(this.cwdSlots.get(pid));
33516
33708
  this.cwdSlots.delete(pid);
33709
+ this.cwdPaths.delete(pid);
33517
33710
  this.freshModules.forget(pid);
33518
33711
  const spent = handle.times?.();
33519
33712
  if (spent) this.spent.set(pid, spent);
@@ -33547,6 +33740,7 @@ var Launcher = class {
33547
33740
  }
33548
33741
  this.jobs.remove(pid);
33549
33742
  this.described.delete(pid);
33743
+ this.programs.delete(pid);
33550
33744
  const spent = this.spent.get(pid);
33551
33745
  if (spent) {
33552
33746
  this.reapedTimes.utime += spent.utime;
@@ -33846,7 +34040,21 @@ var Launcher = class {
33846
34040
  cwdChdir: (path) => this.chdirHeld(pid, path)
33847
34041
  };
33848
34042
  }
34043
+ async processFacts(pid) {
34044
+ const described2 = this.described.get(pid);
34045
+ const cwd = this.cwdPaths.get(pid);
34046
+ if (!described2 || described2.exe === void 0 || cwd === void 0) return void 0;
34047
+ const real = (path) => physicalPathAsync(this.base, path).catch(() => path);
34048
+ const command = this.programs.get(pid);
34049
+ return {
34050
+ exe: command !== void 0 ? `/usr/bin/${command}` : await real(described2.exe),
34051
+ argv: [...described2.argv],
34052
+ cwd: await real(cwd),
34053
+ tty: described2.tty
34054
+ };
34055
+ }
33849
34056
  async chdirHeld(pid, path) {
34057
+ this.cwdPaths.set(pid, path);
33850
34058
  this.cwds.release(this.cwdSlots.get(pid));
33851
34059
  const slot2 = await this.cwds.hold(path);
33852
34060
  this.cwdSlots.set(pid, slot2);
@@ -33910,6 +34118,7 @@ var Launcher = class {
33910
34118
  wireSealed() {
33911
34119
  this.sealed.tickets.normalise = (path) => physicalPathAsync(this.base, path);
33912
34120
  this.sealed.userOf = async (uid) => (await this.users.lookup(uid))?.name;
34121
+ this.sealed.factsOf = (pid) => this.processFacts(pid);
33913
34122
  }
33914
34123
  embedderBroker(hook) {
33915
34124
  const broker = new EmbedderBroker(