@ai-ecoverse/slicc-kernel 1.180.0 → 1.181.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/node.js CHANGED
@@ -3787,7 +3787,7 @@ function satisfies(version, range2) {
3787
3787
  }
3788
3788
 
3789
3789
  // src/version.ts
3790
- var KERNEL_VERSION = true ? "1.180.0" : "0.0.0-dev";
3790
+ var KERNEL_VERSION = true ? "1.181.0" : "0.0.0-dev";
3791
3791
  var OS_RELEASE = "6.0.0-slicc";
3792
3792
  function procVersion() {
3793
3793
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -4652,27 +4652,27 @@ function hashId(text) {
4652
4652
  return hex4(a) + hex4(b);
4653
4653
  }
4654
4654
  function split(text, count2) {
4655
- const words2 = [];
4655
+ const words3 = [];
4656
4656
  let rest2 = text;
4657
4657
  for (let i = 0; i < count2; i++) {
4658
4658
  const match = /^(\S+)\s+/.exec(rest2);
4659
4659
  if (!match) return void 0;
4660
- words2.push(match[1]);
4660
+ words3.push(match[1]);
4661
4661
  rest2 = rest2.slice(match[0].length);
4662
4662
  }
4663
- return [words2, rest2.trim()];
4663
+ return [words3, rest2.trim()];
4664
4664
  }
4665
4665
  function jobOf(content, pending, ctx, env) {
4666
4666
  const fields = content.startsWith("@") ? 1 : 5;
4667
4667
  const parts2 = split(content, fields + (ctx.system ? 1 : 0));
4668
4668
  if (!parts2 || parts2[1] === "") throw new Error("no command");
4669
- const [words2, command] = parts2;
4670
- const schedule = words2.slice(0, fields).join(" ");
4669
+ const [words3, command] = parts2;
4670
+ const schedule = words3.slice(0, fields).join(" ");
4671
4671
  const when = parseWhen(schedule);
4672
4672
  const zone = env.CRON_TZ;
4673
4673
  if (zone && !when.reboot && canonicalZone(zone) === void 0)
4674
4674
  throw new Error(`CRON_TZ: unknown time zone "${zone}"`);
4675
- const user = ctx.system ? words2[fields] : void 0;
4675
+ const user = ctx.system ? words3[fields] : void 0;
4676
4676
  if (user !== void 0 && !USER_NAME.test(user)) throw new Error(`bad user name ${user}`);
4677
4677
  if (pending !== void 0 && !JOB_NAME.test(pending))
4678
4678
  throw new Error(`bad job name "${pending}"`);
@@ -8374,9 +8374,11 @@ var SpawnError = class extends Error {
8374
8374
  };
8375
8375
  var ChangedError = class extends SpawnError {
8376
8376
  notice;
8377
- constructor(notice) {
8377
+ file;
8378
+ constructor(notice, file) {
8378
8379
  super("EPERM");
8379
8380
  this.notice = notice;
8381
+ this.file = file;
8380
8382
  }
8381
8383
  };
8382
8384
  function interrupted(signal) {
@@ -15453,21 +15455,21 @@ function unescaped(double) {
15453
15455
  }
15454
15456
  function splitWords(text) {
15455
15457
  const piece = /'([^']*)'|"((?:[^"\\]|\\[\s\S])*)"|\\([\s\S])|([^\s'"\\]+)|(\s+)/y;
15456
- const words2 = [];
15458
+ const words3 = [];
15457
15459
  let word;
15458
15460
  while (piece.lastIndex < text.length) {
15459
15461
  const m = piece.exec(text);
15460
15462
  if (!m) return void 0;
15461
15463
  const [, single, double, escaped, plain3, space] = m;
15462
15464
  if (space !== void 0) {
15463
- if (word !== void 0) words2.push(word);
15465
+ if (word !== void 0) words3.push(word);
15464
15466
  word = void 0;
15465
15467
  continue;
15466
15468
  }
15467
15469
  word = (word ?? "") + (single ?? (double !== void 0 ? unescaped(double) : escaped ?? plain3));
15468
15470
  }
15469
- if (word !== void 0) words2.push(word);
15470
- return words2;
15471
+ if (word !== void 0) words3.push(word);
15472
+ return words3;
15471
15473
  }
15472
15474
  function quoteWord(word) {
15473
15475
  const text = hasControl(word) ? withoutControl(word) : word;
@@ -15488,14 +15490,14 @@ function addPlain(word, plain3) {
15488
15490
  }
15489
15491
  function splitPatterns(text) {
15490
15492
  const piece = /'([^']*)'|"((?:[^"\\]|\\[\s\S])*)"|\\([\s\S])|([^\s'"\\]+)|(\s+)/y;
15491
- const words2 = [];
15493
+ const words3 = [];
15492
15494
  let word;
15493
15495
  while (piece.lastIndex < text.length) {
15494
15496
  const m = piece.exec(text);
15495
15497
  if (!m) return void 0;
15496
15498
  const [, single, double, escaped, plain3, space] = m;
15497
15499
  if (space !== void 0) {
15498
- if (word !== void 0) words2.push(word);
15500
+ if (word !== void 0) words3.push(word);
15499
15501
  word = void 0;
15500
15502
  continue;
15501
15503
  }
@@ -15503,8 +15505,8 @@ function splitPatterns(text) {
15503
15505
  if (plain3 !== void 0) addPlain(word, plain3);
15504
15506
  else addPart(word, single ?? (double !== void 0 ? unescaped(double) : escaped));
15505
15507
  }
15506
- if (word !== void 0) words2.push(word);
15507
- return words2;
15508
+ if (word !== void 0) words3.push(word);
15509
+ return words3;
15508
15510
  }
15509
15511
  var literal = (pattern) => pattern.map((p) => p ?? "*").join("");
15510
15512
  var isAny = (pattern) => pattern?.length === 1 && pattern[0] === null;
@@ -15577,14 +15579,14 @@ function whoError(who) {
15577
15579
  return `expected a user name, %group or ALL first, not ${shown(who)}`;
15578
15580
  }
15579
15581
  function cwdOf(word) {
15580
- const words2 = splitWords(word);
15581
- const path = words2?.length === 1 ? words2[0] : "";
15582
+ const words3 = splitWords(word);
15583
+ const path = words3?.length === 1 ? words3[0] : "";
15582
15584
  return path.startsWith("/") ? normalizePath(path) : "";
15583
15585
  }
15584
15586
  function commandOf2(text, after) {
15585
- const words2 = splitPatterns(text);
15586
- if (words2 === void 0) return "a quote in the command is not closed, or it ends in a \\";
15587
- const [first, ...args] = words2;
15587
+ const words3 = splitPatterns(text);
15588
+ if (words3 === void 0) return "a quote in the command is not closed, or it ends in a \\";
15589
+ const [first, ...args] = words3;
15588
15590
  const path = first ? literal(first) : "";
15589
15591
  if (path === "") return `expected a command after ${after}`;
15590
15592
  if (!path.startsWith("/")) return `expected an absolute command path, not ${shown(path)}`;
@@ -16274,6 +16276,9 @@ function recordable(cwd, argv) {
16274
16276
  function changedText(file) {
16275
16277
  return `sudo: ${file} changed since it was approved`;
16276
16278
  }
16279
+ function requestedText(file) {
16280
+ return `sudo: ${file} changed since it was requested`;
16281
+ }
16277
16282
  function changedFile(approved, now) {
16278
16283
  const files = /* @__PURE__ */ new Set([...Object.keys(approved), ...Object.keys(now)]);
16279
16284
  return [...files].find((file) => approved[file] !== now[file]);
@@ -16281,6 +16286,67 @@ function changedFile(approved, now) {
16281
16286
  function pinned(rule, bound3) {
16282
16287
  return rule.sha256 === void 0 || changedFile(rule.sha256, bound3) === void 0;
16283
16288
  }
16289
+ var REPLAY_ID = /^[!-~]{1,256}$/;
16290
+ var REPLAY_TAGS_MAX = 64;
16291
+ var REPLAY_ARGV_MAX = 4096;
16292
+ var REPLAY_BYTES_MAX = 1024 * 1024;
16293
+ var NOT_A_REQUEST = "sudo: that isn't a sudo request this kernel can run";
16294
+ function strings(value2, max) {
16295
+ return Array.isArray(value2) && value2.length <= max && value2.every((s) => typeof s === "string");
16296
+ }
16297
+ function words(value2) {
16298
+ if (!strings(value2, REPLAY_ARGV_MAX) || value2.length === 0) return false;
16299
+ const bytes = value2.reduce((n, s) => n + s.length + 1, 0);
16300
+ return bytes <= REPLAY_BYTES_MAX && !value2.some((s) => s.includes("\0"));
16301
+ }
16302
+ function filesOf(value2) {
16303
+ if (value2 === void 0) return {};
16304
+ if (!Array.isArray(value2) || value2.length > OPERANDS_MAX) return void 0;
16305
+ const files = {};
16306
+ for (const file of value2) {
16307
+ const { path, sha256: hex4 } = file ?? {};
16308
+ if (typeof path !== "string" || typeof hex4 !== "string") return void 0;
16309
+ files[path] = hex4;
16310
+ }
16311
+ return files;
16312
+ }
16313
+ function flagsOf(value2) {
16314
+ if (typeof value2 !== "object" || value2 === null || Array.isArray(value2)) return void 0;
16315
+ const entries = Object.entries(value2);
16316
+ if (entries.length > ROOT_ONLY_MAX || entries.some(([, v]) => typeof v !== "boolean")) {
16317
+ return void 0;
16318
+ }
16319
+ return Object.fromEntries(entries);
16320
+ }
16321
+ function replayOf(value2) {
16322
+ if (typeof value2 !== "object" || value2 === null) return failed2("EINVAL", NOT_A_REQUEST);
16323
+ const r = value2;
16324
+ if (r.validate === true) return failed2("EINVAL", "sudo: a sudo -v request runs no command");
16325
+ if (r.stdin === "pipe" || r.stdin === "file") {
16326
+ return failed2("EINVAL", `sudo: the request read a ${r.stdin}, and that input is gone`);
16327
+ }
16328
+ const { id: id2, user, uid, target, argv, path, cwd, stdin, tag, tags } = r;
16329
+ const rootOnly2 = flagsOf(r.rootOnly);
16330
+ const bound3 = filesOf(r.userWritable);
16331
+ const named = (s) => typeof s === "string" && s !== "";
16332
+ const absolute2 = (s) => typeof s === "string" && s.startsWith("/") && s.length <= PATH_MAX && !s.includes("\0");
16333
+ const valid2 = typeof id2 === "string" && REPLAY_ID.test(id2) && named(user) && named(target) && Number.isInteger(uid) && uid >= 0 && words(argv) && absolute2(path) && absolute2(cwd) && (stdin === "tty" || stdin === "none") && (tag === void 0 || typeof tag === "string") && (tags === void 0 || strings(tags, REPLAY_TAGS_MAX)) && rootOnly2 !== void 0 && bound3 !== void 0;
16334
+ if (!valid2) return failed2("EINVAL", NOT_A_REQUEST);
16335
+ const all = tags ?? (tag === void 0 ? [] : [tag]);
16336
+ return {
16337
+ id: id2,
16338
+ user,
16339
+ uid,
16340
+ target,
16341
+ argv,
16342
+ path,
16343
+ cwd,
16344
+ stdin,
16345
+ rootOnly: rootOnly2,
16346
+ bound: bound3,
16347
+ tags: [...new Set(all)]
16348
+ };
16349
+ }
16284
16350
  var VISUDO_FILES_MAX = 64;
16285
16351
  var VISUDO_TEXT_MAX = 1024 * 1024;
16286
16352
  function visudoCall(req) {
@@ -16682,6 +16748,61 @@ var Sudo = class {
16682
16748
  if (parsed2.validate) return this.validate(session, root, mine, call.cwd);
16683
16749
  return this.command(session, root, mine, call);
16684
16750
  }
16751
+ async replay(value2) {
16752
+ const r = replayOf(value2);
16753
+ if ("error" in r) return r;
16754
+ if (this.o.mode !== "enforce") {
16755
+ return failed2("EINVAL", `sudo: users are '${this.o.mode}', so no request needed approval`);
16756
+ }
16757
+ const self = await this.o.users.lookup(r.user);
16758
+ if (!self) return failed2("ENOENT", `sudo: unknown user ${r.user}`);
16759
+ if (self.uid !== r.uid) return failed2("EPERM", `sudo: ${self.name} is no longer uid ${r.uid}`);
16760
+ const target = await this.lookup(r.target);
16761
+ if (!target) return failed2("ENOENT", `sudo: unknown user ${r.target}`);
16762
+ const cred = credOf(self);
16763
+ const [word = "", ...args] = r.argv;
16764
+ const argv = [r.path, ...args];
16765
+ if (!await this.mayAsk(self, target, r, argv)) {
16766
+ const quoted = argv.map(quoteWord).join(" ");
16767
+ return failed2(
16768
+ "EACCES",
16769
+ `sudo: ${self.name} may no longer ask to run ${quoted} as ${target.name}`
16770
+ );
16771
+ }
16772
+ if (await this.canonical(r.cwd) !== r.cwd) {
16773
+ return failed2("EACCES", `sudo: ${r.cwd} changed since it was requested`);
16774
+ }
16775
+ if (!await this.reachable(cred, r.cwd)) {
16776
+ return failed2(
16777
+ "EACCES",
16778
+ `sudo: cannot run in ${r.cwd}: it isn't a directory ${self.name} can enter`
16779
+ );
16780
+ }
16781
+ const path = await this.locate(word, r.cwd);
16782
+ if (!path) return failed2("ENOENT", `sudo: ${word}: command not found`);
16783
+ if (path !== r.path) return failed2("EPERM", `sudo: ${word} now runs ${path}, not ${r.path}`);
16784
+ const bound3 = await this.bound(path, r.cwd);
16785
+ if (!bound3) return failed2("ENOENT", `sudo: ${word}: command not found`);
16786
+ const changed = changedFile(r.bound, bound3);
16787
+ if (changed !== void 0) return failed2("EPERM", requestedText(changed));
16788
+ const now = await this.rootOnly(r.cwd, args);
16789
+ const flipped = Object.keys(r.rootOnly).find((at) => r.rootOnly[at] && now[at] !== true);
16790
+ if (flipped !== void 0) return failed2("EPERM", `sudo: ${flipped} is no longer root-only`);
16791
+ const { grant } = this.grant(self, target, cred, r.argv, path, r.cwd, void 0, bound3);
16792
+ (this.o.warn ?? console.warn)(
16793
+ `sudo: ${self.name} : TTY=unknown ; PWD=${quoteWord(r.cwd)} ; USER=${target.name} ; REPLAY=${r.id} ; COMMAND=${argv.map(quoteWord).join(" ")}`
16794
+ );
16795
+ return { grant, caller: self, target, tags: r.tags };
16796
+ }
16797
+ async mayAsk(self, target, r, argv) {
16798
+ if (self.uid === 0) return true;
16799
+ const cred = credOf(self);
16800
+ const { rules } = await this.policyRules({ cred, pid: 0, stdin: r.stdin });
16801
+ const applies = await this.applies(self, cred);
16802
+ return rules.some(
16803
+ (rule) => applies(rule.who) && (rule.target === "ALL" || rule.target === target.name) && (rule.cwd === void 0 || rule.cwd === r.cwd) && stdinMatches(rule, r.stdin) && commandMatches(rule.cmnd, argv) && pinned(rule, r.bound)
16804
+ );
16805
+ }
16685
16806
  async command(s, root, mine, call) {
16686
16807
  const { caller, self, target, parsed: parsed2 } = s;
16687
16808
  const { cred } = caller;
@@ -16861,8 +16982,8 @@ ${lines2}
16861
16982
  const st = await fs.lstat(path).catch(() => void 0);
16862
16983
  if (st && !await this.trusted(path)) return false;
16863
16984
  const old = st ? await fs.readFile(path) : "";
16864
- const words2 = recordedCommand(argv);
16865
- const line = `NOPASSWD ${self.name} (${target.name}) Cwd ${quoteWord(cwd)} Stdin none ${sha256Text(bound3)}Cmnd ${words2}
16985
+ const words3 = recordedCommand(argv);
16986
+ const line = `NOPASSWD ${self.name} (${target.name}) Cwd ${quoteWord(cwd)} Stdin none ${sha256Text(bound3)}Cmnd ${words3}
16866
16987
  `;
16867
16988
  const text = old === "" || old.endsWith("\n") ? `${old}${line}` : `${old}
16868
16989
  ${line}`;
@@ -18167,8 +18288,8 @@ function ipv6(host) {
18167
18288
  const head = groups(halves[0]);
18168
18289
  const back = halves.length === 2 ? groups(halves[1]) : [];
18169
18290
  if (![...head, ...back].every((g) => /^[0-9A-Fa-f]{1,4}$/.test(g))) return void 0;
18170
- const words2 = [...head, ...back].map((g) => Number.parseInt(g, 16));
18171
- const missing6 = 8 - words2.length - tail2.length;
18291
+ const words3 = [...head, ...back].map((g) => Number.parseInt(g, 16));
18292
+ const missing6 = 8 - words3.length - tail2.length;
18172
18293
  if (halves.length === 2 ? missing6 < 1 : missing6 !== 0) return void 0;
18173
18294
  const all = [
18174
18295
  ...head.map((g) => Number.parseInt(g, 16)),
@@ -21652,7 +21773,7 @@ function selectFlag(req) {
21652
21773
  throw new KernelError("EINVAL", "ERR_TLS_ALPN_CALLBACK_WITH_PROTOCOLS: alpn with select");
21653
21774
  return TLS_SERVER_SELECT;
21654
21775
  }
21655
- function flagsOf(req) {
21776
+ function flagsOf2(req) {
21656
21777
  return certFlags(req) | selectFlag(req) | (req.keylog ? TLS_KEYLOG : 0);
21657
21778
  }
21658
21779
  async function engineOf(proc, { select, keylog }) {
@@ -21703,7 +21824,7 @@ async function serverOf(proc, req) {
21703
21824
  let mine;
21704
21825
  try {
21705
21826
  mine = req.ca && req.ca.length > 0 ? storeOf(engine, req.ca) : void 0;
21706
- const flags2 = flagsOf(req);
21827
+ const flags2 = flagsOf2(req);
21707
21828
  const store = mine ?? (flags2 & CERT_FLAGS ? await defaultStore(engine, supportOf(proc.net).roots) : void 0);
21708
21829
  return {
21709
21830
  engine,
@@ -23592,7 +23713,7 @@ var WRITE_ACCESS = 3;
23592
23713
  var KEYBOARD = /* @__PURE__ */ new Set([SIG.INT, SIG.QUIT, SIG.TSTP]);
23593
23714
  var STUCK_ESCAPES = /* @__PURE__ */ new Set([SIG.INT, SIG.TERM, SIG.QUIT, SIG.HUP]);
23594
23715
  var DEFAULT_UMASK2 = 18;
23595
- function words(value2) {
23716
+ function words2(value2) {
23596
23717
  if (!Array.isArray(value2) || !value2.every((w) => typeof w === "string")) {
23597
23718
  throw new KernelError("EINVAL");
23598
23719
  }
@@ -25045,7 +25166,7 @@ var WasmProcess = class {
25045
25166
  if (!sudo) throw new KernelError("ENOSYS");
25046
25167
  const call = {
25047
25168
  op: "proc-sudo",
25048
- args: words(req.args),
25169
+ args: words2(req.args),
25049
25170
  cwd: typeof req.cwd === "string" && req.cwd.startsWith("/") ? req.cwd : "/",
25050
25171
  ...typeof req.term === "string" ? { term: req.term } : {}
25051
25172
  };
@@ -28334,8 +28455,8 @@ function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
28334
28455
  function noticeOf(data) {
28335
28456
  if (Array.isArray(data)) return { paths: data.filter((p) => typeof p === "string"), trees: [] };
28336
28457
  const notice = data ?? {};
28337
- const strings = (list) => Array.isArray(list) ? list.filter((p) => typeof p === "string") : [];
28338
- return { paths: strings(notice.paths), trees: strings(notice.trees), outside: true };
28458
+ const strings2 = (list) => Array.isArray(list) ? list.filter((p) => typeof p === "string") : [];
28459
+ return { paths: strings2(notice.paths), trees: strings2(notice.trees), outside: true };
28339
28460
  }
28340
28461
 
28341
28462
  // src/fs/opfs.ts
@@ -32648,7 +32769,40 @@ function describe(req, tty) {
32648
32769
  }
32649
32770
  function refuseChanged(approved, now) {
32650
32771
  const changed = changedFile(approved, now);
32651
- if (changed !== void 0) throw new ChangedError(changedText(changed));
32772
+ if (changed !== void 0) throw new ChangedError(changedText(changed), changed);
32773
+ }
32774
+ var O_WRONLY2 = 1;
32775
+ var OUTPUT_MODE = 384;
32776
+ var ERRNO_TEXT = {
32777
+ EACCES: "Permission denied",
32778
+ EISDIR: "Is a directory",
32779
+ ENOENT: "No such file or directory",
32780
+ ENOTDIR: "Not a directory",
32781
+ EROFS: "Read-only file system"
32782
+ };
32783
+ function sudoFailure(errno3, message3) {
32784
+ return Object.assign(new Error(message3), { code: errno3 });
32785
+ }
32786
+ function sudoCause(err) {
32787
+ const code = codeOf8(err);
32788
+ if (typeof code !== "string") throw err;
32789
+ return code;
32790
+ }
32791
+ function requested(err) {
32792
+ if (err instanceof ChangedError) throw sudoFailure(err.code, requestedText(err.file));
32793
+ throw err;
32794
+ }
32795
+ var MAX_SPAWN_TAGS = 8;
32796
+ function tagList(value2) {
32797
+ if (!Array.isArray(value2)) {
32798
+ tagOption(value2);
32799
+ return value2 === void 0 ? [] : [value2];
32800
+ }
32801
+ if (value2.length > MAX_SPAWN_TAGS) {
32802
+ throw new TypeError(`a spawn adds at most ${MAX_SPAWN_TAGS} tags`);
32803
+ }
32804
+ for (const each of value2) tagOption(each ?? 0);
32805
+ return value2;
32652
32806
  }
32653
32807
  function linksOf2(req) {
32654
32808
  return req.links === void 0 ? {} : { links: req.links };
@@ -32774,27 +32928,27 @@ function appendedRoots(given) {
32774
32928
  }
32775
32929
  return [...roots];
32776
32930
  }
32777
- function plainEnv(words2) {
32778
- const next3 = words2[1];
32779
- if (baseName2(words2[0] ?? "") !== "env" || next3 === void 0) return false;
32931
+ function plainEnv(words3) {
32932
+ const next3 = words3[1];
32933
+ if (baseName2(words3[0] ?? "") !== "env" || next3 === void 0) return false;
32780
32934
  return next3 === "-S" || !next3.startsWith("-") && !next3.includes("=");
32781
32935
  }
32782
- function shebangCommand(words2) {
32783
- if (!plainEnv(words2)) {
32784
- const arg2 = words2.slice(1).join(" ");
32785
- return { interp: words2[0], args: arg2 ? [arg2] : [] };
32936
+ function shebangCommand(words3) {
32937
+ if (!plainEnv(words3)) {
32938
+ const arg2 = words3.slice(1).join(" ");
32939
+ return { interp: words3[0], args: arg2 ? [arg2] : [] };
32786
32940
  }
32787
- const split3 = words2[1] === "-S";
32788
- const [interp, ...rest2] = words2.slice(split3 ? 2 : 1);
32941
+ const split3 = words3[1] === "-S";
32942
+ const [interp, ...rest2] = words3.slice(split3 ? 2 : 1);
32789
32943
  const arg = rest2.join(" ");
32790
32944
  return { interp, args: split3 ? rest2 : arg ? [arg] : [] };
32791
32945
  }
32792
32946
  function interpreter(head) {
32793
- const words2 = shebang(head);
32794
- if (!words2) return "node";
32795
- if (baseName2(words2[0]) === "env") words2.shift();
32796
- if (words2[0] === "-S") words2.shift();
32797
- return baseName2(words2[0] ?? "");
32947
+ const words3 = shebang(head);
32948
+ if (!words3) return "node";
32949
+ if (baseName2(words3[0]) === "env") words3.shift();
32950
+ if (words3[0] === "-S") words3.shift();
32951
+ return baseName2(words3[0] ?? "");
32798
32952
  }
32799
32953
  function expandDefaults(defaults2, env) {
32800
32954
  const out = {};
@@ -33569,9 +33723,9 @@ var Launcher = class {
33569
33723
  const head = await this.head(script);
33570
33724
  if (!head) return void 0;
33571
33725
  const binfmt = isWasm(head) ? void 0 : binfmtOf(await this.interpreters(scope), script);
33572
- const words2 = shebang(head) ?? (binfmt ? [binfmt] : void 0);
33573
- if (!words2) return void 0;
33574
- const { interp, args } = shebangCommand(words2);
33726
+ const words3 = shebang(head) ?? (binfmt ? [binfmt] : void 0);
33727
+ if (!words3) return void 0;
33728
+ const { interp, args } = shebangCommand(words3);
33575
33729
  const passed = [...args, command ? script : file, ...argv.slice(1)];
33576
33730
  if (!interp) return void 0;
33577
33731
  const found = await this.resolve(interp, interp, cwd, scope);
@@ -33802,7 +33956,7 @@ var Launcher = class {
33802
33956
  const hex4 = pinned2[real];
33803
33957
  if (hex4 !== void 0) return { [real]: hex4 };
33804
33958
  if (await this.sudoer.trusted(real)) return {};
33805
- throw new ChangedError(changedText(real));
33959
+ throw new ChangedError(changedText(real), real);
33806
33960
  }
33807
33961
  async pinnedBytes(pin, bytes) {
33808
33962
  const [real = ""] = Object.keys(pin);
@@ -34081,6 +34235,10 @@ var Launcher = class {
34081
34235
  }
34082
34236
  async launch(asked, req) {
34083
34237
  const cwd = await physicalPathAsync(this.base, req.cwd);
34238
+ if (req.exactCwd && cwd !== req.cwd) {
34239
+ await req.fds.closeAll();
34240
+ throw sudoFailure("EACCES", `sudo: ${req.cwd} changed since it was requested`);
34241
+ }
34084
34242
  const seen = { ...req.env, HOSTNAME: this.hostname, cwd };
34085
34243
  await this.pnpmReady();
34086
34244
  let loaded;
@@ -35776,6 +35934,96 @@ var Launcher = class {
35776
35934
  const signal = handle.termsig();
35777
35935
  return { status, ...signal ? { signal } : {}, stdout: concat7(out), stderr: concat7(err) };
35778
35936
  }
35937
+ async sudoRun(replay, options = {}) {
35938
+ const added = tagList(options.tag);
35939
+ const replayed = await this.sudoer.replay(replay);
35940
+ if ("error" in replayed) throw sudoFailure(replayed.errno, replayed.error);
35941
+ const { grant, caller, target } = replayed;
35942
+ let tags = withTag(void 0, options.base);
35943
+ for (const tag of [...replayed.tags, ...added]) tags = withTag(tags, tag);
35944
+ const tagged = { tags, tagBase: options.base === void 0 ? 0 : 1 };
35945
+ await this.swaps.settled();
35946
+ this.resetCatalogs();
35947
+ const req = { file: grant.file, argv: grant.argv, env: grant.env, cwd: grant.cwd };
35948
+ const ctx = { ppid: 0, cred: grant.cred, scope: "" };
35949
+ const planned = await this.planOne(req, ctx).catch((err2) => {
35950
+ const code = sudoCause(err2);
35951
+ const word = grant.argv[0] ?? grant.file;
35952
+ if (code === "ENOENT") throw sudoFailure(code, `sudo: ${word}: command not found`);
35953
+ throw sudoFailure(code, `sudo: ${grant.file}: ${ERRNO_TEXT[code] ?? code}`);
35954
+ });
35955
+ const pin = await this.unchanged(planned, grant.file, grant.cwd, grant).catch(requested);
35956
+ const fds = await this.sudoOutputs(options, caller, this.stackFor(tags, tagged.tagBase));
35957
+ const err = fds.get(2).file;
35958
+ const report = (message3) => void Promise.resolve(err.write?.(encoder10.encode(`${message3}
35959
+ `))).catch(() => void 0);
35960
+ const replan = () => this.planOne(req, ctx);
35961
+ if (!await this.grantedCwd(grant.caller, grant.cwd)) {
35962
+ await fds.closeAll();
35963
+ throw sudoFailure("EACCES", `sudo: ${grant.cwd} changed since it was requested`);
35964
+ }
35965
+ const handle = await this.launch(
35966
+ { ...planned, replan },
35967
+ {
35968
+ ...pin,
35969
+ env: grant.env,
35970
+ cwd: grant.cwd,
35971
+ fds,
35972
+ report,
35973
+ ...this.identity(target, tagged, options.session),
35974
+ ...caller.umask !== void 0 ? { umask: caller.umask } : {},
35975
+ links: grant.cred.euid,
35976
+ exactCwd: true
35977
+ }
35978
+ ).catch(requested);
35979
+ const ended = handle.exited.then((status) => {
35980
+ const signal = handle.termsig();
35981
+ return { status, ...signal ? { signal } : {} };
35982
+ });
35983
+ return { pid: handle.pid, ended };
35984
+ }
35985
+ async sudoOutputs(options, caller, stack) {
35986
+ const fds = new FdTable();
35987
+ const opened2 = /* @__PURE__ */ new Map();
35988
+ fds.installAt(0, deviceFile("null"));
35989
+ try {
35990
+ fds.installAt(1, await this.sudoOutput(options.stdout, caller, stack, opened2));
35991
+ fds.installAt(2, await this.sudoOutput(options.stderr, caller, stack, opened2));
35992
+ } catch (err) {
35993
+ await fds.closeAll();
35994
+ throw err;
35995
+ }
35996
+ return fds;
35997
+ }
35998
+ async sudoOutput(path, caller, stack, opened2) {
35999
+ if (path === void 0) return deviceFile("null");
36000
+ if (typeof path !== "string" || !path.startsWith("/")) {
36001
+ throw sudoFailure("EINVAL", `sudo: ${String(path)} is not an absolute path`);
36002
+ }
36003
+ const cred = credOf(caller);
36004
+ const walked = await walk(this.fs, cred, normalizePath(path), true, caller.uid);
36005
+ const real = walked.path;
36006
+ const same2 = opened2.get(real);
36007
+ if (same2) return same2.retain();
36008
+ try {
36009
+ if (walked.denied !== void 0) throw fsError("EACCES", path);
36010
+ await writable({ ...this.execGuard(cred, 0), links: caller.uid }, "open", real);
36011
+ const owner = { uid: caller.uid, gid: caller.gid, mode: OUTPUT_MODE };
36012
+ await stack.noLinks.writeFile(real, new Uint8Array(0), owner);
36013
+ } catch (err) {
36014
+ const code = sudoCause(err);
36015
+ throw sudoFailure(code, `sudo: ${path}: ${ERRNO_TEXT[code] ?? code}`);
36016
+ }
36017
+ const file = vfsFile(stack.fs, { path: real, flags: O_WRONLY2, position: 0 }, stack.nodes);
36018
+ const again = await walk(this.fs, cred, real, true, caller.uid);
36019
+ const st = await stack.noLinks.lstat(real).catch(() => void 0);
36020
+ if (again.path !== real || again.denied !== void 0 || !st?.isFile) {
36021
+ await file.release();
36022
+ throw sudoFailure("EACCES", `sudo: ${path} changed while it was opened`);
36023
+ }
36024
+ opened2.set(real, file);
36025
+ return file;
36026
+ }
35779
36027
  };
35780
36028
  function cronLocks() {
35781
36029
  return globalThis.navigator?.locks;
@@ -36260,7 +36508,7 @@ function serveTransport(peer, transport, {
36260
36508
  }
36261
36509
 
36262
36510
  // src/client/protocol.ts
36263
- var PROTOCOL = [1, 15];
36511
+ var PROTOCOL = [1, 16];
36264
36512
  var FS_METHODS = [
36265
36513
  "readFile",
36266
36514
  "writeFile",
@@ -36416,9 +36664,9 @@ async function accountOf(l, user) {
36416
36664
  if (!found) throw fsError("ENOENT", `no user ${String(user)}`);
36417
36665
  return found;
36418
36666
  }
36419
- async function runAs(l, self, requested) {
36420
- if (requested === void 0) return self;
36421
- const target = await accountOf(l, requested);
36667
+ async function runAs(l, self, requested2) {
36668
+ if (requested2 === void 0) return self;
36669
+ const target = await accountOf(l, requested2);
36422
36670
  if (self.uid !== 0 && target.uid !== self.uid) {
36423
36671
  throw fsError("EPERM", `${self.name} may not run as ${target.name}`);
36424
36672
  }
@@ -36427,21 +36675,9 @@ async function runAs(l, self, requested) {
36427
36675
  function clientCred(self) {
36428
36676
  return self ? credOf(self) : copyCred(ROOT);
36429
36677
  }
36430
- var MAX_SPAWN_TAGS = 8;
36431
- function spawnTags(value2) {
36432
- if (!Array.isArray(value2)) {
36433
- tagOption(value2);
36434
- return value2 === void 0 ? [] : [value2];
36435
- }
36436
- if (value2.length > MAX_SPAWN_TAGS) {
36437
- throw new TypeError(`a spawn adds at most ${MAX_SPAWN_TAGS} tags`);
36438
- }
36439
- for (const each of value2) tagOption(each ?? 0);
36440
- return value2;
36441
- }
36442
36678
  async function runTagged(l, self, options, tag, session = -1) {
36443
36679
  let tags = withTag(void 0, tag);
36444
- for (const added of spawnTags(options.tag)) tags = withTag(tags, added);
36680
+ for (const added of tagList(options.tag)) tags = withTag(tags, added);
36445
36681
  return {
36446
36682
  user: await runAs(l, self, options.user),
36447
36683
  tag,
@@ -36626,6 +36862,20 @@ function requestBinding(send2, port) {
36626
36862
  close: () => unbind?.()
36627
36863
  };
36628
36864
  }
36865
+ async function sudoRunAs(l, self, host, req, reply) {
36866
+ if (self.uid !== 0) throw sudoFailure("EPERM", "sudo: only a root client may run a sudo request");
36867
+ const { stdout, stderr, tag } = req.options;
36868
+ const running = await l.sudoRun(req.replay, {
36869
+ stdout,
36870
+ stderr,
36871
+ tag,
36872
+ base: host.tag,
36873
+ session: host.session
36874
+ });
36875
+ reply(req.id, { started: running.pid });
36876
+ const { status, signal } = await running.ended;
36877
+ return { result: status, ...signalField(signal) };
36878
+ }
36629
36879
  function answered(pending, req, reply, detach) {
36630
36880
  pending.then(
36631
36881
  ({ result, signal, transfer }) => {
@@ -36775,6 +37025,8 @@ function serveClient(port, host) {
36775
37025
  case "trace":
36776
37026
  case "untrace":
36777
37027
  return traces.answer(l, self, req);
37028
+ case "sudo-run":
37029
+ return sudoRunAs(l, self, host, req, reply);
36778
37030
  case "detach":
36779
37031
  return { result: true };
36780
37032
  default:
@@ -36880,16 +37132,16 @@ function placeOf(address) {
36880
37132
  const kind = isIP3(plain3);
36881
37133
  if (kind === 4) return ipv4Place(plain3);
36882
37134
  if (kind !== 6) return void 0;
36883
- const words2 = hextets2(plain3);
36884
- const embedded2 = words2.slice(0, 5).every((w) => w === 0) && (words2[5] === 65535 || words2[5] === 0);
36885
- if (embedded2 && (words2[5] === 65535 || words2[6] !== 0)) return ipv4Place(ipv4Of(words2));
36886
- if (words2.slice(0, 7).every((w) => w === 0) && (words2[7] ?? 0) <= 1) return "loopback";
36887
- if (((words2[0] ?? 0) & 65472) === 65152) return "link-local";
36888
- if (words2.join(":") === METADATA_V6) return "link-local";
37135
+ const words3 = hextets2(plain3);
37136
+ const embedded2 = words3.slice(0, 5).every((w) => w === 0) && (words3[5] === 65535 || words3[5] === 0);
37137
+ if (embedded2 && (words3[5] === 65535 || words3[6] !== 0)) return ipv4Place(ipv4Of(words3));
37138
+ if (words3.slice(0, 7).every((w) => w === 0) && (words3[7] ?? 0) <= 1) return "loopback";
37139
+ if (((words3[0] ?? 0) & 65472) === 65152) return "link-local";
37140
+ if (words3.join(":") === METADATA_V6) return "link-local";
36889
37141
  return void 0;
36890
37142
  }
36891
- function ipv4Of(words2) {
36892
- const [high = 0, low = 0] = words2.slice(6);
37143
+ function ipv4Of(words3) {
37144
+ const [high = 0, low = 0] = words3.slice(6);
36893
37145
  return [high >> 8, high & 255, low >> 8, low & 255].join(".");
36894
37146
  }
36895
37147
  function hextets2(address) {
@@ -37970,6 +38222,19 @@ async function spawnOn(channel, argv, opts) {
37970
38222
  signal: async (name2 = "SIGTERM") => void await channel.call({ op: "kill", pid: -pgid, signal: name2 })
37971
38223
  };
37972
38224
  }
38225
+ async function sudoRunOn(channel, replay, { stdout, stderr, tag }) {
38226
+ await since(channel, 16, "sudoRun", async () => {
38227
+ });
38228
+ const started = Promise.withResolvers();
38229
+ let endedBy;
38230
+ const { done: done4 } = channel.request(
38231
+ { op: "sudo-run", replay, options: { stdout, stderr, tag } },
38232
+ { started: started.resolve, ended: (signal) => endedBy = signal }
38233
+ );
38234
+ done4.catch(started.reject);
38235
+ const pid = await started.promise;
38236
+ return { pid, pgid: pid, ended: endedOf(done4, () => endedBy) };
38237
+ }
37973
38238
  function endedOf(done4, signal) {
37974
38239
  return quiet(
37975
38240
  done4.then((status) => {
@@ -38171,6 +38436,7 @@ async function attachKernel(port, options = {}) {
38171
38436
  return () => channel.traitsListeners.delete(listener);
38172
38437
  },
38173
38438
  trace: (tag, onEvents, options2 = {}) => traceOn(channel, tag, onEvents, options2),
38439
+ sudoRun: (replay, options2 = {}) => sudoRunOn(channel, replay, options2),
38174
38440
  async close(o = {}) {
38175
38441
  if (channel.failure) return;
38176
38442
  await channel.call({ op: "detach", ...o.kill ? { kill: true } : {} }).catch(() => void 0);
@@ -38485,6 +38751,30 @@ function checkedRequests(hook) {
38485
38751
  const kinds = kindsOf(hook);
38486
38752
  return { kinds, handle: (request) => hook.handle(request) };
38487
38753
  }
38754
+ function nodeAccounts(launcher, guard2) {
38755
+ return {
38756
+ users: {
38757
+ mode: launcher.usersMode,
38758
+ add: (user) => guard2(() => launcher.users.add(user)),
38759
+ remove: (name2, options) => guard2(() => launcher.users.remove(name2, options)),
38760
+ list: () => guard2(() => launcher.users.list())
38761
+ },
38762
+ groups: {
38763
+ list: () => guard2(() => launcher.users.groups()),
38764
+ add: (name2, options) => guard2(() => launcher.users.addGroup(name2, options?.gid)),
38765
+ addMember: (group, user) => guard2(() => launcher.users.addMember(group, user)),
38766
+ removeMember: (group, user) => guard2(() => launcher.users.removeMember(group, user))
38767
+ }
38768
+ };
38769
+ }
38770
+ async function nodeSudoRun(launcher, guard2, replay, options) {
38771
+ const running = await guard2(() => launcher.sudoRun(replay, options));
38772
+ const ended = guard2(async () => {
38773
+ const { status, signal } = await running.ended;
38774
+ return { status, ...signalField(signal) };
38775
+ });
38776
+ return { pid: running.pid, pgid: running.pid, ended: quiet(ended) };
38777
+ }
38488
38778
  async function createNodeKernel(options = {}) {
38489
38779
  const hooks = sealedHooks(options.sealed);
38490
38780
  const timeZone = systemTimeZone(options.timeZone, clockZone(options.cron?.clock));
@@ -38613,18 +38903,8 @@ async function createNodeKernel(options = {}) {
38613
38903
  mounts: async () => launcher.mounts.listed(),
38614
38904
  setRoutes: (routes) => guard2(async () => launcher.setRoutes(routes)),
38615
38905
  updateTransportTraits: () => guard2(async () => launcher.updateTransportTraits()),
38616
- users: {
38617
- mode: launcher.usersMode,
38618
- add: (user) => guard2(() => launcher.users.add(user)),
38619
- remove: (name2, options2) => guard2(() => launcher.users.remove(name2, options2)),
38620
- list: () => guard2(() => launcher.users.list())
38621
- },
38622
- groups: {
38623
- list: () => guard2(() => launcher.users.groups()),
38624
- add: (name2, options2) => guard2(() => launcher.users.addGroup(name2, options2?.gid)),
38625
- addMember: (group, user) => guard2(() => launcher.users.addMember(group, user)),
38626
- removeMember: (group, user) => guard2(() => launcher.users.removeMember(group, user))
38627
- },
38906
+ ...nodeAccounts(launcher, guard2),
38907
+ sudoRun: (replay, sudoOptions = {}) => nodeSudoRun(launcher, guard2, replay, sudoOptions),
38628
38908
  async connect(connectOptions = {}) {
38629
38909
  if (terminated) throw new Error(TERMINATED);
38630
38910
  const { user } = connectOptions;