@ai-ecoverse/slicc-kernel 1.90.0 → 1.92.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
@@ -470,8 +470,8 @@ async function access(fs, cred, path, want, options = {}) {
470
470
  if (await present(fs, walked.path, false)) return { ...walked, denied: walked.path };
471
471
  return { ...walked, missing: true };
472
472
  }
473
- function refused(path, errno = "EACCES") {
474
- return fsError(errno, path);
473
+ function refused(path, errno2 = "EACCES") {
474
+ return fsError(errno2, path);
475
475
  }
476
476
 
477
477
  // src/fs/dac.ts
@@ -531,9 +531,9 @@ var Created = class {
531
531
  function linked(g, options) {
532
532
  return g.links === void 0 ? options : { ...options, links: g.links };
533
533
  }
534
- function deny(g, op, path, errno = "EACCES") {
534
+ function deny(g, op, path, errno2 = "EACCES") {
535
535
  const tag = g.tag === void 0 ? {} : { tag: g.tag };
536
- g.dac.denied({ pid: g.pid, uid: g.cred.euid, op, path, errno, ...tag });
536
+ g.dac.denied({ pid: g.pid, uid: g.cred.euid, op, path, errno: errno2, ...tag });
537
537
  }
538
538
  var TTY_MODE = 400;
539
539
  function ttyOpenable(g, tty) {
@@ -1200,7 +1200,7 @@ var WASI_SIGNAL_TO_POSIX = Object.fromEntries(
1200
1200
 
1201
1201
  // src/process/wasi-errno.ts
1202
1202
  var WASI_ERRNO = {
1203
- ...Object.fromEntries(Object.entries(E).map(([name2, errno]) => [`E${name2}`, errno])),
1203
+ ...Object.fromEntries(Object.entries(E).map(([name2, errno2]) => [`E${name2}`, errno2])),
1204
1204
  EWOULDBLOCK: E.AGAIN,
1205
1205
  ENOTSUP: 138,
1206
1206
  EOPNOTSUPP: 138,
@@ -1297,7 +1297,7 @@ function dialError(e, timedOut, closed) {
1297
1297
  if (timedOut) return new KernelError("ETIMEDOUT");
1298
1298
  if (closed) return new KernelError("ECONNABORTED");
1299
1299
  const code = e?.code;
1300
- const known = DIAL_ERRORS.find((errno) => errno === code);
1300
+ const known = DIAL_ERRORS.find((errno2) => errno2 === code);
1301
1301
  return new KernelError(known ?? "EHOSTUNREACH");
1302
1302
  }
1303
1303
  function isLoopback(host) {
@@ -2153,11 +2153,11 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
2153
2153
  };
2154
2154
  const sockets = /* @__PURE__ */ new Map();
2155
2155
  const credit = /* @__PURE__ */ new Map();
2156
- const refill = (nid, n, closing = false) => {
2156
+ const refill = (nid, n, closing2 = false) => {
2157
2157
  const flow = credit.get(nid);
2158
2158
  if (!flow) return;
2159
2159
  flow.inflight -= n;
2160
- if (closing && !flow.closing) {
2160
+ if (closing2 && !flow.closing) {
2161
2161
  flow.closing = true;
2162
2162
  flow.deadline();
2163
2163
  }
@@ -4797,15 +4797,15 @@ var UnlinkedKeeper = class {
4797
4797
  wrap(fs = this.fs) {
4798
4798
  const keeper = this;
4799
4799
  const visible = (names) => names.filter((name2) => name2 !== UNLINKED_DIR);
4800
- const counted2 = (st) => {
4800
+ const counted3 = (st) => {
4801
4801
  const hidden = keeper.links.get(`${st.dev ?? 0}:${st.ino}`) ?? 0;
4802
4802
  return hidden === 0 || st.nlink === void 0 ? st : { ...st, nlink: Math.max(0, st.nlink - hidden) };
4803
4803
  };
4804
4804
  return {
4805
4805
  ...fs,
4806
4806
  links: (path) => mayLink(fs, path),
4807
- stat: async (path) => counted2(await fs.stat(path)),
4808
- lstat: async (path) => counted2(await fs.lstat(path)),
4807
+ stat: async (path) => counted3(await fs.stat(path)),
4808
+ lstat: async (path) => counted3(await fs.lstat(path)),
4809
4809
  async rm(path, options) {
4810
4810
  const target = fs.resolvePath("/", path);
4811
4811
  const pinned = await keeper.pin(target);
@@ -4819,7 +4819,7 @@ var UnlinkedKeeper = class {
4819
4819
  await keeper.settle(pinned, () => fs.rename(from, to));
4820
4820
  },
4821
4821
  readdir: async (path) => visible(await fs.readdir(path)),
4822
- readdirStat: async (path) => (await fs.readdirStat(path)).filter(([name2]) => name2 !== UNLINKED_DIR).map(([name2, st]) => [name2, st && counted2(st)])
4822
+ readdirStat: async (path) => (await fs.readdirStat(path)).filter(([name2]) => name2 !== UNLINKED_DIR).map(([name2, st]) => [name2, st && counted3(st)])
4823
4823
  };
4824
4824
  }
4825
4825
  async settle(pinned, op) {
@@ -5246,6 +5246,9 @@ async function acceptKey(headers) {
5246
5246
  }
5247
5247
  const key = field(headers, "sec-websocket-key") ?? "";
5248
5248
  if (!/^[A-Za-z0-9+/]{22}==$/.test(key)) throw new HttpError(400, "bad Sec-WebSocket-Key");
5249
+ return acceptFor(key);
5250
+ }
5251
+ async function acceptFor(key) {
5249
5252
  const digest = await crypto.subtle.digest("SHA-1", new TextEncoder().encode(key + GUID));
5250
5253
  return btoa(String.fromCharCode(...new Uint8Array(digest)));
5251
5254
  }
@@ -5269,6 +5272,19 @@ function frame(opcode, payload) {
5269
5272
  out.set(payload, size);
5270
5273
  return out;
5271
5274
  }
5275
+ function clientFrame(opcode, payload) {
5276
+ const plain = frame(opcode, payload);
5277
+ const head = plain.length - payload.length;
5278
+ const mask = crypto.getRandomValues(new Uint8Array(4));
5279
+ const out = new Uint8Array(plain.length + 4);
5280
+ out.set(plain.subarray(0, head));
5281
+ out[1] = out[1] | 128;
5282
+ out.set(mask, head);
5283
+ for (let i = 0; i < payload.length; i++) {
5284
+ out[head + 4 + i] = payload[i] ^ mask[i & 3];
5285
+ }
5286
+ return out;
5287
+ }
5272
5288
  function closeFrame(code, reason = "") {
5273
5289
  let bytes = new TextEncoder().encode(reason);
5274
5290
  if (bytes.length > 123) {
@@ -5308,10 +5324,12 @@ var WsReader = class {
5308
5324
  continuing = false;
5309
5325
  binaryMessage = false;
5310
5326
  binary;
5311
- constructor(incoming, max = MAX_MESSAGE, binary) {
5327
+ fromServer;
5328
+ constructor(incoming, max = MAX_MESSAGE, binary, fromServer = false) {
5312
5329
  this.incoming = incoming;
5313
5330
  this.max = max;
5314
5331
  this.binary = binary;
5332
+ this.fromServer = fromServer;
5315
5333
  }
5316
5334
  async next(signal) {
5317
5335
  for (; ; ) {
@@ -5323,7 +5341,7 @@ var WsReader = class {
5323
5341
  const fin = (head[0] & 128) !== 0;
5324
5342
  const opcode = head[0] & 15;
5325
5343
  if ((head[0] & 112) !== 0) throw new WsError(CLOSE.protocol, "reserved bits are set");
5326
- if ((head[1] & 128) === 0) throw new WsError(CLOSE.protocol, "client frames must be masked");
5344
+ const masked = this.masking(head[1]);
5327
5345
  const length2 = await this.length(head[1] & 127, signal);
5328
5346
  const control = opcode >= 8;
5329
5347
  if (control && (length2 > 125 || !fin)) {
@@ -5332,15 +5350,19 @@ var WsReader = class {
5332
5350
  if (!control && this.size + length2 > this.max) {
5333
5351
  throw new WsError(CLOSE.tooBig, `a message is over ${this.max} bytes`);
5334
5352
  }
5335
- const mask = new Uint8Array(4);
5336
- await fill(this.incoming, mask, signal);
5337
- const payload = new Uint8Array(length2);
5338
- await fill(this.incoming, payload, signal);
5339
- for (let i = 0; i < length2; i++) payload[i] ^= mask[i & 3];
5353
+ const payload = await this.payload(length2, masked, signal);
5340
5354
  const event = this.take(opcode, fin, payload);
5341
5355
  if (event) return event;
5342
5356
  }
5343
5357
  }
5358
+ masking(second) {
5359
+ const masked = (second & 128) !== 0;
5360
+ if (masked !== this.fromServer) return masked;
5361
+ throw new WsError(
5362
+ CLOSE.protocol,
5363
+ this.fromServer ? "server frames must not be masked" : "client frames must be masked"
5364
+ );
5365
+ }
5344
5366
  data(opcode, fin, payload) {
5345
5367
  if (opcode === OP.continuation !== this.continuing) {
5346
5368
  throw new WsError(CLOSE.protocol, "unexpected continuation frame");
@@ -5355,6 +5377,14 @@ var WsReader = class {
5355
5377
  this.size = 0;
5356
5378
  return this.binaryMessage ? { kind: "binary", bytes: whole } : { kind: "text", text: decode(whole) };
5357
5379
  }
5380
+ async payload(length2, masked, signal) {
5381
+ const mask = new Uint8Array(4);
5382
+ if (masked) await fill(this.incoming, mask, signal);
5383
+ const payload = new Uint8Array(length2);
5384
+ await fill(this.incoming, payload, signal);
5385
+ if (masked) for (let i = 0; i < length2; i++) payload[i] ^= mask[i & 3];
5386
+ return payload;
5387
+ }
5358
5388
  async length(short, signal) {
5359
5389
  if (short < 126) return short;
5360
5390
  const ext = new Uint8Array(short === 126 ? 2 : 8);
@@ -6007,6 +6037,33 @@ function toHostLoopback(href) {
6007
6037
  url.hostname = "127.0.0.1";
6008
6038
  return url.href;
6009
6039
  }
6040
+ var LOOPBACK_V4 = /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
6041
+ var LOOPBACK_V6 = /* @__PURE__ */ new Set(["::1", "::", "0:0:0:0:0:0:0:1"]);
6042
+ var MAPPED_V4 = /^::ffff:7f[0-9a-f]{2}:[0-9a-f]{1,4}$/;
6043
+ var KERNEL_PORT = /^(\d{1,5})\.kernel\.localhost$/;
6044
+ var SCHEMES = {
6045
+ "http:": 80,
6046
+ "ws:": 80,
6047
+ "https:": 443,
6048
+ "wss:": 443
6049
+ };
6050
+ function isKernelLoopbackHost(hostname) {
6051
+ const host = hostname.replace(/^\[(.*)\]$/, "$1").replace(/\.$/, "").toLowerCase();
6052
+ return host === "localhost" || host.endsWith(".localhost") || host === "0.0.0.0" || LOOPBACK_V4.test(host) || LOOPBACK_V6.has(host) || MAPPED_V4.test(host);
6053
+ }
6054
+ function kernelLoopbackPort(href) {
6055
+ let url;
6056
+ try {
6057
+ url = new URL(href);
6058
+ } catch {
6059
+ return void 0;
6060
+ }
6061
+ const fallback = SCHEMES[url.protocol];
6062
+ if (fallback === void 0 || !isKernelLoopbackHost(url.hostname)) return void 0;
6063
+ const named = KERNEL_PORT.exec(url.hostname.replace(/\.$/, "").toLowerCase())?.[1];
6064
+ const port = Number(named ?? (url.port || fallback));
6065
+ return port >= 1 && port <= 65535 ? port : void 0;
6066
+ }
6010
6067
  var DEFAULT_HOSTNAME = "slicc";
6011
6068
  var HOSTNAME_ADDRESS = "127.0.1.1";
6012
6069
  var LIBC_HOSTNAMES = ["emscripten", "wasmer.sh"];
@@ -6411,10 +6468,10 @@ var RealmProxy = class {
6411
6468
  encodedBodies: this.options.transport.traits.encodedBodies,
6412
6469
  bodiless: bodiless2
6413
6470
  });
6414
- const chunked2 = !bodiless2 && minor >= 1;
6415
- const keep2 = keepAlive && (bodiless2 || chunked2);
6471
+ const chunked3 = !bodiless2 && minor >= 1;
6472
+ const keep2 = keepAlive && (bodiless2 || chunked3);
6416
6473
  headers.push(["Connection", keep2 ? "keep-alive" : "close"]);
6417
- if (chunked2) headers.push(["Transfer-Encoding", "chunked"]);
6474
+ if (chunked3) headers.push(["Transfer-Encoding", "chunked"]);
6418
6475
  const signal = this.stop.signal;
6419
6476
  await conn.write(
6420
6477
  responseHead(status, response.statusText || REASON[status] || "", headers),
@@ -6426,13 +6483,13 @@ var RealmProxy = class {
6426
6483
  }
6427
6484
  try {
6428
6485
  for await (const piece of response.body) {
6429
- if (piece.length > 0) await conn.write(chunked2 ? chunk(piece) : piece, signal);
6486
+ if (piece.length > 0) await conn.write(chunked3 ? chunk(piece) : piece, signal);
6430
6487
  }
6431
6488
  } catch {
6432
6489
  await response.cancel().catch(() => void 0);
6433
6490
  return false;
6434
6491
  }
6435
- if (chunked2) await conn.write(LAST_CHUNK, signal);
6492
+ if (chunked3) await conn.write(LAST_CHUNK, signal);
6436
6493
  return keep2;
6437
6494
  }
6438
6495
  async connect(conn, incoming, req, idle) {
@@ -9102,6 +9159,7 @@ async function storageUsage() {
9102
9159
  if (typeof estimate?.quota !== "number") return null;
9103
9160
  return { quota: estimate.quota, usage: estimate.usage ?? 0 };
9104
9161
  }
9162
+ var PLAIN_DIR_MODE = 493;
9105
9163
  var done = { ok: true, kind: "void" };
9106
9164
  function json2(value) {
9107
9165
  return { ok: true, kind: "json", json: value };
@@ -9111,6 +9169,11 @@ function ownerFor2(entry, mode) {
9111
9169
  if (!cred) return void 0;
9112
9170
  return ownerOf(cred, mode === void 0 ? void 0 : mode & ~(entry.umask?.() ?? 18));
9113
9171
  }
9172
+ function plainDirMode(entry, mode) {
9173
+ if (entry.cred || mode === void 0) return void 0;
9174
+ const wanted = mode & 4095 & ~(entry.umask?.() ?? 18);
9175
+ return wanted === PLAIN_DIR_MODE ? void 0 : wanted;
9176
+ }
9114
9177
  function mkdirOptions(entry, mode = 511) {
9115
9178
  const owner = ownerFor2(entry, mode);
9116
9179
  if (!owner) return { recursive: true };
@@ -9152,9 +9215,13 @@ async function run(entry, path, req) {
9152
9215
  return json2(await fs.readdir(path));
9153
9216
  case "readdir-stat":
9154
9217
  return json2(await readdirStat(fs, path));
9155
- case "mkdir":
9218
+ case "mkdir": {
9219
+ const plain = plainDirMode(entry, req.mode);
9220
+ const fresh2 = plain !== void 0 && !await fs.exists(path);
9156
9221
  await (req.exclusive ? mkdirExclusive(entry, path, req.mode) : fs.mkdir(path, mkdirOptions(entry, req.mode)));
9222
+ if (fresh2) await fs.chmod(path, plain);
9157
9223
  return done;
9224
+ }
9158
9225
  case "rm":
9159
9226
  await fs.rm(path, { recursive: true });
9160
9227
  return done;
@@ -10007,7 +10074,7 @@ function mountTable(mounts = []) {
10007
10074
  }
10008
10075
 
10009
10076
  // src/version.ts
10010
- var KERNEL_VERSION = true ? "1.90.0" : "0.0.0-dev";
10077
+ var KERNEL_VERSION = true ? "1.92.0" : "0.0.0-dev";
10011
10078
  var OS_RELEASE = "6.0.0-slicc";
10012
10079
  function procVersion() {
10013
10080
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -10595,6 +10662,339 @@ async function hostSyscall(req, ctx) {
10595
10662
  }
10596
10663
  }
10597
10664
 
10665
+ // src/kernel/net/loopback-transport.ts
10666
+ var LOOPBACK_TRAITS = {
10667
+ manualRedirects: true,
10668
+ encodedBodies: true,
10669
+ maxRequestBody: 64 * 1024 * 1024,
10670
+ crossOrigin: "any",
10671
+ websocket: true
10672
+ };
10673
+ var MAX_HEAD2 = 64 * 1024;
10674
+ var OWN_HEADERS = /* @__PURE__ */ new Set([
10675
+ "host",
10676
+ "connection",
10677
+ "content-length",
10678
+ "transfer-encoding",
10679
+ "upgrade",
10680
+ "sec-websocket-key",
10681
+ "sec-websocket-version",
10682
+ "sec-websocket-protocol",
10683
+ "sec-websocket-extensions"
10684
+ ]);
10685
+ var OP_CLOSE = 8;
10686
+ var OP_PONG = 10;
10687
+ var TOKEN3 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
10688
+ var SAFE_VALUE = /^[^\r\n\0]*$/;
10689
+ var CLOSE_WAIT_MS3 = 5e3;
10690
+ var MAX_UNREAD = 16 * 1024 * 1024;
10691
+ function errno(code, message3) {
10692
+ return Object.assign(new Error(`${code}: ${message3}`), { code });
10693
+ }
10694
+ function requestHead(method, url, headers, extra) {
10695
+ if (!TOKEN3.test(method)) throw errno("EINVAL", `method ${method}`);
10696
+ const lines2 = [`${method} ${url.pathname}${url.search} HTTP/1.1`, `Host: ${url.host}`];
10697
+ for (const [name2, value] of headers) {
10698
+ if (OWN_HEADERS.has(name2.toLowerCase())) continue;
10699
+ if (!TOKEN3.test(name2) || !SAFE_VALUE.test(value)) throw errno("EINVAL", `header ${name2}`);
10700
+ lines2.push(`${name2}: ${value}`);
10701
+ }
10702
+ for (const [name2, value] of extra) lines2.push(`${name2}: ${value}`);
10703
+ return latin1Bytes(`${lines2.join("\r\n")}\r
10704
+ \r
10705
+ `);
10706
+ }
10707
+ function parseHead(bytes) {
10708
+ const [statusLine = "", ...lines2] = latin1(bytes).split(/\r?\n/);
10709
+ const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
10710
+ if (!match) throw errno("EPROTO", `not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);
10711
+ const headers = [];
10712
+ for (const line of lines2) {
10713
+ const colon = line.indexOf(":");
10714
+ if (colon > 0) headers.push([line.slice(0, colon).trim(), line.slice(colon + 1).trim()]);
10715
+ }
10716
+ return { status: Number(match[1]), statusText: match[2] ?? "", headers };
10717
+ }
10718
+ async function readHead(incoming, signal) {
10719
+ for (; ; ) {
10720
+ let raw;
10721
+ try {
10722
+ raw = await incoming.head(MAX_HEAD2, signal);
10723
+ } catch (err) {
10724
+ if (err instanceof KernelError) throw err;
10725
+ throw errno("EPROTO", "the response head is malformed or too large");
10726
+ }
10727
+ if (!raw) throw errno("ECONNRESET", "the connection closed before a response");
10728
+ const head = parseHead(raw);
10729
+ if (head.status < 100 || head.status >= 200 || head.status === 101) return head;
10730
+ }
10731
+ }
10732
+ async function* chunked(incoming, signal) {
10733
+ for (; ; ) {
10734
+ const line = await incoming.line(4096, signal);
10735
+ const size = /^([0-9A-Fa-f]{1,12})[ \t]*(;.*)?$/.exec(line);
10736
+ if (!size) throw errno("EPROTO", "a malformed chunk size");
10737
+ const n = Number.parseInt(size[1], 16);
10738
+ if (n === 0) {
10739
+ while (await incoming.line(MAX_HEAD2, signal) !== "") ;
10740
+ return;
10741
+ }
10742
+ yield await incoming.exactly(n, signal);
10743
+ if (await incoming.line(2, signal) !== "") throw errno("EPROTO", "a malformed chunk");
10744
+ }
10745
+ }
10746
+ async function* counted(incoming, length2, signal) {
10747
+ for (let left = length2; left > 0; ) {
10748
+ const data = await incoming.some(Math.min(left, 64 * 1024), signal);
10749
+ if (data.length === 0) throw errno("EPROTO", "the body ended early");
10750
+ left -= data.length;
10751
+ yield data;
10752
+ }
10753
+ }
10754
+ async function* untilClose(incoming, signal) {
10755
+ for (; ; ) {
10756
+ const data = await incoming.some(64 * 1024, signal);
10757
+ if (data.length === 0) return;
10758
+ yield data;
10759
+ }
10760
+ }
10761
+ function bodyOf(incoming, head, method, signal) {
10762
+ if (method === "HEAD" || head.status === 204 || head.status === 304) return [];
10763
+ if (fieldTokens(head.headers, "transfer-encoding").includes("chunked")) {
10764
+ return chunked(incoming, signal);
10765
+ }
10766
+ const lengths = fieldValues(head.headers, "content-length");
10767
+ if (lengths.length === 0) return untilClose(incoming, signal);
10768
+ const length2 = lengths[0]?.trim() ?? "";
10769
+ if (!/^\d{1,15}$/.test(length2) || lengths.some((l) => l.trim() !== length2)) {
10770
+ throw errno("EPROTO", `a malformed Content-Length: ${lengths.join(", ")}`);
10771
+ }
10772
+ return counted(incoming, Number(length2), signal);
10773
+ }
10774
+ var KernelLoopback = class {
10775
+ net;
10776
+ cred;
10777
+ constructor(net, cred) {
10778
+ this.net = net;
10779
+ this.cred = cred;
10780
+ }
10781
+ port(url) {
10782
+ return kernelLoopbackPort(url);
10783
+ }
10784
+ connect(url, port, signal) {
10785
+ if (url.protocol === "https:" || url.protocol === "wss:") {
10786
+ throw errno("ENOTSUP", `TLS to the kernel's loopback is not supported: ${url.origin}`);
10787
+ }
10788
+ signal.throwIfAborted();
10789
+ const socket = this.net.socket("inet");
10790
+ try {
10791
+ void socket.connect({ family: "inet", host: "127.0.0.1", port }, false, this.cred());
10792
+ } catch (err) {
10793
+ socket.close();
10794
+ const refused2 = err instanceof KernelError && err.code === "ECONNREFUSED";
10795
+ throw refused2 ? errno("ECONNREFUSED", `nothing listens on the kernel's port ${port}`) : err;
10796
+ }
10797
+ signal.addEventListener("abort", () => socket.close(), { once: true });
10798
+ return socket;
10799
+ }
10800
+ async fetch(req, port) {
10801
+ const url = new URL(req.url);
10802
+ const body = req.body ?? (/^(POST|PUT|PATCH)$/i.test(req.method) ? new Uint8Array(0) : void 0);
10803
+ const length2 = body ? [["Content-Length", String(body.length)]] : [];
10804
+ const head = requestHead(req.method, url, req.headers, [...length2, ["Connection", "close"]]);
10805
+ const socket = this.connect(url, port, req.signal);
10806
+ try {
10807
+ await writeAll2(socket, body ? concat([head, body]) : head, req.signal);
10808
+ const incoming = new Incoming(socket);
10809
+ const response = await readHead(incoming, req.signal);
10810
+ const chunks2 = bodyOf(incoming, response, req.method, req.signal);
10811
+ return {
10812
+ url: req.url,
10813
+ status: response.status,
10814
+ statusText: response.statusText,
10815
+ headers: response.headers,
10816
+ body: closing(chunks2, socket),
10817
+ cancel: async () => socket.close()
10818
+ };
10819
+ } catch (err) {
10820
+ socket.close();
10821
+ throw err;
10822
+ }
10823
+ }
10824
+ async websocket(req, port) {
10825
+ const url = new URL(req.url);
10826
+ const socket = this.connect(url, port, req.signal);
10827
+ try {
10828
+ const key = btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(16))));
10829
+ const extra = [
10830
+ ["Upgrade", "websocket"],
10831
+ ["Connection", "Upgrade"],
10832
+ ["Sec-WebSocket-Key", key],
10833
+ ["Sec-WebSocket-Version", "13"]
10834
+ ];
10835
+ if (req.protocols.length > 0)
10836
+ extra.push(["Sec-WebSocket-Protocol", req.protocols.join(", ")]);
10837
+ await writeAll2(socket, requestHead("GET", url, req.headers, extra), req.signal);
10838
+ const incoming = new Incoming(socket);
10839
+ const head = await readHead(incoming, req.signal);
10840
+ if (head.status !== 101) {
10841
+ throw errno(
10842
+ "EPROTO",
10843
+ `the server answered ${head.status} ${head.statusText}, not an upgrade`
10844
+ );
10845
+ }
10846
+ const upgraded = fieldTokens(head.headers, "upgrade").includes("websocket") && fieldTokens(head.headers, "connection").includes("upgrade");
10847
+ const accept2 = fieldValues(head.headers, "sec-websocket-accept")[0];
10848
+ if (!upgraded || accept2 !== await acceptFor(key)) {
10849
+ throw errno("EPROTO", "the server did not accept the WebSocket key");
10850
+ }
10851
+ const protocol = fieldValues(head.headers, "sec-websocket-protocol")[0] ?? "";
10852
+ return new LoopbackWebSocket(socket, incoming, protocol);
10853
+ } catch (err) {
10854
+ socket.close();
10855
+ throw err;
10856
+ }
10857
+ }
10858
+ };
10859
+ async function writeAll2(socket, bytes, signal) {
10860
+ for (let at = 0; at < bytes.length; ) at += await socket.write(bytes.subarray(at), signal);
10861
+ }
10862
+ async function* closing(chunks2, socket) {
10863
+ try {
10864
+ yield* chunks2;
10865
+ } catch (err) {
10866
+ if (typeof err?.code === "string") throw err;
10867
+ throw errno("EPROTO", err instanceof Error ? err.message : String(err));
10868
+ } finally {
10869
+ socket.close();
10870
+ }
10871
+ }
10872
+ var LoopbackWebSocket = class {
10873
+ protocol;
10874
+ messages;
10875
+ closed;
10876
+ pending = 0;
10877
+ writes = Promise.resolve();
10878
+ sentClose = false;
10879
+ stop = new AbortController();
10880
+ socket;
10881
+ settle;
10882
+ unread = 0;
10883
+ room;
10884
+ constructor(socket, incoming, protocol) {
10885
+ this.socket = socket;
10886
+ this.protocol = protocol;
10887
+ this.closed = new Promise((resolve2) => {
10888
+ this.settle = resolve2;
10889
+ });
10890
+ this.stop.signal.addEventListener("abort", () => this.room?.(), { once: true });
10891
+ const queue = [];
10892
+ let wake;
10893
+ let ended = false;
10894
+ void this.pump(new WsReader(incoming, void 0, true, true), (message3) => {
10895
+ queue.push(message3);
10896
+ this.unread += message3.length;
10897
+ wake?.();
10898
+ }).finally(() => {
10899
+ ended = true;
10900
+ wake?.();
10901
+ });
10902
+ const taken = (message3) => {
10903
+ this.unread -= message3.length;
10904
+ const room = this.room;
10905
+ this.room = void 0;
10906
+ room?.();
10907
+ };
10908
+ this.messages = {
10909
+ [Symbol.asyncIterator]: async function* () {
10910
+ for (; ; ) {
10911
+ const next2 = queue.shift();
10912
+ if (next2 !== void 0) {
10913
+ taken(next2);
10914
+ yield next2;
10915
+ continue;
10916
+ }
10917
+ if (ended) return;
10918
+ await new Promise((resolve2) => {
10919
+ wake = resolve2;
10920
+ });
10921
+ wake = void 0;
10922
+ }
10923
+ }
10924
+ };
10925
+ }
10926
+ async space() {
10927
+ while (this.unread > MAX_UNREAD && !this.stop.signal.aborted) {
10928
+ await new Promise((resolve2) => {
10929
+ this.room = resolve2;
10930
+ });
10931
+ }
10932
+ }
10933
+ get buffered() {
10934
+ return this.pending;
10935
+ }
10936
+ write(bytes) {
10937
+ this.pending += bytes.length;
10938
+ const previous = this.writes;
10939
+ this.writes = (async () => {
10940
+ await previous;
10941
+ try {
10942
+ await writeAll2(this.socket, bytes, this.stop.signal);
10943
+ } catch {
10944
+ this.stop.abort();
10945
+ }
10946
+ this.pending -= bytes.length;
10947
+ })();
10948
+ }
10949
+ async pump(reader, deliver) {
10950
+ let close = { code: 1006, reason: "" };
10951
+ try {
10952
+ for (; ; ) {
10953
+ await this.space();
10954
+ const event = await reader.next(this.stop.signal);
10955
+ if (!event) break;
10956
+ if (event.kind === "text") deliver(event.text);
10957
+ else if (event.kind === "binary") deliver(event.bytes);
10958
+ else if (event.kind === "ping") this.write(clientFrame(OP_PONG, event.payload));
10959
+ else {
10960
+ close = { code: event.code, reason: event.reason };
10961
+ if (!this.sentClose) this.sendClose(event.code === 1005 ? 1e3 : event.code, "");
10962
+ break;
10963
+ }
10964
+ }
10965
+ } catch (err) {
10966
+ if (err instanceof WsError) this.sendClose(err.code, err.message);
10967
+ close = { code: err instanceof WsError ? err.code : 1006, reason: "" };
10968
+ } finally {
10969
+ const late = setTimeout(() => this.stop.abort(), CLOSE_WAIT_MS3);
10970
+ await this.writes;
10971
+ clearTimeout(late);
10972
+ this.socket.close();
10973
+ this.settle(close);
10974
+ }
10975
+ }
10976
+ sendClose(code, reason) {
10977
+ if (this.sentClose) return;
10978
+ this.sentClose = true;
10979
+ const late = setTimeout(() => this.stop.abort(), CLOSE_WAIT_MS3);
10980
+ void this.closed.then(() => clearTimeout(late));
10981
+ const bytes = new TextEncoder().encode(reason).subarray(0, 123);
10982
+ const payload = new Uint8Array(2 + bytes.length);
10983
+ new DataView(payload.buffer).setUint16(0, sendableCode(code) ? code : CLOSE.error);
10984
+ payload.set(bytes, 2);
10985
+ this.write(clientFrame(OP_CLOSE, payload));
10986
+ }
10987
+ send(data) {
10988
+ if (this.sentClose) throw errno("EPIPE", "the websocket is closing");
10989
+ const text = typeof data === "string";
10990
+ const payload = text ? new TextEncoder().encode(data) : data;
10991
+ this.write(clientFrame(text ? OP_TEXT : OP_BINARY, payload));
10992
+ }
10993
+ close(code = 1e3, reason = "") {
10994
+ this.sendClose(code, reason);
10995
+ }
10996
+ };
10997
+
10598
10998
  // src/kernel/net/x509.ts
10599
10999
  function concat4(parts2) {
10600
11000
  const out = new Uint8Array(parts2.reduce((n, p) => n + p.length, 0));
@@ -11268,7 +11668,7 @@ var WS_OPS = [
11268
11668
  "net-ws-send",
11269
11669
  "net-ws-close"
11270
11670
  ];
11271
- var TOKEN3 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
11671
+ var TOKEN4 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
11272
11672
  function message(e) {
11273
11673
  return e instanceof Error ? e.message : String(e);
11274
11674
  }
@@ -11277,16 +11677,18 @@ function errnoOf(e) {
11277
11677
  const code = e?.code;
11278
11678
  return typeof code === "string" && ERRNO.test(code) ? code : "ECONNREFUSED";
11279
11679
  }
11280
- function fail(errno, text) {
11281
- return { ok: false, errno, message: text };
11680
+ function fail(errno2, text) {
11681
+ return { ok: false, errno: errno2, message: text };
11282
11682
  }
11283
11683
  var WsHandles = class {
11284
11684
  transport;
11285
11685
  open = /* @__PURE__ */ new Map();
11286
11686
  next;
11287
- constructor(transport, next2) {
11687
+ local;
11688
+ constructor(transport, next2, local) {
11288
11689
  this.transport = transport;
11289
11690
  this.next = next2;
11691
+ this.local = local;
11290
11692
  }
11291
11693
  owns(handle) {
11292
11694
  return this.open.has(handle);
@@ -11315,16 +11717,18 @@ var WsHandles = class {
11315
11717
  return { ok: true, kind: "json", json: handle };
11316
11718
  }
11317
11719
  async connect(handle, req, entry) {
11318
- const { transport } = this;
11319
- if (!transport.traits.websocket || !transport.websocket) return fail("ENOTSUP", NO_WEBSOCKET2);
11720
+ const { transport, local } = this;
11721
+ const port = local?.port(req.url);
11722
+ const open = { protocols: req.protocols, headers: req.headers, signal: entry.abort.signal };
11723
+ if (port === void 0 && (!transport.traits.websocket || !transport.websocket)) {
11724
+ return fail("ENOTSUP", NO_WEBSOCKET2);
11725
+ }
11320
11726
  let socket;
11321
11727
  try {
11322
- socket = await transport.websocket({
11728
+ socket = port === void 0 ? await transport.websocket({
11323
11729
  url: toHostLoopback(req.url),
11324
- protocols: req.protocols,
11325
- headers: req.headers,
11326
- signal: entry.abort.signal
11327
- });
11730
+ ...open
11731
+ }) : await local.websocket({ url: req.url, ...open }, port);
11328
11732
  } catch (e) {
11329
11733
  return fail(errnoOf(e), message(e));
11330
11734
  }
@@ -11332,7 +11736,7 @@ var WsHandles = class {
11332
11736
  socket.close(1001, "the program went away");
11333
11737
  return fail("EBADF", `no websocket ${handle}`);
11334
11738
  }
11335
- if (socket.protocol && !(req.protocols.includes(socket.protocol) && TOKEN3.test(socket.protocol))) {
11739
+ if (socket.protocol && !(req.protocols.includes(socket.protocol) && TOKEN4.test(socket.protocol))) {
11336
11740
  socket.close(1002, "subprotocol not offered");
11337
11741
  return fail("EPROTO", "the far end chose a subprotocol the program did not offer");
11338
11742
  }
@@ -11395,6 +11799,10 @@ var EMPTY = new Uint8Array(0);
11395
11799
  function message2(e) {
11396
11800
  return e instanceof Error ? e.message : String(e);
11397
11801
  }
11802
+ function codeOf2(e) {
11803
+ const code = e?.code;
11804
+ return typeof code === "string" && /^E[A-Z]+$/.test(code) ? code : "EIO";
11805
+ }
11398
11806
  async function* once(bytes) {
11399
11807
  yield bytes;
11400
11808
  }
@@ -11404,10 +11812,12 @@ var HttpHandles = class {
11404
11812
  nextHandle = 0;
11405
11813
  ws;
11406
11814
  owner;
11407
- constructor(transport, owner) {
11815
+ local;
11816
+ constructor(transport, owner, local) {
11408
11817
  this.transport = transport;
11409
11818
  this.owner = owner;
11410
- this.ws = new WsHandles(transport, () => ++this.nextHandle);
11819
+ this.local = local;
11820
+ this.ws = new WsHandles(transport, () => ++this.nextHandle, local);
11411
11821
  }
11412
11822
  syscall(req) {
11413
11823
  if (req.op.startsWith("net-ws-")) return this.ws.syscall(req);
@@ -11415,10 +11825,13 @@ var HttpHandles = class {
11415
11825
  if (req.op === "net-open") return Promise.resolve(this.opened(req));
11416
11826
  if (req.op === "net-head") return this.head(req.handle);
11417
11827
  if (req.op === "net-read") return this.read(req.handle, req.max);
11418
- if (req.op === "net-traits") return Promise.resolve(this.traits());
11828
+ if (req.op === "net-traits") return Promise.resolve(this.traits(req.url));
11419
11829
  return this.close(req.handle);
11420
11830
  }
11421
- traits() {
11831
+ traits(url) {
11832
+ if (url !== void 0 && this.local?.port(url) !== void 0) {
11833
+ return { ok: true, kind: "json", json: LOOPBACK_TRAITS };
11834
+ }
11422
11835
  const { unavailable, crossOrigin = "any", ...rest2 } = this.transport.traits;
11423
11836
  if (unavailable) return { ok: false, errno: "ENETUNREACH", message: NO_TRANSPORT };
11424
11837
  return { ok: true, kind: "json", json: { ...rest2, crossOrigin } };
@@ -11449,28 +11862,30 @@ var HttpHandles = class {
11449
11862
  return { handle, head: entry.head };
11450
11863
  }
11451
11864
  async respond(handle, req, entry) {
11865
+ const port = this.local?.port(req.url);
11452
11866
  try {
11453
- const response = await this.transport.fetch(
11454
- {
11455
- url: req.url,
11456
- method: req.method,
11457
- headers: req.headers,
11458
- ...req.body ? { body: req.body } : {},
11459
- signal: entry.abort.signal
11460
- },
11461
- this.owner
11462
- );
11867
+ const target = port === void 0 ? toHostLoopback(req.url) : req.url;
11868
+ const request = {
11869
+ url: target,
11870
+ method: req.method,
11871
+ headers: req.headers,
11872
+ ...req.body ? { body: req.body } : {},
11873
+ signal: entry.abort.signal
11874
+ };
11875
+ const response = port === void 0 ? await this.transport.fetch(request, this.owner) : await this.local.fetch(request, port);
11463
11876
  entry.body = response.body[Symbol.asyncIterator]();
11464
11877
  entry.cancel = () => response.cancel();
11465
11878
  if (!this.open.has(handle)) await entry.cancel().catch(() => void 0);
11466
- const head = this.headOf(handle, response.url ?? req.url, response);
11879
+ const shown = response.url && response.url !== target ? response.url : req.url;
11880
+ const head = this.headOf(handle, shown, response);
11467
11881
  return response.redirected ? { ...head, json: { ...head.json, redirected: true } } : head;
11468
11882
  } catch (e) {
11469
11883
  const text = message2(e);
11884
+ if (port !== void 0) return { ok: false, errno: codeOf2(e), message: text };
11470
11885
  const status = e.status;
11471
11886
  if (text === NO_TRANSPORT || typeof status !== "number" || status === 502) {
11472
- const errno = text === NO_TRANSPORT ? "ENETUNREACH" : "ECONNREFUSED";
11473
- return { ok: false, errno, message: text };
11887
+ const errno2 = text === NO_TRANSPORT ? "ENETUNREACH" : "ECONNREFUSED";
11888
+ return { ok: false, errno: errno2, message: text };
11474
11889
  }
11475
11890
  entry.body = once(new TextEncoder().encode(text))[Symbol.asyncIterator]();
11476
11891
  return this.headOf(handle, req.url, {
@@ -13039,6 +13454,7 @@ function aborted(signal) {
13039
13454
  signal.addEventListener("abort", () => reject(new KernelError("EINTR")), { once: true });
13040
13455
  });
13041
13456
  }
13457
+ var PLAIN_FILE_MODE = 420;
13042
13458
  var JOB_OPS = /* @__PURE__ */ new Set([
13043
13459
  "proc-setpgid",
13044
13460
  "proc-getpgid",
@@ -13493,15 +13909,21 @@ var WasmProcess = class {
13493
13909
  }
13494
13910
  if (this.writesAt(req) && this.options.readonlyAt?.(path)) throw new KernelError("EROFS");
13495
13911
  const open = { ...req, path };
13912
+ const plain = this.plainMode(req.mode);
13496
13913
  if (req.exclusive) {
13497
- await nodes.createExclusive(path, this.newOwner());
13914
+ await nodes.createExclusive(path, this.newOwner(req.mode));
13915
+ if (plain !== void 0) await this.options.fs.chmod?.(path, plain);
13498
13916
  this.fds.installAt(fd, this.openVfsFile(open));
13499
13917
  } else if (req.create) {
13500
13918
  await nodes.onEntry(path, async () => {
13501
- const fresh2 = this.options.owners && !await present2(this.options.fs, path, true);
13919
+ const marks = this.options.owners || plain !== void 0;
13920
+ const fresh2 = marks && !await present2(this.options.fs, path, true);
13502
13921
  this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
13503
13922
  await this.fds.get(fd).file.stat?.();
13504
- if (fresh2) await this.options.fs.chown?.(path, { ...this.newOwner(), kind: "new" });
13923
+ if (fresh2 && plain !== void 0) await this.options.fs.chmod?.(path, plain);
13924
+ else if (fresh2) {
13925
+ await this.options.fs.chown?.(path, { ...this.newOwner(req.mode), kind: "new" });
13926
+ }
13505
13927
  });
13506
13928
  } else {
13507
13929
  this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
@@ -13614,9 +14036,14 @@ var WasmProcess = class {
13614
14036
  if (!tty) throw new KernelError("ENOENT");
13615
14037
  return tty;
13616
14038
  }
13617
- newOwner() {
14039
+ plainMode(mode) {
14040
+ if (this.options.owners || mode === void 0) return void 0;
14041
+ const wanted = mode & 4095 & ~this.umask;
14042
+ return wanted === PLAIN_FILE_MODE ? void 0 : wanted;
14043
+ }
14044
+ newOwner(mode = 438) {
13618
14045
  if (!this.options.owners) return void 0;
13619
- return { uid: this.cred.euid, gid: this.cred.egid, mode: 438 & ~this.umask };
14046
+ return { uid: this.cred.euid, gid: this.cred.egid, mode: mode & 4095 & ~this.umask };
13620
14047
  }
13621
14048
  async openProc(req, path) {
13622
14049
  return this.fds.install(await this.procFile(req, path), 3);
@@ -14356,7 +14783,7 @@ function inherited(opts) {
14356
14783
  }
14357
14784
  function holdingToken(opts, holds, caller) {
14358
14785
  return mintSyncFsToken({
14359
- ...opts.owners ? caller : {},
14786
+ ...opts.owners ? caller : { umask: caller.umask },
14360
14787
  ...opts.dac ? { dac: opts.dac, pid: opts.pid, created: caller.created } : {},
14361
14788
  ...opts.links !== void 0 ? { links: opts.links } : {},
14362
14789
  ...opts.fresh ? { fresh: (path) => opts.fresh?.(path, caller.umask()) } : {},
@@ -15306,7 +15733,7 @@ function scrubberOf(secrets) {
15306
15733
  const usable = secrets.filter((s) => encoder5.encode(s.value).length >= MIN_VALUE);
15307
15734
  return usable.length > 0 ? compile(usable, { idleMs: SCRUB_IDLE_MS }) : void 0;
15308
15735
  }
15309
- async function writeAll2(file, bytes) {
15736
+ async function writeAll3(file, bytes) {
15310
15737
  const write = file.write;
15311
15738
  for (let at = 0; at < bytes.length; ) at += await write.call(file, bytes.subarray(at));
15312
15739
  }
@@ -15340,7 +15767,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
15340
15767
  const later = (bytes) => {
15341
15768
  if (bytes.length === 0) return;
15342
15769
  pending += 1;
15343
- tail = tail.then(() => writeAll2(file, bytes)).catch((err) => {
15770
+ tail = tail.then(() => writeAll3(file, bytes)).catch((err) => {
15344
15771
  if (failed3 === void 0) failed3 = err;
15345
15772
  }).finally(() => {
15346
15773
  pending -= 1;
@@ -15971,8 +16398,8 @@ var FSA_CAPABILITIES = {
15971
16398
  ranges: true,
15972
16399
  sessions: true
15973
16400
  };
15974
- var codeOf2 = (err) => err?.code;
15975
- var denied = (err) => codeOf2(err) === "EACCES" || err?.name === "NotAllowedError";
16401
+ var codeOf3 = (err) => err?.code;
16402
+ var denied = (err) => codeOf3(err) === "EACCES" || err?.name === "NotAllowedError";
15976
16403
  function attrOf(st) {
15977
16404
  return {
15978
16405
  kind: st.isDirectory ? "directory" : st.isSymbolicLink ? "symlink" : "file",
@@ -15994,7 +16421,7 @@ function kernelFsHandlers(fs) {
15994
16421
  try {
15995
16422
  return await fs.stat(path);
15996
16423
  } catch (err) {
15997
- if (codeOf2(err) === "ENOENT") return void 0;
16424
+ if (codeOf3(err) === "ENOENT") return void 0;
15998
16425
  throw err;
15999
16426
  }
16000
16427
  }
@@ -16187,7 +16614,7 @@ function parseFstab(text) {
16187
16614
  });
16188
16615
  return lines2;
16189
16616
  }
16190
- function codeOf3(err) {
16617
+ function codeOf4(err) {
16191
16618
  const code = err?.code;
16192
16619
  return typeof code === "string" ? code : "EIO";
16193
16620
  }
@@ -16225,18 +16652,18 @@ async function mountLine({ line, spec, invalid }, mount, signal, retries, discar
16225
16652
  break;
16226
16653
  } catch (err) {
16227
16654
  failed3 = err;
16228
- if (FINAL.has(codeOf3(err)) || attempt === retries.length) break;
16655
+ if (FINAL.has(codeOf4(err)) || attempt === retries.length) break;
16229
16656
  await wait(retries[attempt], signal);
16230
16657
  }
16231
16658
  }
16232
- const code = codeOf3(failed3);
16659
+ const code = codeOf4(failed3);
16233
16660
  return { line, target: spec.target, code, error: String(failed3?.message ?? code) };
16234
16661
  }
16235
16662
 
16236
16663
  // src/mount/hostfs.ts
16237
16664
  var HOSTFS_IO = 16 * 1024 * 1024;
16238
- var codeOf4 = (err) => err?.code;
16239
- var unreachable = (err) => codeOf4(err) === "ENOTCONN";
16665
+ var codeOf5 = (err) => err?.code;
16666
+ var unreachable = (err) => codeOf5(err) === "ENOTCONN";
16240
16667
  function rel(path) {
16241
16668
  return path.replace(/^\/+/, "");
16242
16669
  }
@@ -16296,9 +16723,9 @@ var HostfsClient = class {
16296
16723
  throw fsError2("ESTALE", "the folder was granted again, so its open files are gone");
16297
16724
  return this.send(path, init, false, true);
16298
16725
  }
16299
- const errno = response.headers.get("X-Hostfs-Errno") ?? "EIO";
16726
+ const errno2 = response.headers.get("X-Hostfs-Errno") ?? "EIO";
16300
16727
  const body = await response.json().catch(() => ({}));
16301
- throw fsError2(errno, typeof body.message === "string" ? body.message : errno);
16728
+ throw fsError2(errno2, typeof body.message === "string" ? body.message : errno2);
16302
16729
  }
16303
16730
  async call(op, body = {}, handle = false) {
16304
16731
  const response = await this.send(
@@ -16413,7 +16840,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
16413
16840
  try {
16414
16841
  return { opened: await opening2(), restore: void 0 };
16415
16842
  } catch (err) {
16416
- if (!flags2.write || codeOf4(err) !== "EACCES") throw err;
16843
+ if (!flags2.write || codeOf5(err) !== "EACCES") throw err;
16417
16844
  const was = await client.call("stat", { path: rel(path) });
16418
16845
  const mode = was.mode ?? 0;
16419
16846
  if (mode & 128) throw err;
@@ -16838,7 +17265,7 @@ var PortTransport = class {
16838
17265
  }
16839
17266
  };
16840
17267
  var errorOf = (err) => ({
16841
- code: typeof codeOf4(err) === "string" ? codeOf4(err) : "EIO",
17268
+ code: typeof codeOf5(err) === "string" ? codeOf5(err) : "EIO",
16842
17269
  message: err?.message ?? String(err)
16843
17270
  });
16844
17271
  function serveHostfsPort(port, transport) {
@@ -17429,8 +17856,8 @@ var RULE = new RegExp(
17429
17856
  var TERM = /^[A-Za-z0-9._+-]+$/;
17430
17857
  var PLAIN = /^[A-Za-z0-9_@%+=:,./-]+$/;
17431
17858
  var USAGE = "usage: sudo [-n] [-u user] [--] command [args...]\n sudo -l";
17432
- function failed2(errno, error) {
17433
- return { error, errno };
17859
+ function failed2(errno2, error) {
17860
+ return { error, errno: errno2 };
17434
17861
  }
17435
17862
  function unescaped(double) {
17436
17863
  return double.replace(/\\(["\\$`])/g, "$1");
@@ -18724,6 +19151,12 @@ var Launcher = class {
18724
19151
  groupSession(pgid) {
18725
19152
  return this.jobs.sessionOf(pgid);
18726
19153
  }
19154
+ loopbackFor(pid) {
19155
+ return new KernelLoopback(this.net, () => {
19156
+ const cred = this.credOf(pid);
19157
+ return { pid, uid: cred.euid, gid: cred.egid };
19158
+ });
19159
+ }
18727
19160
  treeScrubs(req) {
18728
19161
  const inherited2 = req.ppid !== void 0 ? this.scrubs.get(req.ppid) : void 0;
18729
19162
  return req.scrub ? [...inherited2 ?? [], req.scrub] : inherited2;
@@ -18801,7 +19234,7 @@ var Launcher = class {
18801
19234
  jobs: this.jobs,
18802
19235
  ptys: this.ptys,
18803
19236
  net: this.net,
18804
- http: new HttpHandles(this.transport, pid),
19237
+ http: new HttpHandles(this.transport, pid, this.loopbackFor(pid)),
18805
19238
  locks: this.locks,
18806
19239
  resolver: this.resolver,
18807
19240
  onReap: (child) => this.reaped(child, pid),
@@ -19983,7 +20416,7 @@ import { connect as tls } from "node:tls";
19983
20416
  var PROBE_MS = 3e3;
19984
20417
  var PASSED = /* @__PURE__ */ new Set(["ECONNREFUSED", "EHOSTUNREACH", "ENETUNREACH", "ETIMEDOUT", "ECONNRESET"]);
19985
20418
  var NO_NAME = /* @__PURE__ */ new Set(["ENOTFOUND", "EAI_AGAIN", "EAI_NONAME", "EAI_FAIL", "ENODATA"]);
19986
- function codeOf5(e) {
20419
+ function codeOf6(e) {
19987
20420
  const code = e?.code;
19988
20421
  return typeof code === "string" ? code : void 0;
19989
20422
  }
@@ -19998,7 +20431,7 @@ function probe(secure, host, port, ms) {
19998
20431
  const timer = setTimeout(() => done4("ETIMEDOUT"), ms);
19999
20432
  socket.once(secure ? "secureConnect" : "connect", () => done4("EPROTO"));
20000
20433
  socket.once("error", (e) => {
20001
- const code = codeOf5(e) ?? "";
20434
+ const code = codeOf6(e) ?? "";
20002
20435
  done4(PASSED.has(code) ? code : "EPROTO");
20003
20436
  });
20004
20437
  });
@@ -20011,7 +20444,7 @@ async function diagnose(url, ms = PROBE_MS) {
20011
20444
  try {
20012
20445
  await lookup2(host);
20013
20446
  } catch (e) {
20014
- return NO_NAME.has(codeOf5(e) ?? "") ? "EAI_NONAME" : void 0;
20447
+ return NO_NAME.has(codeOf6(e) ?? "") ? "EAI_NONAME" : void 0;
20015
20448
  }
20016
20449
  return probe(secure, host, port, ms);
20017
20450
  }
@@ -20027,7 +20460,7 @@ function diagnosing(transport, check = diagnose) {
20027
20460
  try {
20028
20461
  return await open(request);
20029
20462
  } catch (e) {
20030
- if (request.signal.aborted || !(e instanceof Error) || codeOf5(e)) throw e;
20463
+ if (request.signal.aborted || !(e instanceof Error) || codeOf6(e)) throw e;
20031
20464
  const code = await check(request.url).catch(() => void 0);
20032
20465
  throw code ? Object.assign(e, { code }) : e;
20033
20466
  }
@@ -20285,7 +20718,7 @@ function dialStream(port, onClose) {
20285
20718
  }
20286
20719
 
20287
20720
  // src/client/loopback-fetch.ts
20288
- var OWN_HEADERS = /* @__PURE__ */ new Set(["host", "connection", "content-length", "transfer-encoding"]);
20721
+ var OWN_HEADERS2 = /* @__PURE__ */ new Set(["host", "connection", "content-length", "transfer-encoding"]);
20289
20722
  var encoder8 = new TextEncoder();
20290
20723
  var latin12 = new TextDecoder("latin1");
20291
20724
  var ByteSource = class {
@@ -20332,7 +20765,7 @@ function indexOf(haystack, needle) {
20332
20765
  }
20333
20766
  return -1;
20334
20767
  }
20335
- function parseHead(bytes) {
20768
+ function parseHead2(bytes) {
20336
20769
  const [statusLine = "", ...lines2] = latin12.decode(bytes).split("\r\n");
20337
20770
  const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
20338
20771
  if (!match) throw errorWithCode(`EPROTO: not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);
@@ -20346,7 +20779,7 @@ function parseHead(bytes) {
20346
20779
  function header3(head, name2) {
20347
20780
  return head.headers.find(([key]) => key.toLowerCase() === name2)?.[1];
20348
20781
  }
20349
- async function* chunked(source) {
20782
+ async function* chunked2(source) {
20350
20783
  for (; ; ) {
20351
20784
  const line = await source.until("\r\n");
20352
20785
  if (!line) throw errorWithCode("EPROTO: the chunked body ended early");
@@ -20358,7 +20791,7 @@ async function* chunked(source) {
20358
20791
  yield data;
20359
20792
  }
20360
20793
  }
20361
- async function* counted(source, length2) {
20794
+ async function* counted2(source, length2) {
20362
20795
  let left = length2;
20363
20796
  while (left > 0) {
20364
20797
  const data = await source.some(left);
@@ -20367,19 +20800,19 @@ async function* counted(source, length2) {
20367
20800
  yield data;
20368
20801
  }
20369
20802
  }
20370
- async function* untilClose(source) {
20803
+ async function* untilClose2(source) {
20371
20804
  for (let data = await source.some(65536); data; data = await source.some(65536)) yield data;
20372
20805
  }
20373
- function bodyOf(source, head, method) {
20806
+ function bodyOf2(source, head, method) {
20374
20807
  if (method === "HEAD" || head.status === 204 || head.status === 304) return void 0;
20375
- if (header3(head, "transfer-encoding")?.toLowerCase().includes("chunked")) return chunked(source);
20808
+ if (header3(head, "transfer-encoding")?.toLowerCase().includes("chunked")) return chunked2(source);
20376
20809
  const length2 = header3(head, "content-length");
20377
- if (length2 === void 0) return untilClose(source);
20810
+ if (length2 === void 0) return untilClose2(source);
20378
20811
  const size = Number(length2);
20379
20812
  if (!/^\d+$/.test(length2.trim()) || !Number.isSafeInteger(size)) {
20380
20813
  throw errorWithCode(`EPROTO: a malformed Content-Length: ${length2}`);
20381
20814
  }
20382
- return counted(source, size);
20815
+ return counted2(source, size);
20383
20816
  }
20384
20817
  function stream(chunks2, socket, signal) {
20385
20818
  return new ReadableStream({
@@ -20408,7 +20841,7 @@ async function requestBytes(request) {
20408
20841
  const body = request.body ? new Uint8Array(await request.arrayBuffer()) : void 0;
20409
20842
  const lines2 = [`${request.method} ${url.pathname}${url.search} HTTP/1.1`, `Host: ${url.host}`];
20410
20843
  request.headers.forEach((value, name2) => {
20411
- if (!OWN_HEADERS.has(name2)) lines2.push(`${name2}: ${value}`);
20844
+ if (!OWN_HEADERS2.has(name2)) lines2.push(`${name2}: ${value}`);
20412
20845
  });
20413
20846
  if (body) lines2.push(`Content-Length: ${body.length}`);
20414
20847
  lines2.push("Connection: close", "", "");
@@ -20436,9 +20869,9 @@ async function loopbackFetch(dial, input, options) {
20436
20869
  do {
20437
20870
  const raw = await source.until("\r\n\r\n");
20438
20871
  if (!raw) throw errorWithCode("ECONNRESET: the connection closed before a response");
20439
- head = parseHead(raw);
20872
+ head = parseHead2(raw);
20440
20873
  } while (head.status >= 100 && head.status < 200);
20441
- const chunks2 = bodyOf(source, head, request.method);
20874
+ const chunks2 = bodyOf2(source, head, request.method);
20442
20875
  if (!chunks2) socket.close();
20443
20876
  return new Response(chunks2 ? stream(chunks2, socket, options.signal) : null, {
20444
20877
  status: head.status,