@ai-ecoverse/slicc-kernel 1.43.1 → 1.45.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.
@@ -873,8 +873,8 @@ var MEDIA_STORE = "handles";
873
873
  function memoryMedia() {
874
874
  const handles = /* @__PURE__ */ new Map();
875
875
  return {
876
- get: async (id) => handles.get(id),
877
- put: async (id, handle) => void handles.set(id, handle)
876
+ get: async (id2) => handles.get(id2),
877
+ put: async (id2, handle) => void handles.set(id2, handle)
878
878
  };
879
879
  }
880
880
  function settle2(request2, value) {
@@ -890,11 +890,11 @@ function openMedia(name2, factory) {
890
890
  const db = request2.result;
891
891
  const store = (mode) => db.transaction(MEDIA_STORE, mode).objectStore(MEDIA_STORE);
892
892
  return {
893
- get: (id) => {
894
- const got = store("readonly").get(id);
893
+ get: (id2) => {
894
+ const got = store("readonly").get(id2);
895
895
  return settle2(got, () => got.result);
896
896
  },
897
- put: (id, handle) => settle2(store("readwrite").put(handle, id), () => void 0)
897
+ put: (id2, handle) => settle2(store("readwrite").put(handle, id2), () => void 0)
898
898
  };
899
899
  });
900
900
  }
@@ -959,7 +959,7 @@ function portCdpHook(ask, gone) {
959
959
  }
960
960
 
961
961
  // src/client/protocol.ts
962
- var PROTOCOL = [1, 7];
962
+ var PROTOCOL = [1, 8];
963
963
  var FS_METHODS = [
964
964
  "readFile",
965
965
  "writeFile",
@@ -1920,8 +1920,8 @@ function serveSocket(socket, port, onClose) {
1920
1920
  let writes = Promise.resolve();
1921
1921
  let credit = 0;
1922
1922
  let granted2;
1923
- const send = (message2, transfer) => {
1924
- if (!closed) port.postMessage(message2, transfer);
1923
+ const send = (message3, transfer) => {
1924
+ if (!closed) port.postMessage(message3, transfer);
1925
1925
  };
1926
1926
  const close = (reset = false) => {
1927
1927
  if (closed) return;
@@ -2036,8 +2036,8 @@ var BODY_IDLE_MS = 5 * 60 * 1e3;
2036
2036
  var INBOUND_LIMIT = 16 * 1024 * 1024;
2037
2037
  var MESSAGE_LIMIT = 64 * 1024 * 1024;
2038
2038
  var TOO_BIG = 1009;
2039
- function payloadSize(message2) {
2040
- return typeof message2 === "string" ? new TextEncoder().encode(message2).length : message2.length;
2039
+ function payloadSize(message3) {
2040
+ return typeof message3 === "string" ? new TextEncoder().encode(message3).length : message3.length;
2041
2041
  }
2042
2042
  var MessageQueue = class {
2043
2043
  items = [];
@@ -2057,9 +2057,9 @@ var MessageQueue = class {
2057
2057
  this.settle = resolve;
2058
2058
  });
2059
2059
  }
2060
- push(message2) {
2060
+ push(message3) {
2061
2061
  if (this.ended) return;
2062
- const size = payloadSize(message2);
2062
+ const size = payloadSize(message3);
2063
2063
  const huge = size > MESSAGE_LIMIT;
2064
2064
  if (huge || this.items.length > 0 && this.size + size > this.limit) {
2065
2065
  const reason = huge ? `a message is over ${MESSAGE_LIMIT} bytes` : `over ${this.limit} bytes of messages are waiting`;
@@ -2068,7 +2068,7 @@ var MessageQueue = class {
2068
2068
  return;
2069
2069
  }
2070
2070
  this.size += size;
2071
- this.items.push(message2);
2071
+ this.items.push(message3);
2072
2072
  this.wake();
2073
2073
  }
2074
2074
  end(close) {
@@ -2107,7 +2107,7 @@ var NO_WEBSOCKET = "WebSocket not supported by this transport (fetchTransport ne
2107
2107
  var MAX_REQUEST_BODY = 64 * 1024 * 1024;
2108
2108
  function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
2109
2109
  const open = /* @__PURE__ */ new Map();
2110
- const fail = (nid, e) => {
2110
+ const fail2 = (nid, e) => {
2111
2111
  open.delete(nid);
2112
2112
  const { status, code } = e;
2113
2113
  peer.postMessage({
@@ -2143,7 +2143,7 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
2143
2143
  headers: response.headers
2144
2144
  });
2145
2145
  } catch (e) {
2146
- fail(call.nid, e);
2146
+ fail2(call.nid, e);
2147
2147
  }
2148
2148
  };
2149
2149
  const sockets = /* @__PURE__ */ new Map();
@@ -2177,7 +2177,7 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
2177
2177
  peer.postMessage({ net: "ws-opened", nid: call.nid, protocol: socket.protocol });
2178
2178
  void relay(call.nid, socket);
2179
2179
  } catch (e) {
2180
- fail(call.nid, e);
2180
+ fail2(call.nid, e);
2181
2181
  }
2182
2182
  };
2183
2183
  const relay = async (nid, socket) => {
@@ -2249,7 +2249,7 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
2249
2249
  peer.postMessage({ net: "chunk", nid, bytes }, [bytes.buffer]);
2250
2250
  }
2251
2251
  } catch (e) {
2252
- fail(nid, e);
2252
+ fail2(nid, e);
2253
2253
  }
2254
2254
  };
2255
2255
  const drop = (nid) => {
@@ -2278,6 +2278,287 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
2278
2278
  };
2279
2279
  }
2280
2280
 
2281
+ // src/kernel/cred.ts
2282
+ var ROOT = Object.freeze({
2283
+ ruid: 0,
2284
+ euid: 0,
2285
+ suid: 0,
2286
+ rgid: 0,
2287
+ egid: 0,
2288
+ sgid: 0,
2289
+ groups: [0]
2290
+ });
2291
+ var MAX_ID = 4294967294;
2292
+ var MAX_GROUPS = 65536;
2293
+ var UIDS = ["ruid", "euid", "suid"];
2294
+ var GIDS = ["rgid", "egid", "sgid"];
2295
+ function userCred(uid, gid, groups = [gid]) {
2296
+ return { ruid: uid, euid: uid, suid: uid, rgid: gid, egid: gid, sgid: gid, groups: [...groups] };
2297
+ }
2298
+ function copyCred(cred) {
2299
+ return { ...cred, groups: [...cred.groups] };
2300
+ }
2301
+ function id(value) {
2302
+ if (value === void 0 || value === -1 || value === 4294967295) return void 0;
2303
+ if (!Number.isInteger(value) || value < 0 || value > MAX_ID) {
2304
+ throw fsError("EINVAL", `not a user or group id: ${String(value)}`);
2305
+ }
2306
+ return value;
2307
+ }
2308
+ function changeCred(cur, change) {
2309
+ const privileged = cur.euid === 0;
2310
+ const next2 = copyCred(cur);
2311
+ for (const keys of [UIDS, GIDS]) {
2312
+ const own = keys.map((key) => cur[key]);
2313
+ for (const key of keys) {
2314
+ const value = id(change[key]);
2315
+ if (value === void 0) continue;
2316
+ if (!privileged && !own.includes(value)) throw fsError("EPERM", `${key} ${value}`);
2317
+ next2[key] = value;
2318
+ }
2319
+ }
2320
+ if (change.groups !== void 0) {
2321
+ if (!Array.isArray(change.groups) || change.groups.length > MAX_GROUPS) {
2322
+ throw fsError("EINVAL", "not a group list");
2323
+ }
2324
+ const groups = change.groups.map((g) => id(g) ?? Number.NaN);
2325
+ if (groups.some(Number.isNaN)) throw fsError("EINVAL", "not a group list");
2326
+ if (!privileged) throw fsError("EPERM", "setgroups");
2327
+ next2.groups = groups;
2328
+ }
2329
+ return next2;
2330
+ }
2331
+ function maySignal(sender, target) {
2332
+ if (sender.euid === 0) return true;
2333
+ return [sender.ruid, sender.euid].some((uid) => uid === target.ruid || uid === target.suid);
2334
+ }
2335
+ function maySee(viewer, target) {
2336
+ return viewer.euid === 0 || target.ruid === viewer.euid || target.euid === viewer.euid;
2337
+ }
2338
+
2339
+ // src/users.ts
2340
+ var PASSWD = "/etc/passwd";
2341
+ var GROUP = "/etc/group";
2342
+ var DEFAULT_PASSWD = "root:x:0:0:root:/root:/bin/bash\nnobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin\n";
2343
+ var DEFAULT_GROUP = "root:x:0:\nsudo:x:27:\nusers:x:100:\nnogroup:x:65534:\n";
2344
+ var NAME = /^[a-z_][a-z0-9_.-]*$/;
2345
+ var MAX_NAME = 255;
2346
+ var FIRST_UID = 1e3;
2347
+ var LAST_UID = 6e4;
2348
+ var USERS_GROUP = "users";
2349
+ var HOME_DIR = /^\/home\/[^/]+$/;
2350
+ var SYSTEM_PNPM_HOME = "/usr/local/share/pnpm";
2351
+ var isId = (text) => text !== void 0 && /^\d+$/.test(text);
2352
+ function lines(text) {
2353
+ return text.split("\n").filter((line, i, all) => line !== "" || i < all.length - 1);
2354
+ }
2355
+ function parsePasswd(text) {
2356
+ return lines(text).map((line) => {
2357
+ const [name2, , uid, gid, gecos = "", home = "/", shell = "/bin/sh"] = line.split(":");
2358
+ if (!name2 || !isId(uid) || !isId(gid)) return line;
2359
+ return { name: name2, uid: Number(uid), gid: Number(gid), gecos, home, shell };
2360
+ });
2361
+ }
2362
+ function parseGroup(text) {
2363
+ return lines(text).map((line) => {
2364
+ const [name2, , gid, members = ""] = line.split(":");
2365
+ if (!name2 || !isId(gid)) return line;
2366
+ return { name: name2, gid: Number(gid), members: members.split(",").filter(Boolean) };
2367
+ });
2368
+ }
2369
+ function formatPasswd(rows) {
2370
+ const line = (r) => `${r.name}:x:${r.uid}:${r.gid}:${r.gecos}:${r.home}:${r.shell}`;
2371
+ return `${rows.map((r) => typeof r === "string" ? r : line(r)).join("\n")}
2372
+ `;
2373
+ }
2374
+ function formatGroup(rows) {
2375
+ const line = (r) => `${r.name}:x:${r.gid}:${r.members.join(",")}`;
2376
+ return `${rows.map((r) => typeof r === "string" ? r : line(r)).join("\n")}
2377
+ `;
2378
+ }
2379
+ var parsed = (rows) => rows.filter((r) => typeof r !== "string");
2380
+ function homeOf(home) {
2381
+ if (typeof home !== "string" || !home.startsWith("/") || /[:\r\n]/.test(home)) {
2382
+ throw fsError("EINVAL", `not a home directory: ${String(home)}`);
2383
+ }
2384
+ return home;
2385
+ }
2386
+ function freeId(users, groups) {
2387
+ const taken = /* @__PURE__ */ new Set([...users.map((r) => r.uid), ...groups.map((r) => r.gid)]);
2388
+ let uid = FIRST_UID;
2389
+ while (taken.has(uid)) uid++;
2390
+ if (uid > LAST_UID) throw fsError("ENOSPC", "no free user id");
2391
+ return uid;
2392
+ }
2393
+ function credOf(account) {
2394
+ return userCred(account.uid, account.gid, account.groups);
2395
+ }
2396
+ function pnpmHomeOf(account, system = SYSTEM_PNPM_HOME) {
2397
+ return account.uid === 0 ? system : `${account.home}/.local/share/pnpm`;
2398
+ }
2399
+ function userEnv(account, system) {
2400
+ const pnpm = pnpmHomeOf(account, system);
2401
+ return {
2402
+ HOME: account.home,
2403
+ USER: account.name,
2404
+ LOGNAME: account.name,
2405
+ PNPM_HOME: pnpm,
2406
+ PATH: `${pnpm}/bin:/usr/local/bin:/usr/bin:/bin`
2407
+ };
2408
+ }
2409
+ var UserDb = class {
2410
+ fs;
2411
+ umasks = /* @__PURE__ */ new Map();
2412
+ writing = Promise.resolve();
2413
+ constructor(fs) {
2414
+ this.fs = fs;
2415
+ }
2416
+ async read(path, fallback) {
2417
+ try {
2418
+ return await this.fs.readFile(path);
2419
+ } catch (err) {
2420
+ if (err?.code === "ENOENT") return fallback;
2421
+ throw err;
2422
+ }
2423
+ }
2424
+ async tables() {
2425
+ return {
2426
+ passwd: parsePasswd(await this.read(PASSWD, DEFAULT_PASSWD)),
2427
+ group: parseGroup(await this.read(GROUP, DEFAULT_GROUP))
2428
+ };
2429
+ }
2430
+ async commit(passwd, group, then) {
2431
+ const before = [await this.read(PASSWD, DEFAULT_PASSWD), await this.read(GROUP, DEFAULT_GROUP)];
2432
+ try {
2433
+ await this.fs.writeFile(GROUP, group);
2434
+ await this.fs.writeFile(PASSWD, passwd);
2435
+ await then?.();
2436
+ } catch (err) {
2437
+ await this.fs.writeFile(GROUP, before[1]);
2438
+ await this.fs.writeFile(PASSWD, before[0]);
2439
+ throw err;
2440
+ }
2441
+ }
2442
+ serial(work) {
2443
+ const next2 = this.writing.then(work);
2444
+ this.writing = next2.catch(() => void 0);
2445
+ return next2;
2446
+ }
2447
+ async ensure() {
2448
+ await this.fs.mkdir("/etc", { recursive: true });
2449
+ for (const [path, text] of [
2450
+ [PASSWD, DEFAULT_PASSWD],
2451
+ [GROUP, DEFAULT_GROUP]
2452
+ ]) {
2453
+ if (!await this.fs.exists(path)) await this.fs.writeFile(path, text);
2454
+ }
2455
+ if (!await this.fs.exists("/root")) {
2456
+ await this.fs.mkdir("/root", { recursive: true });
2457
+ try {
2458
+ await this.fs.chmod("/root", 448);
2459
+ } catch (err) {
2460
+ await this.fs.rm("/root", { recursive: true, force: true });
2461
+ throw err;
2462
+ }
2463
+ }
2464
+ }
2465
+ account(row, groups) {
2466
+ const extra = groups.filter((g) => g.members.includes(row.name) && g.gid !== row.gid);
2467
+ const umask = this.umasks.get(row.name);
2468
+ return {
2469
+ name: row.name,
2470
+ uid: row.uid,
2471
+ gid: row.gid,
2472
+ home: row.home,
2473
+ shell: row.shell,
2474
+ groups: [row.gid, ...extra.map((g) => g.gid)],
2475
+ ...umask !== void 0 ? { umask } : {}
2476
+ };
2477
+ }
2478
+ async lookup(user) {
2479
+ const { passwd, group } = await this.tables();
2480
+ const key = typeof user === "number" ? "uid" : "name";
2481
+ const row = parsed(passwd).find((r) => r[key] === user);
2482
+ return row && this.account(row, parsed(group));
2483
+ }
2484
+ async list() {
2485
+ const { passwd, group } = await this.tables();
2486
+ const groups = parsed(group);
2487
+ return parsed(passwd).map((row) => this.account(row, groups));
2488
+ }
2489
+ add(options) {
2490
+ return this.serial(async () => {
2491
+ const { name: name2 } = options;
2492
+ if (typeof name2 !== "string" || name2.length > MAX_NAME || !NAME.test(name2)) {
2493
+ throw fsError("EINVAL", `not a user name: ${String(name2)}`);
2494
+ }
2495
+ const remember = () => {
2496
+ if (options.umask !== void 0) this.umasks.set(name2, options.umask & 511);
2497
+ };
2498
+ const { passwd, group } = await this.tables();
2499
+ const users = parsed(passwd);
2500
+ const groups = parsed(group);
2501
+ const found = users.find((r) => r.name === name2);
2502
+ if (found) {
2503
+ remember();
2504
+ return this.account(found, groups);
2505
+ }
2506
+ const home = homeOf(options.home ?? `/home/${name2}`);
2507
+ const extra = [USERS_GROUP, ...options.groups ?? []].map((g) => {
2508
+ const row2 = groups.find((r) => r.name === g);
2509
+ if (!row2) throw fsError("ENOENT", `no group ${g}`);
2510
+ return row2;
2511
+ });
2512
+ if (groups.some((r) => r.name === name2)) throw fsError("EEXIST", `group ${name2} exists`);
2513
+ const uid = freeId(users, groups);
2514
+ const row = { name: name2, uid, gid: uid, gecos: name2, home, shell: "/bin/bash" };
2515
+ for (const g of extra) if (!g.members.includes(name2)) g.members.push(name2);
2516
+ group.push({ name: name2, gid: uid, members: [] });
2517
+ passwd.push(row);
2518
+ const fresh = !await this.fs.exists(home);
2519
+ await this.fs.mkdir(home, { recursive: true });
2520
+ try {
2521
+ await this.fs.mkdir("/etc", { recursive: true });
2522
+ await this.commit(formatPasswd(passwd), formatGroup(group));
2523
+ } catch (err) {
2524
+ if (fresh) await this.fs.rm(home, { recursive: true, force: true });
2525
+ throw err;
2526
+ }
2527
+ remember();
2528
+ return this.account(row, parsed(group));
2529
+ });
2530
+ }
2531
+ remove(name2, options = {}) {
2532
+ return this.serial(async () => {
2533
+ const { passwd, group } = await this.tables();
2534
+ const row = parsed(passwd).find((r) => r.name === name2);
2535
+ if (!row) return false;
2536
+ if (row.uid < FIRST_UID || row.uid > LAST_UID) {
2537
+ throw fsError("EPERM", `${name2} is a system account`);
2538
+ }
2539
+ const keptGroups = group.filter(
2540
+ (r) => typeof r === "string" || !(r.name === name2 && r.gid === row.gid)
2541
+ );
2542
+ for (const r of parsed(keptGroups)) r.members = r.members.filter((m) => m !== name2);
2543
+ const home = this.fs.resolvePath("/", row.home);
2544
+ const shared = parsed(passwd).some(
2545
+ (r) => r !== row && this.fs.resolvePath("/", r.home) === home
2546
+ );
2547
+ const removeHome = !options.keepHome && !shared && HOME_DIR.test(home) && await this.fs.exists(home);
2548
+ const staged = `/home/.${name2}.removed-${Date.now()}`;
2549
+ await this.commit(
2550
+ formatPasswd(passwd.filter((r) => r !== row)),
2551
+ formatGroup(keptGroups),
2552
+ removeHome ? () => this.fs.rename(home, staged) : void 0
2553
+ );
2554
+ this.umasks.delete(name2);
2555
+ if (removeHome)
2556
+ await this.fs.rm(staged, { recursive: true, force: true }).catch(() => void 0);
2557
+ return true;
2558
+ });
2559
+ }
2560
+ };
2561
+
2281
2562
  // src/client/serve-client.ts
2282
2563
  var MAX_LINKS2 = 40;
2283
2564
  function act(session, req, signal) {
@@ -2355,6 +2636,39 @@ function dialFor(l, req, open) {
2355
2636
  );
2356
2637
  return { result: port2, transfer: [port2] };
2357
2638
  }
2639
+ async function accountOf(l, user) {
2640
+ const found = await l.users.lookup(user);
2641
+ if (!found) throw fsError("ENOENT", `no user ${String(user)}`);
2642
+ return found;
2643
+ }
2644
+ async function runAs(l, self, requested) {
2645
+ if (requested === void 0) return self;
2646
+ const target = await accountOf(l, requested);
2647
+ if (self.uid !== 0 && target.uid !== self.uid) {
2648
+ throw fsError("EPERM", `${self.name} may not run as ${target.name}`);
2649
+ }
2650
+ return target;
2651
+ }
2652
+ function processes(l, self) {
2653
+ const viewer = credOf(self);
2654
+ return l.list(viewer).map(({ tid: _, cred = viewer, umask: _umask, ...entry }) => ({
2655
+ ...entry,
2656
+ uid: cred.euid,
2657
+ gid: cred.egid
2658
+ }));
2659
+ }
2660
+ function killAs(l, host, self, req) {
2661
+ if (!l.kill(req.pid, signalOf(host)(req.signal), credOf(self))) {
2662
+ throw fsError("ESRCH", `pid ${req.pid}`);
2663
+ }
2664
+ return { result: true };
2665
+ }
2666
+ function terminalAction(terminals, req, host) {
2667
+ const session = terminals.get(req.terminal);
2668
+ if (!session) throw new Error(`no terminal ${req.terminal}`);
2669
+ act(session, req, signalOf(host));
2670
+ return { result: true };
2671
+ }
2358
2672
  function killGroups(host, pgids) {
2359
2673
  if (pgids.length === 0) return;
2360
2674
  void host.launcher().then((launcher) => {
@@ -2368,9 +2682,9 @@ function joinable(l, pgid, ours) {
2368
2682
  }
2369
2683
  function cdpRegistry(send, gone) {
2370
2684
  const registered = /* @__PURE__ */ new Map();
2371
- const drop = (id) => {
2372
- registered.get(id)?.();
2373
- return registered.delete(id);
2685
+ const drop = (id2) => {
2686
+ registered.get(id2)?.();
2687
+ return registered.delete(id2);
2374
2688
  };
2375
2689
  return {
2376
2690
  serve(l, req) {
@@ -2385,7 +2699,7 @@ function cdpRegistry(send, gone) {
2385
2699
  },
2386
2700
  drop,
2387
2701
  close: () => {
2388
- for (const id of [...registered.keys()]) drop(id);
2702
+ for (const id2 of [...registered.keys()]) drop(id2);
2389
2703
  }
2390
2704
  };
2391
2705
  }
@@ -2405,16 +2719,17 @@ function watchOp(l, req, watches, send) {
2405
2719
  }
2406
2720
  function serveClient(port, host) {
2407
2721
  let state = "new";
2722
+ let self;
2408
2723
  const terminals = /* @__PURE__ */ new Map();
2409
2724
  const groups = /* @__PURE__ */ new Set();
2410
2725
  const sessions = /* @__PURE__ */ new Set();
2411
2726
  const watches = /* @__PURE__ */ new Map();
2412
2727
  let net;
2413
2728
  const ended = Promise.withResolvers();
2414
- const send = (message2, transfer = []) => {
2415
- if (state !== "closed") port.postMessage(message2, transfer);
2729
+ const send = (message3, transfer = []) => {
2730
+ if (state !== "closed") port.postMessage(message3, transfer);
2416
2731
  };
2417
- const reply = (id, body, transfer) => send({ id, ...body }, transfer);
2732
+ const reply = (id2, body, transfer) => send({ id: id2, ...body }, transfer);
2418
2733
  const cdps = cdpRegistry(send, ended.promise);
2419
2734
  function detach(kill = false, bye = "the slicc-kernel detached this client") {
2420
2735
  if (state === "closed") return;
@@ -2431,18 +2746,19 @@ function serveClient(port, host) {
2431
2746
  port.close?.();
2432
2747
  ended.resolve();
2433
2748
  }
2434
- async function hello(message2) {
2435
- const refused = versionError(message2.protocol);
2749
+ async function hello(message3) {
2750
+ const refused = versionError(message3.protocol);
2436
2751
  if (refused) {
2437
2752
  send({ hello: { protocol: PROTOCOL, error: refused } });
2438
2753
  detach();
2439
2754
  return;
2440
2755
  }
2441
2756
  const launcher = await host.launcher();
2757
+ if (host.scope !== "transport") self = await accountOf(launcher, host.user ?? 0);
2442
2758
  const transport = host.transport?.() ?? launcher.transport;
2443
2759
  net = serveTransport({ postMessage: (m, t) => send(m, t) }, transport);
2444
2760
  state = "open";
2445
- if (message2.lock && host.locks) void host.locks.request(message2.lock, () => detach());
2761
+ if (message3.lock && host.locks) void host.locks.request(message3.lock, () => detach());
2446
2762
  send({
2447
2763
  hello: {
2448
2764
  protocol: PROTOCOL,
@@ -2454,6 +2770,7 @@ function serveClient(port, host) {
2454
2770
  async function terminal(req, l) {
2455
2771
  const session = await l.openTerminal(req.argv, {
2456
2772
  ...req.options,
2773
+ user: await runAs(l, self, req.options.user),
2457
2774
  onData: (bytes) => reply(req.id, { fd: 1, bytes: bytes.slice() })
2458
2775
  });
2459
2776
  if (state === "closed") {
@@ -2475,6 +2792,7 @@ function serveClient(port, host) {
2475
2792
  let leader;
2476
2793
  const result = await l.run(req.argv, {
2477
2794
  ...req.options,
2795
+ user: await runAs(l, self, req.options.user),
2478
2796
  collect: false,
2479
2797
  onStdout: out(1),
2480
2798
  onStderr: out(2),
@@ -2500,19 +2818,12 @@ function serveClient(port, host) {
2500
2818
  return spawn(req, l);
2501
2819
  case "open-terminal":
2502
2820
  return terminal(req, l);
2503
- case "terminal": {
2504
- const session = terminals.get(req.terminal);
2505
- if (!session) throw new Error(`no terminal ${req.terminal}`);
2506
- act(session, req, signalOf(host));
2507
- return { result: true };
2508
- }
2821
+ case "terminal":
2822
+ return terminalAction(terminals, req, host);
2509
2823
  case "kill":
2510
- if (!l.kill(req.pid, signalOf(host)(req.signal))) throw fsError("ESRCH", `pid ${req.pid}`);
2511
- return { result: true };
2824
+ return killAs(l, host, self, req);
2512
2825
  case "ps":
2513
- return {
2514
- result: l.list().map(({ tid: _, ...entry }) => entry)
2515
- };
2826
+ return { result: processes(l, self) };
2516
2827
  case "fs":
2517
2828
  if (!FS_METHODS.includes(req.method)) {
2518
2829
  throw new Error(`unknown file system call ${req.method}`);
@@ -2607,8 +2918,8 @@ function ownNames(hostname) {
2607
2918
  // src/kernel/net/http1.ts
2608
2919
  var HttpError = class extends Error {
2609
2920
  status;
2610
- constructor(status, message2) {
2611
- super(message2);
2921
+ constructor(status, message3) {
2922
+ super(message3);
2612
2923
  this.status = status;
2613
2924
  }
2614
2925
  };
@@ -2732,21 +3043,21 @@ function latin1Bytes(text) {
2732
3043
  return out;
2733
3044
  }
2734
3045
  function parseRequestHead(bytes) {
2735
- const lines = latin1(bytes).split(/\r?\n/);
2736
- while (lines.length > 0 && lines[0] === "") lines.shift();
2737
- const match = /^([!#$%&'*+\-.^_`|~0-9A-Za-z]+) (\S+) HTTP\/(\d)\.(\d)$/.exec(lines.shift() ?? "");
3046
+ const lines2 = latin1(bytes).split(/\r?\n/);
3047
+ while (lines2.length > 0 && lines2[0] === "") lines2.shift();
3048
+ const match = /^([!#$%&'*+\-.^_`|~0-9A-Za-z]+) (\S+) HTTP\/(\d)\.(\d)$/.exec(lines2.shift() ?? "");
2738
3049
  if (!match) throw new HttpError(400, "malformed request line");
2739
3050
  if (match[3] !== "1") throw new HttpError(505, `HTTP/${match[3]}.${match[4]} is not supported`);
2740
3051
  return {
2741
3052
  method: match[1],
2742
3053
  target: match[2],
2743
3054
  minor: Number(match[4]),
2744
- headers: parseFields(lines)
3055
+ headers: parseFields(lines2)
2745
3056
  };
2746
3057
  }
2747
- function parseFields(lines) {
3058
+ function parseFields(lines2) {
2748
3059
  const headers = [];
2749
- for (const line of lines) {
3060
+ for (const line of lines2) {
2750
3061
  if (line === "") continue;
2751
3062
  if (line.startsWith(" ") || line.startsWith(" ")) {
2752
3063
  throw new HttpError(400, "obsolete line folding");
@@ -2871,8 +3182,8 @@ var OP = {
2871
3182
  };
2872
3183
  var WsError = class extends Error {
2873
3184
  code;
2874
- constructor(code, message2) {
2875
- super(message2);
3185
+ constructor(code, message3) {
3186
+ super(message3);
2876
3187
  this.code = code;
2877
3188
  }
2878
3189
  };
@@ -3238,8 +3549,8 @@ function untilAborted(response, signal) {
3238
3549
  response.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
3239
3550
  });
3240
3551
  }
3241
- function plainResponse(status, message2) {
3242
- const body = latin1Bytes(`slicc realm proxy: ${message2}
3552
+ function plainResponse(status, message3) {
3553
+ const body = latin1Bytes(`slicc realm proxy: ${message3}
3243
3554
  `);
3244
3555
  return {
3245
3556
  status,
@@ -3421,10 +3732,10 @@ var RealmProxy = class {
3421
3732
  return await untilAborted(fetching, signal);
3422
3733
  } catch (e) {
3423
3734
  if (signal.aborted) throw e;
3424
- const message2 = e instanceof Error ? e.message : String(e);
3735
+ const message3 = e instanceof Error ? e.message : String(e);
3425
3736
  const status = e?.status;
3426
3737
  const own = typeof status === "number" && status >= 400 && status <= 599 ? status : 502;
3427
- return plainResponse(own, message2 || "upstream request failed");
3738
+ return plainResponse(own, message3 || "upstream request failed");
3428
3739
  }
3429
3740
  }
3430
3741
  async relay(conn, method, minor, response, keepAlive) {
@@ -3661,8 +3972,8 @@ function name(dn) {
3661
3972
  rdns.push(set(seq(oid("2.5.4.3"), utf8(dn.commonName))));
3662
3973
  return seq(...rdns);
3663
3974
  }
3664
- function extension(id, critical, value) {
3665
- return critical ? seq(oid(id), TRUE, octets(value)) : seq(oid(id), octets(value));
3975
+ function extension(id2, critical, value) {
3976
+ return critical ? seq(oid(id2), TRUE, octets(value)) : seq(oid(id2), octets(value));
3666
3977
  }
3667
3978
  function children(der) {
3668
3979
  const out = [];
@@ -3778,9 +4089,9 @@ function pem(der) {
3778
4089
  for (let i = 0; i < der.length; i += 32768) {
3779
4090
  b64 += String.fromCharCode(...der.subarray(i, i + 32768));
3780
4091
  }
3781
- const lines = btoa(b64).match(/.{1,64}/g) ?? [];
4092
+ const lines2 = btoa(b64).match(/.{1,64}/g) ?? [];
3782
4093
  return `-----BEGIN CERTIFICATE-----
3783
- ${lines.join("\n")}
4094
+ ${lines2.join("\n")}
3784
4095
  -----END CERTIFICATE-----
3785
4096
  `;
3786
4097
  }
@@ -3864,11 +4175,11 @@ var RealmCa = class _RealmCa {
3864
4175
  static async create(owner, now) {
3865
4176
  const pair = await crypto.subtle.generateKey(ECDSA, false, ["sign", "verify"]);
3866
4177
  const spki = new Uint8Array(await crypto.subtle.exportKey("spki", pair.publicKey));
3867
- const id = Array.from(
4178
+ const id2 = Array.from(
3868
4179
  crypto.getRandomValues(new Uint8Array(4)),
3869
4180
  (b) => b.toString(16).padStart(2, "0")
3870
4181
  ).join("");
3871
- const name2 = { organization: "SLICC", commonName: `SLICC realm CA ${owner} ${id}` };
4182
+ const name2 = { organization: "SLICC", commonName: `SLICC realm CA ${owner} ${id2}` };
3872
4183
  const notAfter = now + CA_LIFETIME;
3873
4184
  const cert = await certificate({
3874
4185
  serial: randomSerial(),
@@ -3929,8 +4240,8 @@ var SCRATCH = 64 * 1024;
3929
4240
  var EOF = -1;
3930
4241
  var TlsError = class extends Error {
3931
4242
  code;
3932
- constructor(code, message2) {
3933
- super(message2);
4243
+ constructor(code, message3) {
4244
+ super(message3);
3934
4245
  this.code = code;
3935
4246
  }
3936
4247
  };
@@ -4319,8 +4630,8 @@ function enableNetwork(net, options) {
4319
4630
  var TransportError = class extends Error {
4320
4631
  status;
4321
4632
  code;
4322
- constructor(message2, status, code) {
4323
- super(message2);
4633
+ constructor(message3, status, code) {
4634
+ super(message3);
4324
4635
  this.status = status;
4325
4636
  this.code = code;
4326
4637
  }
@@ -4517,12 +4828,12 @@ function parsePrefix(text) {
4517
4828
  const parts = text.trim().split("/");
4518
4829
  if (parts.length > 2) return void 0;
4519
4830
  const [address = "", size] = parts;
4520
- const parsed = addressBits(address);
4521
- if (!parsed) return void 0;
4522
- const max = parsed.v6 ? 128 : 32;
4831
+ const parsed2 = addressBits(address);
4832
+ if (!parsed2) return void 0;
4833
+ const max = parsed2.v6 ? 128 : 32;
4523
4834
  const length2 = size === void 0 ? max : /^\d{1,3}$/.test(size) ? Number(size) : -1;
4524
4835
  if (length2 < 0 || length2 > max) return void 0;
4525
- return { ...parsed, length: length2 };
4836
+ return { ...parsed2, length: length2 };
4526
4837
  }
4527
4838
  function within2(prefix, addr) {
4528
4839
  if (prefix.v6 !== addr.v6) return false;
@@ -4548,10 +4859,10 @@ var Routes = class {
4548
4859
  this.ipv6 = ipv62;
4549
4860
  }
4550
4861
  set(table) {
4551
- const parsed = (table.prefixes ?? []).map(parsePrefix);
4552
- const bad = (table.prefixes ?? []).find((_, i) => !parsed[i]);
4862
+ const parsed2 = (table.prefixes ?? []).map(parsePrefix);
4863
+ const bad = (table.prefixes ?? []).find((_, i) => !parsed2[i]);
4553
4864
  if (bad !== void 0) throw new Error(`not an address prefix: ${bad}`);
4554
- this.table = parsed;
4865
+ this.table = parsed2;
4555
4866
  this.exit = table.exit === true;
4556
4867
  }
4557
4868
  classify(host) {
@@ -4778,11 +5089,11 @@ var CdpFacade = class {
4778
5089
  clearTimeout(timer);
4779
5090
  reject(new HttpError(502, `the CDP host closed: ${reason ?? "no reason"}`));
4780
5091
  };
4781
- conn.onmessage = (message2) => {
4782
- const parsed = JSON.parse(message2);
4783
- if (parsed.id !== 1) return;
5092
+ conn.onmessage = (message3) => {
5093
+ const parsed2 = JSON.parse(message3);
5094
+ if (parsed2.id !== 1) return;
4784
5095
  clearTimeout(timer);
4785
- resolve(parsed);
5096
+ resolve(parsed2);
4786
5097
  };
4787
5098
  conn.send(JSON.stringify({ id: 1, method: "Target.getTargets" }));
4788
5099
  }
@@ -4847,9 +5158,9 @@ var Relay = class {
4847
5158
  void this.end(reason ? CLOSE.error : CLOSE.normal, reason ?? "").then(() => resolve());
4848
5159
  };
4849
5160
  });
4850
- this.host.onmessage = (message2) => {
5161
+ this.host.onmessage = (message3) => {
4851
5162
  if (this.ended) return;
4852
- const bytes = new TextEncoder().encode(message2);
5163
+ const bytes = new TextEncoder().encode(message3);
4853
5164
  if (bytes.length > this.max)
4854
5165
  void this.end(CLOSE.tooBig, `a message is over ${this.max} bytes`);
4855
5166
  else void this.send(frame(OP_TEXT, bytes));
@@ -4955,7 +5266,7 @@ function closeAll(entry) {
4955
5266
  }
4956
5267
 
4957
5268
  // src/commands.ts
4958
- var NAME = /^[A-Za-z0-9._+-]+$/;
5269
+ var NAME2 = /^[A-Za-z0-9._+-]+$/;
4959
5270
  var ENV_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
4960
5271
  var PACKAGE = /\$\{package\}/g;
4961
5272
  function inside(pkg, rel2) {
@@ -5044,7 +5355,7 @@ function commandsOf(pkg, manifest) {
5044
5355
  const shared = envOf(pkg, slicc.env);
5045
5356
  const out = [];
5046
5357
  for (const [name2, raw] of Object.entries(entries)) {
5047
- if (!NAME.test(name2) || name2 === "." || name2 === ".." || !raw || typeof raw !== "object") {
5358
+ if (!NAME2.test(name2) || name2 === "." || name2 === ".." || !raw || typeof raw !== "object") {
5048
5359
  continue;
5049
5360
  }
5050
5361
  const command = commandOf(pkg, name2, raw, packageAbi, shared);
@@ -5128,7 +5439,7 @@ async function scanFilesystems(fs, modules) {
5128
5439
  if (!declared || typeof declared !== "object") continue;
5129
5440
  for (const [type, entry] of Object.entries(declared)) {
5130
5441
  const module = inside(pkg, entry?.module);
5131
- if (NAME.test(type) && module && !found.has(type)) found.set(type, module);
5442
+ if (NAME2.test(type) && module && !found.has(type)) found.set(type, module);
5132
5443
  }
5133
5444
  }
5134
5445
  return found;
@@ -5148,7 +5459,7 @@ async function scanBinfmts(fs, modules, warn = () => {
5148
5459
  const declared = manifest.slicc?.binfmt;
5149
5460
  if (!declared || typeof declared !== "object") continue;
5150
5461
  for (const [suffix, command] of Object.entries(declared)) {
5151
- if (!SUFFIX.test(suffix) || typeof command !== "string" || !NAME.test(command)) continue;
5462
+ if (!SUFFIX.test(suffix) || typeof command !== "string" || !NAME2.test(command)) continue;
5152
5463
  const owner = owners.get(suffix);
5153
5464
  if (owner === void 0) {
5154
5465
  found.set(suffix, command);
@@ -5516,9 +5827,9 @@ var UnlinkedKeeper = class {
5516
5827
  if (!names) return;
5517
5828
  const held = query && new Set(((await query()).held ?? []).map((lock) => lock.name));
5518
5829
  for (const name2 of names) {
5519
- const id = name2.slice(0, name2.lastIndexOf("."));
5520
- const at = id.indexOf("~");
5521
- const dead = id !== this.instance && (at < 0 ? held !== void 0 && !held.has(lockName(id)) : owner?.alive(id.slice(0, at)) === false);
5830
+ const id2 = name2.slice(0, name2.lastIndexOf("."));
5831
+ const at = id2.indexOf("~");
5832
+ const dead = id2 !== this.instance && (at < 0 ? held !== void 0 && !held.has(lockName(id2)) : owner?.alive(id2.slice(0, at)) === false);
5522
5833
  if (dead) await this.fs.rm(`${dir}/${name2}`, { recursive: true }).catch(() => void 0);
5523
5834
  }
5524
5835
  await this.tidy(dir);
@@ -5628,10 +5939,10 @@ var SpawnError = class extends Error {
5628
5939
  };
5629
5940
  function interrupted(signal) {
5630
5941
  const { promise, reject } = Promise.withResolvers();
5631
- const fail = () => reject(new KernelError("EINTR"));
5632
- if (signal?.aborted) fail();
5633
- else signal?.addEventListener("abort", fail, { once: true });
5634
- return { promise, done: () => signal?.removeEventListener("abort", fail) };
5942
+ const fail2 = () => reject(new KernelError("EINTR"));
5943
+ if (signal?.aborted) fail2();
5944
+ else signal?.addEventListener("abort", fail2, { once: true });
5945
+ return { promise, done: () => signal?.removeEventListener("abort", fail2) };
5635
5946
  }
5636
5947
  function stoppedStatus(sig) {
5637
5948
  return (sig & 255) << 8 | 127;
@@ -5822,8 +6133,8 @@ function unref(timer) {
5822
6133
  timer.unref?.();
5823
6134
  return timer;
5824
6135
  }
5825
- function errnoError(code, message2) {
5826
- return Object.assign(new Error(`${code}: ${message2}`), { code });
6136
+ function errnoError(code, message3) {
6137
+ return Object.assign(new Error(`${code}: ${message3}`), { code });
5827
6138
  }
5828
6139
  var DriverConnection = class _DriverConnection {
5829
6140
  capabilities;
@@ -5876,11 +6187,11 @@ var DriverConnection = class _DriverConnection {
5876
6187
  }
5877
6188
  call(call, transfer = []) {
5878
6189
  if (this.failure) return Promise.reject(this.failure);
5879
- const id = ++this.nextId;
6190
+ const id2 = ++this.nextId;
5880
6191
  return new Promise((resolve, reject) => {
5881
6192
  const timer = unref(
5882
6193
  setTimeout(() => {
5883
- this.waiting.delete(id);
6194
+ this.waiting.delete(id2);
5884
6195
  const error = errnoError(
5885
6196
  "EIO",
5886
6197
  `the driver did not answer ${call.op} within ${this.timeoutMs} ms`
@@ -5890,8 +6201,8 @@ var DriverConnection = class _DriverConnection {
5890
6201
  this.onFail?.(error);
5891
6202
  }, this.timeoutMs)
5892
6203
  );
5893
- this.waiting.set(id, { resolve, reject, timer });
5894
- this.port.postMessage({ ...call, id }, transfer);
6204
+ this.waiting.set(id2, { resolve, reject, timer });
6205
+ this.port.postMessage({ ...call, id: id2 }, transfer);
5895
6206
  });
5896
6207
  }
5897
6208
  fail(error) {
@@ -5966,22 +6277,22 @@ var HeldPaths = class extends Map {
5966
6277
  }
5967
6278
  isKept(path) {
5968
6279
  const ids = this.handlesAt(path);
5969
- return ids.length === 0 ? this.unlinked.has(path) : ids.every((id) => this.keptHandles.has(id));
6280
+ return ids.length === 0 ? this.unlinked.has(path) : ids.every((id2) => this.keptHandles.has(id2));
5970
6281
  }
5971
6282
  handlesAt(path) {
5972
- return [...this.handles].filter(([, key]) => key === path).map(([id]) => id);
6283
+ return [...this.handles].filter(([, key]) => key === path).map(([id2]) => id2);
5973
6284
  }
5974
6285
  keptFor(handle) {
5975
6286
  return this.keptHandles.get(handle);
5976
6287
  }
5977
6288
  keep(path, file) {
5978
- const fresh = this.handlesAt(path).filter((id) => !this.keptHandles.has(id));
5979
- for (const id of fresh) this.keptHandles.set(id, file);
6289
+ const fresh = this.handlesAt(path).filter((id2) => !this.keptHandles.has(id2));
6290
+ for (const id2 of fresh) this.keptHandles.set(id2, file);
5980
6291
  this.uses.set(file, Math.max(fresh.length, 1));
5981
6292
  this.unlinked.set(path, file);
5982
6293
  }
5983
6294
  unkeep(path, file) {
5984
- for (const [id, kept] of this.keptHandles) if (kept === file) this.keptHandles.delete(id);
6295
+ for (const [id2, kept] of this.keptHandles) if (kept === file) this.keptHandles.delete(id2);
5985
6296
  this.uses.delete(file);
5986
6297
  if (this.unlinked.get(path) === file) this.unlinked.delete(path);
5987
6298
  }
@@ -6018,7 +6329,7 @@ var HeldPaths = class extends Map {
6018
6329
  if (count > 0) this.opens.set(key, count);
6019
6330
  else {
6020
6331
  this.opens.delete(key);
6021
- for (const id of this.handlesAt(key)) this.handles.delete(id);
6332
+ for (const id2 of this.handlesAt(key)) this.handles.delete(id2);
6022
6333
  const kept = this.unlinked.get(key);
6023
6334
  if (kept) this.letGo(kept, true);
6024
6335
  for (const [alias, target] of this.aliases) if (target === key) this.aliases.delete(alias);
@@ -6065,8 +6376,8 @@ var HeldPaths = class extends Map {
6065
6376
  this.opens.delete(path);
6066
6377
  this.opens.set(moved2, (this.opens.get(moved2) ?? 0) + count);
6067
6378
  }
6068
- for (const [id, key] of this.handles) {
6069
- if (within4(key, from)) this.handles.set(id, to + key.slice(from.length));
6379
+ for (const [id2, key] of this.handles) {
6380
+ if (within4(key, from)) this.handles.set(id2, to + key.slice(from.length));
6070
6381
  }
6071
6382
  }
6072
6383
  isRevoked(path) {
@@ -6805,12 +7116,12 @@ function syncError(code, label) {
6805
7116
 
6806
7117
  // src/realm/sync-fs-dispatch.ts
6807
7118
  function toErrno(err) {
6808
- const message2 = err instanceof Error ? err.message : String(err);
7119
+ const message3 = err instanceof Error ? err.message : String(err);
6809
7120
  const code = err?.code;
6810
7121
  if (typeof code === "string" && /^E[A-Z]+$/.test(code)) {
6811
- return { ok: false, errno: code, message: message2 };
7122
+ return { ok: false, errno: code, message: message3 };
6812
7123
  }
6813
- return { ok: false, errno: "EIO", message: message2 };
7124
+ return { ok: false, errno: "EIO", message: message3 };
6814
7125
  }
6815
7126
  function statJson(s) {
6816
7127
  return {
@@ -7035,15 +7346,15 @@ function attachSyncSabResponder(port, sab, token, opts) {
7035
7346
  const pending = /* @__PURE__ */ new Map();
7036
7347
  let disposed = false;
7037
7348
  const { dispatch: dispatch2 } = opts;
7038
- function drop(id) {
7039
- const entry = pending.get(id);
7349
+ function drop(id2) {
7350
+ const entry = pending.get(id2);
7040
7351
  if (!entry) return;
7041
7352
  clearTimeout(entry.timer);
7042
- pending.delete(id);
7353
+ pending.delete(id2);
7043
7354
  }
7044
- function publish(id, entry, offset) {
7045
- if (Atomics.load(header2, SAB_I_REQ) !== id) {
7046
- drop(id);
7355
+ function publish(id2, entry, offset) {
7356
+ if (Atomics.load(header2, SAB_I_REQ) !== id2) {
7357
+ drop(id2);
7047
7358
  return;
7048
7359
  }
7049
7360
  const chunk2 = Math.min(window.byteLength, entry.payload.byteLength - offset);
@@ -7052,23 +7363,23 @@ function attachSyncSabResponder(port, sab, token, opts) {
7052
7363
  storeU53(header2, SAB_I_TOTAL, SAB_I_TOTAL_HI, entry.payload.byteLength);
7053
7364
  Atomics.store(header2, SAB_I_CHUNK, chunk2);
7054
7365
  storeU53(header2, SAB_I_OFFSET, SAB_I_OFFSET_HI, offset);
7055
- Atomics.store(header2, SAB_I_SEQ, id);
7366
+ Atomics.store(header2, SAB_I_SEQ, id2);
7056
7367
  Atomics.store(header2, SAB_I_STATE, SAB_STATE_READY);
7057
7368
  Atomics.add(header2, SAB_I_PUB, 1);
7058
7369
  Atomics.notify(header2, SAB_I_PUB);
7059
- if (offset + chunk2 >= entry.payload.byteLength) drop(id);
7370
+ if (offset + chunk2 >= entry.payload.byteLength) drop(id2);
7060
7371
  }
7061
- function settle3(id, result) {
7372
+ function settle3(id2, result) {
7062
7373
  if (disposed) return;
7063
7374
  const { status, payload } = encodeSabResult(result);
7064
- drop(id);
7375
+ drop(id2);
7065
7376
  const entry = {
7066
7377
  status,
7067
7378
  payload,
7068
- timer: setTimeout(() => drop(id), PENDING_TTL_MS)
7379
+ timer: setTimeout(() => drop(id2), PENDING_TTL_MS)
7069
7380
  };
7070
- pending.set(id, entry);
7071
- publish(id, entry, 0);
7381
+ pending.set(id2, entry);
7382
+ publish(id2, entry, 0);
7072
7383
  }
7073
7384
  const handler = (event) => {
7074
7385
  const data = event.data;
@@ -7084,14 +7395,14 @@ function attachSyncSabResponder(port, sab, token, opts) {
7084
7395
  return;
7085
7396
  }
7086
7397
  if (data.type !== SYNC_SAB_REQ_MSG || !data.req || typeof data.req !== "object") return;
7087
- const id = data.id;
7398
+ const id2 = data.id;
7088
7399
  const body = data.req;
7089
7400
  const req = { ...body, token };
7090
7401
  let dispatched;
7091
7402
  try {
7092
7403
  dispatched = dispatch2(req);
7093
7404
  } catch (err) {
7094
- settle3(id, {
7405
+ settle3(id2, {
7095
7406
  ok: false,
7096
7407
  errno: "EIO",
7097
7408
  message: err instanceof Error ? err.message : String(err)
@@ -7099,8 +7410,8 @@ function attachSyncSabResponder(port, sab, token, opts) {
7099
7410
  return;
7100
7411
  }
7101
7412
  void dispatched.then(
7102
- (result) => settle3(id, result),
7103
- (err) => settle3(id, {
7413
+ (result) => settle3(id2, result),
7414
+ (err) => settle3(id2, {
7104
7415
  ok: false,
7105
7416
  errno: "EIO",
7106
7417
  message: err instanceof Error ? err.message : String(err)
@@ -7113,7 +7424,7 @@ function attachSyncSabResponder(port, sab, token, opts) {
7113
7424
  if (disposed) return;
7114
7425
  disposed = true;
7115
7426
  port.removeEventListener("message", handler);
7116
- for (const id of [...pending.keys()]) drop(id);
7427
+ for (const id2 of [...pending.keys()]) drop(id2);
7117
7428
  }
7118
7429
  };
7119
7430
  }
@@ -7173,47 +7484,47 @@ var AsyncOps = class {
7173
7484
  this.onReady = onReady;
7174
7485
  }
7175
7486
  submit(req) {
7176
- const id = this.hold();
7487
+ const id2 = this.hold();
7177
7488
  const running = new AbortController();
7178
- this.running.set(id, running);
7489
+ this.running.set(id2, running);
7179
7490
  this.run(req, running.signal).then(
7180
- (result) => this.complete(id, result),
7181
- (err) => this.complete(id, { ok: false, errno: "EIO", message: String(err) })
7491
+ (result) => this.complete(id2, result),
7492
+ (err) => this.complete(id2, { ok: false, errno: "EIO", message: String(err) })
7182
7493
  );
7183
- return id;
7494
+ return id2;
7184
7495
  }
7185
7496
  resolve(value) {
7186
- const id = this.hold();
7187
- this.complete(id, json3(value));
7188
- return id;
7497
+ const id2 = this.hold();
7498
+ this.complete(id2, json3(value));
7499
+ return id2;
7189
7500
  }
7190
7501
  hold() {
7191
- const id = this.next++;
7192
- this.results.set(id, void 0);
7193
- return id;
7502
+ const id2 = this.next++;
7503
+ this.results.set(id2, void 0);
7504
+ return id2;
7194
7505
  }
7195
- take(id) {
7196
- if (!this.results.has(id)) throw new KernelError("EINVAL");
7197
- const result = this.results.get(id);
7506
+ take(id2) {
7507
+ if (!this.results.has(id2)) throw new KernelError("EINVAL");
7508
+ const result = this.results.get(id2);
7198
7509
  if (!result) throw new KernelError("EAGAIN");
7199
- this.forget(id);
7510
+ this.forget(id2);
7200
7511
  return result;
7201
7512
  }
7202
- cancel(id) {
7203
- this.running.get(id)?.abort();
7204
- this.forget(id);
7513
+ cancel(id2) {
7514
+ this.running.get(id2)?.abort();
7515
+ this.forget(id2);
7205
7516
  }
7206
- forget(id) {
7207
- this.running.delete(id);
7208
- this.results.delete(id);
7209
- const at = this.ready.indexOf(id);
7517
+ forget(id2) {
7518
+ this.running.delete(id2);
7519
+ this.results.delete(id2);
7520
+ const at = this.ready.indexOf(id2);
7210
7521
  if (at >= 0) this.ready.splice(at, 1);
7211
7522
  }
7212
7523
  async wait(timeoutMs, signal) {
7213
7524
  for (; ; ) {
7214
7525
  if (this.closed) return -1;
7215
- const id = this.ready.shift();
7216
- if (id !== void 0) return id;
7526
+ const id2 = this.ready.shift();
7527
+ if (id2 !== void 0) return id2;
7217
7528
  if (timeoutMs !== void 0 && timeoutMs <= 0) return 0;
7218
7529
  const woke = await this.sleep(timeoutMs, signal);
7219
7530
  if (!woke) return 0;
@@ -7249,11 +7560,11 @@ var AsyncOps = class {
7249
7560
  }
7250
7561
  });
7251
7562
  }
7252
- complete(id, result) {
7253
- this.running.delete(id);
7254
- if (!this.results.has(id)) return;
7255
- this.results.set(id, result);
7256
- this.ready.push(id);
7563
+ complete(id2, result) {
7564
+ this.running.delete(id2);
7565
+ if (!this.results.has(id2)) return;
7566
+ this.results.set(id2, result);
7567
+ this.ready.push(id2);
7257
7568
  this.wake();
7258
7569
  this.onReady?.();
7259
7570
  }
@@ -7291,10 +7602,133 @@ async function hostSyscall(req, ctx) {
7291
7602
  }
7292
7603
  }
7293
7604
 
7605
+ // src/kernel/net/ws-syscalls.ts
7606
+ var WS_OPS = [
7607
+ "net-ws-open",
7608
+ "net-ws-ready",
7609
+ "net-ws-recv",
7610
+ "net-ws-wait",
7611
+ "net-ws-send",
7612
+ "net-ws-close"
7613
+ ];
7614
+ var TOKEN3 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
7615
+ function message(e) {
7616
+ return e instanceof Error ? e.message : String(e);
7617
+ }
7618
+ function fail(errno, text) {
7619
+ return { ok: false, errno, message: text };
7620
+ }
7621
+ var WsHandles = class {
7622
+ transport;
7623
+ open = /* @__PURE__ */ new Map();
7624
+ next;
7625
+ constructor(transport, next2) {
7626
+ this.transport = transport;
7627
+ this.next = next2;
7628
+ }
7629
+ owns(handle) {
7630
+ return this.open.has(handle);
7631
+ }
7632
+ syscall(req) {
7633
+ if (req.op === "net-ws-open") return Promise.resolve(this.start(req));
7634
+ const entry = this.open.get(req.handle);
7635
+ if (!entry) return Promise.resolve(fail("EBADF", `no websocket ${req.handle}`));
7636
+ if (req.op === "net-ws-ready") return entry.ready;
7637
+ if (!entry.socket) return Promise.resolve(fail("ENOTCONN", "websocket not open"));
7638
+ if (req.op === "net-ws-recv") return this.recv(entry.socket, entry);
7639
+ if (req.op === "net-ws-wait") return this.closed(entry.socket);
7640
+ if (req.op === "net-ws-send") return Promise.resolve(this.send(entry.socket, req, entry));
7641
+ entry.ended = true;
7642
+ entry.socket.close(req.code ?? 1e3, req.reason ?? "");
7643
+ return Promise.resolve({ ok: true, kind: "void" });
7644
+ }
7645
+ start(req) {
7646
+ const handle = this.next();
7647
+ const entry = {
7648
+ abort: new AbortController(),
7649
+ ready: Promise.resolve({ ok: true, kind: "void" })
7650
+ };
7651
+ this.open.set(handle, entry);
7652
+ entry.ready = this.connect(handle, req, entry);
7653
+ return { ok: true, kind: "json", json: handle };
7654
+ }
7655
+ async connect(handle, req, entry) {
7656
+ const { transport } = this;
7657
+ if (!transport.traits.websocket || !transport.websocket) return fail("ENOTSUP", NO_WEBSOCKET2);
7658
+ let socket;
7659
+ try {
7660
+ socket = await transport.websocket({
7661
+ url: req.url,
7662
+ protocols: req.protocols,
7663
+ headers: req.headers,
7664
+ signal: entry.abort.signal
7665
+ });
7666
+ } catch (e) {
7667
+ return fail("ECONNREFUSED", message(e));
7668
+ }
7669
+ if (!this.open.has(handle)) {
7670
+ socket.close(1001, "the program went away");
7671
+ return fail("EBADF", `no websocket ${handle}`);
7672
+ }
7673
+ if (socket.protocol && !(req.protocols.includes(socket.protocol) && TOKEN3.test(socket.protocol))) {
7674
+ socket.close(1002, "subprotocol not offered");
7675
+ return fail("EPROTO", "the far end chose a subprotocol the program did not offer");
7676
+ }
7677
+ entry.socket = socket;
7678
+ void socket.closed.then(() => {
7679
+ entry.ended = true;
7680
+ });
7681
+ entry.messages = socket.messages[Symbol.asyncIterator]();
7682
+ return { ok: true, kind: "json", json: { protocol: socket.protocol } };
7683
+ }
7684
+ async recv(socket, entry) {
7685
+ try {
7686
+ const next2 = await entry.messages.next();
7687
+ if (!next2.done) {
7688
+ return typeof next2.value === "string" ? { ok: true, kind: "json", json: { text: next2.value } } : { ok: true, kind: "bytes", bytes: next2.value };
7689
+ }
7690
+ const closed = await socket.closed;
7691
+ return { ok: true, kind: "json", json: { code: closed.code, reason: closed.reason } };
7692
+ } catch (e) {
7693
+ return fail("ECONNRESET", message(e));
7694
+ }
7695
+ }
7696
+ async closed(socket) {
7697
+ const { code, reason } = await socket.closed;
7698
+ return { ok: true, kind: "json", json: { code, reason } };
7699
+ }
7700
+ send(socket, req, entry) {
7701
+ const data = req.text ?? req.body;
7702
+ if (entry.ended) return fail("EPIPE", "the websocket is closed");
7703
+ try {
7704
+ if (data !== void 0) socket.send(data);
7705
+ } catch (e) {
7706
+ return fail("EPIPE", message(e));
7707
+ }
7708
+ return { ok: true, kind: "json", json: socket.buffered };
7709
+ }
7710
+ close(handle) {
7711
+ const entry = this.open.get(handle);
7712
+ if (!entry) return;
7713
+ this.open.delete(handle);
7714
+ entry.abort.abort();
7715
+ entry.socket?.close(1001, "the program went away");
7716
+ }
7717
+ closeAll() {
7718
+ for (const handle of [...this.open.keys()]) this.close(handle);
7719
+ }
7720
+ };
7721
+
7294
7722
  // src/kernel/net/http-syscalls.ts
7295
- var HTTP_OPS = ["net-request", "net-read", "net-close", "net-traits"];
7723
+ var HTTP_OPS = [
7724
+ "net-request",
7725
+ "net-read",
7726
+ "net-close",
7727
+ "net-traits",
7728
+ ...WS_OPS
7729
+ ];
7296
7730
  var EMPTY = new Uint8Array(0);
7297
- function message(e) {
7731
+ function message2(e) {
7298
7732
  return e instanceof Error ? e.message : String(e);
7299
7733
  }
7300
7734
  async function* once(bytes) {
@@ -7304,10 +7738,13 @@ var HttpHandles = class {
7304
7738
  transport;
7305
7739
  open = /* @__PURE__ */ new Map();
7306
7740
  nextHandle = 0;
7741
+ ws;
7307
7742
  constructor(transport) {
7308
7743
  this.transport = transport;
7744
+ this.ws = new WsHandles(transport, () => ++this.nextHandle);
7309
7745
  }
7310
7746
  syscall(req) {
7747
+ if (req.op.startsWith("net-ws-")) return this.ws.syscall(req);
7311
7748
  if (req.op === "net-request") return this.request(req);
7312
7749
  if (req.op === "net-read") return this.read(req.handle, req.max);
7313
7750
  if (req.op === "net-traits") return Promise.resolve(this.traits());
@@ -7350,7 +7787,7 @@ var HttpHandles = class {
7350
7787
  };
7351
7788
  return { ok: true, kind: "json", json: head };
7352
7789
  } catch (e) {
7353
- const text = message(e);
7790
+ const text = message2(e);
7354
7791
  if (text === NO_TRANSPORT) return { ok: false, errno: "ENETUNREACH", message: text };
7355
7792
  const status = e.status;
7356
7793
  if (typeof status !== "number" || status === 502) {
@@ -7384,7 +7821,7 @@ var HttpHandles = class {
7384
7821
  } catch (e) {
7385
7822
  entry.done = true;
7386
7823
  const timedOut = e?.code === "ETIMEDOUT";
7387
- return { ok: false, errno: timedOut ? "ETIMEDOUT" : "EIO", message: message(e) };
7824
+ return { ok: false, errno: timedOut ? "ETIMEDOUT" : "EIO", message: message2(e) };
7388
7825
  }
7389
7826
  }
7390
7827
  const bytes = entry.left.subarray(0, Math.max(0, max));
@@ -7392,6 +7829,10 @@ var HttpHandles = class {
7392
7829
  return { ok: true, kind: "bytes", bytes };
7393
7830
  }
7394
7831
  async close(handle) {
7832
+ if (this.ws.owns(handle)) {
7833
+ this.ws.close(handle);
7834
+ return { ok: true, kind: "void" };
7835
+ }
7395
7836
  const entry = this.open.get(handle);
7396
7837
  if (!entry) return { ok: false, errno: "EBADF", message: `no request ${handle}` };
7397
7838
  this.open.delete(handle);
@@ -7400,6 +7841,7 @@ var HttpHandles = class {
7400
7841
  return { ok: true, kind: "void" };
7401
7842
  }
7402
7843
  async closeAll() {
7844
+ this.ws.closeAll();
7403
7845
  await Promise.all([...this.open.keys()].map((handle) => this.close(handle)));
7404
7846
  }
7405
7847
  };
@@ -7432,7 +7874,7 @@ var Resolver = class {
7432
7874
  this.routes = options.routes;
7433
7875
  this.now = options.now ?? Date.now;
7434
7876
  this.timeoutMs = options.timeoutMs ?? RESOLVE_TIMEOUT_MS;
7435
- this.log = options.log ?? ((message2) => console.warn(message2));
7877
+ this.log = options.log ?? ((message3) => console.warn(message3));
7436
7878
  this.own = ownNames(options.hostname ?? DEFAULT_HOSTNAME);
7437
7879
  }
7438
7880
  async resolve(name2, family, blocking) {
@@ -7449,21 +7891,21 @@ var Resolver = class {
7449
7891
  if (!this.uplink || key === "") return [];
7450
7892
  const asked = this.uplink.traits.ipv6 ? family : 4;
7451
7893
  if (family === 6 && asked !== 6) return [];
7452
- const id = `${asked}:${key}`;
7453
- const hit = this.cache.get(id);
7894
+ const id2 = `${asked}:${key}`;
7895
+ const hit = this.cache.get(id2);
7454
7896
  if (hit && hit.expires > this.now()) return hit.addresses;
7455
7897
  const signal = blocking?.();
7456
7898
  if (signal?.aborted) throw new KernelError("EINTR");
7457
- let lookup = this.pending.get(id);
7899
+ let lookup = this.pending.get(id2);
7458
7900
  if (!lookup) {
7459
7901
  const abort = new AbortController();
7460
- lookup = { work: this.lookup(key, asked, id, abort.signal), abort, waiters: 0 };
7461
- this.pending.set(id, lookup);
7462
- void lookup.work.then(() => this.pending.delete(id));
7902
+ lookup = { work: this.lookup(key, asked, id2, abort.signal), abort, waiters: 0 };
7903
+ this.pending.set(id2, lookup);
7904
+ void lookup.work.then(() => this.pending.delete(id2));
7463
7905
  }
7464
7906
  return waitFor(lookup, signal);
7465
7907
  }
7466
- async lookup(name2, family, id, abandoned) {
7908
+ async lookup(name2, family, id2, abandoned) {
7467
7909
  const uplink = this.uplink;
7468
7910
  const timeout = AbortSignal.any([AbortSignal.timeout(this.timeoutMs), abandoned]);
7469
7911
  let answer;
@@ -7480,7 +7922,7 @@ var Resolver = class {
7480
7922
  return [];
7481
7923
  }
7482
7924
  const addresses = answer.addresses.filter((a) => this.usable(name2, a, family));
7483
- if (addresses.length > 0) this.remember(id, addresses, answer.ttl);
7925
+ if (addresses.length > 0) this.remember(id2, addresses, answer.ttl);
7484
7926
  return addresses;
7485
7927
  }
7486
7928
  usable(name2, address, family) {
@@ -7492,11 +7934,11 @@ var Resolver = class {
7492
7934
  this.log(`slicc-kernel: dropped the uplink's answer ${address} for ${name2}: it is ${kind}`);
7493
7935
  return false;
7494
7936
  }
7495
- remember(id, addresses, ttl) {
7937
+ remember(id2, addresses, ttl) {
7496
7938
  const seconds = Math.min(ttl ?? RESOLVE_TTL_MAX, RESOLVE_TTL_MAX);
7497
7939
  if (seconds <= 0) return;
7498
- this.cache.delete(id);
7499
- this.cache.set(id, { addresses, expires: this.now() + seconds * 1e3 });
7940
+ this.cache.delete(id2);
7941
+ this.cache.set(id2, { addresses, expires: this.now() + seconds * 1e3 });
7500
7942
  if (this.cache.size > RESOLVE_CACHE_MAX) {
7501
7943
  this.cache.delete(this.cache.keys().next().value);
7502
7944
  }
@@ -7992,10 +8434,10 @@ function ready(fds, read, write) {
7992
8434
  }
7993
8435
  function interruption(signal) {
7994
8436
  const { promise, reject } = Promise.withResolvers();
7995
- const fail = () => reject(new KernelError("EINTR"));
7996
- if (signal.aborted) fail();
7997
- else signal.addEventListener("abort", fail, { once: true });
7998
- return { promise, done: () => signal.removeEventListener("abort", fail) };
8437
+ const fail2 = () => reject(new KernelError("EINTR"));
8438
+ if (signal.aborted) fail2();
8439
+ else signal.addEventListener("abort", fail2, { once: true });
8440
+ return { promise, done: () => signal.removeEventListener("abort", fail2) };
7999
8441
  }
8000
8442
  async function selectFds(fds, read, write, timeoutMs, interrupt) {
8001
8443
  const deadline = timeoutMs < 0 ? Number.POSITIVE_INFINITY : Date.now() + timeoutMs;
@@ -8938,6 +9380,8 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
8938
9380
  "fd-list",
8939
9381
  "proc-alarm",
8940
9382
  "proc-umask",
9383
+ "proc-cred",
9384
+ "proc-setcred",
8941
9385
  "dl-log",
8942
9386
  "fd-renumber",
8943
9387
  "fd-promote",
@@ -8988,6 +9432,7 @@ var WasmProcess = class {
8988
9432
  interrupt = new AbortController();
8989
9433
  dying = false;
8990
9434
  umask = DEFAULT_UMASK;
9435
+ cred;
8991
9436
  execChild;
8992
9437
  killed = false;
8993
9438
  execTermsig;
@@ -9007,6 +9452,7 @@ var WasmProcess = class {
9007
9452
  this.options = options;
9008
9453
  this.inherited = options.ignored ?? 0;
9009
9454
  this.umask = options.umask ?? DEFAULT_UMASK;
9455
+ this.cred = copyCred(options.cred ?? ROOT);
9010
9456
  this.children = new ChildTable(fds, options.spawner, options.forker);
9011
9457
  this.nodes = options.nodes ?? new VfsNodes(options.fs);
9012
9458
  if (!options.nodes) options.openFiles?.add(this.nodes);
@@ -9523,6 +9969,11 @@ var WasmProcess = class {
9523
9969
  return { ok: true, kind: "void" };
9524
9970
  case "proc-umask":
9525
9971
  return { ok: true, kind: "json", json: this.setUmask(req.mask) };
9972
+ case "proc-cred":
9973
+ return { ok: true, kind: "json", json: copyCred(this.cred) };
9974
+ case "proc-setcred":
9975
+ this.cred = changeCred(this.cred, req.change);
9976
+ return { ok: true, kind: "json", json: copyCred(this.cred) };
9526
9977
  case "dl-log":
9527
9978
  if (req.append) this.dlLog.push(req.append);
9528
9979
  return { ok: true, kind: "json", json: this.dlLog.slice(req.from) };
@@ -9617,9 +10068,9 @@ var KILL_GRACE_MS = 2e3;
9617
10068
  var descIds = /* @__PURE__ */ new WeakMap();
9618
10069
  var nextDescId = 1;
9619
10070
  function descId(file) {
9620
- const id = descIds.get(file) ?? nextDescId++;
9621
- descIds.set(file, id);
9622
- return id;
10071
+ const id2 = descIds.get(file) ?? nextDescId++;
10072
+ descIds.set(file, id2);
10073
+ return id2;
9623
10074
  }
9624
10075
  function inheritedFds(fds) {
9625
10076
  return fds.numbers().filter((fd) => fd > 2 && !fds.get(fd).file.held).map((fd) => {
@@ -9736,7 +10187,8 @@ function processInit(opts, sab, umask, lane) {
9736
10187
  function inherited(opts) {
9737
10188
  return {
9738
10189
  ...opts.ignored ? { ignored: opts.ignored } : {},
9739
- ...opts.umask !== void 0 ? { umask: opts.umask } : {}
10190
+ ...opts.umask !== void 0 ? { umask: opts.umask } : {},
10191
+ ...opts.cred ? { cred: opts.cred } : {}
9740
10192
  };
9741
10193
  }
9742
10194
  function holdingToken(opts, holds) {
@@ -9900,6 +10352,7 @@ function spawnWasmProcess(opts) {
9900
10352
  ignoredSignals: (fork) => process.inheritable(fork),
9901
10353
  signalMasks: () => process.signalMasks(),
9902
10354
  umask: () => process.umask,
10355
+ cred: () => copyCred(process.cred),
9903
10356
  writesBack: () => opts.program.abi === "wasi" && !done4
9904
10357
  };
9905
10358
  }
@@ -9958,6 +10411,9 @@ var JobTable = class {
9958
10411
  pgidOf(pid) {
9959
10412
  return this.members.get(pid)?.pgid;
9960
10413
  }
10414
+ sidOf(pid) {
10415
+ return this.members.get(pid)?.sid;
10416
+ }
9961
10417
  member(pid) {
9962
10418
  const member = this.members.get(pid);
9963
10419
  if (!member) throw new KernelError("ESRCH");
@@ -9966,27 +10422,27 @@ var JobTable = class {
9966
10422
  idOf(pid) {
9967
10423
  return this.members.get(pid)?.shownAs ?? pid;
9968
10424
  }
9969
- byId(id) {
9970
- for (const member of this.members.values()) if (member.shownAs === id) return member;
9971
- return this.member(id);
10425
+ byId(id2) {
10426
+ for (const member of this.members.values()) if (member.shownAs === id2) return member;
10427
+ return this.member(id2);
9972
10428
  }
9973
10429
  setpgid(caller, pid, pgid) {
9974
10430
  const me = this.idOf(caller);
9975
10431
  const target = this.byId(pid || me);
9976
- const id = this.idOf(target.pid);
9977
- const group = pgid || id;
10432
+ const id2 = this.idOf(target.pid);
10433
+ const group = pgid || id2;
9978
10434
  if (group < 0) throw new KernelError("EINVAL");
9979
10435
  const self = this.member(caller);
9980
- if (id !== me) {
10436
+ if (id2 !== me) {
9981
10437
  if (target.ppid === void 0 || this.idOf(target.ppid) !== me) {
9982
10438
  throw new KernelError("ESRCH");
9983
10439
  }
9984
10440
  if (target.execed) throw new KernelError("EACCES");
9985
10441
  }
9986
10442
  if (target.sid !== self.sid) throw new KernelError("EPERM");
9987
- if (id === target.sid) throw new KernelError("EPERM");
10443
+ if (id2 === target.sid) throw new KernelError("EPERM");
9988
10444
  const exists = [...this.members.values()].some((m) => m.pgid === group && m.sid === target.sid);
9989
- if (group !== id && !exists) throw new KernelError("EPERM");
10445
+ if (group !== id2 && !exists) throw new KernelError("EPERM");
9990
10446
  target.pgid = group;
9991
10447
  }
9992
10448
  getpgid(caller, pid) {
@@ -9997,12 +10453,12 @@ var JobTable = class {
9997
10453
  }
9998
10454
  setsid(pid) {
9999
10455
  const member = this.member(pid);
10000
- const id = this.idOf(pid);
10001
- if (member.pgid === id) throw new KernelError("EPERM");
10002
- member.sid = id;
10003
- member.pgid = id;
10004
- this.terminals.delete(id);
10005
- return id;
10456
+ const id2 = this.idOf(pid);
10457
+ if (member.pgid === id2) throw new KernelError("EPERM");
10458
+ member.sid = id2;
10459
+ member.pgid = id2;
10460
+ this.terminals.delete(id2);
10461
+ return id2;
10006
10462
  }
10007
10463
  terminalNamed(name2) {
10008
10464
  return this.knownTerminals().find((tty) => tty.name === name2);
@@ -10019,15 +10475,17 @@ var JobTable = class {
10019
10475
  }
10020
10476
  acquireTerminal(pid, tty) {
10021
10477
  const member = this.members.get(pid);
10022
- const id = this.idOf(pid);
10023
- if (!member || member.sid !== id || this.terminals.has(id)) return false;
10478
+ const id2 = this.idOf(pid);
10479
+ if (!member || member.sid !== id2 || this.terminals.has(id2)) return false;
10024
10480
  if ([...this.terminals.values()].includes(tty)) return false;
10025
- this.terminals.set(id, tty);
10481
+ this.terminals.set(id2, tty);
10026
10482
  this.foreground.set(tty, member.pgid);
10027
10483
  return true;
10028
10484
  }
10029
- killGroup(pgid, sig) {
10030
- const targets = [...this.members.values()].filter((m) => m.pgid === pgid);
10485
+ killGroup(pgid, sig, may = () => true) {
10486
+ const group = [...this.members.values()].filter((m) => m.pgid === pgid);
10487
+ const targets = group.filter((m) => may(m.pid));
10488
+ if (targets.length === 0 && group.length > 0) throw new KernelError("EPERM");
10031
10489
  const pids = new Set(targets.map((m) => m.pid));
10032
10490
  if (sig !== 0) {
10033
10491
  for (const m of targets)
@@ -10128,8 +10586,8 @@ var SettlingChildren = class {
10128
10586
  };
10129
10587
 
10130
10588
  // src/mount/driver.ts
10131
- function fsError2(code, message2 = code) {
10132
- return Object.assign(new Error(message2), { code });
10589
+ function fsError2(code, message3 = code) {
10590
+ return Object.assign(new Error(message3), { code });
10133
10591
  }
10134
10592
  async function dispatch(h, req) {
10135
10593
  switch (req.op) {
@@ -10179,15 +10637,15 @@ async function dispatch(h, req) {
10179
10637
  }
10180
10638
  function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
10181
10639
  let open = false;
10182
- const reply = (message2, transfer = []) => port.postMessage(message2, transfer);
10183
- async function hello(message2) {
10184
- const refused = driverVersionError(message2.protocol);
10640
+ const reply = (message3, transfer = []) => port.postMessage(message3, transfer);
10641
+ async function hello(message3) {
10642
+ const refused = driverVersionError(message3.protocol);
10185
10643
  if (refused) {
10186
10644
  reply({ hello: { protocol: DRIVER_PROTOCOL, error: refused, errno: "EPROTO" } });
10187
10645
  return;
10188
10646
  }
10189
10647
  try {
10190
- const refined = await handlers.mount?.(message2.mount);
10648
+ const refined = await handlers.mount?.(message3.mount);
10191
10649
  open = true;
10192
10650
  reply({
10193
10651
  hello: { protocol: DRIVER_PROTOCOL, capabilities: { ...capabilities, ...refined } }
@@ -10489,7 +10947,7 @@ function unescape(field2) {
10489
10947
  );
10490
10948
  }
10491
10949
  function parseFstab(text) {
10492
- const lines = [];
10950
+ const lines2 = [];
10493
10951
  text.split("\n").forEach((raw, index) => {
10494
10952
  const content = raw.replace(/#.*$/, "").trim();
10495
10953
  if (!content) return;
@@ -10498,12 +10956,12 @@ function parseFstab(text) {
10498
10956
  if (options.split(",").includes("noauto")) return;
10499
10957
  const spec = { source, target, type, options: {} };
10500
10958
  try {
10501
- lines.push({ line: index + 1, spec: { ...spec, options: parseMountOptions(options) } });
10959
+ lines2.push({ line: index + 1, spec: { ...spec, options: parseMountOptions(options) } });
10502
10960
  } catch (err) {
10503
- lines.push({ line: index + 1, spec, invalid: err });
10961
+ lines2.push({ line: index + 1, spec, invalid: err });
10504
10962
  }
10505
10963
  });
10506
- return lines;
10964
+ return lines2;
10507
10965
  }
10508
10966
  function codeOf3(err) {
10509
10967
  const code = err?.code;
@@ -10521,11 +10979,11 @@ function wait(ms, signal) {
10521
10979
  signal.addEventListener("abort", done4, { once: true });
10522
10980
  });
10523
10981
  }
10524
- async function mountFstab(lines, mount, signal, retries = FSTAB_RETRIES, discard = () => {
10982
+ async function mountFstab(lines2, mount, signal, retries = FSTAB_RETRIES, discard = () => {
10525
10983
  }, settled = () => {
10526
10984
  }) {
10527
10985
  return Promise.all(
10528
- lines.map(async (each) => {
10986
+ lines2.map(async (each) => {
10529
10987
  const result = await mountLine(each, mount, signal, retries, discard);
10530
10988
  settled(each, result);
10531
10989
  return result;
@@ -11172,10 +11630,10 @@ function importedMemory(bytes) {
11172
11630
  return { initial, maximum, shared: (flags & 2) !== 0 };
11173
11631
  };
11174
11632
  while (at < bytes.length) {
11175
- const id = bytes[at++];
11633
+ const id2 = bytes[at++];
11176
11634
  const size = u32();
11177
11635
  const end = at + size;
11178
- if (id !== 2) {
11636
+ if (id2 !== 2) {
11179
11637
  at = end;
11180
11638
  continue;
11181
11639
  }
@@ -11295,18 +11753,17 @@ function foreignImports(bytes) {
11295
11753
  const r = new Reader(bytes);
11296
11754
  let types = [];
11297
11755
  while (r.at < bytes.length) {
11298
- const id = r.byte();
11756
+ const id2 = r.byte();
11299
11757
  const end = r.u32() + r.at;
11300
- if (id === 1) types = readTypes(r);
11758
+ if (id2 === 1) types = readTypes(r);
11301
11759
  if (!types) return void 0;
11302
- if (id === 2) return readImports(r, types);
11760
+ if (id2 === 2) return readImports(r, types);
11303
11761
  r.at = end;
11304
11762
  }
11305
11763
  return {};
11306
11764
  }
11307
11765
 
11308
11766
  // src/launcher.ts
11309
- var PNPM_HOME = "/home/.local/share/pnpm";
11310
11767
  var COMMAND2 = /^\/(?:usr\/)?bin\/([^/]+)$/;
11311
11768
  var PACKAGE_ROOT = /^(.*\/node_modules\/(?:@[^/]+\/)?[^/]+)\//;
11312
11769
  var ENV_REFERENCE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
@@ -11406,6 +11863,8 @@ var Launcher = class {
11406
11863
  zombies = /* @__PURE__ */ new Set();
11407
11864
  orphans = /* @__PURE__ */ new Set();
11408
11865
  described = /* @__PURE__ */ new Map();
11866
+ creds = /* @__PURE__ */ new Map();
11867
+ users;
11409
11868
  jobs = new JobTable();
11410
11869
  locks = new LockTable();
11411
11870
  settling = new SettlingChildren((pid) => this.jobs.pgidOf(pid));
@@ -11457,7 +11916,7 @@ var Launcher = class {
11457
11916
  this.hostfs = options;
11458
11917
  this.fstabRetries = options.fstabRetries ?? FSTAB_RETRIES;
11459
11918
  this.processMounts = options.processMounts ?? true;
11460
- this.pnpmHome = options.env?.PNPM_HOME ?? PNPM_HOME;
11919
+ this.pnpmHome = options.env?.PNPM_HOME ?? SYSTEM_PNPM_HOME;
11461
11920
  this.modulesDir = options.modules ?? "/node_modules";
11462
11921
  this.cdp = new CdpHosts(options.cdp);
11463
11922
  const hostname = options.hostname ?? DEFAULT_HOSTNAME;
@@ -11493,6 +11952,7 @@ var Launcher = class {
11493
11952
  }
11494
11953
  );
11495
11954
  this.fs = this.unlinked.wrap(keepingOpen(fs, this.nodes));
11955
+ this.users = new UserDb(this.fs);
11496
11956
  void this.unlinked.sweep("/");
11497
11957
  this.watchers.watch([this.modulesDir, this.pnpmHome], { recursive: true }, () => {
11498
11958
  this.catalog = void 0;
@@ -11508,10 +11968,10 @@ var Launcher = class {
11508
11968
  }
11509
11969
  interpreters() {
11510
11970
  this.binfmts ??= this.roots().then(
11511
- (roots) => scanBinfmts(this.base, roots, (message2) => {
11512
- if (this.warned.has(message2)) return;
11513
- this.warned.add(message2);
11514
- console.warn(message2);
11971
+ (roots) => scanBinfmts(this.base, roots, (message3) => {
11972
+ if (this.warned.has(message3)) return;
11973
+ this.warned.add(message3);
11974
+ console.warn(message3);
11515
11975
  })
11516
11976
  );
11517
11977
  return this.binfmts;
@@ -11693,6 +12153,7 @@ var Launcher = class {
11693
12153
  },
11694
12154
  ...req.ignored ? { ignored: req.ignored } : {},
11695
12155
  ...req.umask !== void 0 ? { umask: req.umask } : {},
12156
+ cred: req.cred,
11696
12157
  program: req.program,
11697
12158
  argv0: req.argv0,
11698
12159
  args: req.args,
@@ -11704,9 +12165,9 @@ var Launcher = class {
11704
12165
  onError: req.report,
11705
12166
  spawner: this.spawner(pid, req.report),
11706
12167
  forker: this.forker(pid, req),
11707
- kill: (target, sig) => this.kill(target, sig),
12168
+ kill: (target, sig) => this.kill(target, sig, this.credOf(pid), pid),
11708
12169
  writesBack: (target) => this.processes.get(target)?.writesBack() === true,
11709
- processes: () => ({ boot: this.boot, processes: this.list() }),
12170
+ processes: () => ({ boot: this.boot, processes: this.list(this.credOf(pid)) }),
11710
12171
  openFiles: this.openFiles,
11711
12172
  nodes: this.nodes,
11712
12173
  held: this.held,
@@ -11726,6 +12187,8 @@ var Launcher = class {
11726
12187
  ...this.shownIds(req)
11727
12188
  });
11728
12189
  this.processes.set(pid, handle);
12190
+ const { cred } = req;
12191
+ this.creds.set(pid, () => handle.cred?.() ?? copyCred(cred));
11729
12192
  if (req.ppid !== void 0) this.settling.started(pid, req.ppid);
11730
12193
  this.jobs.add(pid, req.ppid, (sig) => handle.signal(sig), terminal);
11731
12194
  if (req.exec && req.ppid !== void 0) this.jobs.exec(req.ppid, pid, true);
@@ -11752,12 +12215,13 @@ var Launcher = class {
11752
12215
  forget(pid) {
11753
12216
  this.jobs.remove(pid);
11754
12217
  this.described.delete(pid);
12218
+ this.creds.delete(pid);
11755
12219
  }
11756
12220
  reaped(pid) {
11757
12221
  if (this.zombies.delete(pid)) this.forget(pid);
11758
12222
  else this.orphans.add(pid);
11759
12223
  }
11760
- list() {
12224
+ list(viewer = ROOT) {
11761
12225
  const members = new Map(this.jobs.list().map((member) => [member.pid, member]));
11762
12226
  const rootOf = (member) => {
11763
12227
  let root = member;
@@ -11775,6 +12239,8 @@ var Launcher = class {
11775
12239
  for (const member of members.values()) {
11776
12240
  const described = this.described.get(member.pid);
11777
12241
  if (member.execed || !described) continue;
12242
+ const cred = this.credOf(member.pid);
12243
+ if (!maySee(viewer, cred)) continue;
11778
12244
  const root = rootOf(member);
11779
12245
  listed.push({
11780
12246
  pid: root.pid,
@@ -11787,6 +12253,7 @@ var Launcher = class {
11787
12253
  state: this.processes.has(member.pid) ? "S" : "Z",
11788
12254
  memory: this.processes.get(member.pid)?.memory() ?? 0,
11789
12255
  umask: this.umaskOf(member.pid),
12256
+ cred,
11790
12257
  ...this.signalMasksOf(member.pid)
11791
12258
  });
11792
12259
  }
@@ -11810,6 +12277,7 @@ var Launcher = class {
11810
12277
  ppid,
11811
12278
  ignored: this.ignoredBy(ppid),
11812
12279
  umask: this.umaskOf(ppid),
12280
+ cred: this.credOf(ppid),
11813
12281
  ...exec
11814
12282
  });
11815
12283
  deciding?.attach();
@@ -11855,7 +12323,8 @@ var Launcher = class {
11855
12323
  ppid,
11856
12324
  fork: state,
11857
12325
  ignored: this.ignoredBy(ppid, true),
11858
- umask: this.umaskOf(ppid)
12326
+ umask: this.umaskOf(ppid),
12327
+ cred: this.credOf(ppid)
11859
12328
  })
11860
12329
  );
11861
12330
  }
@@ -11867,6 +12336,18 @@ var Launcher = class {
11867
12336
  umaskOf(pid) {
11868
12337
  return this.processes.get(pid)?.umask?.() ?? DEFAULT_UMASK;
11869
12338
  }
12339
+ imageOf(pid) {
12340
+ const members = this.jobs.list();
12341
+ let image = pid;
12342
+ for (; ; ) {
12343
+ const next2 = members.find((m) => m.execParent === image);
12344
+ if (!next2) return image;
12345
+ image = next2.pid;
12346
+ }
12347
+ }
12348
+ credOf(pid) {
12349
+ return this.creds.get(pid)?.() ?? copyCred(ROOT);
12350
+ }
11870
12351
  released(path) {
11871
12352
  const at = normalizePath(path);
11872
12353
  this.unlinked.closed(at);
@@ -11880,10 +12361,13 @@ var Launcher = class {
11880
12361
  ignoredBy(pid, fork = false) {
11881
12362
  return this.processes.get(pid)?.ignoredSignals(fork) ?? 0;
11882
12363
  }
11883
- kill(pid, sig) {
11884
- if (pid < 0) return this.jobs.killGroup(-pid, sig);
12364
+ kill(pid, sig, sender, from) {
12365
+ const session = from === void 0 ? void 0 : this.jobs.sidOf(from);
12366
+ const may = (target) => !sender || maySignal(sender, this.credOf(target)) || sig === SIG.CONT && session !== void 0 && this.jobs.sidOf(target) === session || from !== void 0 && this.imageOf(from) === this.imageOf(target);
12367
+ if (pid < 0) return this.jobs.killGroup(-pid, sig, (member) => may(this.imageOf(member)));
11885
12368
  const handle = this.processes.get(pid);
11886
12369
  if (!handle) return false;
12370
+ if (!may(this.imageOf(pid))) throw fsError("EPERM", `pid ${pid}`);
11887
12371
  if (sig !== 0) handle.signal(sig);
11888
12372
  return true;
11889
12373
  }
@@ -11954,9 +12438,9 @@ var Launcher = class {
11954
12438
  ask(handle);
11955
12439
  });
11956
12440
  const served = serveFilesystem(port2, medium.handlers, FSA_CAPABILITIES);
11957
- const id = source.slice("fsa:".length);
11958
- this.removable.set(target, { id, medium, served });
11959
- const stored = this.inserted.get(id) ?? await this.media.get(id);
12441
+ const id2 = source.slice("fsa:".length);
12442
+ this.removable.set(target, { id: id2, medium, served });
12443
+ const stored = this.inserted.get(id2) ?? await this.media.get(id2);
11960
12444
  if (stored && await granted(stored)) medium.insert(stored);
11961
12445
  else ask(stored);
11962
12446
  return {
@@ -12040,15 +12524,16 @@ var Launcher = class {
12040
12524
  }
12041
12525
  async prepare() {
12042
12526
  for (const dir of SHARED_DIRS) await this.base.mkdir(dir, { recursive: true });
12527
+ await this.users.ensure();
12043
12528
  await writeCaFile(this.base, this.ca).catch(() => void 0);
12044
12529
  this.fstab = this.mountFstab();
12045
12530
  }
12046
12531
  async mountFstab() {
12047
12532
  const text = await this.base.readFile(FSTAB_PATH).catch(() => "");
12048
- const lines = parseFstab(text);
12049
- for (const { spec } of lines) this.mounts.note(spec, "pending");
12533
+ const lines2 = parseFstab(text);
12534
+ for (const { spec } of lines2) this.mounts.note(spec, "pending");
12050
12535
  return mountFstab(
12051
- lines,
12536
+ lines2,
12052
12537
  (spec) => this.mount(spec).catch((err) => {
12053
12538
  this.mounts.note(spec, "pending", err?.message ?? String(err));
12054
12539
  throw err;
@@ -12063,14 +12548,19 @@ var Launcher = class {
12063
12548
  }
12064
12549
  );
12065
12550
  }
12066
- environment(cwd, extra) {
12551
+ environment(cwd, extra, account) {
12552
+ return { ...this.env, ...userEnv(account, this.pnpmHome), PWD: cwd, ...extra };
12553
+ }
12554
+ async account(user) {
12555
+ if (typeof user === "object") return user;
12556
+ const found = await this.users.lookup(user ?? 0);
12557
+ if (!found) throw fsError("ENOENT", `no user ${String(user)}`);
12558
+ return found;
12559
+ }
12560
+ identity(account) {
12067
12561
  return {
12068
- PATH: `/usr/bin:/bin:${this.pnpmHome}/bin`,
12069
- HOME: "/home",
12070
- PNPM_HOME: this.pnpmHome,
12071
- ...this.env,
12072
- PWD: cwd,
12073
- ...extra
12562
+ cred: credOf(account),
12563
+ ...account.umask !== void 0 ? { umask: account.umask } : {}
12074
12564
  };
12075
12565
  }
12076
12566
  async starting(argv, dir) {
@@ -12083,6 +12573,7 @@ var Launcher = class {
12083
12573
  return { cwd, file, planned: await this.plan(file, argv, cwd) };
12084
12574
  }
12085
12575
  async openTerminal(argv, options) {
12576
+ const account = await this.account(options.user);
12086
12577
  const { cwd, file, planned } = await this.starting(argv, options.cwd);
12087
12578
  if (!planned) throw new Error(`${file}: command not found`);
12088
12579
  let leader = 0;
@@ -12098,14 +12589,18 @@ var Launcher = class {
12098
12589
  fds.installAt(0, stdio);
12099
12590
  fds.installAt(1, stdio.retain());
12100
12591
  fds.installAt(2, stdio.retain());
12101
- const env = this.environment(cwd, {
12102
- TERM: "xterm-256color",
12103
- COLORTERM: "truecolor",
12104
- ...options.env
12105
- });
12106
- const report = (message2) => options.onData(encoder3.encode(`${message2}\r
12592
+ const env = this.environment(
12593
+ cwd,
12594
+ {
12595
+ TERM: "xterm-256color",
12596
+ COLORTERM: "truecolor",
12597
+ ...options.env
12598
+ },
12599
+ account
12600
+ );
12601
+ const report = (message3) => options.onData(encoder3.encode(`${message3}\r
12107
12602
  `));
12108
- const handle = await this.launch(planned, { env, cwd, fds, report });
12603
+ const handle = await this.launch(planned, { env, cwd, fds, report, ...this.identity(account) });
12109
12604
  leader = handle.pid;
12110
12605
  void handle.exited.then(() => tty.hangup());
12111
12606
  return {
@@ -12121,8 +12616,9 @@ var Launcher = class {
12121
12616
  };
12122
12617
  }
12123
12618
  async run(argv, options = {}) {
12619
+ const account = await this.account(options.user);
12124
12620
  const { cwd, file, planned } = await this.starting(argv, options.cwd);
12125
- const env = this.environment(cwd, options.env);
12621
+ const env = this.environment(cwd, options.env, account);
12126
12622
  const out = [];
12127
12623
  const err = [];
12128
12624
  const keep = options.collect !== false;
@@ -12132,7 +12628,7 @@ var Launcher = class {
12132
12628
  };
12133
12629
  const stdout = collect(out, options.onStdout);
12134
12630
  const stderr = collect(err, options.onStderr);
12135
- const report = (message2) => stderr(encoder3.encode(`${message2}
12631
+ const report = (message3) => stderr(encoder3.encode(`${message3}
12136
12632
  `));
12137
12633
  if (!planned) {
12138
12634
  report(`${file}: command not found`);
@@ -12143,7 +12639,14 @@ var Launcher = class {
12143
12639
  fds.installAt(1, sinkFile(stdout));
12144
12640
  fds.installAt(2, sinkFile(stderr));
12145
12641
  const group = options.pgid !== void 0 ? { pgid: options.pgid } : {};
12146
- const handle = await this.launch(planned, { env, cwd, fds, report, ...group });
12642
+ const handle = await this.launch(planned, {
12643
+ env,
12644
+ cwd,
12645
+ fds,
12646
+ report,
12647
+ ...group,
12648
+ ...this.identity(account)
12649
+ });
12147
12650
  options.onStarted?.(handle.pid);
12148
12651
  const status = await handle.exited;
12149
12652
  return { status, stdout: concat5(out), stderr: concat5(err) };
@@ -12209,12 +12712,28 @@ function answered(data, remote, uplink) {
12209
12712
  else return false;
12210
12713
  return true;
12211
12714
  }
12715
+ var USERS_OPS = /* @__PURE__ */ new Set(["users-add", "users-remove", "users-list"]);
12716
+ function isUsersRequest(req) {
12717
+ return USERS_OPS.has(req.op);
12718
+ }
12719
+ function failure(err) {
12720
+ const code = err?.code;
12721
+ return {
12722
+ error: err instanceof Error ? err.message : String(err),
12723
+ ...typeof code === "string" ? { code } : {}
12724
+ };
12725
+ }
12726
+ function usersOp(l, req) {
12727
+ if (req.op === "users-add") return l.users.add(req.user);
12728
+ if (req.op === "users-remove") return l.users.remove(req.name, req.options);
12729
+ return l.users.list();
12730
+ }
12212
12731
  function serveKernel(port, deps) {
12213
12732
  let launcher;
12214
12733
  let remote;
12215
12734
  let uplink;
12216
12735
  const terminals = /* @__PURE__ */ new Map();
12217
- const reply = (id, body, transfer) => port.postMessage({ id, ...body }, transfer);
12736
+ const reply = (id2, body, transfer) => port.postMessage({ id: id2, ...body }, transfer);
12218
12737
  const clients = /* @__PURE__ */ new Set();
12219
12738
  const locks = deps.locks === null ? void 0 : deps.locks ?? locksOf();
12220
12739
  let holding;
@@ -12229,13 +12748,17 @@ function serveKernel(port, deps) {
12229
12748
  });
12230
12749
  return holding;
12231
12750
  }
12232
- async function connect() {
12233
- await ready2();
12751
+ async function connect(user) {
12752
+ const l = await ready2();
12753
+ if (user !== void 0 && !await l.users.lookup(user)) {
12754
+ throw fsError("ENOENT", `no user ${String(user)}`);
12755
+ }
12234
12756
  const { port1, port2 } = new MessageChannel();
12235
12757
  const held = locks ? await holdLock(locks) : void 0;
12236
12758
  const served = serveClient(port1, {
12237
12759
  launcher: ready2,
12238
12760
  signal: signalNumber,
12761
+ ...user !== void 0 ? { user } : {},
12239
12762
  ...locks ? { locks } : {},
12240
12763
  ...held ? { lock: held } : {}
12241
12764
  });
@@ -12304,7 +12827,8 @@ function serveKernel(port, deps) {
12304
12827
  async function handle(req) {
12305
12828
  if (req.op === "init") return init(req);
12306
12829
  if (req.op === "open-terminal") return terminal(req);
12307
- if (req.op === "connect") return connect();
12830
+ if (req.op === "connect") return connect(req.user);
12831
+ if (isUsersRequest(req)) return usersOp(await ready2(), req);
12308
12832
  if (req.op === "routes") {
12309
12833
  (await ready2()).setRoutes(req.routes);
12310
12834
  return true;
@@ -12327,25 +12851,25 @@ function serveKernel(port, deps) {
12327
12851
  const grants = /* @__PURE__ */ new Map();
12328
12852
  let nextGrant = 0;
12329
12853
  const askGrant = (source, options) => {
12330
- const id = ++nextGrant;
12854
+ const id2 = ++nextGrant;
12331
12855
  const waiting = Promise.withResolvers();
12332
- grants.set(id, waiting);
12333
- port.postMessage({ hostfs: { id, source, ...options } });
12856
+ grants.set(id2, waiting);
12857
+ port.postMessage({ hostfs: { id: id2, source, ...options } });
12334
12858
  return waiting.promise;
12335
12859
  };
12336
- function granted2({ id, grant, error }) {
12337
- const waiting = grants.get(id);
12860
+ function granted2({ id: id2, grant, error }) {
12861
+ const waiting = grants.get(id2);
12338
12862
  if (!waiting) return;
12339
- grants.delete(id);
12863
+ grants.delete(id2);
12340
12864
  if (grant) waiting.resolve(grant);
12341
12865
  else waiting.reject(Object.assign(new Error(error ?? "no grant"), { code: "EACCES" }));
12342
12866
  }
12343
12867
  const policies = /* @__PURE__ */ new Map();
12344
12868
  let nextPolicy = 0;
12345
12869
  const askPolicy = (req) => new Promise((resolve) => {
12346
- const id = ++nextPolicy;
12347
- policies.set(id, resolve);
12348
- port.postMessage({ mountPolicy: { id, req } });
12870
+ const id2 = ++nextPolicy;
12871
+ policies.set(id2, resolve);
12872
+ port.postMessage({ mountPolicy: { id: id2, req } });
12349
12873
  });
12350
12874
  const mountPolicy = (policy) => policy === void 0 ? {} : { processMounts: policy === "ask" ? askPolicy : policy };
12351
12875
  port.addEventListener("message", (event) => {
@@ -12361,7 +12885,7 @@ function serveKernel(port, deps) {
12361
12885
  const req = event.data;
12362
12886
  handle(req).then(
12363
12887
  (result) => reply(req.id, { result }, result instanceof MessagePort ? [result] : void 0),
12364
- (err) => reply(req.id, { error: err instanceof Error ? err.message : String(err) })
12888
+ (err) => reply(req.id, failure(err))
12365
12889
  );
12366
12890
  });
12367
12891
  }