@ai-ecoverse/slicc-kernel 1.12.0 → 1.14.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.
@@ -194,7 +194,7 @@ function pinScript(url) {
194
194
  }
195
195
 
196
196
  // src/client/protocol.ts
197
- var PROTOCOL = [1, 1];
197
+ var PROTOCOL = [1, 2];
198
198
  var FS_METHODS = [
199
199
  "readFile",
200
200
  "writeFile",
@@ -447,6 +447,11 @@ async function fsCall(fs, method, args) {
447
447
  return { result: await fs.exists(path(0)) };
448
448
  }
449
449
  }
450
+ function signalOf(host) {
451
+ return host.signal ?? ((name2) => {
452
+ throw fsError("EINVAL", `this kernel names no signal ${name2}`);
453
+ });
454
+ }
450
455
  function serveClient(port, host) {
451
456
  let state = "new";
452
457
  const terminals = /* @__PURE__ */ new Map();
@@ -534,6 +539,9 @@ function serveClient(port, host) {
534
539
  return { result: result.status };
535
540
  }
536
541
  async function handle(req) {
542
+ if (host.scope === "transport" && req.op !== "detach") {
543
+ throw fsError("EPERM", `${req.op}: this client may only use the network transport`);
544
+ }
537
545
  const l = await host.launcher();
538
546
  switch (req.op) {
539
547
  case "spawn":
@@ -543,11 +551,11 @@ function serveClient(port, host) {
543
551
  case "terminal": {
544
552
  const session = terminals.get(req.terminal);
545
553
  if (!session) throw new Error(`no terminal ${req.terminal}`);
546
- act(session, req, host.signal);
554
+ act(session, req, signalOf(host));
547
555
  return { result: true };
548
556
  }
549
557
  case "kill":
550
- if (!l.kill(req.pid, host.signal(req.signal))) throw fsError("ESRCH", `pid ${req.pid}`);
558
+ if (!l.kill(req.pid, signalOf(host)(req.signal))) throw fsError("ESRCH", `pid ${req.pid}`);
551
559
  return { result: true };
552
560
  case "ps":
553
561
  return {
@@ -571,6 +579,13 @@ function serveClient(port, host) {
571
579
  watches.get(req.watch)?.();
572
580
  watches.delete(req.watch);
573
581
  return { result: true };
582
+ case "mount":
583
+ return { result: await l.mount(req.spec) };
584
+ case "umount":
585
+ l.umount(req.target);
586
+ return { result: true };
587
+ case "mounts":
588
+ return { result: l.mounts.list() };
574
589
  case "detach":
575
590
  return { result: true };
576
591
  default:
@@ -3007,9 +3022,28 @@ async function packages(fs, modules) {
3007
3022
  }
3008
3023
  return dirs;
3009
3024
  }
3025
+ async function pnpmGlobalRoots(fs, home) {
3026
+ const roots = [];
3027
+ const global = `${home}/global`;
3028
+ for (const version of (await fs.readdir(global).catch(() => [])).sort()) {
3029
+ for (const group of (await fs.readdir(`${global}/${version}`).catch(() => [])).sort()) {
3030
+ const dir = `${global}/${version}/${group}`;
3031
+ const st = await fs.lstat(dir).catch(() => void 0);
3032
+ if (st?.isDirectory && await fs.exists(`${dir}/node_modules`))
3033
+ roots.push(`${dir}/node_modules`);
3034
+ }
3035
+ }
3036
+ return roots;
3037
+ }
3038
+ async function packagesIn(fs, roots) {
3039
+ const dirs = [];
3040
+ for (const root of typeof roots === "string" ? [roots] : roots)
3041
+ dirs.push(...await packages(fs, root));
3042
+ return dirs;
3043
+ }
3010
3044
  async function scanCommands(fs, modules) {
3011
3045
  const found = /* @__PURE__ */ new Map();
3012
- for (const pkg of await packages(fs, modules)) {
3046
+ for (const pkg of await packagesIn(fs, modules)) {
3013
3047
  let manifest;
3014
3048
  try {
3015
3049
  manifest = JSON.parse(await fs.readFile(`${pkg}/package.json`));
@@ -3026,6 +3060,24 @@ async function scanCommands(fs, modules) {
3026
3060
  }
3027
3061
  return found;
3028
3062
  }
3063
+ async function scanFilesystems(fs, modules) {
3064
+ const found = /* @__PURE__ */ new Map();
3065
+ for (const pkg of await packagesIn(fs, modules)) {
3066
+ let manifest;
3067
+ try {
3068
+ manifest = JSON.parse(await fs.readFile(`${pkg}/package.json`));
3069
+ } catch {
3070
+ continue;
3071
+ }
3072
+ const declared = manifest.slicc?.filesystems;
3073
+ if (!declared || typeof declared !== "object") continue;
3074
+ for (const [type, entry] of Object.entries(declared)) {
3075
+ const module = inside(pkg, entry?.module);
3076
+ if (NAME.test(type) && module && !found.has(type)) found.set(type, module);
3077
+ }
3078
+ }
3079
+ return found;
3080
+ }
3029
3081
 
3030
3082
  // src/fs/commands.ts
3031
3083
  var S_IFDIR2 = 16384;
@@ -3444,7 +3496,8 @@ function statJson(s) {
3444
3496
  mtimeMs: s.mtime.getTime(),
3445
3497
  atimeMs: s.atime.getTime(),
3446
3498
  ctimeMs: s.ctime.getTime(),
3447
- ino: s.ino
3499
+ ino: s.ino,
3500
+ ...s.dev !== void 0 ? { dev: s.dev } : {}
3448
3501
  };
3449
3502
  }
3450
3503
  async function readdirStat(fs, dir) {
@@ -3505,8 +3558,6 @@ async function run(fs, path, req, cwd) {
3505
3558
  case "utimes":
3506
3559
  await fs.utimes(path, new Date(req.atimeMs ?? 0), new Date(req.mtimeMs ?? 0));
3507
3560
  return done;
3508
- case "statfs":
3509
- return json(await storageUsage());
3510
3561
  default:
3511
3562
  return { ok: false, errno: "EINVAL", message: `sync-fs: unknown op '${req.op}'` };
3512
3563
  }
@@ -3516,7 +3567,13 @@ async function dispatchSyncFs(req) {
3516
3567
  if (!entry) return { ok: false, errno: "EACCES", message: "sync-fs: unknown or revoked token" };
3517
3568
  const { fs, cwd } = entry;
3518
3569
  try {
3519
- return await run(fs, fs.resolvePath(cwd, req.path), req, cwd);
3570
+ const path = fs.resolvePath(cwd, req.path);
3571
+ if (req.op === "statfs") return json(await entry.statfs?.(path) ?? await storageUsage());
3572
+ if (req.op === "hold") {
3573
+ entry.hold?.(path, req.mode === 1);
3574
+ return done;
3575
+ }
3576
+ return await run(fs, path, req, cwd);
3520
3577
  } catch (err) {
3521
3578
  return toErrno(err);
3522
3579
  }
@@ -3580,7 +3637,7 @@ function attachSyncSabResponder(port, sab, token, opts) {
3580
3637
  const { header: header2, window } = views;
3581
3638
  const pending = /* @__PURE__ */ new Map();
3582
3639
  let disposed = false;
3583
- const { dispatch } = opts;
3640
+ const { dispatch: dispatch2 } = opts;
3584
3641
  function drop(id) {
3585
3642
  const entry = pending.get(id);
3586
3643
  if (!entry) return;
@@ -3635,7 +3692,7 @@ function attachSyncSabResponder(port, sab, token, opts) {
3635
3692
  const req = { ...body, token };
3636
3693
  let dispatched;
3637
3694
  try {
3638
- dispatched = dispatch(req);
3695
+ dispatched = dispatch2(req);
3639
3696
  } catch (err) {
3640
3697
  settle2(id, {
3641
3698
  ok: false,
@@ -4964,6 +5021,10 @@ var VfsNodes = class {
4964
5021
  node.opens++;
4965
5022
  return node;
4966
5023
  }
5024
+ holds(prefix) {
5025
+ for (const path of this.byPath.keys()) if (within2(path, prefix)) return true;
5026
+ return false;
5027
+ }
4967
5028
  closed(node) {
4968
5029
  node.opens--;
4969
5030
  if (node.opens === 0 && this.byPath.get(node.path) === node) this.byPath.delete(node.path);
@@ -5141,6 +5202,7 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
5141
5202
  "proc-fork",
5142
5203
  "proc-kill",
5143
5204
  "proc-list",
5205
+ "mount-list",
5144
5206
  "proc-exec",
5145
5207
  "proc-setpgid",
5146
5208
  "proc-getpgid",
@@ -5184,6 +5246,7 @@ var WasmProcess = class {
5184
5246
  this.options = options;
5185
5247
  this.children = new ChildTable(fds, options.spawner, options.forker);
5186
5248
  this.nodes = new VfsNodes(options.fs);
5249
+ options.openFiles?.add(this.nodes);
5187
5250
  this.children.onChildState = () => this.signal(SIG.CHLD);
5188
5251
  this.children.onReap = options.onReap;
5189
5252
  }
@@ -5595,6 +5658,8 @@ var WasmProcess = class {
5595
5658
  kind: "json",
5596
5659
  json: this.options.processes?.() ?? { boot: 0, processes: [] }
5597
5660
  };
5661
+ case "mount-list":
5662
+ return { ok: true, kind: "json", json: this.options.mounts?.() ?? [] };
5598
5663
  case "proc-alarm":
5599
5664
  this.setAlarm(req.sig, req.firstMs ?? req.ms, req.repeat ? req.ms : 0, req.timer);
5600
5665
  return { ok: true, kind: "void" };
@@ -5652,6 +5717,7 @@ var WasmProcess = class {
5652
5717
  this.asyncOps.close();
5653
5718
  for (const pid of this.children.pids()) this.options.onReap?.(pid);
5654
5719
  await this.fds.closeAll();
5720
+ this.options.openFiles?.delete(this.nodes);
5655
5721
  await this.options.http?.closeAll();
5656
5722
  }
5657
5723
  };
@@ -5699,6 +5765,8 @@ function spawnWasmProcess(opts) {
5699
5765
  fs: opts.fs,
5700
5766
  kill: opts.kill,
5701
5767
  processes: opts.processes,
5768
+ openFiles: opts.openFiles,
5769
+ mounts: opts.mounts,
5702
5770
  jobs: opts.jobs,
5703
5771
  ptys: opts.ptys,
5704
5772
  net: opts.net,
@@ -5711,13 +5779,24 @@ function spawnWasmProcess(opts) {
5711
5779
  raise: (sig) => signal(sig),
5712
5780
  ...opts.program.abi === "wasi" ? { pendingBits: () => Atomics.load(header2, SAB_I_SIGNALS) } : {}
5713
5781
  });
5714
- const token = mintSyncFsToken({ fs: opts.fs, cwd: opts.cwd });
5782
+ const holds = /* @__PURE__ */ new Map();
5783
+ opts.held?.add(holds);
5784
+ const token = mintSyncFsToken({
5785
+ fs: opts.fs,
5786
+ cwd: opts.cwd,
5787
+ ...opts.statfs ? { statfs: opts.statfs } : {},
5788
+ hold: (path, on) => {
5789
+ const count = (holds.get(path) ?? 0) + (on ? 1 : -1);
5790
+ if (count > 0) holds.set(path, count);
5791
+ else holds.delete(path);
5792
+ }
5793
+ });
5715
5794
  const worker = opts.createWorker();
5716
- const dispatch = async (req) => {
5795
+ const dispatch2 = async (req) => {
5717
5796
  if (isWasmSyscall(req)) return process.syscall(req);
5718
5797
  return dispatchSyncFs(req);
5719
5798
  };
5720
- const responder = attachSyncSabResponder(worker, sab, token, { dispatch });
5799
+ const responder = attachSyncSabResponder(worker, sab, token, { dispatch: dispatch2 });
5721
5800
  let settle2;
5722
5801
  const exited = new Promise((resolve) => settle2 = resolve);
5723
5802
  let done4 = false;
@@ -5731,6 +5810,7 @@ function spawnWasmProcess(opts) {
5731
5810
  for (const tid of [...threads.keys()]) endThread(tid);
5732
5811
  responder.dispose();
5733
5812
  revokeSyncFsToken(token);
5813
+ opts.held?.delete(holds);
5734
5814
  worker.terminate();
5735
5815
  void process.exit().then(
5736
5816
  () => settle2(code),
@@ -5770,7 +5850,7 @@ function spawnWasmProcess(opts) {
5770
5850
  }
5771
5851
  const tsab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
5772
5852
  const tw = opts.createWorker();
5773
- const tresponder = attachSyncSabResponder(tw, tsab, token, { dispatch });
5853
+ const tresponder = attachSyncSabResponder(tw, tsab, token, { dispatch: dispatch2 });
5774
5854
  const onThreadMessage = (event) => {
5775
5855
  const data = event.data;
5776
5856
  if (data?.type === WASM_THREAD_EXIT) endThread(thread.tid);
@@ -5936,6 +6016,815 @@ var JobTable = class {
5936
6016
  }
5937
6017
  };
5938
6018
 
6019
+ // src/mount/protocol.ts
6020
+ var DRIVER_PROTOCOL = [1, 0];
6021
+ function driverVersionError(theirs) {
6022
+ const major = Array.isArray(theirs) ? theirs[0] : void 0;
6023
+ if (major === DRIVER_PROTOCOL[0]) return void 0;
6024
+ const named = typeof major === "number" ? `${major}.x` : "without a version";
6025
+ return `slicc-kernel driver protocol ${named} is not supported: this side speaks ${DRIVER_PROTOCOL.join(".")}`;
6026
+ }
6027
+
6028
+ // src/mount/driver.ts
6029
+ function fsError2(code, message2 = code) {
6030
+ return Object.assign(new Error(message2), { code });
6031
+ }
6032
+ async function dispatch(h, req) {
6033
+ switch (req.op) {
6034
+ case "getattr":
6035
+ return h.getattr(req.path);
6036
+ case "readdir":
6037
+ return h.readdir(req.path);
6038
+ case "open":
6039
+ return h.open(req.path, {
6040
+ write: req.write,
6041
+ create: req.create,
6042
+ truncate: req.truncate,
6043
+ exclusive: req.exclusive
6044
+ });
6045
+ case "read":
6046
+ return h.read(req.fh, req.offset, req.size);
6047
+ case "write":
6048
+ return h.write(req.fh, req.offset, req.bytes);
6049
+ case "release":
6050
+ return h.release(req.fh);
6051
+ case "mkdir":
6052
+ return h.mkdir(req.path);
6053
+ case "rmdir":
6054
+ return h.rmdir(req.path);
6055
+ case "unlink":
6056
+ return h.unlink(req.path);
6057
+ case "rename":
6058
+ return h.rename(req.from, req.to);
6059
+ case "symlink":
6060
+ if (!h.symlink) throw fsError2("EPERM", "this file system has no symlinks");
6061
+ return h.symlink(req.target, req.path);
6062
+ case "readlink":
6063
+ if (!h.readlink) throw fsError2("EINVAL", `${req.path} is not a symlink`);
6064
+ return h.readlink(req.path);
6065
+ case "setattr":
6066
+ return h.setattr?.(req.path, {
6067
+ ...req.mode !== void 0 ? { mode: req.mode } : {},
6068
+ ...req.mtime !== void 0 ? { mtime: req.mtime } : {}
6069
+ });
6070
+ case "statfs":
6071
+ return h.statfs?.() ?? null;
6072
+ default:
6073
+ throw fsError2("ENOSYS", `unknown driver op ${req.op}`);
6074
+ }
6075
+ }
6076
+ function serveFilesystem(port, handlers, capabilities = {}) {
6077
+ let open = false;
6078
+ const reply = (message2, transfer = []) => port.postMessage(message2, transfer);
6079
+ async function hello(message2) {
6080
+ const refused = driverVersionError(message2.protocol);
6081
+ if (refused) {
6082
+ reply({ hello: { protocol: DRIVER_PROTOCOL, error: refused, errno: "EPROTO" } });
6083
+ return;
6084
+ }
6085
+ try {
6086
+ const refined = await handlers.mount?.(message2.mount);
6087
+ open = true;
6088
+ reply({
6089
+ hello: { protocol: DRIVER_PROTOCOL, capabilities: { ...capabilities, ...refined } }
6090
+ });
6091
+ } catch (err) {
6092
+ const code = err?.code;
6093
+ reply({
6094
+ hello: {
6095
+ protocol: DRIVER_PROTOCOL,
6096
+ error: err instanceof Error ? err.message : String(err),
6097
+ errno: typeof code === "string" ? code : "EIO"
6098
+ }
6099
+ });
6100
+ }
6101
+ }
6102
+ port.addEventListener("message", (event) => {
6103
+ const data = event.data;
6104
+ if (data.hello) {
6105
+ void hello(data.hello);
6106
+ return;
6107
+ }
6108
+ if (!open) return;
6109
+ dispatch(handlers, data).then(
6110
+ (result) => {
6111
+ if (result instanceof Uint8Array) {
6112
+ const bytes = result.slice();
6113
+ reply({ id: data.id, result: bytes }, [bytes.buffer]);
6114
+ } else reply({ id: data.id, result: result ?? null });
6115
+ },
6116
+ (err) => {
6117
+ const code = err?.code;
6118
+ reply({
6119
+ id: data.id,
6120
+ errno: typeof code === "string" ? code : "EIO",
6121
+ message: err instanceof Error ? err.message : String(err)
6122
+ });
6123
+ }
6124
+ );
6125
+ });
6126
+ port.start?.();
6127
+ return { invalidate: (paths) => reply({ invalidate: paths }) };
6128
+ }
6129
+
6130
+ // src/mount/connection.ts
6131
+ var DRIVER_TIMEOUT_MS = 3e4;
6132
+ function unref(timer) {
6133
+ timer.unref?.();
6134
+ return timer;
6135
+ }
6136
+ function errnoError(code, message2) {
6137
+ return Object.assign(new Error(`${code}: ${message2}`), { code });
6138
+ }
6139
+ var DriverConnection = class _DriverConnection {
6140
+ capabilities;
6141
+ failure;
6142
+ port;
6143
+ waiting;
6144
+ timeoutMs;
6145
+ nextId = 0;
6146
+ onFail;
6147
+ constructor(port, capabilities, waiting, timeoutMs, onFail) {
6148
+ this.port = port;
6149
+ this.capabilities = capabilities;
6150
+ this.waiting = waiting;
6151
+ this.timeoutMs = timeoutMs;
6152
+ this.onFail = onFail;
6153
+ }
6154
+ static async open(port, mount, options = {}) {
6155
+ const timeoutMs = options.timeoutMs ?? DRIVER_TIMEOUT_MS;
6156
+ const waiting = /* @__PURE__ */ new Map();
6157
+ const greeted = Promise.withResolvers();
6158
+ port.addEventListener("message", (event) => {
6159
+ const data = event.data;
6160
+ if (data.hello) return greeted.resolve(data.hello);
6161
+ if (data.invalidate !== void 0) return options.onInvalidate?.(data.invalidate);
6162
+ const call = waiting.get(data.id);
6163
+ if (!call) return;
6164
+ waiting.delete(data.id);
6165
+ clearTimeout(call.timer);
6166
+ if (data.errno) call.reject(errnoError(data.errno, data.message ?? data.errno));
6167
+ else call.resolve(data.result);
6168
+ });
6169
+ port.start?.();
6170
+ port.postMessage({ hello: { protocol: DRIVER_PROTOCOL, mount } });
6171
+ const timer = unref(
6172
+ setTimeout(
6173
+ () => greeted.reject(errnoError("EIO", `the driver did not answer within ${timeoutMs} ms`)),
6174
+ timeoutMs
6175
+ )
6176
+ );
6177
+ let hello;
6178
+ try {
6179
+ hello = await greeted.promise;
6180
+ } finally {
6181
+ clearTimeout(timer);
6182
+ }
6183
+ const refused = driverVersionError(hello.protocol);
6184
+ if (refused) throw errnoError("EPROTO", refused);
6185
+ if (hello.error) throw errnoError(hello.errno ?? "EIO", hello.error);
6186
+ return new _DriverConnection(port, hello.capabilities ?? {}, waiting, timeoutMs, options.onFail);
6187
+ }
6188
+ call(call, transfer = []) {
6189
+ if (this.failure) return Promise.reject(this.failure);
6190
+ const id = ++this.nextId;
6191
+ return new Promise((resolve, reject) => {
6192
+ const timer = unref(
6193
+ setTimeout(() => {
6194
+ this.waiting.delete(id);
6195
+ const error = errnoError(
6196
+ "EIO",
6197
+ `the driver did not answer ${call.op} within ${this.timeoutMs} ms`
6198
+ );
6199
+ reject(error);
6200
+ this.fail(error);
6201
+ this.onFail?.(error);
6202
+ }, this.timeoutMs)
6203
+ );
6204
+ this.waiting.set(id, { resolve, reject, timer });
6205
+ this.port.postMessage({ ...call, id }, transfer);
6206
+ });
6207
+ }
6208
+ fail(error) {
6209
+ if (this.failure) return;
6210
+ this.failure = error;
6211
+ for (const call of this.waiting.values()) {
6212
+ clearTimeout(call.timer);
6213
+ call.reject(error);
6214
+ }
6215
+ this.waiting.clear();
6216
+ }
6217
+ close() {
6218
+ this.fail(errnoError("EIO", "the file system is unmounted"));
6219
+ this.port.close?.();
6220
+ }
6221
+ };
6222
+
6223
+ // src/mount/mount-fs.ts
6224
+ var MAX_LINKS4 = 40;
6225
+ var DEFAULT_IO = 8 * 1024 * 1024;
6226
+ var UNITS = { "": 1, k: 1024, m: 1024 ** 2, g: 1024 ** 3, t: 1024 ** 4 };
6227
+ function parseSize(text) {
6228
+ const match = /^(\d+(?:\.\d+)?)\s*([kmgt]?)i?b?$/i.exec(text.trim());
6229
+ if (!match) throw errnoError("EINVAL", `not a size: ${text}`);
6230
+ return Math.floor(Number(match[1]) * UNITS[match[2].toLowerCase()]);
6231
+ }
6232
+ var within3 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
6233
+ function heldUnder(held, target) {
6234
+ for (const paths of held) for (const path of paths.keys()) if (within3(path, target)) return true;
6235
+ return false;
6236
+ }
6237
+ var parentOf4 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
6238
+ function statOf(attr, path, dev) {
6239
+ const mtime = new Date(attr.mtime);
6240
+ const fallback = attr.kind === "directory" ? 16877 : attr.kind === "symlink" ? 41471 : 33188;
6241
+ return {
6242
+ isFile: attr.kind === "file",
6243
+ isDirectory: attr.kind === "directory",
6244
+ isSymbolicLink: attr.kind === "symlink",
6245
+ size: attr.size,
6246
+ mode: attr.mode ?? fallback,
6247
+ mtime,
6248
+ atime: mtime,
6249
+ ctime: mtime,
6250
+ ino: attr.ino ?? inodeOf(path),
6251
+ dev
6252
+ };
6253
+ }
6254
+ function concat4(chunks, total) {
6255
+ if (chunks.length === 1 && chunks[0]?.length === total) return chunks[0];
6256
+ const out = new Uint8Array(total);
6257
+ let at = 0;
6258
+ for (const chunk2 of chunks) {
6259
+ out.set(chunk2, at);
6260
+ at += chunk2.length;
6261
+ }
6262
+ return out;
6263
+ }
6264
+ var MountTable = class {
6265
+ mounts = /* @__PURE__ */ new Map();
6266
+ devices = 0;
6267
+ pending = /* @__PURE__ */ new Set();
6268
+ deps;
6269
+ constructor(deps) {
6270
+ this.deps = deps;
6271
+ }
6272
+ list() {
6273
+ return [...this.mounts.values()].map(({ type, source, target, options, state, error }) => ({
6274
+ type,
6275
+ source,
6276
+ target,
6277
+ options: { ...options },
6278
+ state,
6279
+ ...error ? { error } : {}
6280
+ }));
6281
+ }
6282
+ locate(path) {
6283
+ let best;
6284
+ for (const mount of this.mounts.values()) {
6285
+ if (within3(path, mount.target) && (!best || mount.target.length > best.target.length))
6286
+ best = mount;
6287
+ }
6288
+ if (!best) return void 0;
6289
+ const rel = best.target === "/" ? path : path.slice(best.target.length);
6290
+ return { mount: best, rel: rel || "/" };
6291
+ }
6292
+ async mount(spec, fs) {
6293
+ const target = normalizePath(spec.target);
6294
+ if (this.mounts.has(target) || this.pending.has(target)) {
6295
+ throw errnoError("EBUSY", `${target} is already mounted`);
6296
+ }
6297
+ this.pending.add(target);
6298
+ try {
6299
+ return await this.attach(spec, target, fs);
6300
+ } finally {
6301
+ this.pending.delete(target);
6302
+ }
6303
+ }
6304
+ async attach(spec, target, fs) {
6305
+ const st = await fs.stat(target);
6306
+ if (!st.isDirectory) throw errnoError("ENOTDIR", target);
6307
+ const options = { ...spec.options };
6308
+ const opened2 = await this.deps.open(spec.type, { ...spec, target, options });
6309
+ let mounted;
6310
+ const started = Promise.withResolvers();
6311
+ opened2.onCrash?.((error) => {
6312
+ if (mounted) this.crashed(mounted, error);
6313
+ else
6314
+ started.reject(
6315
+ errnoError("EIO", `the ${spec.type} driver failed to start: ${error.message}`)
6316
+ );
6317
+ });
6318
+ let conn;
6319
+ try {
6320
+ conn = await Promise.race([
6321
+ DriverConnection.open(
6322
+ opened2.port,
6323
+ { source: spec.source, options },
6324
+ {
6325
+ ...this.deps.timeoutMs ? { timeoutMs: this.deps.timeoutMs } : {},
6326
+ onInvalidate: (paths) => this.invalidated(target, paths),
6327
+ onFail: (error) => {
6328
+ if (mounted) this.crashed(mounted, error);
6329
+ }
6330
+ }
6331
+ ),
6332
+ started.promise
6333
+ ]);
6334
+ } catch (err) {
6335
+ opened2.dispose();
6336
+ throw err;
6337
+ }
6338
+ const caps = conn.capabilities;
6339
+ const mount = {
6340
+ type: spec.type,
6341
+ source: spec.source,
6342
+ target,
6343
+ options,
6344
+ state: "ok",
6345
+ dev: 256 + ++this.devices,
6346
+ conn,
6347
+ caps,
6348
+ maxFile: options.maxfile !== void 0 ? parseSize(options.maxfile) : caps.maxFile ?? 0,
6349
+ attrs: /* @__PURE__ */ new Map(),
6350
+ lists: /* @__PURE__ */ new Map(),
6351
+ dispose: () => opened2.dispose()
6352
+ };
6353
+ mounted = mount;
6354
+ this.mounts.set(target, mount);
6355
+ return this.list().find((m) => m.target === target);
6356
+ }
6357
+ umount(target) {
6358
+ const at = normalizePath(target);
6359
+ const mount = this.mounts.get(at);
6360
+ if (!mount) throw errnoError("EINVAL", `${at} is not mounted`);
6361
+ if (this.deps.busy(at)) throw errnoError("EBUSY", `${at} has open files`);
6362
+ this.mounts.delete(at);
6363
+ mount.conn.close();
6364
+ mount.dispose();
6365
+ }
6366
+ crashed(mount, error) {
6367
+ mount.state = "failed";
6368
+ mount.attrs.clear();
6369
+ mount.lists.clear();
6370
+ mount.error = error.message;
6371
+ mount.conn.fail(
6372
+ errnoError("EIO", `the ${mount.type} driver for ${mount.target} failed: ${error.message}`)
6373
+ );
6374
+ }
6375
+ invalidated(target, paths) {
6376
+ const mount = this.mounts.get(target);
6377
+ if (!mount) return;
6378
+ if (paths === true) {
6379
+ mount.attrs.clear();
6380
+ mount.lists.clear();
6381
+ this.deps.changed?.(target);
6382
+ return;
6383
+ }
6384
+ const full = paths.map((p) => normalizePath(`${target}/${p}`));
6385
+ for (const path of full) this.forget(mount, path);
6386
+ this.deps.changed?.(...full);
6387
+ }
6388
+ forget(mount, path) {
6389
+ for (const key of [...mount.attrs.keys()]) if (within3(key, path)) mount.attrs.delete(key);
6390
+ mount.lists.delete(path);
6391
+ mount.lists.delete(parentOf4(path));
6392
+ for (const key of [...mount.lists.keys()]) if (within3(key, path)) mount.lists.delete(key);
6393
+ }
6394
+ async getattr(mount, path, rel) {
6395
+ if (mount.conn.failure) throw mount.conn.failure;
6396
+ const cached = mount.attrs.get(path);
6397
+ if (cached && cached.expires > Date.now()) return cached.value;
6398
+ const attr = await mount.conn.call({ op: "getattr", path: rel });
6399
+ const ttl = mount.caps.attrTtl ?? 1e3;
6400
+ if (ttl > 0) mount.attrs.set(path, { value: attr, expires: Date.now() + ttl });
6401
+ return attr;
6402
+ }
6403
+ async readdir(mount, path, rel) {
6404
+ if (mount.conn.failure) throw mount.conn.failure;
6405
+ const cached = mount.lists.get(path);
6406
+ if (cached && cached.expires > Date.now()) return cached.value;
6407
+ const entries = await mount.conn.call({ op: "readdir", path: rel });
6408
+ const ttl = mount.caps.entryTtl ?? mount.caps.attrTtl ?? 1e3;
6409
+ if (ttl > 0) mount.lists.set(path, { value: entries, expires: Date.now() + ttl });
6410
+ return entries;
6411
+ }
6412
+ async statfs(path) {
6413
+ const found = this.locate(normalizePath(path));
6414
+ if (!found) return void 0;
6415
+ const st = await found.mount.conn.call({ op: "statfs" });
6416
+ if (!st) return { quota: 0, usage: 0 };
6417
+ return { quota: st.blocks * st.bsize, usage: (st.blocks - st.bfree) * st.bsize };
6418
+ }
6419
+ wrap(base) {
6420
+ return new MountFs(this, base);
6421
+ }
6422
+ };
6423
+ var MountFs = class {
6424
+ table;
6425
+ base;
6426
+ constructor(table, base) {
6427
+ this.table = table;
6428
+ this.base = base;
6429
+ }
6430
+ at(path) {
6431
+ return this.table.locate(normalizePath(path));
6432
+ }
6433
+ writable(mount, path) {
6434
+ if (mount.caps.readonly) throw errnoError("EROFS", path);
6435
+ }
6436
+ async follow(path, hops = 0, create = false) {
6437
+ const found = this.at(path);
6438
+ if (!found) return path;
6439
+ const real = normalizePath(path);
6440
+ let attr;
6441
+ try {
6442
+ attr = await this.table.getattr(found.mount, real, found.rel);
6443
+ } catch (err) {
6444
+ if (create && err.code === "ENOENT") return real;
6445
+ throw err;
6446
+ }
6447
+ if (attr.kind !== "symlink") return real;
6448
+ if (hops >= MAX_LINKS4) throw errnoError("ELOOP", path);
6449
+ const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
6450
+ return this.follow(this.base.resolvePath(parentOf4(real), target), hops + 1, create);
6451
+ }
6452
+ async open(mount, rel, write) {
6453
+ return await mount.conn.call({
6454
+ op: "open",
6455
+ path: rel,
6456
+ write,
6457
+ create: write,
6458
+ truncate: write,
6459
+ exclusive: false
6460
+ });
6461
+ }
6462
+ resolvePath(base, path) {
6463
+ return this.base.resolvePath(base, path);
6464
+ }
6465
+ async readFile(path) {
6466
+ return new TextDecoder().decode(await this.readFileBuffer(path));
6467
+ }
6468
+ async readFileBuffer(path) {
6469
+ const real = await this.follow(path);
6470
+ const found = this.at(real);
6471
+ if (!found) return this.base.readFileBuffer(real);
6472
+ const { mount, rel } = found;
6473
+ const attr = await this.table.getattr(mount, real, rel);
6474
+ if (attr.kind === "directory") throw errnoError("EISDIR", real);
6475
+ if (mount.maxFile > 0 && attr.size > mount.maxFile) {
6476
+ throw errnoError("EFBIG", `${real} is larger than this mount's maxfile (${mount.maxFile})`);
6477
+ }
6478
+ const fh = await this.open(mount, rel, false);
6479
+ const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
6480
+ const chunks = [];
6481
+ let total = 0;
6482
+ try {
6483
+ for (; ; ) {
6484
+ const bytes = await mount.conn.call({
6485
+ op: "read",
6486
+ fh,
6487
+ offset: total,
6488
+ size: chunk2
6489
+ });
6490
+ if (bytes.length === 0) break;
6491
+ chunks.push(bytes);
6492
+ total += bytes.length;
6493
+ if (bytes.length < chunk2 && total >= attr.size) break;
6494
+ }
6495
+ } finally {
6496
+ await mount.conn.call({ op: "release", fh });
6497
+ }
6498
+ return concat4(chunks, total);
6499
+ }
6500
+ async writeFile(path, content) {
6501
+ const real = await this.follow(path, 0, true);
6502
+ const found = this.at(real);
6503
+ if (!found) return this.base.writeFile(real, content);
6504
+ const { mount, rel } = found;
6505
+ this.writable(mount, real);
6506
+ const bytes = typeof content === "string" ? new TextEncoder().encode(content) : content;
6507
+ if (mount.maxFile > 0 && bytes.length > mount.maxFile) {
6508
+ throw errnoError(
6509
+ "EFBIG",
6510
+ `${real} would be larger than this mount's maxfile (${mount.maxFile})`
6511
+ );
6512
+ }
6513
+ this.table.forget(mount, real);
6514
+ const fh = await this.open(mount, rel, true);
6515
+ const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
6516
+ try {
6517
+ for (let offset = 0; offset < bytes.length; offset += chunk2) {
6518
+ await mount.conn.call({
6519
+ op: "write",
6520
+ fh,
6521
+ offset,
6522
+ bytes: bytes.slice(offset, offset + chunk2)
6523
+ });
6524
+ }
6525
+ } finally {
6526
+ await mount.conn.call({ op: "release", fh });
6527
+ this.table.forget(mount, real);
6528
+ }
6529
+ }
6530
+ async exists(path) {
6531
+ if (!this.at(path)) return this.base.exists(path);
6532
+ try {
6533
+ await this.stat(path);
6534
+ return true;
6535
+ } catch (err) {
6536
+ const code = err.code;
6537
+ if (code === "ENOENT" || code === "ENOTDIR") return false;
6538
+ throw err;
6539
+ }
6540
+ }
6541
+ async lstat(path) {
6542
+ const found = this.at(path);
6543
+ if (!found) return this.base.lstat(path);
6544
+ const real = normalizePath(path);
6545
+ return statOf(await this.table.getattr(found.mount, real, found.rel), real, found.mount.dev);
6546
+ }
6547
+ async stat(path) {
6548
+ const real = await this.follow(path);
6549
+ return this.at(real) ? this.lstat(real) : this.base.stat(real);
6550
+ }
6551
+ async entries(path) {
6552
+ const real = await this.follow(path);
6553
+ const found = this.at(real);
6554
+ if (!found) throw errnoError("ENOTDIR", real);
6555
+ return {
6556
+ real,
6557
+ mount: found.mount,
6558
+ list: await this.table.readdir(found.mount, real, found.rel)
6559
+ };
6560
+ }
6561
+ async readdir(path) {
6562
+ if (!this.at(path)) return this.base.readdir(path);
6563
+ return (await this.entries(path)).list.map((e) => e.name);
6564
+ }
6565
+ async readdirStat(path) {
6566
+ if (!this.at(path)) return this.base.readdirStat(path);
6567
+ const { real, mount, list } = await this.entries(path);
6568
+ return Promise.all(
6569
+ list.map(async (entry) => {
6570
+ const child = normalizePath(`${real}/${entry.name}`);
6571
+ if (entry.attr && mount.caps.listingStats) {
6572
+ return [entry.name, statOf(entry.attr, child, mount.dev)];
6573
+ }
6574
+ return [entry.name, await this.lstat(child).catch(() => null)];
6575
+ })
6576
+ );
6577
+ }
6578
+ async mkdir(path, options = {}) {
6579
+ const found = this.at(path);
6580
+ if (!found) return this.base.mkdir(path, options);
6581
+ const { mount, rel } = found;
6582
+ const real = normalizePath(path);
6583
+ this.writable(mount, real);
6584
+ if (!options.recursive) {
6585
+ if (rel === "/") throw errnoError("EEXIST", real);
6586
+ this.table.forget(mount, real);
6587
+ await mount.conn.call({ op: "mkdir", path: rel });
6588
+ return;
6589
+ }
6590
+ const parts = rel.split("/").filter(Boolean);
6591
+ for (let n = 1; n <= parts.length; n++) {
6592
+ const sub = `/${parts.slice(0, n).join("/")}`;
6593
+ const full = normalizePath(`${mount.target}${sub}`);
6594
+ const exists = await this.table.getattr(mount, full, sub).catch(() => void 0);
6595
+ if (exists?.kind === "directory") continue;
6596
+ if (exists) throw errnoError("ENOTDIR", full);
6597
+ this.table.forget(mount, full);
6598
+ await mount.conn.call({ op: "mkdir", path: sub });
6599
+ }
6600
+ }
6601
+ async rm(path, options = {}) {
6602
+ const found = this.at(path);
6603
+ if (!found) return this.base.rm(path, options);
6604
+ const { mount, rel } = found;
6605
+ const real = normalizePath(path);
6606
+ if (rel === "/") throw errnoError("EBUSY", `${real} is a mount point`);
6607
+ this.writable(mount, real);
6608
+ let attr;
6609
+ try {
6610
+ attr = await this.table.getattr(mount, real, rel);
6611
+ } catch (err) {
6612
+ if (options.force && err.code === "ENOENT") return;
6613
+ throw err;
6614
+ }
6615
+ this.table.forget(mount, real);
6616
+ if (attr.kind === "directory" && options.recursive) {
6617
+ for (const entry of await this.table.readdir(mount, real, rel)) {
6618
+ await this.rm(`${real}/${entry.name}`, { recursive: true, force: options.force ?? false });
6619
+ }
6620
+ this.table.forget(mount, real);
6621
+ }
6622
+ await mount.conn.call({ op: attr.kind === "directory" ? "rmdir" : "unlink", path: rel });
6623
+ this.table.forget(mount, real);
6624
+ }
6625
+ async rename(from, to) {
6626
+ const a = this.at(from);
6627
+ const b = this.at(to);
6628
+ if (!a && !b) return this.base.rename(from, to);
6629
+ if (!a || !b || a.mount !== b.mount) throw errnoError("EXDEV", `${from} -> ${to}`);
6630
+ this.writable(a.mount, normalizePath(to));
6631
+ this.table.forget(a.mount, normalizePath(from));
6632
+ this.table.forget(a.mount, normalizePath(to));
6633
+ try {
6634
+ await a.mount.conn.call({ op: "rename", from: a.rel, to: b.rel });
6635
+ } finally {
6636
+ this.table.forget(a.mount, normalizePath(from));
6637
+ this.table.forget(a.mount, normalizePath(to));
6638
+ }
6639
+ }
6640
+ async symlink(target, path) {
6641
+ const found = this.at(path);
6642
+ if (!found) return this.base.symlink(target, path);
6643
+ this.writable(found.mount, normalizePath(path));
6644
+ if (!found.mount.caps.symlinks)
6645
+ throw errnoError("EPERM", `${path}: this file system has no symlinks`);
6646
+ this.table.forget(found.mount, normalizePath(path));
6647
+ await found.mount.conn.call({ op: "symlink", target, path: found.rel });
6648
+ }
6649
+ async readlink(path) {
6650
+ const found = this.at(path);
6651
+ if (!found) return this.base.readlink(path);
6652
+ return await found.mount.conn.call({ op: "readlink", path: found.rel });
6653
+ }
6654
+ async chmod(path, mode) {
6655
+ const found = this.at(path);
6656
+ if (!found) return this.base.chmod(path, mode);
6657
+ this.writable(found.mount, normalizePath(path));
6658
+ if (!found.mount.caps.chmod) return;
6659
+ this.table.forget(found.mount, normalizePath(path));
6660
+ await found.mount.conn.call({ op: "setattr", path: found.rel, mode });
6661
+ }
6662
+ async utimes(path, atime, mtime) {
6663
+ const found = this.at(path);
6664
+ if (!found) return this.base.utimes(path, atime, mtime);
6665
+ this.writable(found.mount, normalizePath(path));
6666
+ this.table.forget(found.mount, normalizePath(path));
6667
+ await found.mount.conn.call({ op: "setattr", path: found.rel, mtime: mtime.getTime() });
6668
+ }
6669
+ };
6670
+
6671
+ // src/mount/tmpfs.ts
6672
+ function tmpfs() {
6673
+ let inodes = 0;
6674
+ const make = (kind, mode) => ({
6675
+ kind,
6676
+ ino: ++inodes,
6677
+ mode,
6678
+ mtime: Date.now(),
6679
+ data: new Uint8Array(0),
6680
+ target: "",
6681
+ children: /* @__PURE__ */ new Map()
6682
+ });
6683
+ const root = make("directory", 16877);
6684
+ const handles = /* @__PURE__ */ new Map();
6685
+ let nextFh = 0;
6686
+ const parts = (path) => path.split("/").filter(Boolean);
6687
+ function find(path) {
6688
+ let node = root;
6689
+ for (const name2 of parts(path)) {
6690
+ if (node.kind !== "directory") throw fsError2("ENOTDIR", path);
6691
+ const next = node.children.get(name2);
6692
+ if (!next) throw fsError2("ENOENT", path);
6693
+ node = next;
6694
+ }
6695
+ return node;
6696
+ }
6697
+ function parent(path) {
6698
+ const names = parts(path);
6699
+ const name2 = names.pop();
6700
+ if (name2 === void 0) throw fsError2("EBUSY", "the root of a mount");
6701
+ const dir = find(`/${names.join("/")}`);
6702
+ if (dir.kind !== "directory") throw fsError2("ENOTDIR", path);
6703
+ return { dir, name: name2 };
6704
+ }
6705
+ const attr = (node) => ({
6706
+ kind: node.kind,
6707
+ size: node.kind === "file" ? node.data.length : node.kind === "symlink" ? node.target.length : 4096,
6708
+ mtime: node.mtime,
6709
+ mode: node.mode,
6710
+ ino: node.ino
6711
+ });
6712
+ const handle = (fh) => {
6713
+ const found = handles.get(fh);
6714
+ if (!found) throw fsError2("EBADF", `handle ${fh}`);
6715
+ return found;
6716
+ };
6717
+ function opened2(path, flags) {
6718
+ const { dir, name: name2 } = parent(path);
6719
+ const existing = dir.children.get(name2);
6720
+ if (existing && flags.exclusive) throw fsError2("EEXIST", path);
6721
+ if (existing?.kind === "directory" && flags.write) throw fsError2("EISDIR", path);
6722
+ if (existing) return existing;
6723
+ if (!flags.create) throw fsError2("ENOENT", path);
6724
+ const node = make("file", 33188);
6725
+ dir.children.set(name2, node);
6726
+ dir.mtime = Date.now();
6727
+ return node;
6728
+ }
6729
+ return {
6730
+ async getattr(path) {
6731
+ return attr(find(path));
6732
+ },
6733
+ async readdir(path) {
6734
+ const dir = find(path);
6735
+ if (dir.kind !== "directory") throw fsError2("ENOTDIR", path);
6736
+ return [...dir.children].map(
6737
+ ([name2, node]) => ({ name: name2, kind: node.kind, attr: attr(node) })
6738
+ );
6739
+ },
6740
+ async open(path, flags) {
6741
+ const node = opened2(path, flags);
6742
+ const data = flags.truncate ? new Uint8Array(0) : node.data.slice();
6743
+ handles.set(++nextFh, { node, data, length: data.length, dirty: flags.truncate });
6744
+ return nextFh;
6745
+ },
6746
+ async read(fh, offset, size) {
6747
+ const h = handle(fh);
6748
+ return h.data.slice(offset, Math.min(h.length, offset + size));
6749
+ },
6750
+ async write(fh, offset, bytes) {
6751
+ const h = handle(fh);
6752
+ const end = offset + bytes.length;
6753
+ if (end > h.data.length) {
6754
+ const grown = new Uint8Array(Math.max(end, h.data.length * 2));
6755
+ grown.set(h.data.subarray(0, h.length));
6756
+ h.data = grown;
6757
+ }
6758
+ h.data.set(bytes, offset);
6759
+ h.length = Math.max(h.length, end);
6760
+ h.dirty = true;
6761
+ },
6762
+ async release(fh) {
6763
+ const h = handle(fh);
6764
+ handles.delete(fh);
6765
+ if (!h.dirty) return;
6766
+ h.node.data = h.data.slice(0, h.length);
6767
+ h.node.mtime = Date.now();
6768
+ },
6769
+ async mkdir(path) {
6770
+ const { dir, name: name2 } = parent(path);
6771
+ if (dir.children.has(name2)) throw fsError2("EEXIST", path);
6772
+ dir.children.set(name2, make("directory", 16877));
6773
+ },
6774
+ async rmdir(path) {
6775
+ const { dir, name: name2 } = parent(path);
6776
+ const node = dir.children.get(name2);
6777
+ if (!node) throw fsError2("ENOENT", path);
6778
+ if (node.kind !== "directory") throw fsError2("ENOTDIR", path);
6779
+ if (node.children.size > 0) throw fsError2("ENOTEMPTY", path);
6780
+ dir.children.delete(name2);
6781
+ },
6782
+ async unlink(path) {
6783
+ const { dir, name: name2 } = parent(path);
6784
+ const node = dir.children.get(name2);
6785
+ if (!node) throw fsError2("ENOENT", path);
6786
+ if (node.kind === "directory") throw fsError2("EISDIR", path);
6787
+ dir.children.delete(name2);
6788
+ },
6789
+ async rename(from, to) {
6790
+ const a = `/${parts(from).join("/")}`;
6791
+ const b = `/${parts(to).join("/")}`;
6792
+ if (b !== a && b.startsWith(`${a}/`)) throw fsError2("EINVAL", `${to} is inside ${from}`);
6793
+ const source = parent(from);
6794
+ const node = source.dir.children.get(source.name);
6795
+ if (!node) throw fsError2("ENOENT", from);
6796
+ const target = parent(to);
6797
+ const replaced = target.dir.children.get(target.name);
6798
+ if (replaced === node) return;
6799
+ if (replaced?.kind === "directory" && (node.kind !== "directory" || replaced.children.size > 0)) {
6800
+ throw fsError2(node.kind === "directory" ? "ENOTEMPTY" : "EISDIR", to);
6801
+ }
6802
+ source.dir.children.delete(source.name);
6803
+ target.dir.children.set(target.name, node);
6804
+ },
6805
+ async symlink(target, path) {
6806
+ const { dir, name: name2 } = parent(path);
6807
+ if (dir.children.has(name2)) throw fsError2("EEXIST", path);
6808
+ const node = make("symlink", 41471);
6809
+ node.target = target;
6810
+ dir.children.set(name2, node);
6811
+ },
6812
+ async readlink(path) {
6813
+ const node = find(path);
6814
+ if (node.kind !== "symlink") throw fsError2("EINVAL", path);
6815
+ return node.target;
6816
+ },
6817
+ async setattr(path, change) {
6818
+ const node = find(path);
6819
+ if (change.mode !== void 0) node.mode = node.mode & ~4095 | change.mode & 4095;
6820
+ if (change.mtime !== void 0) node.mtime = change.mtime;
6821
+ },
6822
+ async statfs() {
6823
+ return { bsize: 4096, blocks: 262144, bfree: 262144 };
6824
+ }
6825
+ };
6826
+ }
6827
+
5939
6828
  // src/process/wasi/wasi-module.ts
5940
6829
  function importedMemory(bytes) {
5941
6830
  let at = 8;
@@ -6096,6 +6985,7 @@ function foreignImports(bytes) {
6096
6985
  }
6097
6986
 
6098
6987
  // src/launcher.ts
6988
+ var PNPM_HOME = "/home/.local/share/pnpm";
6099
6989
  var COMMAND2 = /^\/(?:usr\/)?bin\/([^/]+)$/;
6100
6990
  var PACKAGE_ROOT = /^(.*\/node_modules\/(?:@[^/]+\/)?[^/]+)\//;
6101
6991
  var ENV_REFERENCE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
@@ -6157,7 +7047,7 @@ function childHandle(handle) {
6157
7047
  onState: (listener) => handle.onState(listener)
6158
7048
  };
6159
7049
  }
6160
- function concat4(chunks) {
7050
+ function concat5(chunks) {
6161
7051
  const out = new Uint8Array(chunks.reduce((n, chunk2) => n + chunk2.length, 0));
6162
7052
  let offset = 0;
6163
7053
  for (const chunk2 of chunks) {
@@ -6185,12 +7075,24 @@ var Launcher = class {
6185
7075
  ca;
6186
7076
  transport;
6187
7077
  watchers = new FsWatchers();
7078
+ pnpmHome;
7079
+ mounts;
7080
+ createDriverWorker;
7081
+ openFiles = /* @__PURE__ */ new Set();
7082
+ held = /* @__PURE__ */ new Set();
6188
7083
  nextPid = 1e3;
6189
7084
  boot = Date.now();
6190
7085
  terminals = 0;
6191
7086
  constructor(options) {
6192
- this.base = this.watchers.wrap(options.fs);
7087
+ this.mounts = new MountTable({
7088
+ open: (type, spec) => this.driver(type, spec),
7089
+ busy: (target) => [...this.openFiles].some((nodes) => nodes.holds(target)) || heldUnder(this.held, target),
7090
+ changed: this.watchers.changed.bind(this.watchers)
7091
+ });
7092
+ this.base = this.watchers.wrap(this.mounts.wrap(options.fs));
6193
7093
  this.createWorker = options.createWorker;
7094
+ this.createDriverWorker = options.createDriverWorker;
7095
+ this.pnpmHome = options.env?.PNPM_HOME ?? PNPM_HOME;
6194
7096
  this.modulesDir = options.modules ?? "/node_modules";
6195
7097
  this.env = { ...networkEnv(), ...options.env };
6196
7098
  this.ca = kernelCa(options.caStore ?? memoryCaStore());
@@ -6201,11 +7103,17 @@ var Launcher = class {
6201
7103
  ca: this.ca
6202
7104
  });
6203
7105
  this.fs = withCommandDirs(this.base, async () => new Set((await this.commands()).keys()));
7106
+ this.watchers.watch([this.modulesDir, this.pnpmHome], { recursive: true }, () => {
7107
+ this.catalog = void 0;
7108
+ });
6204
7109
  }
6205
7110
  commands() {
6206
- this.catalog ??= scanCommands(this.base, this.modulesDir);
7111
+ this.catalog ??= this.roots().then((roots) => scanCommands(this.base, roots));
6207
7112
  return this.catalog;
6208
7113
  }
7114
+ async roots() {
7115
+ return [this.modulesDir, ...await pnpmGlobalRoots(this.base, this.pnpmHome)];
7116
+ }
6209
7117
  async resolve(file, argv0, cwd) {
6210
7118
  const name2 = COMMAND2.exec(file)?.[1] ?? (file.includes("/") ? void 0 : file);
6211
7119
  if (name2 !== void 0) {
@@ -6359,6 +7267,10 @@ var Launcher = class {
6359
7267
  forker: this.forker(pid, req),
6360
7268
  kill: (target, sig) => this.kill(target, sig),
6361
7269
  processes: () => ({ boot: this.boot, processes: this.list() }),
7270
+ openFiles: this.openFiles,
7271
+ held: this.held,
7272
+ statfs: (path) => this.mounts.statfs(path),
7273
+ mounts: () => this.mounts.list(),
6362
7274
  jobs: this.jobs,
6363
7275
  ptys: this.ptys,
6364
7276
  net: this.net,
@@ -6443,12 +7355,66 @@ var Launcher = class {
6443
7355
  if (sig !== 0) handle.signal(sig);
6444
7356
  return true;
6445
7357
  }
7358
+ async driver(type, _spec) {
7359
+ if (type === "tmpfs") {
7360
+ const { port1, port2 } = new MessageChannel();
7361
+ serveFilesystem(port2, tmpfs(), { symlinks: true, chmod: true, attrTtl: 0 });
7362
+ return {
7363
+ port: port1,
7364
+ dispose: () => {
7365
+ port1.close();
7366
+ port2.close();
7367
+ }
7368
+ };
7369
+ }
7370
+ const module = (await scanFilesystems(this.base, await this.roots())).get(type);
7371
+ if (!module) throw fsError("ENODEV", `unknown file system type ${type}`);
7372
+ if (!this.createDriverWorker)
7373
+ throw fsError("ENODEV", `this kernel cannot start ${type} drivers`);
7374
+ const code = await this.base.readFile(module);
7375
+ const worker = this.createDriverWorker();
7376
+ const driver = new MessageChannel();
7377
+ const client = new MessageChannel();
7378
+ const served = serveClient(client.port1, {
7379
+ launcher: async () => this,
7380
+ scope: "transport"
7381
+ });
7382
+ worker.postMessage({ code, driver: driver.port2, client: client.port2 }, [
7383
+ driver.port2,
7384
+ client.port2
7385
+ ]);
7386
+ return {
7387
+ port: driver.port1,
7388
+ dispose: () => {
7389
+ worker.terminate();
7390
+ served.detach();
7391
+ driver.port1.close();
7392
+ },
7393
+ onCrash: (listener) => worker.addEventListener("error", (event) => {
7394
+ event.preventDefault?.();
7395
+ listener(new Error(String(event.message)));
7396
+ })
7397
+ };
7398
+ }
7399
+ mount(spec) {
7400
+ return this.mounts.mount(spec, this.fs);
7401
+ }
7402
+ umount(target) {
7403
+ this.mounts.umount(target);
7404
+ }
6446
7405
  async prepare() {
6447
7406
  for (const dir of SHARED_DIRS) await this.base.mkdir(dir, { recursive: true });
6448
7407
  await writeCaFile(this.base, this.ca).catch(() => void 0);
6449
7408
  }
6450
7409
  environment(cwd, extra) {
6451
- return { PATH: "/usr/bin:/bin", HOME: "/home", ...this.env, PWD: cwd, ...extra };
7410
+ return {
7411
+ PATH: `/usr/bin:/bin:${this.pnpmHome}/bin`,
7412
+ HOME: "/home",
7413
+ PNPM_HOME: this.pnpmHome,
7414
+ ...this.env,
7415
+ PWD: cwd,
7416
+ ...extra
7417
+ };
6452
7418
  }
6453
7419
  async starting(argv, dir) {
6454
7420
  this.catalog = void 0;
@@ -6510,7 +7476,7 @@ var Launcher = class {
6510
7476
  `));
6511
7477
  if (!planned) {
6512
7478
  report(`${file}: command not found`);
6513
- return { status: NOT_FOUND, stdout: concat4(out), stderr: concat4(err) };
7479
+ return { status: NOT_FOUND, stdout: concat5(out), stderr: concat5(err) };
6514
7480
  }
6515
7481
  const fds = new FdTable();
6516
7482
  fds.installAt(0, options.stdin ? bytesSource(options.stdin) : nullFile());
@@ -6519,7 +7485,7 @@ var Launcher = class {
6519
7485
  const handle = await this.launch(planned, { env, cwd, fds, report });
6520
7486
  options.onStarted?.(handle.pid);
6521
7487
  const status = await handle.exited;
6522
- return { status, stdout: concat4(out), stderr: concat4(err) };
7488
+ return { status, stdout: concat5(out), stderr: concat5(err) };
6523
7489
  }
6524
7490
  };
6525
7491
 
@@ -6587,30 +7553,39 @@ function serveKernel(port, deps) {
6587
7553
  terminals.delete(req.id);
6588
7554
  }
6589
7555
  }
6590
- async function handle(req) {
6591
- if (req.op === "init") {
6592
- launcher = (req.root ? Promise.resolve(req.root) : deps.storage()).then(async (root) => {
6593
- const name2 = req.metadata ?? META_DB;
6594
- const meta = name2 === false ? void 0 : await deps.metadata?.(name2);
6595
- remote = req.transport ? new RemoteTransport(port, req.transport) : void 0;
6596
- const fs = new OpfsFs(root, meta, dirsChannel(name2));
6597
- await fs.reconcile();
6598
- const started = new Launcher({
6599
- fs,
6600
- createWorker: deps.createWorker,
6601
- ...req.modules ? { modules: req.modules } : {},
6602
- ...req.env ? { env: req.env } : {},
6603
- ...remote ? { transport: remote } : {},
6604
- caStore: caStore(deps.metadata ? name2 : false)
6605
- });
6606
- await started.prepare();
6607
- return started;
7556
+ async function init(req) {
7557
+ launcher = (req.root ? Promise.resolve(req.root) : deps.storage()).then(async (root) => {
7558
+ const name2 = req.metadata ?? META_DB;
7559
+ const meta = name2 === false ? void 0 : await deps.metadata?.(name2);
7560
+ remote = req.transport ? new RemoteTransport(port, req.transport) : void 0;
7561
+ const fs = new OpfsFs(root, meta, dirsChannel(name2));
7562
+ await fs.reconcile();
7563
+ const started = new Launcher({
7564
+ fs,
7565
+ createWorker: deps.createWorker,
7566
+ ...deps.createDriverWorker ? { createDriverWorker: deps.createDriverWorker } : {},
7567
+ ...req.modules ? { modules: req.modules } : {},
7568
+ ...req.env ? { env: req.env } : {},
7569
+ ...remote ? { transport: remote } : {},
7570
+ caStore: caStore(deps.metadata ? name2 : false)
6608
7571
  });
6609
- await launcher;
6610
- return true;
6611
- }
7572
+ await started.prepare();
7573
+ return started;
7574
+ });
7575
+ await launcher;
7576
+ return true;
7577
+ }
7578
+ async function mountOp(req) {
7579
+ const l = await ready2();
7580
+ if (req.op === "mount") return l.mount(req.spec);
7581
+ if (req.op === "umount") return l.umount(req.target);
7582
+ return l.mounts.list();
7583
+ }
7584
+ async function handle(req) {
7585
+ if (req.op === "init") return init(req);
6612
7586
  if (req.op === "open-terminal") return terminal(req);
6613
7587
  if (req.op === "connect") return connect();
7588
+ if (req.op === "mount" || req.op === "umount" || req.op === "mounts") return mountOp(req);
6614
7589
  if (req.op === "terminal") {
6615
7590
  const session = terminals.get(req.terminal);
6616
7591
  if (!session) throw new Error(`no terminal ${req.terminal}`);
@@ -6638,9 +7613,11 @@ function serveKernel(port, deps) {
6638
7613
 
6639
7614
  // src/kernel-worker.ts
6640
7615
  var processWorker = pinScript(new URL("./process-worker.js", import.meta.url));
7616
+ var driverWorker = pinScript(new URL("./driver-worker.js", import.meta.url));
6641
7617
  serveKernel(globalThis, {
6642
7618
  storage: () => navigator.storage.getDirectory(),
6643
7619
  createWorker: () => new Worker(processWorker, { type: "module" }),
7620
+ createDriverWorker: () => new Worker(driverWorker, { type: "module" }),
6644
7621
  metadata: (name2) => openMeta(name2, indexedDB)
6645
7622
  });
6646
7623
  //# sourceMappingURL=kernel-worker.js.map