@ai-ecoverse/slicc-kernel 1.174.2 → 1.175.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.
@@ -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.2" : "0.0.0-dev";
7517
+ KERNEL_VERSION = true ? "1.175.0" : "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.2" : "0.0.0-dev";
7516
+ KERNEL_VERSION = true ? "1.175.0" : "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.2" : "0.0.0-dev";
3705
+ var KERNEL_VERSION = true ? "1.175.0" : "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 };
@@ -31638,6 +31699,122 @@ function refusing(command) {
31638
31699
  };
31639
31700
  }
31640
31701
 
31702
+ // src/sealed-command.ts
31703
+ var SEALED_BIN = "/usr/bin/sealed";
31704
+ var SEALED_COMMAND = {
31705
+ name: "sealed",
31706
+ abi: "js",
31707
+ glue: SEALED_BIN,
31708
+ wasm: SEALED_BIN,
31709
+ argv0: "sealed"
31710
+ };
31711
+ var SEALED_SOURCE = `export default async function sealed(ctx) {
31712
+ const USAGE = "usage: sealed ask NAME [--prompt TEXT] [--reveal] [--replace]\\n sealed placeholder NAME\\n sealed list [--json]\\n";
31713
+ const NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
31714
+ const EXIT = { EINVAL: 2, EACCES: 3, EAGAIN: 4, ETIMEDOUT: 124 };
31715
+ const say = (fd, text) => ctx.write(fd, text).catch(() => undefined);
31716
+ const fail = async (status, text) => {
31717
+ await say(2, "sealed: " + text + "\\n");
31718
+ return status;
31719
+ };
31720
+ const usage = async (text) => {
31721
+ if (text !== undefined) await say(2, "sealed: " + text + "\\n");
31722
+ await say(2, USAGE);
31723
+ return 2;
31724
+ };
31725
+ const [action, ...args] = ctx.argv.slice(1);
31726
+ if (action === undefined || action === "-h" || action === "--help") return usage();
31727
+ let name;
31728
+ let prompt;
31729
+ let reveal = false;
31730
+ let replace = false;
31731
+ let asJson = false;
31732
+ for (let i = 0; i < args.length; i++) {
31733
+ const arg = args[i];
31734
+ if (action === "ask" && arg === "--prompt") {
31735
+ prompt = args[++i];
31736
+ if (prompt === undefined) return usage("--prompt needs a text");
31737
+ } else if (action === "ask" && arg.startsWith("--prompt=")) prompt = arg.slice(9);
31738
+ else if (action === "ask" && arg === "--reveal") reveal = true;
31739
+ else if (action === "ask" && arg === "--replace") replace = true;
31740
+ else if (action === "list" && arg === "--json") asJson = true;
31741
+ else if (action !== "list" && name === undefined && !arg.startsWith("-")) name = arg;
31742
+ else return usage("unexpected argument " + JSON.stringify(arg));
31743
+ }
31744
+ let request;
31745
+ if (action === "ask" || action === "placeholder") {
31746
+ if (name === undefined) return usage("a NAME is needed");
31747
+ if (name.length > 128 || !NAME.test(name)) return fail(2, JSON.stringify(name) + " is not a valid name");
31748
+ if (prompt !== undefined && [...prompt].length > 200) return fail(2, "the prompt is longer than 200 characters");
31749
+ request = action === "ask"
31750
+ ? { v: 1, op: "ask", name, ...(prompt !== undefined ? { prompt } : {}), reveal, replace }
31751
+ : { v: 1, op: "placeholder", name };
31752
+ } else if (action === "list") request = { v: 1, op: "list" };
31753
+ else return usage("unknown command " + JSON.stringify(action));
31754
+ let reply;
31755
+ try {
31756
+ reply = await exchange(request);
31757
+ } catch (err) {
31758
+ const code = err && err.code ? err.code : String(err);
31759
+ return fail(EXIT[code] ?? 1, "/dev/sealed: " + code);
31760
+ }
31761
+ if (reply.error !== undefined) {
31762
+ const code = String(reply.error && reply.error.code);
31763
+ const message = reply.error && reply.error.message ? reply.error.message : code;
31764
+ return fail(EXIT[code] ?? 1, (name !== undefined ? name + ": " : "") + message);
31765
+ }
31766
+ const newline = (await ctx.isatty(1).catch(() => false)) ? "\\n" : "";
31767
+ if (action === "list") {
31768
+ const secrets = Array.isArray(reply.secrets) ? reply.secrets : [];
31769
+ if (asJson) {
31770
+ await say(1, JSON.stringify(secrets) + "\\n");
31771
+ return 0;
31772
+ }
31773
+ await say(1, secrets.map((s) => s.name + "\\t" + s.placeholder + "\\t" + s.state + "\\n").join(""));
31774
+ return 0;
31775
+ }
31776
+ if (action === "placeholder") {
31777
+ if (typeof reply.placeholder !== "string") return fail(1, name + ": the vault gave no placeholder");
31778
+ await say(1, reply.placeholder + newline);
31779
+ return 0;
31780
+ }
31781
+ if (reply.state === "cancelled") return fail(1, name + ": cancelled");
31782
+ if (reply.state === "expired") return fail(124, name + ": the request expired");
31783
+ if (reply.state !== "done") return fail(1, name + ": the vault gave no answer");
31784
+ const out = reveal ? reply.value : reply.placeholder;
31785
+ if (typeof out !== "string") return fail(1, name + ": the vault gave no " + (reveal ? "value" : "placeholder"));
31786
+ await say(1, out + newline);
31787
+ return 0;
31788
+
31789
+ async function exchange(req) {
31790
+ const file = await ctx.open("/dev/sealed", { read: true, write: true });
31791
+ try {
31792
+ await ctx.write(file.fd, JSON.stringify(req));
31793
+ const parts = [];
31794
+ for (;;) {
31795
+ const chunk = await ctx.read(file.fd, 65536);
31796
+ if (chunk.length === 0) break;
31797
+ parts.push(chunk);
31798
+ }
31799
+ const bytes = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
31800
+ let at = 0;
31801
+ for (const part of parts) {
31802
+ bytes.set(part, at);
31803
+ at += part.length;
31804
+ part.fill(0);
31805
+ }
31806
+ const text = new TextDecoder().decode(bytes);
31807
+ bytes.fill(0);
31808
+ const parsed = JSON.parse(text);
31809
+ if (typeof parsed !== "object" || parsed === null) throw Object.assign(new Error("EIO"), { code: "EIO" });
31810
+ return parsed;
31811
+ } finally {
31812
+ await file.close().catch(() => undefined);
31813
+ }
31814
+ }
31815
+ }
31816
+ `;
31817
+
31641
31818
  // src/sudo-command.ts
31642
31819
  var SUDO_PATH = "/usr/bin/sudo";
31643
31820
  var SUDO_COMMAND = {
@@ -31847,7 +32024,7 @@ var LAYOUT = [
31847
32024
  ["/usr", 493],
31848
32025
  ["/usr/local", 493]
31849
32026
  ];
31850
- var IN_TREE = /* @__PURE__ */ new Set([SUDO_PATH, VISUDO_PATH, CRONTAB_PATH]);
32027
+ var IN_TREE = /* @__PURE__ */ new Set([SUDO_PATH, VISUDO_PATH, CRONTAB_PATH, SEALED_BIN]);
31851
32028
  var LOAD_SAMPLE_MS = 5e3;
31852
32029
  var LOAD_WINDOWS_MS = [6e4, 3e5, 9e5];
31853
32030
  var encoder10 = new TextEncoder();
@@ -31976,6 +32153,7 @@ function expandDefaults(defaults2, env) {
31976
32153
  function targetOf(command) {
31977
32154
  return {
31978
32155
  abi: command.abi,
32156
+ command: command.name,
31979
32157
  glue: command.glue,
31980
32158
  wasm: command.wasm,
31981
32159
  argv0: command.argv0,
@@ -32072,6 +32250,8 @@ var Launcher = class {
32072
32250
  orphans = /* @__PURE__ */ new Set();
32073
32251
  described = /* @__PURE__ */ new Map();
32074
32252
  creds = /* @__PURE__ */ new Map();
32253
+ programs = /* @__PURE__ */ new Map();
32254
+ cwdPaths = /* @__PURE__ */ new Map();
32075
32255
  tags = /* @__PURE__ */ new Map();
32076
32256
  sessions = /* @__PURE__ */ new Map();
32077
32257
  tagBases = /* @__PURE__ */ new Map();
@@ -32399,7 +32579,7 @@ var Launcher = class {
32399
32579
  this.refusedOf.set(commands, refused4.map(refusing));
32400
32580
  return commands;
32401
32581
  }).then(
32402
- (commands) => this.refusals(commands).set(SUDO_COMMAND.name, SUDO_COMMAND).set(VISUDO_COMMAND.name, VISUDO_COMMAND).set(CRONTAB_COMMAND.name, CRONTAB_COMMAND)
32582
+ (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
32583
  );
32404
32584
  this.catalogs.set(scope, found);
32405
32585
  }
@@ -33172,6 +33352,9 @@ var Launcher = class {
33172
33352
  if (target.abi === "js" && target.wasm === CRONTAB_PATH) {
33173
33353
  return { abi: "js", glue: CRONTAB_SOURCE, path: CRONTAB_PATH };
33174
33354
  }
33355
+ if (target.abi === "js" && target.wasm === SEALED_BIN) {
33356
+ return { abi: "js", glue: SEALED_SOURCE, path: SEALED_BIN };
33357
+ }
33175
33358
  if (target.abi === "js" && target.wasm === REFUSED_PATH) {
33176
33359
  return { abi: "js", glue: REFUSED_SOURCE, path: REFUSED_PATH };
33177
33360
  }
@@ -33230,6 +33413,7 @@ var Launcher = class {
33230
33413
  argv0: target.argv0,
33231
33414
  exe: target.copy ?? target.glue,
33232
33415
  ...exeId,
33416
+ ...target.command !== void 0 && target.copy === void 0 ? { command: target.command } : {},
33233
33417
  args,
33234
33418
  env,
33235
33419
  cwdSlot
@@ -33346,6 +33530,8 @@ var Launcher = class {
33346
33530
  if (session) this.sessions.set(pid, session);
33347
33531
  if (req.tags?.length) this.tagBases.set(pid, req.tagBase ?? req.tags.length);
33348
33532
  if (req.links !== void 0) this.links.set(pid, req.links);
33533
+ if (req.command !== void 0) this.programs.set(pid, req.command);
33534
+ this.cwdPaths.set(pid, req.cwd);
33349
33535
  }
33350
33536
  async admitted(wasm, fds, start2) {
33351
33537
  for (; ; ) {
@@ -33514,6 +33700,7 @@ var Launcher = class {
33514
33700
  void handle.exited.then(() => {
33515
33701
  this.cwds.release(this.cwdSlots.get(pid));
33516
33702
  this.cwdSlots.delete(pid);
33703
+ this.cwdPaths.delete(pid);
33517
33704
  this.freshModules.forget(pid);
33518
33705
  const spent = handle.times?.();
33519
33706
  if (spent) this.spent.set(pid, spent);
@@ -33547,6 +33734,7 @@ var Launcher = class {
33547
33734
  }
33548
33735
  this.jobs.remove(pid);
33549
33736
  this.described.delete(pid);
33737
+ this.programs.delete(pid);
33550
33738
  const spent = this.spent.get(pid);
33551
33739
  if (spent) {
33552
33740
  this.reapedTimes.utime += spent.utime;
@@ -33846,7 +34034,21 @@ var Launcher = class {
33846
34034
  cwdChdir: (path) => this.chdirHeld(pid, path)
33847
34035
  };
33848
34036
  }
34037
+ async processFacts(pid) {
34038
+ const described2 = this.described.get(pid);
34039
+ const cwd = this.cwdPaths.get(pid);
34040
+ if (!described2 || described2.exe === void 0 || cwd === void 0) return void 0;
34041
+ const real = (path) => physicalPathAsync(this.base, path).catch(() => path);
34042
+ const command = this.programs.get(pid);
34043
+ return {
34044
+ exe: command !== void 0 ? `/usr/bin/${command}` : await real(described2.exe),
34045
+ argv: [...described2.argv],
34046
+ cwd: await real(cwd),
34047
+ tty: described2.tty
34048
+ };
34049
+ }
33849
34050
  async chdirHeld(pid, path) {
34051
+ this.cwdPaths.set(pid, path);
33850
34052
  this.cwds.release(this.cwdSlots.get(pid));
33851
34053
  const slot2 = await this.cwds.hold(path);
33852
34054
  this.cwdSlots.set(pid, slot2);
@@ -33910,6 +34112,7 @@ var Launcher = class {
33910
34112
  wireSealed() {
33911
34113
  this.sealed.tickets.normalise = (path) => physicalPathAsync(this.base, path);
33912
34114
  this.sealed.userOf = async (uid) => (await this.users.lookup(uid))?.name;
34115
+ this.sealed.factsOf = (pid) => this.processFacts(pid);
33913
34116
  }
33914
34117
  embedderBroker(hook) {
33915
34118
  const broker = new EmbedderBroker(