@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.
@@ -8740,7 +8740,7 @@ function satisfies(version, range2) {
8740
8740
  }
8741
8741
 
8742
8742
  // src/version.ts
8743
- var KERNEL_VERSION = true ? "1.174.2" : "0.0.0-dev";
8743
+ var KERNEL_VERSION = true ? "1.175.0" : "0.0.0-dev";
8744
8744
  var OS_RELEASE = "6.0.0-slicc";
8745
8745
  function procVersion() {
8746
8746
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -19063,7 +19063,12 @@ function sessionValues(reply, asked) {
19063
19063
  // src/kernel/sealed.ts
19064
19064
  var SEALED_MAX_BYTES = 64 * 1024;
19065
19065
  var SEALED_TIMEOUT_MS = 1e4;
19066
+ var SEALED_ASK_TIMEOUT_MS = 305e3;
19067
+ var SEALED_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
19068
+ var SEALED_NAME_MAX = 128;
19069
+ var SEALED_PROMPT_MAX = 200;
19066
19070
  var SEALED_OPS = /* @__PURE__ */ new Set([
19071
+ "ask",
19067
19072
  "seal",
19068
19073
  "placeholder",
19069
19074
  "list",
@@ -19078,6 +19083,16 @@ function tagFields(tags) {
19078
19083
  if (tag === void 0) return {};
19079
19084
  return tags.length > 1 ? { tag, tags: [...tags] } : { tag };
19080
19085
  }
19086
+ var ASK_FIELDS = /* @__PURE__ */ new Set([
19087
+ "v",
19088
+ "op",
19089
+ "name",
19090
+ "prompt",
19091
+ "reveal",
19092
+ "replace",
19093
+ "caller",
19094
+ "requester"
19095
+ ]);
19081
19096
  var encoder6 = new TextEncoder();
19082
19097
  var decoder3 = new TextDecoder("utf-8", { fatal: true });
19083
19098
  function parseSealed(bytes) {
@@ -19095,6 +19110,7 @@ function parseSealed(bytes) {
19095
19110
  if (req.v !== 1 || typeof req.op !== "string" || !SEALED_OPS.has(req.op))
19096
19111
  throw new KernelError("EINVAL");
19097
19112
  if (req.op === "materialize" && !materializable(req)) throw new KernelError("EINVAL");
19113
+ if (req.op === "ask" && !askable(req)) throw new KernelError("EINVAL");
19098
19114
  return req;
19099
19115
  } finally {
19100
19116
  bytes.fill(0);
@@ -19104,6 +19120,27 @@ function materializable(req) {
19104
19120
  if (req.shellGrants !== true && !isStrings(req.names)) return false;
19105
19121
  return isStrings(req.argv) && typeof req.path === "string" && typeof req.cwd === "string";
19106
19122
  }
19123
+ function askable(req) {
19124
+ if (Object.keys(req).some((key) => !ASK_FIELDS.has(key))) return false;
19125
+ const { name: name2, prompt, reveal, replace } = req;
19126
+ if (typeof name2 !== "string" || name2.length > SEALED_NAME_MAX || !SEALED_NAME.test(name2))
19127
+ return false;
19128
+ if (prompt !== void 0 && (typeof prompt !== "string" || [...prompt].length > SEALED_PROMPT_MAX))
19129
+ return false;
19130
+ return [reveal, replace].every((flag) => flag === void 0 || typeof flag === "boolean");
19131
+ }
19132
+ function askRequest(fields, caller, requester) {
19133
+ return {
19134
+ v: 1,
19135
+ op: "ask",
19136
+ name: fields.name,
19137
+ ...fields.prompt !== void 0 ? { prompt: fields.prompt } : {},
19138
+ reveal: fields.reveal === true,
19139
+ replace: fields.replace === true,
19140
+ caller,
19141
+ requester
19142
+ };
19143
+ }
19107
19144
  function errorReply(code, message3) {
19108
19145
  return { v: 1, error: { code, message: message3 } };
19109
19146
  }
@@ -19136,7 +19173,7 @@ var SealedChannel = class {
19136
19173
  this.broker = broker;
19137
19174
  this.access = access2;
19138
19175
  }
19139
- send(bytes, caller, gone) {
19176
+ send(bytes, caller, gone, requester = async () => void 0) {
19140
19177
  if (this.access === "read") {
19141
19178
  bytes.fill(0);
19142
19179
  throw new KernelError("EBADF");
@@ -19149,10 +19186,15 @@ var SealedChannel = class {
19149
19186
  const withdraw = () => control.abort("withdrawn");
19150
19187
  if (gone.aborted) withdraw();
19151
19188
  else gone.addEventListener("abort", withdraw, { once: true });
19152
- const timer = setTimeout(() => control.abort("timeout"), this.broker.timeoutMs);
19189
+ const ask = fields.op === "ask";
19190
+ const timer = setTimeout(
19191
+ () => control.abort(ask ? "expired" : "timeout"),
19192
+ ask ? this.broker.askTimeoutMs : this.broker.timeoutMs
19193
+ );
19153
19194
  const req = { ...fields, v: 1, op: fields.op, caller };
19195
+ const asked = ask ? this.asking(fields, caller, requester, control.signal) : void 0;
19154
19196
  const flight = { control, done: Promise.resolve() };
19155
- flight.done = this.answer(req, control.signal).then(async (reply) => {
19197
+ flight.done = (asked ?? this.answer(req, control.signal)).then(async (reply) => {
19156
19198
  clearTimeout(timer);
19157
19199
  gone.removeEventListener("abort", withdraw);
19158
19200
  if (reply && req.op === "materialize")
@@ -19164,6 +19206,17 @@ var SealedChannel = class {
19164
19206
  this.inflight = flight;
19165
19207
  return length2;
19166
19208
  }
19209
+ async asking(fields, caller, requester, signal) {
19210
+ const facts = await requester().catch(() => void 0);
19211
+ if (signal.aborted) return this.stopped(signal);
19212
+ if (!facts) return errorReply("EIO", "the kernel has no record of this process");
19213
+ return this.answer(askRequest(fields, caller, facts), signal);
19214
+ }
19215
+ stopped(signal) {
19216
+ if (signal.reason === "timeout")
19217
+ return errorReply("ETIMEDOUT", "the vault did not answer within 10 s");
19218
+ return signal.reason === "expired" ? { v: 1, state: "expired" } : void 0;
19219
+ }
19167
19220
  async answer(req, signal) {
19168
19221
  const hook = this.broker.hook;
19169
19222
  if (!hook) return errorReply("ENOTCONN", "no vault is connected");
@@ -19176,8 +19229,7 @@ var SealedChannel = class {
19176
19229
  (err) => errorReply("EIO", lineOf3(err))
19177
19230
  );
19178
19231
  const reply = await Promise.race([asked, stopped]);
19179
- if (!signal.aborted) return reply;
19180
- return signal.reason === "timeout" ? errorReply("ETIMEDOUT", "the vault did not answer within 10 s") : void 0;
19232
+ return signal.aborted ? this.stopped(signal) : reply;
19181
19233
  }
19182
19234
  drop() {
19183
19235
  this.reply?.fill(0);
@@ -19239,7 +19291,9 @@ var SealedBroker = class {
19239
19291
  tagOf;
19240
19292
  tickets = new TicketBook();
19241
19293
  sessions = new SessionScrubs();
19294
+ askTimeoutMs = SEALED_ASK_TIMEOUT_MS;
19242
19295
  userOf = async () => void 0;
19296
+ factsOf = async () => void 0;
19243
19297
  constructor(hook, users, timeoutMs = SEALED_TIMEOUT_MS, tagOf = () => void 0) {
19244
19298
  this.hook = hook;
19245
19299
  this.users = users;
@@ -27626,7 +27680,10 @@ var WasmProcess = class {
27626
27680
  }
27627
27681
  async write(fd, body, nonblock = false, cancelled) {
27628
27682
  const file = this.fds.get(fd).file;
27629
- if (file.sealed) return file.sealed.send(body, await this.sealedCaller(), this.gone.signal);
27683
+ if (file.sealed) {
27684
+ const caller = await this.sealedCaller();
27685
+ return file.sealed.send(body, caller, this.gone.signal, () => this.sealedRequester());
27686
+ }
27630
27687
  if (!file.write) throw new KernelError("EBADF");
27631
27688
  if (file.embedder) return this.writeEmbedder(file, body, nonblock, cancelled);
27632
27689
  if (!pollFile(file).writable) {
@@ -27831,6 +27888,10 @@ var WasmProcess = class {
27831
27888
  const ids = { pid: shown2?.pid ?? this.pid, ppid: shown2?.ppid ?? 0 };
27832
27889
  return broker.stamp(ids, this.cred, broker.tagOf(this.pid));
27833
27890
  }
27891
+ async sealedRequester() {
27892
+ const facts = await this.options.sealed.factsOf(this.pid);
27893
+ return facts && { ...facts, stdin: this.stdinKind() };
27894
+ }
27834
27895
  guarded() {
27835
27896
  const { guard: guard2 } = this.options;
27836
27897
  return guard2 && { fs: guard2.fs, dac: guard2.dac, cred: this.cred, pid: this.pid, links: guard2.links };
@@ -32738,6 +32799,122 @@ function refusing(command) {
32738
32799
  };
32739
32800
  }
32740
32801
 
32802
+ // src/sealed-command.ts
32803
+ var SEALED_BIN = "/usr/bin/sealed";
32804
+ var SEALED_COMMAND = {
32805
+ name: "sealed",
32806
+ abi: "js",
32807
+ glue: SEALED_BIN,
32808
+ wasm: SEALED_BIN,
32809
+ argv0: "sealed"
32810
+ };
32811
+ var SEALED_SOURCE = `export default async function sealed(ctx) {
32812
+ const USAGE = "usage: sealed ask NAME [--prompt TEXT] [--reveal] [--replace]\\n sealed placeholder NAME\\n sealed list [--json]\\n";
32813
+ const NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
32814
+ const EXIT = { EINVAL: 2, EACCES: 3, EAGAIN: 4, ETIMEDOUT: 124 };
32815
+ const say = (fd, text) => ctx.write(fd, text).catch(() => undefined);
32816
+ const fail = async (status, text) => {
32817
+ await say(2, "sealed: " + text + "\\n");
32818
+ return status;
32819
+ };
32820
+ const usage = async (text) => {
32821
+ if (text !== undefined) await say(2, "sealed: " + text + "\\n");
32822
+ await say(2, USAGE);
32823
+ return 2;
32824
+ };
32825
+ const [action, ...args] = ctx.argv.slice(1);
32826
+ if (action === undefined || action === "-h" || action === "--help") return usage();
32827
+ let name;
32828
+ let prompt;
32829
+ let reveal = false;
32830
+ let replace = false;
32831
+ let asJson = false;
32832
+ for (let i = 0; i < args.length; i++) {
32833
+ const arg = args[i];
32834
+ if (action === "ask" && arg === "--prompt") {
32835
+ prompt = args[++i];
32836
+ if (prompt === undefined) return usage("--prompt needs a text");
32837
+ } else if (action === "ask" && arg.startsWith("--prompt=")) prompt = arg.slice(9);
32838
+ else if (action === "ask" && arg === "--reveal") reveal = true;
32839
+ else if (action === "ask" && arg === "--replace") replace = true;
32840
+ else if (action === "list" && arg === "--json") asJson = true;
32841
+ else if (action !== "list" && name === undefined && !arg.startsWith("-")) name = arg;
32842
+ else return usage("unexpected argument " + JSON.stringify(arg));
32843
+ }
32844
+ let request;
32845
+ if (action === "ask" || action === "placeholder") {
32846
+ if (name === undefined) return usage("a NAME is needed");
32847
+ if (name.length > 128 || !NAME.test(name)) return fail(2, JSON.stringify(name) + " is not a valid name");
32848
+ if (prompt !== undefined && [...prompt].length > 200) return fail(2, "the prompt is longer than 200 characters");
32849
+ request = action === "ask"
32850
+ ? { v: 1, op: "ask", name, ...(prompt !== undefined ? { prompt } : {}), reveal, replace }
32851
+ : { v: 1, op: "placeholder", name };
32852
+ } else if (action === "list") request = { v: 1, op: "list" };
32853
+ else return usage("unknown command " + JSON.stringify(action));
32854
+ let reply;
32855
+ try {
32856
+ reply = await exchange(request);
32857
+ } catch (err) {
32858
+ const code = err && err.code ? err.code : String(err);
32859
+ return fail(EXIT[code] ?? 1, "/dev/sealed: " + code);
32860
+ }
32861
+ if (reply.error !== undefined) {
32862
+ const code = String(reply.error && reply.error.code);
32863
+ const message = reply.error && reply.error.message ? reply.error.message : code;
32864
+ return fail(EXIT[code] ?? 1, (name !== undefined ? name + ": " : "") + message);
32865
+ }
32866
+ const newline = (await ctx.isatty(1).catch(() => false)) ? "\\n" : "";
32867
+ if (action === "list") {
32868
+ const secrets = Array.isArray(reply.secrets) ? reply.secrets : [];
32869
+ if (asJson) {
32870
+ await say(1, JSON.stringify(secrets) + "\\n");
32871
+ return 0;
32872
+ }
32873
+ await say(1, secrets.map((s) => s.name + "\\t" + s.placeholder + "\\t" + s.state + "\\n").join(""));
32874
+ return 0;
32875
+ }
32876
+ if (action === "placeholder") {
32877
+ if (typeof reply.placeholder !== "string") return fail(1, name + ": the vault gave no placeholder");
32878
+ await say(1, reply.placeholder + newline);
32879
+ return 0;
32880
+ }
32881
+ if (reply.state === "cancelled") return fail(1, name + ": cancelled");
32882
+ if (reply.state === "expired") return fail(124, name + ": the request expired");
32883
+ if (reply.state !== "done") return fail(1, name + ": the vault gave no answer");
32884
+ const out = reveal ? reply.value : reply.placeholder;
32885
+ if (typeof out !== "string") return fail(1, name + ": the vault gave no " + (reveal ? "value" : "placeholder"));
32886
+ await say(1, out + newline);
32887
+ return 0;
32888
+
32889
+ async function exchange(req) {
32890
+ const file = await ctx.open("/dev/sealed", { read: true, write: true });
32891
+ try {
32892
+ await ctx.write(file.fd, JSON.stringify(req));
32893
+ const parts = [];
32894
+ for (;;) {
32895
+ const chunk = await ctx.read(file.fd, 65536);
32896
+ if (chunk.length === 0) break;
32897
+ parts.push(chunk);
32898
+ }
32899
+ const bytes = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
32900
+ let at = 0;
32901
+ for (const part of parts) {
32902
+ bytes.set(part, at);
32903
+ at += part.length;
32904
+ part.fill(0);
32905
+ }
32906
+ const text = new TextDecoder().decode(bytes);
32907
+ bytes.fill(0);
32908
+ const parsed = JSON.parse(text);
32909
+ if (typeof parsed !== "object" || parsed === null) throw Object.assign(new Error("EIO"), { code: "EIO" });
32910
+ return parsed;
32911
+ } finally {
32912
+ await file.close().catch(() => undefined);
32913
+ }
32914
+ }
32915
+ }
32916
+ `;
32917
+
32741
32918
  // src/sudo-command.ts
32742
32919
  var SUDO_PATH = "/usr/bin/sudo";
32743
32920
  var SUDO_COMMAND = {
@@ -32951,7 +33128,7 @@ var LAYOUT = [
32951
33128
  ["/usr", 493],
32952
33129
  ["/usr/local", 493]
32953
33130
  ];
32954
- var IN_TREE = /* @__PURE__ */ new Set([SUDO_PATH, VISUDO_PATH, CRONTAB_PATH]);
33131
+ var IN_TREE = /* @__PURE__ */ new Set([SUDO_PATH, VISUDO_PATH, CRONTAB_PATH, SEALED_BIN]);
32955
33132
  var LOAD_SAMPLE_MS = 5e3;
32956
33133
  var LOAD_WINDOWS_MS = [6e4, 3e5, 9e5];
32957
33134
  var encoder10 = new TextEncoder();
@@ -33080,6 +33257,7 @@ function expandDefaults(defaults2, env) {
33080
33257
  function targetOf(command) {
33081
33258
  return {
33082
33259
  abi: command.abi,
33260
+ command: command.name,
33083
33261
  glue: command.glue,
33084
33262
  wasm: command.wasm,
33085
33263
  argv0: command.argv0,
@@ -33176,6 +33354,8 @@ var Launcher = class {
33176
33354
  orphans = /* @__PURE__ */ new Set();
33177
33355
  described = /* @__PURE__ */ new Map();
33178
33356
  creds = /* @__PURE__ */ new Map();
33357
+ programs = /* @__PURE__ */ new Map();
33358
+ cwdPaths = /* @__PURE__ */ new Map();
33179
33359
  tags = /* @__PURE__ */ new Map();
33180
33360
  sessions = /* @__PURE__ */ new Map();
33181
33361
  tagBases = /* @__PURE__ */ new Map();
@@ -33503,7 +33683,7 @@ var Launcher = class {
33503
33683
  this.refusedOf.set(commands, refused4.map(refusing));
33504
33684
  return commands;
33505
33685
  }).then(
33506
- (commands) => this.refusals(commands).set(SUDO_COMMAND.name, SUDO_COMMAND).set(VISUDO_COMMAND.name, VISUDO_COMMAND).set(CRONTAB_COMMAND.name, CRONTAB_COMMAND)
33686
+ (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)
33507
33687
  );
33508
33688
  this.catalogs.set(scope, found);
33509
33689
  }
@@ -34276,6 +34456,9 @@ var Launcher = class {
34276
34456
  if (target.abi === "js" && target.wasm === CRONTAB_PATH) {
34277
34457
  return { abi: "js", glue: CRONTAB_SOURCE, path: CRONTAB_PATH };
34278
34458
  }
34459
+ if (target.abi === "js" && target.wasm === SEALED_BIN) {
34460
+ return { abi: "js", glue: SEALED_SOURCE, path: SEALED_BIN };
34461
+ }
34279
34462
  if (target.abi === "js" && target.wasm === REFUSED_PATH) {
34280
34463
  return { abi: "js", glue: REFUSED_SOURCE, path: REFUSED_PATH };
34281
34464
  }
@@ -34334,6 +34517,7 @@ var Launcher = class {
34334
34517
  argv0: target.argv0,
34335
34518
  exe: target.copy ?? target.glue,
34336
34519
  ...exeId,
34520
+ ...target.command !== void 0 && target.copy === void 0 ? { command: target.command } : {},
34337
34521
  args,
34338
34522
  env,
34339
34523
  cwdSlot
@@ -34450,6 +34634,8 @@ var Launcher = class {
34450
34634
  if (session) this.sessions.set(pid, session);
34451
34635
  if (req.tags?.length) this.tagBases.set(pid, req.tagBase ?? req.tags.length);
34452
34636
  if (req.links !== void 0) this.links.set(pid, req.links);
34637
+ if (req.command !== void 0) this.programs.set(pid, req.command);
34638
+ this.cwdPaths.set(pid, req.cwd);
34453
34639
  }
34454
34640
  async admitted(wasm, fds, start2) {
34455
34641
  for (; ; ) {
@@ -34618,6 +34804,7 @@ var Launcher = class {
34618
34804
  void handle.exited.then(() => {
34619
34805
  this.cwds.release(this.cwdSlots.get(pid));
34620
34806
  this.cwdSlots.delete(pid);
34807
+ this.cwdPaths.delete(pid);
34621
34808
  this.freshModules.forget(pid);
34622
34809
  const spent = handle.times?.();
34623
34810
  if (spent) this.spent.set(pid, spent);
@@ -34651,6 +34838,7 @@ var Launcher = class {
34651
34838
  }
34652
34839
  this.jobs.remove(pid);
34653
34840
  this.described.delete(pid);
34841
+ this.programs.delete(pid);
34654
34842
  const spent = this.spent.get(pid);
34655
34843
  if (spent) {
34656
34844
  this.reapedTimes.utime += spent.utime;
@@ -34950,7 +35138,21 @@ var Launcher = class {
34950
35138
  cwdChdir: (path) => this.chdirHeld(pid, path)
34951
35139
  };
34952
35140
  }
35141
+ async processFacts(pid) {
35142
+ const described2 = this.described.get(pid);
35143
+ const cwd = this.cwdPaths.get(pid);
35144
+ if (!described2 || described2.exe === void 0 || cwd === void 0) return void 0;
35145
+ const real = (path) => physicalPathAsync(this.base, path).catch(() => path);
35146
+ const command = this.programs.get(pid);
35147
+ return {
35148
+ exe: command !== void 0 ? `/usr/bin/${command}` : await real(described2.exe),
35149
+ argv: [...described2.argv],
35150
+ cwd: await real(cwd),
35151
+ tty: described2.tty
35152
+ };
35153
+ }
34953
35154
  async chdirHeld(pid, path) {
35155
+ this.cwdPaths.set(pid, path);
34954
35156
  this.cwds.release(this.cwdSlots.get(pid));
34955
35157
  const slot2 = await this.cwds.hold(path);
34956
35158
  this.cwdSlots.set(pid, slot2);
@@ -35014,6 +35216,7 @@ var Launcher = class {
35014
35216
  wireSealed() {
35015
35217
  this.sealed.tickets.normalise = (path) => physicalPathAsync(this.base, path);
35016
35218
  this.sealed.userOf = async (uid) => (await this.users.lookup(uid))?.name;
35219
+ this.sealed.factsOf = (pid) => this.processFacts(pid);
35017
35220
  }
35018
35221
  embedderBroker(hook) {
35019
35222
  const broker = new EmbedderBroker(