@ai-ecoverse/slicc-kernel 1.58.1 → 1.59.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/node.js CHANGED
@@ -1323,6 +1323,11 @@ function blockedReason(url, e) {
1323
1323
  }
1324
1324
  return `${url}: ${e instanceof Error ? e.message : String(e)}`;
1325
1325
  }
1326
+ function unopened(url, code) {
1327
+ return Object.assign(new Error(`${url}: the WebSocket closed (${code}) before it opened`), {
1328
+ status: 502
1329
+ });
1330
+ }
1326
1331
  function openWebSocket(Socket, request, withHeaders) {
1327
1332
  const { url, protocols, signal } = request;
1328
1333
  signal.throwIfAborted();
@@ -1336,13 +1341,22 @@ function openWebSocket(Socket, request, withHeaders) {
1336
1341
  const queue = new MessageQueue(() => socket.close(1e3, "too many waiting messages"));
1337
1342
  return new Promise((resolve, reject) => {
1338
1343
  let open = false;
1344
+ let settled = false;
1345
+ const fail3 = (error) => {
1346
+ if (settled) return;
1347
+ settled = true;
1348
+ signal.removeEventListener("abort", abort);
1349
+ reject(error);
1350
+ };
1339
1351
  const abort = () => {
1340
1352
  socket.close(1e3, "aborted");
1341
- reject(signal.reason);
1353
+ fail3(signal.reason);
1342
1354
  };
1343
1355
  signal.addEventListener("abort", abort, { once: true });
1344
1356
  socket.addEventListener("open", () => {
1357
+ if (settled) return;
1345
1358
  open = true;
1359
+ settled = true;
1346
1360
  signal.removeEventListener("abort", abort);
1347
1361
  resolve({
1348
1362
  protocol: socket.protocol,
@@ -1358,15 +1372,19 @@ function openWebSocket(Socket, request, withHeaders) {
1358
1372
  socket.addEventListener("message", (event) => {
1359
1373
  queue.push(typeof event.data === "string" ? event.data : new Uint8Array(event.data));
1360
1374
  });
1375
+ socket.addEventListener("error", () => {
1376
+ if (open || settled) return;
1377
+ try {
1378
+ socket.close();
1379
+ } catch {
1380
+ }
1381
+ queue.end({ code: 1006, reason: "" });
1382
+ fail3(unopened(url, 1006));
1383
+ });
1361
1384
  socket.addEventListener("close", (event) => {
1362
1385
  queue.end({ code: event.code, reason: event.reason });
1363
1386
  if (open) return;
1364
- signal.removeEventListener("abort", abort);
1365
- reject(
1366
- Object.assign(new Error(`${url}: the WebSocket closed (${event.code}) before it opened`), {
1367
- status: 502
1368
- })
1369
- );
1387
+ fail3(unopened(url, event.code));
1370
1388
  });
1371
1389
  });
1372
1390
  }
@@ -1774,12 +1792,22 @@ var UserDb = class {
1774
1792
  async stampHome(row, shared) {
1775
1793
  const home = this.fs.resolvePath("/", row.home);
1776
1794
  if (!this.owners || !this.fs.chown || !HOME_DIR.test(home)) return;
1777
- if (await this.fs.realpath?.(home, false).catch(() => void 0) !== home) return;
1778
- if (!(await this.fs.lstat(home)).isDirectory) return;
1795
+ if (!await this.realDir(home)) return;
1779
1796
  const gid = shared?.gid ?? row.gid;
1780
1797
  await this.fs.chown(home, { uid: row.uid, gid, follow: false, kind: "adopt" });
1781
- const mode = shared ? 1512 : row.name.startsWith("scoop-") ? 488 : 448;
1782
- await this.fs.chmod(home, mode);
1798
+ const scoop = row.name.startsWith("scoop-");
1799
+ await this.fs.chmod(home, shared ? 1512 : scoop ? 488 : 448);
1800
+ const pnpm = `${home}/.local/share/pnpm`;
1801
+ if (await this.realDir(pnpm)) await this.fs.chmod(pnpm, scoop ? 488 : 448);
1802
+ }
1803
+ async realDir(path) {
1804
+ const parts = path.split("/").filter(Boolean);
1805
+ for (let n = 1; n <= parts.length; n++) {
1806
+ const at = `/${parts.slice(0, n).join("/")}`;
1807
+ const st = await this.fs.lstat(at).catch(() => void 0);
1808
+ if (!st?.isDirectory) return false;
1809
+ }
1810
+ return true;
1783
1811
  }
1784
1812
  async read(path, fallback) {
1785
1813
  try {
@@ -1796,15 +1824,17 @@ var UserDb = class {
1796
1824
  };
1797
1825
  }
1798
1826
  async provision(row) {
1827
+ const owner = this.owners ? { uid: row.uid, gid: row.gid } : void 0;
1828
+ const made2 = owner ? { recursive: true, owner } : { recursive: true };
1799
1829
  const pnpm = `${row.home}/.local/share/pnpm`;
1800
1830
  if (!await this.fs.exists(pnpm)) {
1801
- await this.fs.mkdir(pnpm, { recursive: true });
1831
+ await this.fs.mkdir(pnpm, made2);
1802
1832
  await this.fs.chmod(pnpm, row.name.startsWith("scoop-") ? 488 : 448);
1803
1833
  }
1804
1834
  const config = `${row.home}/.config/pnpm/config.yaml`;
1805
1835
  if (!await this.fs.exists(config)) {
1806
- await this.fs.mkdir(`${row.home}/.config/pnpm`, { recursive: true });
1807
- await this.fs.writeFile(config, PNPM_CONFIG);
1836
+ await this.fs.mkdir(`${row.home}/.config/pnpm`, made2);
1837
+ await this.fs.writeFile(config, PNPM_CONFIG, owner);
1808
1838
  }
1809
1839
  }
1810
1840
  async commit(passwd, group, then) {
@@ -2008,7 +2038,7 @@ async function fsCall(fs, method, args, owner) {
2008
2038
  return { result: bytes, transfer: [bytes.buffer] };
2009
2039
  }
2010
2040
  case "writeFile":
2011
- await fs.writeFile(path(0), args[1], owner);
2041
+ await fs.writeFile(path(0), args[1], created(owner, 438));
2012
2042
  return { result: true };
2013
2043
  case "stat":
2014
2044
  return { result: await fs.stat(path(0)) };
@@ -2017,7 +2047,10 @@ async function fsCall(fs, method, args, owner) {
2017
2047
  case "readdir":
2018
2048
  return { result: await fs.readdir(path(0)) };
2019
2049
  case "mkdir":
2020
- await fs.mkdir(path(0), owner ? { recursive: true, owner } : { recursive: true });
2050
+ await fs.mkdir(
2051
+ path(0),
2052
+ owner ? { recursive: true, owner: made(owner) } : { recursive: true }
2053
+ );
2021
2054
  return { result: true };
2022
2055
  case "rm":
2023
2056
  await fs.rm(path(0), { recursive: true, force: args[1] === true });
@@ -2028,7 +2061,7 @@ async function fsCall(fs, method, args, owner) {
2028
2061
  case "realpath":
2029
2062
  return { result: await realpath(fs, String(args[0])) };
2030
2063
  case "symlink":
2031
- await fs.symlink(String(args[0]), path(1), owner);
2064
+ await fs.symlink(String(args[0]), path(1), owner && { uid: owner.uid, gid: owner.gid });
2032
2065
  return { result: true };
2033
2066
  case "readlink":
2034
2067
  return { result: await fs.readlink(path(0)) };
@@ -2050,8 +2083,16 @@ function dialFor(l, req, open) {
2050
2083
  );
2051
2084
  return { result: port2, transfer: [port2] };
2052
2085
  }
2086
+ function created(owner, mode) {
2087
+ return owner && { uid: owner.uid, gid: owner.gid, mode: mode & ~owner.umask };
2088
+ }
2089
+ function made(owner) {
2090
+ const mode = 511 & ~owner.umask;
2091
+ return { uid: owner.uid, gid: owner.gid, mode, parents: mode | 192 };
2092
+ }
2053
2093
  function ownerFor(l, self) {
2054
- return l.owners && self ? { uid: self.uid, gid: self.gid } : void 0;
2094
+ if (!l.owners || !self) return void 0;
2095
+ return { uid: self.uid, gid: self.gid, umask: self.umask ?? 18 };
2055
2096
  }
2056
2097
  async function accountOf(l, user) {
2057
2098
  const found = await l.users.lookup(user);
@@ -2925,12 +2966,12 @@ var OpfsFs = class {
2925
2966
  const base = dir === "/" ? "" : dir;
2926
2967
  const metas = await this.meta.get(children2.map(([name2]) => `${base}/${name2}`));
2927
2968
  const handed2 = this.owners ? await this.handedTo(dir) : void 0;
2928
- const owned2 = (stat, entry) => handed2 ? { ...stat, ...ownOf(entry) ?? handed2 } : stat;
2969
+ const owned3 = (stat, entry) => handed2 ? { ...stat, ...ownOf(entry) ?? handed2 } : stat;
2929
2970
  return Promise.all(
2930
2971
  children2.map(async ([name2, child], i) => {
2931
- if (!isHandle(child)) return [name2, owned2(this.linkStat(child), child)];
2972
+ if (!isHandle(child)) return [name2, owned3(this.linkStat(child), child)];
2932
2973
  const stat = await this.statOf(`${base}/${name2}`, child, metas[i]).catch(() => null);
2933
- return [name2, stat && owned2(stat, metas[i])];
2974
+ return [name2, stat && owned3(stat, metas[i])];
2934
2975
  })
2935
2976
  );
2936
2977
  }
@@ -2961,9 +3002,9 @@ var OpfsFs = class {
2961
3002
  const parent = await this.locate(parentOf2(path), true);
2962
3003
  if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
2963
3004
  await parent.handle.getDirectoryHandle(baseOf(path), { create: true });
2964
- const made = `${parent.path === "/" ? "" : parent.path}/${baseOf(path)}`;
2965
- await this.stampNew(made, owner, true);
2966
- this.touch(parent.path, made);
3005
+ const made2 = `${parent.path === "/" ? "" : parent.path}/${baseOf(path)}`;
3006
+ await this.stampNew(made2, owner, true);
3007
+ this.touch(parent.path, made2);
2967
3008
  }
2968
3009
  async rm(path, options = {}) {
2969
3010
  let found;
@@ -4408,7 +4449,17 @@ async function followLinks(fs, path) {
4408
4449
  }
4409
4450
  return current;
4410
4451
  }
4411
- function withCommandDirs(fs, names, userDir = async () => void 0) {
4452
+ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false) {
4453
+ async function nearest(path) {
4454
+ const st = await fs.stat(path).catch(() => void 0);
4455
+ if (st || path === "/") return st ?? {};
4456
+ return nearest(path.slice(0, path.lastIndexOf("/")) || "/");
4457
+ }
4458
+ async function owned3(stat2, dir) {
4459
+ if (!owners) return stat2;
4460
+ const { uid = 0, gid = 0 } = dir === void 0 ? {} : await nearest(dir);
4461
+ return { ...stat2, uid, gid };
4462
+ }
4412
4463
  async function children2(path) {
4413
4464
  if (path === "/") return ["bin", "usr"];
4414
4465
  if (path === "/usr") return ["bin"];
@@ -4417,13 +4468,18 @@ function withCommandDirs(fs, names, userDir = async () => void 0) {
4417
4468
  return own?.size ? [...own] : void 0;
4418
4469
  }
4419
4470
  async function virtual(path) {
4420
- if (path === "/usr" || path === "/bin" || path === "/usr/bin") return synthetic(true, path);
4471
+ if (path === "/usr" || path === "/bin" || path === "/usr/bin") {
4472
+ return owned3(synthetic(true, path));
4473
+ }
4421
4474
  const name2 = COMMAND.exec(path)?.[1];
4422
- if (name2 !== void 0) return (await names()).has(name2) ? synthetic(false, path) : void 0;
4423
- if ((await userDir(path))?.size) return synthetic(true, path);
4475
+ if (name2 !== void 0) {
4476
+ return (await names()).has(name2) ? owned3(synthetic(false, path)) : void 0;
4477
+ }
4478
+ if ((await userDir(path))?.size) return owned3(synthetic(true, path), path);
4424
4479
  const slash = path.lastIndexOf("/");
4425
- const own = await userDir(path.slice(0, slash));
4426
- return own?.has(path.slice(slash + 1)) ? synthetic(false, path) : void 0;
4480
+ const dir = path.slice(0, slash);
4481
+ const own = await userDir(dir);
4482
+ return own?.has(path.slice(slash + 1)) ? owned3(synthetic(false, path), dir) : void 0;
4427
4483
  }
4428
4484
  async function linked(path) {
4429
4485
  return await virtual(path) ?? virtual(await followLinks(fs, path));
@@ -4599,18 +4655,18 @@ var FsWatchers = class {
4599
4655
  const overrides = {
4600
4656
  writeFile: after(fs.writeFile.bind(fs), (path) => [path]),
4601
4657
  mkdir: async (path, options) => {
4602
- const created = [];
4658
+ const created3 = [];
4603
4659
  if (options?.recursive) {
4604
4660
  const parts = normalizePath(path).split("/").filter(Boolean);
4605
4661
  for (let at = 1; at < parts.length; at++) {
4606
4662
  const ancestor = `/${parts.slice(0, at).join("/")}`;
4607
- if (created.length > 0 || !await fs.exists(ancestor)) created.push(ancestor);
4663
+ if (created3.length > 0 || !await fs.exists(ancestor)) created3.push(ancestor);
4608
4664
  }
4609
4665
  }
4610
4666
  try {
4611
4667
  await fs.mkdir(path, options);
4612
4668
  } finally {
4613
- this.changed(...created, path);
4669
+ this.changed(...created3, path);
4614
4670
  }
4615
4671
  },
4616
4672
  rm: after(fs.rm.bind(fs), (path) => [path]),
@@ -4889,7 +4945,7 @@ async function realTarget(fs, path) {
4889
4945
  }
4890
4946
 
4891
4947
  // src/mount/protocol.ts
4892
- var DRIVER_PROTOCOL = [1, 1];
4948
+ var DRIVER_PROTOCOL = [1, 2];
4893
4949
  function driverVersionError(theirs) {
4894
4950
  const major = Array.isArray(theirs) ? theirs[0] : void 0;
4895
4951
  if (major === DRIVER_PROTOCOL[0]) return void 0;
@@ -4991,6 +5047,19 @@ var DriverConnection = class _DriverConnection {
4991
5047
  };
4992
5048
 
4993
5049
  // src/mount/mount-fs.ts
5050
+ var S_ISGID3 = 1024;
5051
+ var ID = /^\d{1,10}$/;
5052
+ var MAX_ID2 = 4294967294;
5053
+ function idOption(value) {
5054
+ return value === void 0 ? 0 : Number(value);
5055
+ }
5056
+ function checkIds(options) {
5057
+ for (const key of ["uid", "gid"]) {
5058
+ const value = options[key];
5059
+ if (value === void 0) continue;
5060
+ if (!ID.test(value) || Number(value) > MAX_ID2) throw errnoError("EINVAL", `${key}=${value}`);
5061
+ }
5062
+ }
4994
5063
  var MAX_LINKS5 = 40;
4995
5064
  var STALE_RETRIES = 3;
4996
5065
  var SETTLE_ROUNDS = 4;
@@ -5157,31 +5226,35 @@ var HeldPaths = class extends Map {
5157
5226
  };
5158
5227
  var parentOf6 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
5159
5228
  var readonly = (mount) => mount.caps.readonly === true || mount.options.ro !== void 0;
5160
- function inodeless(mount, attr) {
5161
- if (attr.ino !== void 0) return false;
5229
+ function inodeless(mount, attr2) {
5230
+ if (attr2.ino !== void 0) return false;
5162
5231
  if (mount.caps.links) mount.caps = { ...mount.caps, links: false };
5163
5232
  return true;
5164
5233
  }
5165
- function statOf(attr, path, mount) {
5166
- const mtime = new Date(attr.mtime);
5167
- const fallback = attr.kind === "directory" ? 16877 : attr.kind === "symlink" ? 41471 : 33188;
5234
+ function statOf(attr2, path, mount) {
5235
+ const mtime = new Date(attr2.mtime);
5236
+ const fallback = attr2.kind === "directory" ? 16877 : attr2.kind === "symlink" ? 41471 : 33188;
5168
5237
  return {
5169
- isFile: attr.kind === "file",
5170
- isDirectory: attr.kind === "directory",
5171
- isSymbolicLink: attr.kind === "symlink",
5172
- size: attr.size,
5173
- mode: attr.mode ?? fallback,
5238
+ isFile: attr2.kind === "file",
5239
+ isDirectory: attr2.kind === "directory",
5240
+ isSymbolicLink: attr2.kind === "symlink",
5241
+ size: attr2.size,
5242
+ mode: attr2.mode ?? fallback,
5174
5243
  mtime,
5175
5244
  atime: mtime,
5176
5245
  ctime: mtime,
5177
- ino: attr.ino ?? inodeOf(path),
5178
- ...!inodeless(mount, attr) && attr.nlink !== void 0 ? { nlink: attr.nlink } : {},
5246
+ ino: attr2.ino ?? inodeOf(path),
5247
+ ...!inodeless(mount, attr2) && attr2.nlink !== void 0 ? { nlink: attr2.nlink } : {},
5179
5248
  dev: mount.dev,
5180
5249
  ...readonly(mount) ? { readonly: true } : {},
5181
5250
  ...mount.maxFile > 0 ? { maxFile: mount.maxFile } : {},
5182
5251
  ...mount.caps.ranges ? { ranged: true } : {},
5183
5252
  ...mount.caps.links ? { linkable: true } : {},
5184
- ...attr.etag !== void 0 ? { version: attr.etag } : {}
5253
+ ...attr2.etag !== void 0 ? { version: attr2.etag } : {},
5254
+ ...mount.owners ? {
5255
+ uid: (mount.caps.owners ? attr2.uid : void 0) ?? idOption(mount.options.uid),
5256
+ gid: (mount.caps.owners ? attr2.gid : void 0) ?? idOption(mount.options.gid)
5257
+ } : {}
5185
5258
  };
5186
5259
  }
5187
5260
  function concat3(chunks2, total) {
@@ -5274,6 +5347,7 @@ var MountTable = class {
5274
5347
  const st = await fs.stat(target);
5275
5348
  if (!st.isDirectory) throw errnoError("ENOTDIR", target);
5276
5349
  const options = { ...spec.options };
5350
+ if (this.deps.owners) checkIds(options);
5277
5351
  const maxFile = options.maxfile !== void 0 ? parseSize(options.maxfile) : void 0;
5278
5352
  const opened2 = await this.deps.open(spec.type, { ...spec, target, options });
5279
5353
  let mounted;
@@ -5319,6 +5393,7 @@ var MountTable = class {
5319
5393
  attrs: /* @__PURE__ */ new Map(),
5320
5394
  lists: /* @__PURE__ */ new Map(),
5321
5395
  epoch: 0,
5396
+ owners: this.deps.owners === true,
5322
5397
  dispose: () => opened2.dispose(),
5323
5398
  ...opened2.present ? { present: opened2.present } : {}
5324
5399
  };
@@ -5440,15 +5515,15 @@ var MountTable = class {
5440
5515
  if (cached && cached.expires > Date.now()) return cached.value;
5441
5516
  const seen = mount.epoch;
5442
5517
  const session = this.sessions.get(path);
5443
- const attr = await mount.conn.call({ op: "getattr", path: rel2 });
5518
+ const attr2 = await mount.conn.call({ op: "getattr", path: rel2 });
5444
5519
  if (mount.epoch !== seen) {
5445
5520
  const moved2 = this.sessions.get(path) !== session || this.committing.has(path);
5446
5521
  if (settled && moved2 && round < SETTLE_ROUNDS) continue;
5447
- return attr;
5522
+ return attr2;
5448
5523
  }
5449
5524
  const ttl = mount.caps.attrTtl ?? 1e3;
5450
- if (ttl > 0) mount.attrs.set(path, { value: attr, expires: Date.now() + ttl });
5451
- return attr;
5525
+ if (ttl > 0) mount.attrs.set(path, { value: attr2, expires: Date.now() + ttl });
5526
+ return attr2;
5452
5527
  }
5453
5528
  }
5454
5529
  async readdir(mount, path, rel2) {
@@ -5495,28 +5570,43 @@ var MountFs = class {
5495
5570
  const found = this.at(path);
5496
5571
  if (!found) return path;
5497
5572
  const real = normalizePath(path);
5498
- let attr;
5573
+ let attr2;
5499
5574
  try {
5500
- attr = await this.table.getattr(found.mount, real, found.rel);
5575
+ attr2 = await this.table.getattr(found.mount, real, found.rel);
5501
5576
  } catch (err) {
5502
5577
  if (create && err.code === "ENOENT") return real;
5503
5578
  throw err;
5504
5579
  }
5505
- if (attr.kind !== "symlink") return real;
5580
+ if (attr2.kind !== "symlink") return real;
5506
5581
  if (hops >= MAX_LINKS5) throw errnoError("ELOOP", path);
5507
5582
  const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
5508
5583
  return this.follow(this.base.resolvePath(parentOf6(real), target), hops + 1, create);
5509
5584
  }
5510
- async open(mount, rel2, write) {
5585
+ async open(mount, rel2, write, owner) {
5511
5586
  return await mount.conn.call({
5512
5587
  op: "open",
5513
5588
  path: rel2,
5514
5589
  write,
5515
5590
  create: write,
5516
5591
  truncate: write,
5517
- exclusive: false
5592
+ exclusive: false,
5593
+ ...owner
5518
5594
  });
5519
5595
  }
5596
+ async driverOwner(found, real, owner, directory) {
5597
+ const { mount, rel: rel2 } = found;
5598
+ if (!owner || !mount.owners || !mount.caps.owners) return void 0;
5599
+ const dir = parentOf6(real);
5600
+ const above = statOf(await this.table.getattr(mount, dir, parentOf6(rel2)), dir, mount);
5601
+ const setgid = (above.mode & S_ISGID3) !== 0;
5602
+ let mode = owner.mode;
5603
+ if (directory && setgid) mode = (mode ?? 493) | S_ISGID3;
5604
+ return {
5605
+ uid: owner.uid,
5606
+ gid: setgid ? above.gid : owner.gid,
5607
+ ...mode !== void 0 ? { mode } : {}
5608
+ };
5609
+ }
5520
5610
  resolvePath(base, path) {
5521
5611
  return this.base.resolvePath(base, path);
5522
5612
  }
@@ -5538,9 +5628,9 @@ var MountFs = class {
5538
5628
  }
5539
5629
  async readWhole(mount, real, rel2) {
5540
5630
  await this.table.commit(real);
5541
- const attr = await this.table.getattr(mount, real, rel2);
5542
- if (attr.kind === "directory") throw errnoError("EISDIR", real);
5543
- if (mount.maxFile > 0 && attr.size > mount.maxFile) {
5631
+ const attr2 = await this.table.getattr(mount, real, rel2);
5632
+ if (attr2.kind === "directory") throw errnoError("EISDIR", real);
5633
+ if (mount.maxFile > 0 && attr2.size > mount.maxFile) {
5544
5634
  throw errnoError("EFBIG", `${real} is larger than this mount's maxfile (${mount.maxFile})`);
5545
5635
  }
5546
5636
  const fh = await this.open(mount, rel2, false);
@@ -5558,7 +5648,7 @@ var MountFs = class {
5558
5648
  if (bytes.length === 0) break;
5559
5649
  chunks2.push(bytes);
5560
5650
  total += bytes.length;
5561
- if (bytes.length < chunk2 && total >= attr.size) break;
5651
+ if (bytes.length < chunk2 && total >= attr2.size) break;
5562
5652
  }
5563
5653
  } finally {
5564
5654
  await mount.conn.call({ op: "release", fh });
@@ -5578,18 +5668,19 @@ var MountFs = class {
5578
5668
  `${real} would be larger than this mount's maxfile (${mount.maxFile})`
5579
5669
  );
5580
5670
  }
5671
+ const made2 = await this.driverOwner(found, real, owner, false);
5581
5672
  for (let attempt = 1; ; attempt++) {
5582
5673
  try {
5583
- return await this.writeWhole(mount, real, rel2, bytes);
5674
+ return await this.writeWhole(mount, real, rel2, bytes, made2);
5584
5675
  } catch (err) {
5585
5676
  if (err.code !== "ESTALE" || attempt >= STALE_RETRIES) throw err;
5586
5677
  }
5587
5678
  }
5588
5679
  }
5589
- async writeWhole(mount, real, rel2, bytes) {
5680
+ async writeWhole(mount, real, rel2, bytes, owner) {
5590
5681
  await this.table.commit(real);
5591
5682
  this.table.forget(mount, real);
5592
- const fh = await this.open(mount, rel2, true);
5683
+ const fh = await this.open(mount, rel2, true, owner);
5593
5684
  const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
5594
5685
  try {
5595
5686
  for (let offset = 0; offset < bytes.length; offset += chunk2) {
@@ -5735,9 +5826,9 @@ var MountFs = class {
5735
5826
  found.mount
5736
5827
  );
5737
5828
  }
5738
- statWriting(attr, real, mount) {
5829
+ statWriting(attr2, real, mount) {
5739
5830
  const ends = (this.table.writing(real)?.extents ?? []).map(([, end]) => end);
5740
- return statOf({ ...attr, size: Math.max(attr.size, ...ends) }, real, mount);
5831
+ return statOf({ ...attr2, size: Math.max(attr2.size, ...ends) }, real, mount);
5741
5832
  }
5742
5833
  async stat(path) {
5743
5834
  const real = await this.follow(path);
@@ -5776,10 +5867,12 @@ var MountFs = class {
5776
5867
  const { mount, rel: rel2 } = found;
5777
5868
  const real = normalizePath(path);
5778
5869
  this.writable(mount, real);
5870
+ const { owner } = options;
5779
5871
  if (!options.recursive) {
5780
5872
  if (rel2 === "/") throw errnoError("EEXIST", real);
5873
+ const made2 = await this.driverOwner(found, real, owner, true);
5781
5874
  this.table.forget(mount, real);
5782
- await mount.conn.call({ op: "mkdir", path: rel2 });
5875
+ await mount.conn.call({ op: "mkdir", path: rel2, ...made2 });
5783
5876
  return;
5784
5877
  }
5785
5878
  const parts = rel2.split("/").filter(Boolean);
@@ -5789,8 +5882,10 @@ var MountFs = class {
5789
5882
  const exists = await this.table.getattr(mount, full, sub).catch(() => void 0);
5790
5883
  if (exists?.kind === "directory") continue;
5791
5884
  if (exists) throw errnoError("ENOTDIR", full);
5885
+ const own = n === parts.length ? owner : owner && { ...owner, mode: owner.parents };
5886
+ const made2 = await this.driverOwner({ mount, rel: sub }, full, own, true);
5792
5887
  this.table.forget(mount, full);
5793
- await mount.conn.call({ op: "mkdir", path: sub });
5888
+ await mount.conn.call({ op: "mkdir", path: sub, ...made2 });
5794
5889
  }
5795
5890
  }
5796
5891
  async rm(path, options = {}) {
@@ -5801,21 +5896,21 @@ var MountFs = class {
5801
5896
  if (rel2 === "/") throw errnoError("EBUSY", `${real} is a mount point`);
5802
5897
  this.writable(mount, real);
5803
5898
  await this.table.commitUnder(real);
5804
- let attr;
5899
+ let attr2;
5805
5900
  try {
5806
- attr = await this.table.getattr(mount, real, rel2);
5901
+ attr2 = await this.table.getattr(mount, real, rel2);
5807
5902
  } catch (err) {
5808
5903
  if (options.force && err.code === "ENOENT") return;
5809
5904
  throw err;
5810
5905
  }
5811
5906
  this.table.forget(mount, real);
5812
- if (attr.kind === "directory" && options.recursive) {
5907
+ if (attr2.kind === "directory" && options.recursive) {
5813
5908
  for (const entry of await this.table.readdir(mount, real, rel2)) {
5814
5909
  await this.rm(`${real}/${entry.name}`, { recursive: true, force: options.force ?? false });
5815
5910
  }
5816
5911
  this.table.forget(mount, real);
5817
5912
  }
5818
- await mount.conn.call({ op: attr.kind === "directory" ? "rmdir" : "unlink", path: rel2 });
5913
+ await mount.conn.call({ op: attr2.kind === "directory" ? "rmdir" : "unlink", path: rel2 });
5819
5914
  this.table.forget(mount, real);
5820
5915
  }
5821
5916
  async rename(from, to) {
@@ -5869,8 +5964,10 @@ var MountFs = class {
5869
5964
  this.writable(found.mount, normalizePath(path));
5870
5965
  if (!found.mount.caps.symlinks)
5871
5966
  throw errnoError("EPERM", `${path}: this file system has no symlinks`);
5967
+ const link2 = owner && { uid: owner.uid, gid: owner.gid };
5968
+ const made2 = await this.driverOwner(found, normalizePath(path), link2, false);
5872
5969
  this.table.forget(found.mount, normalizePath(path));
5873
- await found.mount.conn.call({ op: "symlink", target, path: found.rel });
5970
+ await found.mount.conn.call({ op: "symlink", target, path: found.rel, ...made2 });
5874
5971
  }
5875
5972
  async realpath(path, follow2) {
5876
5973
  if (this.at(path) || !this.base.realpath) throw errnoError("ENOSYS", path);
@@ -5885,13 +5982,29 @@ var MountFs = class {
5885
5982
  }
5886
5983
  async chown(path, change) {
5887
5984
  const real = change.follow === false || change.kind === "new" ? path : await this.follow(path);
5888
- if (this.at(real)) {
5889
- if (change.kind === "new") return;
5890
- throw errnoError("EPERM", `${path}: this file system keeps no owners`);
5891
- }
5985
+ const found = this.at(real);
5986
+ if (found) return this.chownMounted(found, normalizePath(real), change);
5892
5987
  if (!this.base.chown) throw errnoError("EPERM", path);
5893
5988
  return this.base.chown(real, change);
5894
5989
  }
5990
+ async chownMounted(found, real, change) {
5991
+ const { mount, rel: rel2 } = found;
5992
+ if (!mount.owners || !mount.caps.owners) {
5993
+ if (change.kind === "new") return;
5994
+ throw errnoError("EPERM", `${real}: this file system keeps no owners`);
5995
+ }
5996
+ this.writable(mount, real);
5997
+ const { uid, gid, mode } = change;
5998
+ const fresh = change.kind === "new" && { uid, gid, mode };
5999
+ const set2 = fresh ? await this.driverOwner(found, real, fresh, false) : change;
6000
+ this.table.forget(mount, real);
6001
+ const attrs = Object.entries({ uid: set2.uid, gid: set2.gid, mode: set2.mode });
6002
+ await mount.conn.call({
6003
+ op: "setattr",
6004
+ path: rel2,
6005
+ ...Object.fromEntries(attrs.filter(([, value]) => value !== void 0))
6006
+ });
6007
+ }
5895
6008
  async chmod(path, mode) {
5896
6009
  const found = this.at(path);
5897
6010
  if (!found) return this.base.chmod(path, mode);
@@ -5998,7 +6111,10 @@ function mkdirOptions(entry, mode = 511) {
5998
6111
  }
5999
6112
  async function chown(entry, path, req) {
6000
6113
  const cred = entry.cred?.();
6001
- if (!cred) return done;
6114
+ if (!cred) {
6115
+ await (req.op === "chown" ? entry.fs.stat(path) : entry.fs.lstat(path));
6116
+ return done;
6117
+ }
6002
6118
  await chownAs(entry.fs, cred, path, {
6003
6119
  ...req.uid !== void 0 ? { uid: req.uid } : {},
6004
6120
  ...req.gid !== void 0 ? { gid: req.gid } : {},
@@ -8301,7 +8417,7 @@ function enableNetwork(net, options) {
8301
8417
  let proxy;
8302
8418
  const transport = options.transport ?? missingTransport();
8303
8419
  net.activate({ family: "inet", host: "127.0.0.1", port: REALM_PROXY_PORT }, () => {
8304
- const tls = new TlsTerminator({
8420
+ const tls2 = new TlsTerminator({
8305
8421
  ca: options.ca,
8306
8422
  engine: options.engine,
8307
8423
  ...options.now ? { now: options.now } : {}
@@ -8309,7 +8425,7 @@ function enableNetwork(net, options) {
8309
8425
  const started = new RealmProxy({
8310
8426
  net,
8311
8427
  transport,
8312
- tunnel: tls.handler,
8428
+ tunnel: tls2.handler,
8313
8429
  tunnelReady: async () => {
8314
8430
  await options.engine();
8315
8431
  }
@@ -8317,7 +8433,7 @@ function enableNetwork(net, options) {
8317
8433
  proxy = started;
8318
8434
  void started.closed.then(async () => {
8319
8435
  if (proxy === started) proxy = void 0;
8320
- await tls.close();
8436
+ await tls2.close();
8321
8437
  });
8322
8438
  });
8323
8439
  return () => proxy;
@@ -8336,6 +8452,11 @@ var TOKEN3 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
8336
8452
  function message(e) {
8337
8453
  return e instanceof Error ? e.message : String(e);
8338
8454
  }
8455
+ var ERRNO = /^E[A-Z]+$|^EAI_[A-Z]+$/;
8456
+ function errnoOf(e) {
8457
+ const code = e?.code;
8458
+ return typeof code === "string" && ERRNO.test(code) ? code : "ECONNREFUSED";
8459
+ }
8339
8460
  function fail(errno, text) {
8340
8461
  return { ok: false, errno, message: text };
8341
8462
  }
@@ -8379,13 +8500,13 @@ var WsHandles = class {
8379
8500
  let socket;
8380
8501
  try {
8381
8502
  socket = await transport.websocket({
8382
- url: req.url,
8503
+ url: toHostLoopback(req.url),
8383
8504
  protocols: req.protocols,
8384
8505
  headers: req.headers,
8385
8506
  signal: entry.abort.signal
8386
8507
  });
8387
8508
  } catch (e) {
8388
- return fail("ECONNREFUSED", message(e));
8509
+ return fail(errnoOf(e), message(e));
8389
8510
  }
8390
8511
  if (!this.open.has(handle)) {
8391
8512
  socket.close(1001, "the program went away");
@@ -8740,14 +8861,14 @@ var Resolver = class {
8740
8861
  if (hit && hit.expires > this.now()) return hit.addresses;
8741
8862
  const signal = blocking?.();
8742
8863
  if (signal?.aborted) throw new KernelError("EINTR");
8743
- let lookup = this.pending.get(id2);
8744
- if (!lookup) {
8864
+ let lookup2 = this.pending.get(id2);
8865
+ if (!lookup2) {
8745
8866
  const abort = new AbortController();
8746
- lookup = { work: this.lookup(key, asked, id2, abort.signal), abort, waiters: 0 };
8747
- this.pending.set(id2, lookup);
8748
- void lookup.work.then(() => this.pending.delete(id2));
8867
+ lookup2 = { work: this.lookup(key, asked, id2, abort.signal), abort, waiters: 0 };
8868
+ this.pending.set(id2, lookup2);
8869
+ void lookup2.work.then(() => this.pending.delete(id2));
8749
8870
  }
8750
- return waitFor(lookup, signal);
8871
+ return waitFor(lookup2, signal);
8751
8872
  }
8752
8873
  async lookup(name2, family, id2, abandoned) {
8753
8874
  const uplink = this.uplink;
@@ -8788,17 +8909,17 @@ var Resolver = class {
8788
8909
  }
8789
8910
  }
8790
8911
  };
8791
- function waitFor(lookup, signal) {
8792
- lookup.waiters++;
8912
+ function waitFor(lookup2, signal) {
8913
+ lookup2.waiters++;
8793
8914
  return new Promise((resolve, reject) => {
8794
8915
  const abort = () => {
8795
- if (--lookup.waiters === 0) lookup.abort.abort(new KernelError("EINTR"));
8916
+ if (--lookup2.waiters === 0) lookup2.abort.abort(new KernelError("EINTR"));
8796
8917
  reject(new KernelError("EINTR"));
8797
8918
  };
8798
8919
  signal?.addEventListener("abort", abort, { once: true });
8799
- void lookup.work.then((value) => {
8920
+ void lookup2.work.then((value) => {
8800
8921
  if (signal?.aborted) return;
8801
- lookup.waiters--;
8922
+ lookup2.waiters--;
8802
8923
  signal?.removeEventListener("abort", abort);
8803
8924
  resolve(value);
8804
8925
  });
@@ -11170,6 +11291,7 @@ function spawnWasmProcess(opts) {
11170
11291
  cwd: opts.cwd,
11171
11292
  sab: tsab,
11172
11293
  cred: copyCred(process2.cred),
11294
+ owners: opts.owners === true,
11173
11295
  ...opts.ppid !== void 0 ? { ppid: opts.ppid } : {},
11174
11296
  thread
11175
11297
  };
@@ -11563,6 +11685,17 @@ var SettlingChildren = class {
11563
11685
  };
11564
11686
 
11565
11687
  // src/mount/driver.ts
11688
+ function ownerOf2(req) {
11689
+ const owner = {
11690
+ ...req.uid !== void 0 ? { uid: req.uid } : {},
11691
+ ...req.gid !== void 0 ? { gid: req.gid } : {},
11692
+ ...req.mode !== void 0 ? { mode: req.mode } : {}
11693
+ };
11694
+ return Object.keys(owner).length > 0 ? owner : void 0;
11695
+ }
11696
+ function withOwner(base, owner) {
11697
+ return owner ? { ...base, owner } : base;
11698
+ }
11566
11699
  function fsError2(code, message3 = code) {
11567
11700
  return Object.assign(new Error(message3), { code });
11568
11701
  }
@@ -11573,13 +11706,19 @@ async function dispatch(h, req) {
11573
11706
  case "readdir":
11574
11707
  return h.readdir(req.path);
11575
11708
  case "open":
11576
- return h.open(req.path, {
11577
- write: req.write,
11578
- create: req.create,
11579
- truncate: req.truncate,
11580
- exclusive: req.exclusive,
11581
- ...req.ifMatch !== void 0 ? { ifMatch: req.ifMatch } : {}
11582
- });
11709
+ return h.open(
11710
+ req.path,
11711
+ withOwner(
11712
+ {
11713
+ write: req.write,
11714
+ create: req.create,
11715
+ truncate: req.truncate,
11716
+ exclusive: req.exclusive,
11717
+ ...req.ifMatch !== void 0 ? { ifMatch: req.ifMatch } : {}
11718
+ },
11719
+ ownerOf2(req)
11720
+ )
11721
+ );
11583
11722
  case "read":
11584
11723
  return h.read(req.fh, req.offset, req.size);
11585
11724
  case "write":
@@ -11587,7 +11726,7 @@ async function dispatch(h, req) {
11587
11726
  case "release":
11588
11727
  return h.release(req.fh);
11589
11728
  case "mkdir":
11590
- return h.mkdir(req.path);
11729
+ return ownerOf2(req) ? h.mkdir(req.path, ownerOf2(req)) : h.mkdir(req.path);
11591
11730
  case "rmdir":
11592
11731
  return h.rmdir(req.path);
11593
11732
  case "unlink":
@@ -11599,7 +11738,7 @@ async function dispatch(h, req) {
11599
11738
  return h.link(req.from, req.to);
11600
11739
  case "symlink":
11601
11740
  if (!h.symlink) throw fsError2("EPERM", "this file system has no symlinks");
11602
- return h.symlink(req.target, req.path);
11741
+ return ownerOf2(req) ? h.symlink(req.target, req.path, ownerOf2(req)) : h.symlink(req.target, req.path);
11603
11742
  case "readlink":
11604
11743
  if (!h.readlink) throw fsError2("EINVAL", `${req.path} is not a symlink`);
11605
11744
  return h.readlink(req.path);
@@ -11607,7 +11746,9 @@ async function dispatch(h, req) {
11607
11746
  return h.setattr?.(req.path, {
11608
11747
  ...req.mode !== void 0 ? { mode: req.mode } : {},
11609
11748
  ...req.mtime !== void 0 ? { mtime: req.mtime } : {},
11610
- ...req.size !== void 0 ? { size: req.size } : {}
11749
+ ...req.size !== void 0 ? { size: req.size } : {},
11750
+ ...req.uid !== void 0 ? { uid: req.uid } : {},
11751
+ ...req.gid !== void 0 ? { gid: req.gid } : {}
11611
11752
  });
11612
11753
  case "statfs":
11613
11754
  return h.statfs?.() ?? null;
@@ -11758,8 +11899,8 @@ function kernelFsHandlers(fs) {
11758
11899
  getattr: async (path) => attrOf(await fs.lstat(path)),
11759
11900
  readdir: async (path) => (await fs.readdirStat(path)).flatMap(([name2, st]) => {
11760
11901
  if (!st) return [];
11761
- const attr = attrOf(st);
11762
- return [{ name: name2, kind: attr.kind, attr }];
11902
+ const attr2 = attrOf(st);
11903
+ return [{ name: name2, kind: attr2.kind, attr: attr2 }];
11763
11904
  }),
11764
11905
  async open(path, flags) {
11765
11906
  const st = await existing(path);
@@ -12189,7 +12330,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
12189
12330
  "list",
12190
12331
  { path: rel(path) }
12191
12332
  );
12192
- return entries.map(({ name: name2, attr }) => ({ name: name2, kind: attr.kind, attr }));
12333
+ return entries.map(({ name: name2, attr: attr2 }) => ({ name: name2, kind: attr2.kind, attr: attr2 }));
12193
12334
  },
12194
12335
  async open(path, flags) {
12195
12336
  const client = current();
@@ -12782,8 +12923,8 @@ var ChunkedBytes = class {
12782
12923
  }
12783
12924
  return out;
12784
12925
  }
12785
- write(offset, bytes, owned2 = false) {
12786
- const whole = owned2 && bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength;
12926
+ write(offset, bytes, owned3 = false) {
12927
+ const whole = owned3 && bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength;
12787
12928
  for (let done4 = 0; done4 < bytes.length; ) {
12788
12929
  const at = offset + done4;
12789
12930
  const index = Math.floor(at / CHUNK3);
@@ -12816,18 +12957,42 @@ var ChunkedBytes = class {
12816
12957
  };
12817
12958
 
12818
12959
  // src/mount/tmpfs.ts
12819
- function tmpfs() {
12820
- let inodes = 0;
12821
- const make = (kind, mode) => ({
12960
+ function idOption2(value) {
12961
+ return value !== void 0 && /^\d+$/.test(value) ? Number(value) : 0;
12962
+ }
12963
+ function created2(ino, kind, mode, from, owner) {
12964
+ return {
12822
12965
  kind,
12823
- ino: ++inodes,
12824
- mode,
12966
+ ino,
12967
+ mode: owner?.mode === void 0 ? mode : mode & ~4095 | owner.mode & 4095,
12825
12968
  mtime: Date.now(),
12826
12969
  data: new ChunkedBytes(),
12827
12970
  target: "",
12828
12971
  children: /* @__PURE__ */ new Map(),
12829
- links: 1
12830
- });
12972
+ links: 1,
12973
+ uid: owner?.uid ?? from?.uid ?? 0,
12974
+ gid: owner?.gid ?? from?.gid ?? 0
12975
+ };
12976
+ }
12977
+ function owned2(node, change) {
12978
+ if (change.uid !== void 0) node.uid = change.uid;
12979
+ if (change.gid !== void 0) node.gid = change.gid;
12980
+ }
12981
+ function attr(node) {
12982
+ return {
12983
+ kind: node.kind,
12984
+ size: node.kind === "file" ? node.data.size : node.kind === "symlink" ? node.target.length : 4096,
12985
+ mtime: node.mtime,
12986
+ mode: node.mode,
12987
+ ino: node.ino,
12988
+ ...node.kind === "directory" ? {} : { nlink: node.links },
12989
+ uid: node.uid,
12990
+ gid: node.gid
12991
+ };
12992
+ }
12993
+ function tmpfs() {
12994
+ let inodes = 0;
12995
+ const make = (kind, mode, from, owner) => created2(++inodes, kind, mode, from, owner);
12831
12996
  const root = make("directory", 16877);
12832
12997
  const handles = /* @__PURE__ */ new Map();
12833
12998
  let nextFh = 0;
@@ -12854,14 +13019,6 @@ function tmpfs() {
12854
13019
  if (dir.kind !== "directory") throw fsError2("ENOTDIR", path);
12855
13020
  return { dir, name: name2 };
12856
13021
  }
12857
- const attr = (node) => ({
12858
- kind: node.kind,
12859
- size: node.kind === "file" ? node.data.size : node.kind === "symlink" ? node.target.length : 4096,
12860
- mtime: node.mtime,
12861
- mode: node.mode,
12862
- ino: node.ino,
12863
- ...node.kind === "directory" ? {} : { nlink: node.links }
12864
- });
12865
13022
  const handle = (fh) => {
12866
13023
  const found = handles.get(fh);
12867
13024
  if (!found) throw fsError2("EBADF", `handle ${fh}`);
@@ -12874,11 +13031,16 @@ function tmpfs() {
12874
13031
  if (existing?.kind === "directory" && flags.write) throw fsError2("EISDIR", path);
12875
13032
  if (existing) return existing;
12876
13033
  if (!flags.create) throw fsError2("ENOENT", path);
12877
- const node = make("file", 33188);
13034
+ const node = make("file", 33188, dir, flags.owner);
12878
13035
  place(dir, name2, node);
12879
13036
  return node;
12880
13037
  }
12881
13038
  return {
13039
+ mount(info) {
13040
+ root.uid = idOption2(info.options.uid);
13041
+ root.gid = idOption2(info.options.gid);
13042
+ return void 0;
13043
+ },
12882
13044
  async getattr(path) {
12883
13045
  return attr(find(path));
12884
13046
  },
@@ -12911,10 +13073,10 @@ function tmpfs() {
12911
13073
  handle(fh);
12912
13074
  handles.delete(fh);
12913
13075
  },
12914
- async mkdir(path) {
13076
+ async mkdir(path, owner) {
12915
13077
  const { dir, name: name2 } = parent(path);
12916
13078
  if (dir.children.has(name2)) throw fsError2("EEXIST", path);
12917
- place(dir, name2, make("directory", 16877));
13079
+ place(dir, name2, make("directory", 16877, dir, owner));
12918
13080
  },
12919
13081
  async rmdir(path) {
12920
13082
  const { dir, name: name2 } = parent(path);
@@ -12961,10 +13123,10 @@ function tmpfs() {
12961
13123
  place(dir, name2, node);
12962
13124
  node.links++;
12963
13125
  },
12964
- async symlink(target, path) {
13126
+ async symlink(target, path, owner) {
12965
13127
  const { dir, name: name2 } = parent(path);
12966
13128
  if (dir.children.has(name2)) throw fsError2("EEXIST", path);
12967
- const node = make("symlink", 41471);
13129
+ const node = make("symlink", 41471, dir, owner && { ...owner, mode: 511 });
12968
13130
  node.target = target;
12969
13131
  place(dir, name2, node);
12970
13132
  },
@@ -12983,6 +13145,7 @@ function tmpfs() {
12983
13145
  node.mtime = Date.now();
12984
13146
  }
12985
13147
  if (change.mtime !== void 0) node.mtime = change.mtime;
13148
+ owned2(node, change);
12986
13149
  },
12987
13150
  async statfs() {
12988
13151
  return { bsize: 4096, blocks: 262144, bfree: 262144 };
@@ -13303,7 +13466,8 @@ var Launcher = class {
13303
13466
  this.mounts = new MountTable({
13304
13467
  open: (type, spec) => this.driver(type, spec),
13305
13468
  busy: (target) => [...this.openFiles].some((nodes) => nodes.holds(target)) || heldUnder(this.held, target),
13306
- changed: this.watchers.changed.bind(this.watchers)
13469
+ changed: this.watchers.changed.bind(this.watchers),
13470
+ owners: this.owners
13307
13471
  });
13308
13472
  this.base = this.watchers.wrap(this.mounts.wrap(options.fs));
13309
13473
  this.createWorker = options.createWorker;
@@ -13336,7 +13500,8 @@ var Launcher = class {
13336
13500
  const fs = withCommandDirs(
13337
13501
  this.base,
13338
13502
  async () => new Set((await this.commands()).keys()),
13339
- (dir) => this.userCommandDir(dir)
13503
+ (dir) => this.userCommandDir(dir),
13504
+ this.owners
13340
13505
  );
13341
13506
  this.nodes = new VfsNodes(fs, (path) => this.released(path));
13342
13507
  this.nodes.keepsOnDisk = true;
@@ -13866,7 +14031,15 @@ var Launcher = class {
13866
14031
  serveFilesystem(
13867
14032
  port2,
13868
14033
  tmpfs(),
13869
- { symlinks: true, links: true, chmod: true, linkTimes: true, ranges: true, attrTtl: 0 },
14034
+ {
14035
+ symlinks: true,
14036
+ links: true,
14037
+ chmod: true,
14038
+ owners: true,
14039
+ linkTimes: true,
14040
+ ranges: true,
14041
+ attrTtl: 0
14042
+ },
13870
14043
  { owned: true }
13871
14044
  );
13872
14045
  return {
@@ -13939,6 +14112,11 @@ var Launcher = class {
13939
14112
  void this.unlinked.sweep(entry.target);
13940
14113
  return entry;
13941
14114
  }
14115
+ mountedBy(spec, cred) {
14116
+ if (!this.owners || cred.euid === 0 && cred.egid === 0) return spec;
14117
+ const ids = { uid: String(cred.euid), gid: String(cred.egid) };
14118
+ return { ...spec, options: { ...ids, ...spec.options } };
14119
+ }
13942
14120
  mountRoot(path) {
13943
14121
  let root = "/";
13944
14122
  for (const { target } of this.mounts.list()) {
@@ -13982,7 +14160,7 @@ var Launcher = class {
13982
14160
  async processMount(pid, call, signal) {
13983
14161
  const spec = mountCall(call);
13984
14162
  await this.permit({ op: "mount", pid, ...spec, options: { ...spec.options } });
13985
- const entry = await this.mount(spec);
14163
+ const entry = await this.mount(this.mountedBy(spec, this.credOf(pid)));
13986
14164
  if (signal.aborted) this.umount(entry.target, true);
13987
14165
  }
13988
14166
  async processUmount(pid, target, flags) {
@@ -14335,6 +14513,62 @@ function isMemoryRoot(handle) {
14335
14513
  return handle instanceof MemoryDirectory;
14336
14514
  }
14337
14515
 
14516
+ // src/node/ws-diagnose.ts
14517
+ import { lookup } from "node:dns/promises";
14518
+ import { isIP, connect as tcp } from "node:net";
14519
+ import { connect as tls } from "node:tls";
14520
+ var PROBE_MS = 3e3;
14521
+ var PASSED = /* @__PURE__ */ new Set(["ECONNREFUSED", "EHOSTUNREACH", "ENETUNREACH", "ETIMEDOUT", "ECONNRESET"]);
14522
+ var NO_NAME = /* @__PURE__ */ new Set(["ENOTFOUND", "EAI_AGAIN", "EAI_NONAME", "EAI_FAIL", "ENODATA"]);
14523
+ function codeOf5(e) {
14524
+ const code = e?.code;
14525
+ return typeof code === "string" ? code : void 0;
14526
+ }
14527
+ function probe(secure, host, port, ms) {
14528
+ return new Promise((resolve) => {
14529
+ const socket = secure ? tls({ host, port, ...isIP(host) ? {} : { servername: host } }) : tcp({ host, port });
14530
+ const done4 = (code) => {
14531
+ clearTimeout(timer);
14532
+ socket.destroy();
14533
+ resolve(code);
14534
+ };
14535
+ const timer = setTimeout(() => done4("ETIMEDOUT"), ms);
14536
+ socket.once(secure ? "secureConnect" : "connect", () => done4("EPROTO"));
14537
+ socket.once("error", (e) => {
14538
+ const code = codeOf5(e) ?? "";
14539
+ done4(PASSED.has(code) ? code : "EPROTO");
14540
+ });
14541
+ });
14542
+ }
14543
+ async function diagnose(url, ms = PROBE_MS) {
14544
+ const at = new URL(url);
14545
+ const host = at.hostname.replace(/^\[(.*)\]$/, "$1");
14546
+ const secure = at.protocol === "wss:";
14547
+ const port = Number(at.port) || (secure ? 443 : 80);
14548
+ try {
14549
+ await lookup(host);
14550
+ } catch (e) {
14551
+ return NO_NAME.has(codeOf5(e) ?? "") ? "EAI_NONAME" : void 0;
14552
+ }
14553
+ return probe(secure, host, port, ms);
14554
+ }
14555
+ function diagnosing(transport, check = diagnose) {
14556
+ const open = transport.websocket;
14557
+ if (!open) return transport;
14558
+ return {
14559
+ ...transport,
14560
+ websocket: async (request) => {
14561
+ try {
14562
+ return await open(request);
14563
+ } catch (e) {
14564
+ if (request.signal.aborted || !(e instanceof Error) || codeOf5(e)) throw e;
14565
+ const code = await check(request.url).catch(() => void 0);
14566
+ throw code ? Object.assign(e, { code }) : e;
14567
+ }
14568
+ }
14569
+ };
14570
+ }
14571
+
14338
14572
  // src/kernel/net/remote-transport.ts
14339
14573
  var TransportError = class extends Error {
14340
14574
  status;
@@ -15129,7 +15363,7 @@ function hostfsFetchOf(options) {
15129
15363
  }
15130
15364
  function nodeTransport() {
15131
15365
  const transport = fetchTransport({ webSocket: true, webSocketHeaders: true, redirect: "manual" });
15132
- return { ...transport, traits: { ...transport.traits, crossOrigin: "any" } };
15366
+ return diagnosing({ ...transport, traits: { ...transport.traits, crossOrigin: "any" } });
15133
15367
  }
15134
15368
  var TERMINATED = "the kernel is terminated";
15135
15369
  var EXIT_GRACE_MS = 50;