@ai-ecoverse/slicc-kernel 1.43.1 → 1.44.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",
@@ -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,6 +2719,7 @@ 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();
@@ -2414,7 +2729,7 @@ function serveClient(port, host) {
2414
2729
  const send = (message2, transfer = []) => {
2415
2730
  if (state !== "closed") port.postMessage(message2, 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;
@@ -2439,6 +2754,7 @@ function serveClient(port, host) {
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";
@@ -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}`);
@@ -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");
@@ -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(),
@@ -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) {
@@ -4779,10 +5090,10 @@ var CdpFacade = class {
4779
5090
  reject(new HttpError(502, `the CDP host closed: ${reason ?? "no reason"}`));
4780
5091
  };
4781
5092
  conn.onmessage = (message2) => {
4782
- const parsed = JSON.parse(message2);
4783
- if (parsed.id !== 1) return;
5093
+ const parsed2 = JSON.parse(message2);
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
  }
@@ -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);
@@ -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) {
@@ -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
  }
@@ -7449,21 +7760,21 @@ var Resolver = class {
7449
7760
  if (!this.uplink || key === "") return [];
7450
7761
  const asked = this.uplink.traits.ipv6 ? family : 4;
7451
7762
  if (family === 6 && asked !== 6) return [];
7452
- const id = `${asked}:${key}`;
7453
- const hit = this.cache.get(id);
7763
+ const id2 = `${asked}:${key}`;
7764
+ const hit = this.cache.get(id2);
7454
7765
  if (hit && hit.expires > this.now()) return hit.addresses;
7455
7766
  const signal = blocking?.();
7456
7767
  if (signal?.aborted) throw new KernelError("EINTR");
7457
- let lookup = this.pending.get(id);
7768
+ let lookup = this.pending.get(id2);
7458
7769
  if (!lookup) {
7459
7770
  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));
7771
+ lookup = { work: this.lookup(key, asked, id2, abort.signal), abort, waiters: 0 };
7772
+ this.pending.set(id2, lookup);
7773
+ void lookup.work.then(() => this.pending.delete(id2));
7463
7774
  }
7464
7775
  return waitFor(lookup, signal);
7465
7776
  }
7466
- async lookup(name2, family, id, abandoned) {
7777
+ async lookup(name2, family, id2, abandoned) {
7467
7778
  const uplink = this.uplink;
7468
7779
  const timeout = AbortSignal.any([AbortSignal.timeout(this.timeoutMs), abandoned]);
7469
7780
  let answer;
@@ -7480,7 +7791,7 @@ var Resolver = class {
7480
7791
  return [];
7481
7792
  }
7482
7793
  const addresses = answer.addresses.filter((a) => this.usable(name2, a, family));
7483
- if (addresses.length > 0) this.remember(id, addresses, answer.ttl);
7794
+ if (addresses.length > 0) this.remember(id2, addresses, answer.ttl);
7484
7795
  return addresses;
7485
7796
  }
7486
7797
  usable(name2, address, family) {
@@ -7492,11 +7803,11 @@ var Resolver = class {
7492
7803
  this.log(`slicc-kernel: dropped the uplink's answer ${address} for ${name2}: it is ${kind}`);
7493
7804
  return false;
7494
7805
  }
7495
- remember(id, addresses, ttl) {
7806
+ remember(id2, addresses, ttl) {
7496
7807
  const seconds = Math.min(ttl ?? RESOLVE_TTL_MAX, RESOLVE_TTL_MAX);
7497
7808
  if (seconds <= 0) return;
7498
- this.cache.delete(id);
7499
- this.cache.set(id, { addresses, expires: this.now() + seconds * 1e3 });
7809
+ this.cache.delete(id2);
7810
+ this.cache.set(id2, { addresses, expires: this.now() + seconds * 1e3 });
7500
7811
  if (this.cache.size > RESOLVE_CACHE_MAX) {
7501
7812
  this.cache.delete(this.cache.keys().next().value);
7502
7813
  }
@@ -8938,6 +9249,8 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
8938
9249
  "fd-list",
8939
9250
  "proc-alarm",
8940
9251
  "proc-umask",
9252
+ "proc-cred",
9253
+ "proc-setcred",
8941
9254
  "dl-log",
8942
9255
  "fd-renumber",
8943
9256
  "fd-promote",
@@ -8988,6 +9301,7 @@ var WasmProcess = class {
8988
9301
  interrupt = new AbortController();
8989
9302
  dying = false;
8990
9303
  umask = DEFAULT_UMASK;
9304
+ cred;
8991
9305
  execChild;
8992
9306
  killed = false;
8993
9307
  execTermsig;
@@ -9007,6 +9321,7 @@ var WasmProcess = class {
9007
9321
  this.options = options;
9008
9322
  this.inherited = options.ignored ?? 0;
9009
9323
  this.umask = options.umask ?? DEFAULT_UMASK;
9324
+ this.cred = copyCred(options.cred ?? ROOT);
9010
9325
  this.children = new ChildTable(fds, options.spawner, options.forker);
9011
9326
  this.nodes = options.nodes ?? new VfsNodes(options.fs);
9012
9327
  if (!options.nodes) options.openFiles?.add(this.nodes);
@@ -9523,6 +9838,11 @@ var WasmProcess = class {
9523
9838
  return { ok: true, kind: "void" };
9524
9839
  case "proc-umask":
9525
9840
  return { ok: true, kind: "json", json: this.setUmask(req.mask) };
9841
+ case "proc-cred":
9842
+ return { ok: true, kind: "json", json: copyCred(this.cred) };
9843
+ case "proc-setcred":
9844
+ this.cred = changeCred(this.cred, req.change);
9845
+ return { ok: true, kind: "json", json: copyCred(this.cred) };
9526
9846
  case "dl-log":
9527
9847
  if (req.append) this.dlLog.push(req.append);
9528
9848
  return { ok: true, kind: "json", json: this.dlLog.slice(req.from) };
@@ -9617,9 +9937,9 @@ var KILL_GRACE_MS = 2e3;
9617
9937
  var descIds = /* @__PURE__ */ new WeakMap();
9618
9938
  var nextDescId = 1;
9619
9939
  function descId(file) {
9620
- const id = descIds.get(file) ?? nextDescId++;
9621
- descIds.set(file, id);
9622
- return id;
9940
+ const id2 = descIds.get(file) ?? nextDescId++;
9941
+ descIds.set(file, id2);
9942
+ return id2;
9623
9943
  }
9624
9944
  function inheritedFds(fds) {
9625
9945
  return fds.numbers().filter((fd) => fd > 2 && !fds.get(fd).file.held).map((fd) => {
@@ -9736,7 +10056,8 @@ function processInit(opts, sab, umask, lane) {
9736
10056
  function inherited(opts) {
9737
10057
  return {
9738
10058
  ...opts.ignored ? { ignored: opts.ignored } : {},
9739
- ...opts.umask !== void 0 ? { umask: opts.umask } : {}
10059
+ ...opts.umask !== void 0 ? { umask: opts.umask } : {},
10060
+ ...opts.cred ? { cred: opts.cred } : {}
9740
10061
  };
9741
10062
  }
9742
10063
  function holdingToken(opts, holds) {
@@ -9900,6 +10221,7 @@ function spawnWasmProcess(opts) {
9900
10221
  ignoredSignals: (fork) => process.inheritable(fork),
9901
10222
  signalMasks: () => process.signalMasks(),
9902
10223
  umask: () => process.umask,
10224
+ cred: () => copyCred(process.cred),
9903
10225
  writesBack: () => opts.program.abi === "wasi" && !done4
9904
10226
  };
9905
10227
  }
@@ -9958,6 +10280,9 @@ var JobTable = class {
9958
10280
  pgidOf(pid) {
9959
10281
  return this.members.get(pid)?.pgid;
9960
10282
  }
10283
+ sidOf(pid) {
10284
+ return this.members.get(pid)?.sid;
10285
+ }
9961
10286
  member(pid) {
9962
10287
  const member = this.members.get(pid);
9963
10288
  if (!member) throw new KernelError("ESRCH");
@@ -9966,27 +10291,27 @@ var JobTable = class {
9966
10291
  idOf(pid) {
9967
10292
  return this.members.get(pid)?.shownAs ?? pid;
9968
10293
  }
9969
- byId(id) {
9970
- for (const member of this.members.values()) if (member.shownAs === id) return member;
9971
- return this.member(id);
10294
+ byId(id2) {
10295
+ for (const member of this.members.values()) if (member.shownAs === id2) return member;
10296
+ return this.member(id2);
9972
10297
  }
9973
10298
  setpgid(caller, pid, pgid) {
9974
10299
  const me = this.idOf(caller);
9975
10300
  const target = this.byId(pid || me);
9976
- const id = this.idOf(target.pid);
9977
- const group = pgid || id;
10301
+ const id2 = this.idOf(target.pid);
10302
+ const group = pgid || id2;
9978
10303
  if (group < 0) throw new KernelError("EINVAL");
9979
10304
  const self = this.member(caller);
9980
- if (id !== me) {
10305
+ if (id2 !== me) {
9981
10306
  if (target.ppid === void 0 || this.idOf(target.ppid) !== me) {
9982
10307
  throw new KernelError("ESRCH");
9983
10308
  }
9984
10309
  if (target.execed) throw new KernelError("EACCES");
9985
10310
  }
9986
10311
  if (target.sid !== self.sid) throw new KernelError("EPERM");
9987
- if (id === target.sid) throw new KernelError("EPERM");
10312
+ if (id2 === target.sid) throw new KernelError("EPERM");
9988
10313
  const exists = [...this.members.values()].some((m) => m.pgid === group && m.sid === target.sid);
9989
- if (group !== id && !exists) throw new KernelError("EPERM");
10314
+ if (group !== id2 && !exists) throw new KernelError("EPERM");
9990
10315
  target.pgid = group;
9991
10316
  }
9992
10317
  getpgid(caller, pid) {
@@ -9997,12 +10322,12 @@ var JobTable = class {
9997
10322
  }
9998
10323
  setsid(pid) {
9999
10324
  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;
10325
+ const id2 = this.idOf(pid);
10326
+ if (member.pgid === id2) throw new KernelError("EPERM");
10327
+ member.sid = id2;
10328
+ member.pgid = id2;
10329
+ this.terminals.delete(id2);
10330
+ return id2;
10006
10331
  }
10007
10332
  terminalNamed(name2) {
10008
10333
  return this.knownTerminals().find((tty) => tty.name === name2);
@@ -10019,15 +10344,17 @@ var JobTable = class {
10019
10344
  }
10020
10345
  acquireTerminal(pid, tty) {
10021
10346
  const member = this.members.get(pid);
10022
- const id = this.idOf(pid);
10023
- if (!member || member.sid !== id || this.terminals.has(id)) return false;
10347
+ const id2 = this.idOf(pid);
10348
+ if (!member || member.sid !== id2 || this.terminals.has(id2)) return false;
10024
10349
  if ([...this.terminals.values()].includes(tty)) return false;
10025
- this.terminals.set(id, tty);
10350
+ this.terminals.set(id2, tty);
10026
10351
  this.foreground.set(tty, member.pgid);
10027
10352
  return true;
10028
10353
  }
10029
- killGroup(pgid, sig) {
10030
- const targets = [...this.members.values()].filter((m) => m.pgid === pgid);
10354
+ killGroup(pgid, sig, may = () => true) {
10355
+ const group = [...this.members.values()].filter((m) => m.pgid === pgid);
10356
+ const targets = group.filter((m) => may(m.pid));
10357
+ if (targets.length === 0 && group.length > 0) throw new KernelError("EPERM");
10031
10358
  const pids = new Set(targets.map((m) => m.pid));
10032
10359
  if (sig !== 0) {
10033
10360
  for (const m of targets)
@@ -10489,7 +10816,7 @@ function unescape(field2) {
10489
10816
  );
10490
10817
  }
10491
10818
  function parseFstab(text) {
10492
- const lines = [];
10819
+ const lines2 = [];
10493
10820
  text.split("\n").forEach((raw, index) => {
10494
10821
  const content = raw.replace(/#.*$/, "").trim();
10495
10822
  if (!content) return;
@@ -10498,12 +10825,12 @@ function parseFstab(text) {
10498
10825
  if (options.split(",").includes("noauto")) return;
10499
10826
  const spec = { source, target, type, options: {} };
10500
10827
  try {
10501
- lines.push({ line: index + 1, spec: { ...spec, options: parseMountOptions(options) } });
10828
+ lines2.push({ line: index + 1, spec: { ...spec, options: parseMountOptions(options) } });
10502
10829
  } catch (err) {
10503
- lines.push({ line: index + 1, spec, invalid: err });
10830
+ lines2.push({ line: index + 1, spec, invalid: err });
10504
10831
  }
10505
10832
  });
10506
- return lines;
10833
+ return lines2;
10507
10834
  }
10508
10835
  function codeOf3(err) {
10509
10836
  const code = err?.code;
@@ -10521,11 +10848,11 @@ function wait(ms, signal) {
10521
10848
  signal.addEventListener("abort", done4, { once: true });
10522
10849
  });
10523
10850
  }
10524
- async function mountFstab(lines, mount, signal, retries = FSTAB_RETRIES, discard = () => {
10851
+ async function mountFstab(lines2, mount, signal, retries = FSTAB_RETRIES, discard = () => {
10525
10852
  }, settled = () => {
10526
10853
  }) {
10527
10854
  return Promise.all(
10528
- lines.map(async (each) => {
10855
+ lines2.map(async (each) => {
10529
10856
  const result = await mountLine(each, mount, signal, retries, discard);
10530
10857
  settled(each, result);
10531
10858
  return result;
@@ -11172,10 +11499,10 @@ function importedMemory(bytes) {
11172
11499
  return { initial, maximum, shared: (flags & 2) !== 0 };
11173
11500
  };
11174
11501
  while (at < bytes.length) {
11175
- const id = bytes[at++];
11502
+ const id2 = bytes[at++];
11176
11503
  const size = u32();
11177
11504
  const end = at + size;
11178
- if (id !== 2) {
11505
+ if (id2 !== 2) {
11179
11506
  at = end;
11180
11507
  continue;
11181
11508
  }
@@ -11295,18 +11622,17 @@ function foreignImports(bytes) {
11295
11622
  const r = new Reader(bytes);
11296
11623
  let types = [];
11297
11624
  while (r.at < bytes.length) {
11298
- const id = r.byte();
11625
+ const id2 = r.byte();
11299
11626
  const end = r.u32() + r.at;
11300
- if (id === 1) types = readTypes(r);
11627
+ if (id2 === 1) types = readTypes(r);
11301
11628
  if (!types) return void 0;
11302
- if (id === 2) return readImports(r, types);
11629
+ if (id2 === 2) return readImports(r, types);
11303
11630
  r.at = end;
11304
11631
  }
11305
11632
  return {};
11306
11633
  }
11307
11634
 
11308
11635
  // src/launcher.ts
11309
- var PNPM_HOME = "/home/.local/share/pnpm";
11310
11636
  var COMMAND2 = /^\/(?:usr\/)?bin\/([^/]+)$/;
11311
11637
  var PACKAGE_ROOT = /^(.*\/node_modules\/(?:@[^/]+\/)?[^/]+)\//;
11312
11638
  var ENV_REFERENCE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
@@ -11406,6 +11732,8 @@ var Launcher = class {
11406
11732
  zombies = /* @__PURE__ */ new Set();
11407
11733
  orphans = /* @__PURE__ */ new Set();
11408
11734
  described = /* @__PURE__ */ new Map();
11735
+ creds = /* @__PURE__ */ new Map();
11736
+ users;
11409
11737
  jobs = new JobTable();
11410
11738
  locks = new LockTable();
11411
11739
  settling = new SettlingChildren((pid) => this.jobs.pgidOf(pid));
@@ -11457,7 +11785,7 @@ var Launcher = class {
11457
11785
  this.hostfs = options;
11458
11786
  this.fstabRetries = options.fstabRetries ?? FSTAB_RETRIES;
11459
11787
  this.processMounts = options.processMounts ?? true;
11460
- this.pnpmHome = options.env?.PNPM_HOME ?? PNPM_HOME;
11788
+ this.pnpmHome = options.env?.PNPM_HOME ?? SYSTEM_PNPM_HOME;
11461
11789
  this.modulesDir = options.modules ?? "/node_modules";
11462
11790
  this.cdp = new CdpHosts(options.cdp);
11463
11791
  const hostname = options.hostname ?? DEFAULT_HOSTNAME;
@@ -11493,6 +11821,7 @@ var Launcher = class {
11493
11821
  }
11494
11822
  );
11495
11823
  this.fs = this.unlinked.wrap(keepingOpen(fs, this.nodes));
11824
+ this.users = new UserDb(this.fs);
11496
11825
  void this.unlinked.sweep("/");
11497
11826
  this.watchers.watch([this.modulesDir, this.pnpmHome], { recursive: true }, () => {
11498
11827
  this.catalog = void 0;
@@ -11693,6 +12022,7 @@ var Launcher = class {
11693
12022
  },
11694
12023
  ...req.ignored ? { ignored: req.ignored } : {},
11695
12024
  ...req.umask !== void 0 ? { umask: req.umask } : {},
12025
+ cred: req.cred,
11696
12026
  program: req.program,
11697
12027
  argv0: req.argv0,
11698
12028
  args: req.args,
@@ -11704,9 +12034,9 @@ var Launcher = class {
11704
12034
  onError: req.report,
11705
12035
  spawner: this.spawner(pid, req.report),
11706
12036
  forker: this.forker(pid, req),
11707
- kill: (target, sig) => this.kill(target, sig),
12037
+ kill: (target, sig) => this.kill(target, sig, this.credOf(pid), pid),
11708
12038
  writesBack: (target) => this.processes.get(target)?.writesBack() === true,
11709
- processes: () => ({ boot: this.boot, processes: this.list() }),
12039
+ processes: () => ({ boot: this.boot, processes: this.list(this.credOf(pid)) }),
11710
12040
  openFiles: this.openFiles,
11711
12041
  nodes: this.nodes,
11712
12042
  held: this.held,
@@ -11726,6 +12056,8 @@ var Launcher = class {
11726
12056
  ...this.shownIds(req)
11727
12057
  });
11728
12058
  this.processes.set(pid, handle);
12059
+ const { cred } = req;
12060
+ this.creds.set(pid, () => handle.cred?.() ?? copyCred(cred));
11729
12061
  if (req.ppid !== void 0) this.settling.started(pid, req.ppid);
11730
12062
  this.jobs.add(pid, req.ppid, (sig) => handle.signal(sig), terminal);
11731
12063
  if (req.exec && req.ppid !== void 0) this.jobs.exec(req.ppid, pid, true);
@@ -11752,12 +12084,13 @@ var Launcher = class {
11752
12084
  forget(pid) {
11753
12085
  this.jobs.remove(pid);
11754
12086
  this.described.delete(pid);
12087
+ this.creds.delete(pid);
11755
12088
  }
11756
12089
  reaped(pid) {
11757
12090
  if (this.zombies.delete(pid)) this.forget(pid);
11758
12091
  else this.orphans.add(pid);
11759
12092
  }
11760
- list() {
12093
+ list(viewer = ROOT) {
11761
12094
  const members = new Map(this.jobs.list().map((member) => [member.pid, member]));
11762
12095
  const rootOf = (member) => {
11763
12096
  let root = member;
@@ -11775,6 +12108,8 @@ var Launcher = class {
11775
12108
  for (const member of members.values()) {
11776
12109
  const described = this.described.get(member.pid);
11777
12110
  if (member.execed || !described) continue;
12111
+ const cred = this.credOf(member.pid);
12112
+ if (!maySee(viewer, cred)) continue;
11778
12113
  const root = rootOf(member);
11779
12114
  listed.push({
11780
12115
  pid: root.pid,
@@ -11787,6 +12122,7 @@ var Launcher = class {
11787
12122
  state: this.processes.has(member.pid) ? "S" : "Z",
11788
12123
  memory: this.processes.get(member.pid)?.memory() ?? 0,
11789
12124
  umask: this.umaskOf(member.pid),
12125
+ cred,
11790
12126
  ...this.signalMasksOf(member.pid)
11791
12127
  });
11792
12128
  }
@@ -11810,6 +12146,7 @@ var Launcher = class {
11810
12146
  ppid,
11811
12147
  ignored: this.ignoredBy(ppid),
11812
12148
  umask: this.umaskOf(ppid),
12149
+ cred: this.credOf(ppid),
11813
12150
  ...exec
11814
12151
  });
11815
12152
  deciding?.attach();
@@ -11855,7 +12192,8 @@ var Launcher = class {
11855
12192
  ppid,
11856
12193
  fork: state,
11857
12194
  ignored: this.ignoredBy(ppid, true),
11858
- umask: this.umaskOf(ppid)
12195
+ umask: this.umaskOf(ppid),
12196
+ cred: this.credOf(ppid)
11859
12197
  })
11860
12198
  );
11861
12199
  }
@@ -11867,6 +12205,18 @@ var Launcher = class {
11867
12205
  umaskOf(pid) {
11868
12206
  return this.processes.get(pid)?.umask?.() ?? DEFAULT_UMASK;
11869
12207
  }
12208
+ imageOf(pid) {
12209
+ const members = this.jobs.list();
12210
+ let image = pid;
12211
+ for (; ; ) {
12212
+ const next2 = members.find((m) => m.execParent === image);
12213
+ if (!next2) return image;
12214
+ image = next2.pid;
12215
+ }
12216
+ }
12217
+ credOf(pid) {
12218
+ return this.creds.get(pid)?.() ?? copyCred(ROOT);
12219
+ }
11870
12220
  released(path) {
11871
12221
  const at = normalizePath(path);
11872
12222
  this.unlinked.closed(at);
@@ -11880,10 +12230,13 @@ var Launcher = class {
11880
12230
  ignoredBy(pid, fork = false) {
11881
12231
  return this.processes.get(pid)?.ignoredSignals(fork) ?? 0;
11882
12232
  }
11883
- kill(pid, sig) {
11884
- if (pid < 0) return this.jobs.killGroup(-pid, sig);
12233
+ kill(pid, sig, sender, from) {
12234
+ const session = from === void 0 ? void 0 : this.jobs.sidOf(from);
12235
+ 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);
12236
+ if (pid < 0) return this.jobs.killGroup(-pid, sig, (member) => may(this.imageOf(member)));
11885
12237
  const handle = this.processes.get(pid);
11886
12238
  if (!handle) return false;
12239
+ if (!may(this.imageOf(pid))) throw fsError("EPERM", `pid ${pid}`);
11887
12240
  if (sig !== 0) handle.signal(sig);
11888
12241
  return true;
11889
12242
  }
@@ -11954,9 +12307,9 @@ var Launcher = class {
11954
12307
  ask(handle);
11955
12308
  });
11956
12309
  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);
12310
+ const id2 = source.slice("fsa:".length);
12311
+ this.removable.set(target, { id: id2, medium, served });
12312
+ const stored = this.inserted.get(id2) ?? await this.media.get(id2);
11960
12313
  if (stored && await granted(stored)) medium.insert(stored);
11961
12314
  else ask(stored);
11962
12315
  return {
@@ -12040,15 +12393,16 @@ var Launcher = class {
12040
12393
  }
12041
12394
  async prepare() {
12042
12395
  for (const dir of SHARED_DIRS) await this.base.mkdir(dir, { recursive: true });
12396
+ await this.users.ensure();
12043
12397
  await writeCaFile(this.base, this.ca).catch(() => void 0);
12044
12398
  this.fstab = this.mountFstab();
12045
12399
  }
12046
12400
  async mountFstab() {
12047
12401
  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");
12402
+ const lines2 = parseFstab(text);
12403
+ for (const { spec } of lines2) this.mounts.note(spec, "pending");
12050
12404
  return mountFstab(
12051
- lines,
12405
+ lines2,
12052
12406
  (spec) => this.mount(spec).catch((err) => {
12053
12407
  this.mounts.note(spec, "pending", err?.message ?? String(err));
12054
12408
  throw err;
@@ -12063,14 +12417,19 @@ var Launcher = class {
12063
12417
  }
12064
12418
  );
12065
12419
  }
12066
- environment(cwd, extra) {
12420
+ environment(cwd, extra, account) {
12421
+ return { ...this.env, ...userEnv(account, this.pnpmHome), PWD: cwd, ...extra };
12422
+ }
12423
+ async account(user) {
12424
+ if (typeof user === "object") return user;
12425
+ const found = await this.users.lookup(user ?? 0);
12426
+ if (!found) throw fsError("ENOENT", `no user ${String(user)}`);
12427
+ return found;
12428
+ }
12429
+ identity(account) {
12067
12430
  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
12431
+ cred: credOf(account),
12432
+ ...account.umask !== void 0 ? { umask: account.umask } : {}
12074
12433
  };
12075
12434
  }
12076
12435
  async starting(argv, dir) {
@@ -12083,6 +12442,7 @@ var Launcher = class {
12083
12442
  return { cwd, file, planned: await this.plan(file, argv, cwd) };
12084
12443
  }
12085
12444
  async openTerminal(argv, options) {
12445
+ const account = await this.account(options.user);
12086
12446
  const { cwd, file, planned } = await this.starting(argv, options.cwd);
12087
12447
  if (!planned) throw new Error(`${file}: command not found`);
12088
12448
  let leader = 0;
@@ -12098,14 +12458,18 @@ var Launcher = class {
12098
12458
  fds.installAt(0, stdio);
12099
12459
  fds.installAt(1, stdio.retain());
12100
12460
  fds.installAt(2, stdio.retain());
12101
- const env = this.environment(cwd, {
12102
- TERM: "xterm-256color",
12103
- COLORTERM: "truecolor",
12104
- ...options.env
12105
- });
12461
+ const env = this.environment(
12462
+ cwd,
12463
+ {
12464
+ TERM: "xterm-256color",
12465
+ COLORTERM: "truecolor",
12466
+ ...options.env
12467
+ },
12468
+ account
12469
+ );
12106
12470
  const report = (message2) => options.onData(encoder3.encode(`${message2}\r
12107
12471
  `));
12108
- const handle = await this.launch(planned, { env, cwd, fds, report });
12472
+ const handle = await this.launch(planned, { env, cwd, fds, report, ...this.identity(account) });
12109
12473
  leader = handle.pid;
12110
12474
  void handle.exited.then(() => tty.hangup());
12111
12475
  return {
@@ -12121,8 +12485,9 @@ var Launcher = class {
12121
12485
  };
12122
12486
  }
12123
12487
  async run(argv, options = {}) {
12488
+ const account = await this.account(options.user);
12124
12489
  const { cwd, file, planned } = await this.starting(argv, options.cwd);
12125
- const env = this.environment(cwd, options.env);
12490
+ const env = this.environment(cwd, options.env, account);
12126
12491
  const out = [];
12127
12492
  const err = [];
12128
12493
  const keep = options.collect !== false;
@@ -12143,7 +12508,14 @@ var Launcher = class {
12143
12508
  fds.installAt(1, sinkFile(stdout));
12144
12509
  fds.installAt(2, sinkFile(stderr));
12145
12510
  const group = options.pgid !== void 0 ? { pgid: options.pgid } : {};
12146
- const handle = await this.launch(planned, { env, cwd, fds, report, ...group });
12511
+ const handle = await this.launch(planned, {
12512
+ env,
12513
+ cwd,
12514
+ fds,
12515
+ report,
12516
+ ...group,
12517
+ ...this.identity(account)
12518
+ });
12147
12519
  options.onStarted?.(handle.pid);
12148
12520
  const status = await handle.exited;
12149
12521
  return { status, stdout: concat5(out), stderr: concat5(err) };
@@ -12209,12 +12581,28 @@ function answered(data, remote, uplink) {
12209
12581
  else return false;
12210
12582
  return true;
12211
12583
  }
12584
+ var USERS_OPS = /* @__PURE__ */ new Set(["users-add", "users-remove", "users-list"]);
12585
+ function isUsersRequest(req) {
12586
+ return USERS_OPS.has(req.op);
12587
+ }
12588
+ function failure(err) {
12589
+ const code = err?.code;
12590
+ return {
12591
+ error: err instanceof Error ? err.message : String(err),
12592
+ ...typeof code === "string" ? { code } : {}
12593
+ };
12594
+ }
12595
+ function usersOp(l, req) {
12596
+ if (req.op === "users-add") return l.users.add(req.user);
12597
+ if (req.op === "users-remove") return l.users.remove(req.name, req.options);
12598
+ return l.users.list();
12599
+ }
12212
12600
  function serveKernel(port, deps) {
12213
12601
  let launcher;
12214
12602
  let remote;
12215
12603
  let uplink;
12216
12604
  const terminals = /* @__PURE__ */ new Map();
12217
- const reply = (id, body, transfer) => port.postMessage({ id, ...body }, transfer);
12605
+ const reply = (id2, body, transfer) => port.postMessage({ id: id2, ...body }, transfer);
12218
12606
  const clients = /* @__PURE__ */ new Set();
12219
12607
  const locks = deps.locks === null ? void 0 : deps.locks ?? locksOf();
12220
12608
  let holding;
@@ -12229,13 +12617,17 @@ function serveKernel(port, deps) {
12229
12617
  });
12230
12618
  return holding;
12231
12619
  }
12232
- async function connect() {
12233
- await ready2();
12620
+ async function connect(user) {
12621
+ const l = await ready2();
12622
+ if (user !== void 0 && !await l.users.lookup(user)) {
12623
+ throw fsError("ENOENT", `no user ${String(user)}`);
12624
+ }
12234
12625
  const { port1, port2 } = new MessageChannel();
12235
12626
  const held = locks ? await holdLock(locks) : void 0;
12236
12627
  const served = serveClient(port1, {
12237
12628
  launcher: ready2,
12238
12629
  signal: signalNumber,
12630
+ ...user !== void 0 ? { user } : {},
12239
12631
  ...locks ? { locks } : {},
12240
12632
  ...held ? { lock: held } : {}
12241
12633
  });
@@ -12304,7 +12696,8 @@ function serveKernel(port, deps) {
12304
12696
  async function handle(req) {
12305
12697
  if (req.op === "init") return init(req);
12306
12698
  if (req.op === "open-terminal") return terminal(req);
12307
- if (req.op === "connect") return connect();
12699
+ if (req.op === "connect") return connect(req.user);
12700
+ if (isUsersRequest(req)) return usersOp(await ready2(), req);
12308
12701
  if (req.op === "routes") {
12309
12702
  (await ready2()).setRoutes(req.routes);
12310
12703
  return true;
@@ -12327,25 +12720,25 @@ function serveKernel(port, deps) {
12327
12720
  const grants = /* @__PURE__ */ new Map();
12328
12721
  let nextGrant = 0;
12329
12722
  const askGrant = (source, options) => {
12330
- const id = ++nextGrant;
12723
+ const id2 = ++nextGrant;
12331
12724
  const waiting = Promise.withResolvers();
12332
- grants.set(id, waiting);
12333
- port.postMessage({ hostfs: { id, source, ...options } });
12725
+ grants.set(id2, waiting);
12726
+ port.postMessage({ hostfs: { id: id2, source, ...options } });
12334
12727
  return waiting.promise;
12335
12728
  };
12336
- function granted2({ id, grant, error }) {
12337
- const waiting = grants.get(id);
12729
+ function granted2({ id: id2, grant, error }) {
12730
+ const waiting = grants.get(id2);
12338
12731
  if (!waiting) return;
12339
- grants.delete(id);
12732
+ grants.delete(id2);
12340
12733
  if (grant) waiting.resolve(grant);
12341
12734
  else waiting.reject(Object.assign(new Error(error ?? "no grant"), { code: "EACCES" }));
12342
12735
  }
12343
12736
  const policies = /* @__PURE__ */ new Map();
12344
12737
  let nextPolicy = 0;
12345
12738
  const askPolicy = (req) => new Promise((resolve) => {
12346
- const id = ++nextPolicy;
12347
- policies.set(id, resolve);
12348
- port.postMessage({ mountPolicy: { id, req } });
12739
+ const id2 = ++nextPolicy;
12740
+ policies.set(id2, resolve);
12741
+ port.postMessage({ mountPolicy: { id: id2, req } });
12349
12742
  });
12350
12743
  const mountPolicy = (policy) => policy === void 0 ? {} : { processMounts: policy === "ask" ? askPolicy : policy };
12351
12744
  port.addEventListener("message", (event) => {
@@ -12361,7 +12754,7 @@ function serveKernel(port, deps) {
12361
12754
  const req = event.data;
12362
12755
  handle(req).then(
12363
12756
  (result) => reply(req.id, { result }, result instanceof MessagePort ? [result] : void 0),
12364
- (err) => reply(req.id, { error: err instanceof Error ? err.message : String(err) })
12757
+ (err) => reply(req.id, failure(err))
12365
12758
  );
12366
12759
  });
12367
12760
  }