@nimbus-sh/core 0.1.0 → 0.2.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.
Files changed (60) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1 -0
  4. package/dist/runtime/bash-runner.d.ts +63 -0
  5. package/dist/runtime/bash-runner.d.ts.map +1 -0
  6. package/dist/runtime/bash-runner.generated.d.ts +14 -0
  7. package/dist/runtime/bash-runner.generated.d.ts.map +1 -0
  8. package/dist/runtime/bash-runner.generated.js +13 -0
  9. package/dist/runtime/bash-runner.js +290 -0
  10. package/dist/runtime/cpython-runner.d.ts +86 -0
  11. package/dist/runtime/cpython-runner.d.ts.map +1 -0
  12. package/dist/runtime/cpython-runner.js +425 -0
  13. package/dist/runtime/facet-host.d.ts +159 -0
  14. package/dist/runtime/facet-host.d.ts.map +1 -0
  15. package/dist/runtime/facet-host.js +22 -0
  16. package/dist/runtime/installed-runtimes.d.ts +99 -0
  17. package/dist/runtime/installed-runtimes.d.ts.map +1 -0
  18. package/dist/runtime/installed-runtimes.js +162 -0
  19. package/dist/runtime/local-facet-host.d.ts +38 -0
  20. package/dist/runtime/local-facet-host.d.ts.map +1 -0
  21. package/dist/runtime/local-facet-host.js +171 -0
  22. package/dist/runtime/python-pip.d.ts +38 -0
  23. package/dist/runtime/python-pip.d.ts.map +1 -0
  24. package/dist/runtime/python-pip.js +1063 -0
  25. package/dist/runtime/runtime-manifest.d.ts +86 -0
  26. package/dist/runtime/runtime-manifest.d.ts.map +1 -0
  27. package/dist/runtime/runtime-manifest.js +72 -0
  28. package/dist/runtime/runtime-registry.d.ts +161 -0
  29. package/dist/runtime/runtime-registry.d.ts.map +1 -0
  30. package/dist/runtime/runtime-registry.js +363 -0
  31. package/dist/runtime/vfs-snapshot.d.ts.map +1 -1
  32. package/dist/runtime/vfs-snapshot.js +15 -1
  33. package/dist/runtime/vfs-supervisor.d.ts +22 -0
  34. package/dist/runtime/vfs-supervisor.d.ts.map +1 -0
  35. package/dist/runtime/vfs-supervisor.js +65 -0
  36. package/dist/runtime/virtual-socket-kernel.generated.d.ts +14 -0
  37. package/dist/runtime/virtual-socket-kernel.generated.d.ts.map +1 -0
  38. package/dist/runtime/virtual-socket-kernel.generated.js +13 -0
  39. package/dist/runtime/wasm-runner.d.ts +80 -0
  40. package/dist/runtime/wasm-runner.d.ts.map +1 -0
  41. package/dist/runtime/wasm-runner.js +686 -0
  42. package/dist/workspace/nimbus-workspace.d.ts +16 -0
  43. package/dist/workspace/nimbus-workspace.d.ts.map +1 -1
  44. package/dist/workspace/nimbus-workspace.js +57 -2
  45. package/package.json +4 -2
  46. package/src/index.ts +9 -0
  47. package/src/runtime/bash-runner.generated.ts +14 -0
  48. package/src/runtime/bash-runner.ts +347 -0
  49. package/src/runtime/cpython-runner.ts +504 -0
  50. package/src/runtime/facet-host.ts +170 -0
  51. package/src/runtime/installed-runtimes.ts +235 -0
  52. package/src/runtime/local-facet-host.ts +205 -0
  53. package/src/runtime/python-pip.ts +1211 -0
  54. package/src/runtime/runtime-manifest.ts +155 -0
  55. package/src/runtime/runtime-registry.ts +510 -0
  56. package/src/runtime/vfs-snapshot.ts +15 -1
  57. package/src/runtime/vfs-supervisor.ts +67 -0
  58. package/src/runtime/virtual-socket-kernel.generated.ts +14 -0
  59. package/src/runtime/wasm-runner.ts +835 -0
  60. package/src/workspace/nimbus-workspace.ts +102 -3
@@ -20,6 +20,7 @@ import type { Shell } from '../substrate/lifo/shell/Shell.js';
20
20
  import type { ITerminal } from '../substrate/lifo/terminal/ITerminal.js';
21
21
  import { SqliteVFS } from '../vfs/sqlite-vfs.js';
22
22
  import type { SqlDatabase, TransactionHost } from '../runtime/os-contracts.js';
23
+ import type { FacetHost } from '../runtime/facet-host.js';
23
24
  export interface NimbusWorkspaceOptions {
24
25
  /** The host's SQLite. In a Durable Object: `ctx.storage.sql`. */
25
26
  readonly sql: SqlDatabase;
@@ -44,6 +45,21 @@ export interface NimbusWorkspaceOptions {
44
45
  readonly cwd?: string;
45
46
  /** Absent means headless: `.exec` captures output and nothing is drawn. */
46
47
  readonly terminal?: ITerminal;
48
+ /**
49
+ * Where WebAssembly runs.
50
+ *
51
+ * Absent, the workspace is the JavaScript half of Nimbus: the durable
52
+ * filesystem, the shell and the coreutils, and `bash` or `./prog.wasm` is
53
+ * "command not found" — not disabled, ABSENT, because nothing has been
54
+ * supplied that could compile a module or run one. Supplied, the wasm
55
+ * runtimes already installed in this filesystem become invokable commands
56
+ * and `wasm-runner` joins them, which is what makes a `\0asm` file on the
57
+ * PATH executable (see shell/exec-dispatch.ts).
58
+ *
59
+ * A Durable Object passes the workerd host (`@nimbus-sh/worker`'s
60
+ * `loaderFacetHost`); a plain process passes `localFacetHost()`.
61
+ */
62
+ readonly facets?: FacetHost;
47
63
  }
48
64
  /**
49
65
  * A durable filesystem and a shell over it.
@@ -1 +1 @@
1
- {"version":3,"file":"nimbus-workspace.d.ts","sourceRoot":"","sources":["../../src/workspace/nimbus-workspace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AAC/F,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAChE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kCAAkC,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,SAAS,EAAqB,MAAM,sBAAsB,CAAC;AAGpE,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAO/E,MAAM,WAAW,sBAAsB;IACrC,iEAAiE;IACjE,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,eAAe,CAAC;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;CAC/B;AAED;;;;;;;GAOG;AACH,qBAAa,eAAe;IAaxB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAExB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAdtB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,4EAA4E;IAC5E,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAEtB,OAAO;WAiBM,MAAM,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,eAAe,CAAC;IAyB9E,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC;IAInE;;;;;;OAMG;IACH,KAAK,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;IAK3D;;;;;;;OAOG;IACH,OAAO,IAAI,IAAI;CAKhB"}
1
+ {"version":3,"file":"nimbus-workspace.d.ts","sourceRoot":"","sources":["../../src/workspace/nimbus-workspace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AAC/F,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAChE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kCAAkC,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,SAAS,EAAqB,MAAM,sBAAsB,CAAC;AAGpE,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAG/E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAU1D,MAAM,WAAW,sBAAsB;IACrC,iEAAiE;IACjE,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,eAAe,CAAC;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;IAC9B;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;CAC7B;AAED;;;;;;;GAOG;AACH,qBAAa,eAAe;IAaxB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAExB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAdtB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,4EAA4E;IAC5E,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAEtB,OAAO;WAiBM,MAAM,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,eAAe,CAAC;IAmC9E,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC;IAInE;;;;;;OAMG;IACH,KAAK,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;IAK3D;;;;;;;OAOG;IACH,OAAO,IAAI,IAAI;CAKhB"}
@@ -19,10 +19,11 @@ import { SandboxFsImpl } from '../substrate/lifo/sandbox/SandboxFs.js';
19
19
  import { SqliteVFS, SqliteVFSProvider } from '../vfs/sqlite-vfs.js';
20
20
  import { DEFAULT_MOUNT_POINTS, DEFAULT_PATH } from '../constants.js';
21
21
  import { CRED_KERNEL, CRED_SESSION_USER } from '../runtime/os-contracts.js';
22
+ import { PID_GEN_STRIDE } from '../runtime/process-table.js';
23
+ import { SessionProcessSupervisor } from '../runtime/session-process-supervisor.js';
24
+ import { rehydrateInstalledRuntimesView, } from '../runtime/installed-runtimes.js';
22
25
  import { registerUnixCommands } from '../shell/unix-commands.js';
23
26
  import { installPathExecResolver } from '../shell/exec-dispatch.js';
24
- /** Pids are `generation * PID_GEN_STRIDE + seq`; mirrors process-table.ts. */
25
- const PID_GEN_STRIDE = 1_000_000;
26
27
  /**
27
28
  * A durable filesystem and a shell over it.
28
29
  *
@@ -73,6 +74,15 @@ export class NimbusWorkspace {
73
74
  // carry credentials and read this filesystem's uid/gid, so they must win.
74
75
  registerUnixCommands(sandbox.commands.registry, vfs);
75
76
  installPathExecResolver(sandbox.commands.registry, vfs.as(CRED_SESSION_USER), () => sandbox.shell.getCwd());
77
+ if (options.facets) {
78
+ await registerWasmRuntimes({
79
+ facets: options.facets,
80
+ vfs,
81
+ registry: sandbox.commands.registry,
82
+ generation: options.generation ?? 1,
83
+ home: options.env?.HOME ?? '/home/user',
84
+ });
85
+ }
76
86
  return new NimbusWorkspace(sandbox, vfs, options.sql);
77
87
  }
78
88
  exec(command, options) {
@@ -103,6 +113,51 @@ export class NimbusWorkspace {
103
113
  }
104
114
  }
105
115
  }
116
+ /**
117
+ * Turn a facet host into commands: the runtimes this filesystem already holds,
118
+ * plus `wasm-runner` for everything else with a `\0asm` header.
119
+ *
120
+ * The runner factories are held here rather than in the process-global table
121
+ * `nimbus install` writes to, because each one closes over THIS workspace's
122
+ * filesystem and THIS workspace's facet host — a second workspace in the same
123
+ * process would otherwise silently retarget the first one's bash.
124
+ *
125
+ * Imported on demand: the runners carry the WASI shim and the bash scheduler as
126
+ * source strings, and a workspace with no facet host must not pay to parse
127
+ * them.
128
+ */
129
+ async function registerWasmRuntimes(deps) {
130
+ const [{ makeBashRunnerFactory }, { makeCPythonRunnerFactory }, { wasmRunnerSpec }, { buildRuntimeHandler }, esbuildModule,] = await Promise.all([
131
+ import('../runtime/bash-runner.js'),
132
+ import('../runtime/cpython-runner.js'),
133
+ import('../runtime/wasm-runner.js'),
134
+ import('../runtime/runtime-registry.js'),
135
+ import('../runtime/esbuild-service.js'),
136
+ ]);
137
+ // wasm-runner allocates pids for what it runs, so it needs a process table
138
+ // whose pid space is this generation's — the same rule the append writers
139
+ // above are revoked by.
140
+ const processes = new SessionProcessSupervisor();
141
+ processes.setPidBase(deps.generation * PID_GEN_STRIDE);
142
+ let esbuild = null;
143
+ deps.registry.register('wasm-runner', buildRuntimeHandler(wasmRunnerSpec({ vfs: deps.vfs, facets: deps.facets, processes }), {
144
+ vfs: deps.vfs,
145
+ getEsbuild: () => {
146
+ if (!esbuild)
147
+ esbuild = new esbuildModule.EsbuildService(deps.vfs);
148
+ return esbuild;
149
+ },
150
+ registry: deps.registry,
151
+ }));
152
+ const runners = {
153
+ 'bash-runner': makeBashRunnerFactory({ facets: deps.facets, vfs: deps.vfs }),
154
+ // No `startResident`: a workspace owns no actor that could outlive the
155
+ // call, so a program that keeps serving is refused by name rather than
156
+ // run as a one-shot that dies with it.
157
+ 'cpython-runner': makeCPythonRunnerFactory({ facets: deps.facets, vfs: deps.vfs }),
158
+ };
159
+ rehydrateInstalledRuntimesView(deps.vfs.as(CRED_KERNEL), deps.registry, deps.home, (key) => runners[key]);
160
+ }
106
161
  /**
107
162
  * Every table the filesystem creates.
108
163
  *
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nimbus-sh/core",
3
- "version": "0.1.0",
4
- "description": "Backend-agnostic half of Nimbus the durable filesystem, the shell, and the OS/process contracts, over a host-supplied SQL port.",
3
+ "version": "0.2.0",
4
+ "description": "Backend-agnostic half of Nimbus \u2014 the durable filesystem, the shell, and the OS/process contracts, over a host-supplied SQL port.",
5
5
  "keywords": [
6
6
  "filesystem",
7
7
  "shell",
@@ -53,10 +53,12 @@
53
53
  "typecheck": "tsc --noEmit"
54
54
  },
55
55
  "dependencies": {
56
+ "@renovatebot/pep440": "^5.0.0",
56
57
  "acorn": "^8.15.0",
57
58
  "acorn-walk": "^8.3.5",
58
59
  "esbuild-wasm": "0.24.2",
59
60
  "magic-string": "^0.30.21",
61
+ "pip-requirements-js": "^1.0.3",
60
62
  "zod": "^4.4.3"
61
63
  },
62
64
  "devDependencies": {
package/src/index.ts CHANGED
@@ -20,3 +20,12 @@ export type {
20
20
  SqlValue,
21
21
  TransactionHost,
22
22
  } from './runtime/os-contracts.js';
23
+ export { localFacetHost } from './runtime/local-facet-host.js';
24
+ export type {
25
+ Facet,
26
+ FacetBindings,
27
+ FacetFn,
28
+ FacetHost,
29
+ FacetSpec,
30
+ FacetSubmitOptions,
31
+ } from './runtime/facet-host.js';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * bash-runner.generated.ts — AUTO-GENERATED. DO NOT EDIT.
3
+ *
4
+ * Produced by scripts/bundle-facet-workers.mjs from:
5
+ * - @nimbus-sh/core src/runtime/bash/preamble.ts
6
+ *
7
+ * The facet-side bash scheduler as a self-contained IIFE that installs
8
+ * globalThis.__bashBoot / globalThis.__bashFeed. bash-runner.ts re-exports it
9
+ * as BASH_RUNNER_PREAMBLE and passes it as the facet preamble.
10
+ *
11
+ * Size: 58.56 KiB
12
+ */
13
+
14
+ export const BASH_RUNNER_BODY_SRC: string = "\"use strict\";\n(() => {\n // ../core/src/runtime/bash/preamble.ts\n var PAGE = 65536, te = new TextEncoder(), td = new TextDecoder();\n var MAIN_SIZE = 8 << 20, SLOT_SIZE = 256 << 10, NSLOT = 32;\n var E = { ACCES: 2, BADF: 8, EXIST: 20, INVAL: 28, ISDIR: 31, LOOP: 32, NOENT: 44, NOSYS: 52, NOTDIR: 54, NOTEMPTY: 55, PERM: 63, SPIPE: 70 };\n var CLOCK_REALTIME = 0, CLOCK_MONOTONIC = 1, CLOCK_PROCESS_CPUTIME = 2, CLOCK_THREAD_CPUTIME = 3;\n function realtimeNs() {\n return BigInt(Date.now()) * 1000000n;\n }\n function monotonicNs() {\n const ms = typeof performance !== \"undefined\" && performance.now ? performance.now() : Date.now();\n return BigInt(Math.floor(ms * 1e3)) * 1000n;\n }\n function clockNs(id) {\n if (id === CLOCK_REALTIME) return realtimeNs();\n if (id === CLOCK_MONOTONIC || id === CLOCK_PROCESS_CPUTIME || id === CLOCK_THREAD_CPUTIME) return monotonicNs();\n return null;\n }\n var Exit = class {\n constructor(c) {\n this.code = c;\n }\n };\n var S = null;\n function norm(p) {\n const parts = [];\n for (const seg of String(p).split(\"/\")) {\n if (!seg || seg === \".\") continue;\n if (seg === \"..\") {\n parts.pop();\n continue;\n }\n parts.push(seg);\n }\n return parts.join(\"/\");\n }\n function b64ToBytes(b64) {\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n }\n function bytesToB64(bytes) {\n let bin = \"\";\n const CH = 32768;\n for (let i = 0; i < bytes.length; i += CH) bin += String.fromCharCode.apply(null, bytes.subarray(i, i + CH));\n return btoa(bin);\n }\n function newSession(args) {\n const wasmTable = globalThis.__NIMBUS_WASM || {};\n const mod = wasmTable[\"bash.async.wasm\"];\n if (!mod) throw new Error(\"bash.async.wasm missing from __NIMBUS_WASM\");\n const coreutils = /* @__PURE__ */ new Map();\n for (const key of Object.keys(wasmTable)) {\n if (key.startsWith(\"cu_\") && key.endsWith(\".wasm\")) coreutils.set(key.slice(3, -5), wasmTable[key]);\n }\n const busybox = wasmTable[\"cu_busybox.wasm\"];\n if (busybox) for (const name of args.busyboxApplets || []) coreutils.set(name, busybox);\n const fs = {\n files: /* @__PURE__ */ new Map(),\n dirs: /* @__PURE__ */ new Set(),\n modes: /* @__PURE__ */ new Map(),\n written: /* @__PURE__ */ new Set(),\n deleted: /* @__PURE__ */ new Set(),\n dirsCreated: /* @__PURE__ */ new Set(),\n dirsDeleted: /* @__PURE__ */ new Set(),\n modesChanged: /* @__PURE__ */ new Map(),\n // vfsPath → mtime ns. The snapshot carries no times, so an inode the\n // session did not touch reports sessionNs: older than anything written\n // here, which is what makes 'is the target newer than its source' — the\n // question make(1) and every incremental build asks — answerable.\n times: /* @__PURE__ */ new Map(),\n sessionNs: realtimeNs(),\n // vfsPath → target string, stored verbatim per POSIX: a symlink is a dumb\n // string resolved at lookup time, not a pointer to an inode.\n symlinks: /* @__PURE__ */ new Map(),\n symlinksCreated: /* @__PURE__ */ new Map()\n };\n const snap = args.fsSnapshot || { files: {}, dirs: [], modes: {} };\n for (const [path, b64] of Object.entries(snap.files || {})) fs.files.set(norm(path), { bytes: b64ToBytes(b64) });\n for (const d of snap.dirs || []) fs.dirs.add(norm(d));\n for (const [path, m] of Object.entries(snap.modes || {})) fs.modes.set(norm(path), Number(m) & 7);\n for (const seed of [\"etc\", \"dev\", \"bin\", \"usr\", \"usr/bin\"]) {\n fs.dirs.add(seed);\n if (!fs.modes.has(seed)) fs.modes.set(seed, 5);\n }\n if (!fs.files.has(\"etc/nimbus.bashrc\")) {\n fs.files.set(\"etc/nimbus.bashrc\", { bytes: te.encode('command cd \"${NIMBUS_PWD:-/}\" 2>/dev/null\\n') });\n fs.modes.set(\"etc/nimbus.bashrc\", 4);\n }\n const home = norm((args.environ.find((e) => e.startsWith(\"HOME=\")) || \"HOME=/home/user\").slice(5));\n if (args.stdinTty && home && !fs.files.has(home + \"/.bashrc\")) {\n fs.files.set(home + \"/.bashrc\", { bytes: te.encode(\". /etc/nimbus.bashrc\\n\") });\n if (!fs.modes.has(home + \"/.bashrc\")) fs.modes.set(home + \"/.bashrc\", 6);\n }\n const cwd = norm((args.environ.find((e) => e.startsWith(\"NIMBUS_PWD=\")) || \"NIMBUS_PWD=/\").slice(11));\n return {\n mod,\n coreutils,\n fs,\n cwd,\n argv: args.argv,\n environ: args.environ,\n stdinTty: !!args.stdinTty,\n stdin: { chunks: args.stdinData ? [te.encode(args.stdinData)] : [], queued: 0, closed: !!args.stdinClosed, waiters: [] },\n procs: /* @__PURE__ */ new Map(),\n pipes: /* @__PURE__ */ new Map(),\n runnable: [],\n exitStatus: /* @__PURE__ */ new Map(),\n waiters: [],\n pidNext: 100,\n pipeNext: 1,\n rootPid: 0,\n rootExit: null,\n steps: 0,\n out: \"\",\n err: \"\",\n missingWasi: /* @__PURE__ */ new Set(),\n stats: { instances: 0, memPeak: 0, mainHi: 0, slotHi: 0 },\n error: null\n };\n }\n function initStdinQueued(s) {\n s.stdin.queued = s.stdin.chunks.reduce((a, c) => a + c.length, 0);\n }\n function newPipe(s) {\n const id = s.pipeNext++;\n s.pipes.set(id, { chunks: [], queued: 0, readers: 1, writers: 1, readW: [] });\n return id;\n }\n function lowestFd(proc) {\n let fd = 0;\n while (proc.fds.has(fd) || fd === 3) fd++;\n return fd;\n }\n function bumpPipe(s, e, d) {\n const pp = s.pipes.get(e.pipeId);\n if (e.end === \"r\") pp.readers += d;\n else pp.writers += d;\n }\n function closeFd(s, proc, fd) {\n const e = proc.fds.get(fd);\n if (!e) return;\n proc.fds.delete(fd);\n if (e.kind === \"pipe\") {\n bumpPipe(s, e, -1);\n wakePipe(s, s.pipes.get(e.pipeId));\n }\n }\n function takeUpTo(src, max) {\n let need = max;\n const parts = [];\n while (need > 0 && src.chunks.length) {\n const ch = src.chunks[0];\n if (ch.length <= need) {\n parts.push(ch);\n need -= ch.length;\n src.chunks.shift();\n } else {\n parts.push(ch.subarray(0, need));\n src.chunks[0] = ch.subarray(need);\n need = 0;\n }\n }\n const total = max - need;\n src.queued -= total;\n const o = new Uint8Array(total);\n let x = 0;\n for (const p of parts) {\n o.set(p, x);\n x += p.length;\n }\n return o;\n }\n var EMPTY_IOV = { list: [], total: 0 };\n function readIovs(dv, iovs, n) {\n const list = [];\n let total = 0;\n for (let i = 0; i < n; i++) {\n const ptr = dv.getUint32(iovs + i * 8, true), len = dv.getUint32(iovs + i * 8 + 4, true);\n if (len > 0) {\n list.push({ ptr, len });\n total += len;\n }\n }\n return { list, total };\n }\n function scatter(u8, iov, bytes) {\n let off = 0;\n for (const b of iov.list) {\n if (off >= bytes.length) break;\n const n = Math.min(b.len, bytes.length - off);\n u8.set(bytes.subarray(off, off + n), b.ptr);\n off += n;\n }\n return off;\n }\n function wakePipe(s, pp) {\n while (pp.readW.length && (pp.queued > 0 || pp.writers === 0)) {\n const w = pp.readW.shift();\n const proc = w.proc;\n const req = proc.ctx.pipeReq;\n const bytes = pp.queued > 0 ? takeUpTo(pp, req.iov.total) : new Uint8Array(0);\n proc.pendingRead = { iov: req.iov, bytes, nreadPtr: req.nreadPtr, pollUserdata: req.pollUserdata };\n resumeProc(proc);\n }\n }\n function wakeStdin(s) {\n const st = s.stdin;\n while (st.waiters.length && (st.queued > 0 || st.closed)) {\n const w = st.waiters.shift();\n const proc = w.proc;\n const req = proc.ctx.pipeReq;\n const bytes = st.queued > 0 ? takeUpTo(st, req.iov.total) : new Uint8Array(0);\n proc.pendingRead = { iov: req.iov, bytes, nreadPtr: req.nreadPtr, pollUserdata: req.pollUserdata };\n resumeProc(proc);\n }\n }\n function fileLookup(s, path) {\n return s.fs.files.get(path) || null;\n }\n function isDir(s, path) {\n return path === \"\" || s.fs.dirs.has(path);\n }\n function isCoreutil(s, path) {\n if (!path.startsWith(\"bin/\") && !path.startsWith(\"usr/bin/\")) return false;\n return s.coreutils.has(path.split(\"/\").pop());\n }\n function fileExists(s, path) {\n return s.fs.files.has(path) || s.fs.modes.has(path) && !s.fs.dirs.has(path);\n }\n function inodeExists(s, path) {\n return s.fs.files.has(path) || s.fs.dirs.has(path) || s.fs.modes.has(path);\n }\n function effMode(s, path) {\n if (path === \"\") return 7;\n const m = s.fs.modes.get(path);\n if (m !== void 0) return m;\n if (isCoreutil(s, path)) return 5;\n return inodeExists(s, path) ? 0 : 7;\n }\n function parentOf(path) {\n const i = path.lastIndexOf(\"/\");\n return i < 0 ? \"\" : path.slice(0, i);\n }\n function followPath(s, path) {\n let cur = path;\n for (let hops = 0; hops < 32; hops++) {\n if (!s.fs.symlinks.size) return { path: cur, errno: 0 };\n const parts = cur.split(\"/\");\n let prefix = \"\";\n let rewrote = false;\n for (let i = 0; i < parts.length; i++) {\n prefix = prefix ? prefix + \"/\" + parts[i] : parts[i];\n const target = s.fs.symlinks.get(prefix);\n if (target === void 0) continue;\n const base = target.startsWith(\"/\") ? norm(target) : norm(parentOf(prefix) + \"/\" + target);\n const rest = parts.slice(i + 1).join(\"/\");\n cur = rest ? norm(base + \"/\" + rest) : base;\n rewrote = true;\n break;\n }\n if (!rewrote) return { path: cur, errno: 0 };\n }\n return { path: cur, errno: E.LOOP };\n }\n function followParents(s, path) {\n const parent = parentOf(path);\n if (!parent) return { path, errno: 0 };\n const r = followPath(s, parent);\n if (r.errno) return r;\n const name = path.slice(parent.length + 1);\n return { path: r.path ? r.path + \"/\" + name : name, errno: 0 };\n }\n function checkTraversal(s, path) {\n const parts = path.split(\"/\");\n let anc = \"\";\n for (let i = 0; i < parts.length - 1; i++) {\n anc = anc ? anc + \"/\" + parts[i] : parts[i];\n if (!isDir(s, anc)) return inodeExists(s, anc) ? E.NOTDIR : E.NOENT;\n if ((effMode(s, anc) & 1) === 0) return E.ACCES;\n }\n return 0;\n }\n function checkParentWritable(s, path) {\n const parent = parentOf(path);\n if (parent && (effMode(s, parent) & 3) !== 3) return E.ACCES;\n return 0;\n }\n function recordDirAdded(s, path) {\n s.fs.dirs.add(path);\n s.fs.dirsDeleted.delete(path);\n s.fs.dirsCreated.add(path);\n if (!s.fs.modes.has(path)) s.fs.modes.set(path, 7);\n touchPath(s, path);\n }\n function recordDirRemoved(s, path) {\n s.fs.dirs.delete(path);\n s.fs.modes.delete(path);\n s.fs.times.delete(path);\n if (s.fs.dirsCreated.has(path)) s.fs.dirsCreated.delete(path);\n else s.fs.dirsDeleted.add(path);\n }\n function recordFileRemoved(s, path) {\n s.fs.files.delete(path);\n s.fs.written.delete(path);\n s.fs.modes.delete(path);\n s.fs.times.delete(path);\n s.fs.symlinks.delete(path);\n s.fs.symlinksCreated.delete(path);\n s.fs.deleted.add(path);\n }\n function markWritten(s, path) {\n s.fs.written.add(path);\n s.fs.deleted.delete(path);\n if (!s.fs.modes.has(path)) s.fs.modes.set(path, 6);\n touchPath(s, path);\n const parts = path.split(\"/\");\n for (let i = 1; i < parts.length; i++) {\n const dir = parts.slice(0, i).join(\"/\");\n if (!s.fs.dirs.has(dir)) recordDirAdded(s, dir);\n }\n }\n function fileWrite(s, entry, path, pos, bytes) {\n const need = pos + bytes.length;\n if (need > entry.bytes.length) {\n const grown = new Uint8Array(need);\n grown.set(entry.bytes);\n entry.bytes = grown;\n }\n entry.bytes.set(bytes, pos);\n markWritten(s, path);\n }\n function mtimeNs(s, path) {\n const t = s.fs.times.get(path);\n return t === void 0 ? s.fs.sessionNs : t;\n }\n function touchPath(s, path) {\n s.fs.times.set(path, realtimeNs());\n }\n function statBuf(dv, u8, buf, filetype, size, timeNs) {\n u8.fill(0, buf, buf + 64);\n dv.setUint8(buf + 16, filetype);\n dv.setBigUint64(buf + 24, 1n, true);\n dv.setBigUint64(buf + 32, BigInt(size), true);\n dv.setBigUint64(buf + 40, timeNs, true);\n dv.setBigUint64(buf + 48, timeNs, true);\n dv.setBigUint64(buf + 56, timeNs, true);\n }\n function isUnderAbsRoot(p, absRoots) {\n if (!absRoots) return false;\n for (const a of absRoots) {\n if (p === a) return true;\n if (a !== \"\" && p.startsWith(a + \"/\")) return true;\n }\n return false;\n }\n function makeResolve(s, pathBase, absRoots) {\n return (raw) => {\n const p = norm(raw);\n if (!pathBase) return p;\n if (isUnderAbsRoot(p, absRoots)) return p;\n const joined = norm(pathBase + \"/\" + p);\n if (inodeExists(s, joined)) return joined;\n if (inodeExists(s, p)) return p;\n if (isDir(s, parentOf(joined))) return joined;\n if (isDir(s, parentOf(p))) return p;\n return joined;\n };\n }\n function makeUnsupported(s) {\n const nosys = (name) => () => {\n s.missingWasi.add(name);\n return E.NOSYS;\n };\n return {\n // Every write is already durable in the session's own heap, so there is\n // nothing to flush and success is the honest answer.\n fd_sync: () => 0,\n fd_datasync: () => 0,\n fd_advise: () => 0,\n // One process runs at a time under this scheduler; a yield has nothing to\n // yield to that the caller has not already reached.\n sched_yield: () => 0,\n fd_pread: nosys(\"fd_pread\"),\n fd_pwrite: nosys(\"fd_pwrite\"),\n fd_allocate: nosys(\"fd_allocate\"),\n fd_filestat_set_size: nosys(\"fd_filestat_set_size\"),\n fd_filestat_set_times: nosys(\"fd_filestat_set_times\"),\n fd_fdstat_set_rights: nosys(\"fd_fdstat_set_rights\"),\n proc_raise: nosys(\"proc_raise\"),\n sock_send: nosys(\"sock_send\"),\n sock_recv: nosys(\"sock_recv\"),\n sock_shutdown: nosys(\"sock_shutdown\")\n };\n }\n function makeWasiFs(s, proc, DV, U8, io, pathBase, absRoots) {\n const resolve = makeResolve(s, pathBase, absRoots);\n return {\n ...makeUnsupported(s),\n fd_prestat_get: (fd, buf) => {\n if (fd === 3) {\n DV().setUint8(buf, 0);\n DV().setUint32(buf + 4, 1, true);\n return 0;\n }\n return E.BADF;\n },\n // The buffer length is a contract, not decoration: fd_prestat_get reports\n // the name is 1 byte, and a guest that offers less must not be written to.\n fd_prestat_dir_name: (fd, path, plen) => {\n if (fd !== 3) return E.BADF;\n if (plen < 1) return E.INVAL;\n U8()[path] = 47;\n return 0;\n },\n path_open: (dirfd, dirflags, pathPtr, pathLen, oflags, rightsBase, _ri, fdflags, retPtr) => {\n const raw = resolve(td.decode(U8().subarray(pathPtr, pathPtr + pathLen)));\n const r = (dirflags & 1) !== 0 ? followPath(s, raw) : followParents(s, raw);\n if (r.errno) return r.errno;\n const path = r.path;\n const dv = DV();\n if (path === \"dev/null\" || path === \"dev/tty\") {\n const fd2 = lowestFd(proc);\n proc.fds.set(fd2, { kind: path === \"dev/tty\" ? \"tty\" : \"null\" });\n dv.setUint32(retPtr, fd2, true);\n return 0;\n }\n const trav = checkTraversal(s, path);\n if (trav) return trav;\n const wantDir = (oflags & 2) !== 0;\n if (isDir(s, path)) {\n if ((effMode(s, path) & 1) === 0) return E.ACCES;\n const fd2 = lowestFd(proc);\n proc.fds.set(fd2, { kind: \"dir\", path });\n dv.setUint32(retPtr, fd2, true);\n return 0;\n }\n if (wantDir) return fileExists(s, path) ? E.NOTDIR : E.NOENT;\n const rights = typeof rightsBase === \"bigint\" ? rightsBase : BigInt(rightsBase >>> 0);\n let need = 0;\n if ((rights & 2n) !== 0n) need |= 4;\n if ((rights & 64n) !== 0n) need |= 2;\n if ((oflags & 8) !== 0) need |= 2;\n let entry = fileLookup(s, path);\n const exists = entry !== null || fileExists(s, path);\n if (exists && oflags & 4) return E.EXIST;\n if (!exists) {\n if (!(oflags & 1)) return E.NOENT;\n const denied = checkParentWritable(s, path);\n if (denied) return denied;\n entry = { bytes: new Uint8Array(0) };\n s.fs.files.set(path, entry);\n markWritten(s, path);\n } else {\n if (need && (effMode(s, path) & need) !== need) return E.ACCES;\n if (!entry) {\n entry = { bytes: new Uint8Array(0) };\n s.fs.files.set(path, entry);\n }\n if (oflags & 8) {\n entry.bytes = new Uint8Array(0);\n markWritten(s, path);\n }\n }\n const fd = lowestFd(proc);\n proc.fds.set(fd, { kind: \"file\", path, pos: 0, append: (fdflags & 1) !== 0 });\n dv.setUint32(retPtr, fd, true);\n return 0;\n },\n fd_filestat_get: (fd, buf) => {\n const e = proc.fds.get(fd);\n if (!e) return E.BADF;\n if (e.kind === \"file\") {\n const f = fileLookup(s, e.path);\n statBuf(DV(), U8(), buf, 4, f ? f.bytes.length : 0, mtimeNs(s, e.path));\n return 0;\n }\n if (e && (e.kind === \"dir\" || e.kind === \"preopen\")) {\n statBuf(DV(), U8(), buf, 3, 0, mtimeNs(s, e.kind === \"preopen\" ? \"\" : e.path));\n return 0;\n }\n statBuf(DV(), U8(), buf, e && (e.kind === \"stdin\" || e.kind === \"stdout\" || e.kind === \"stderr\" || e.kind === \"tty\") ? 2 : 0, 0, realtimeNs());\n return 0;\n },\n path_filestat_get: (dirfd, flags, pathPtr, pathLen, buf) => {\n const raw = resolve(td.decode(U8().subarray(pathPtr, pathPtr + pathLen)));\n if (raw === \"dev/null\" || raw === \"dev/tty\") {\n statBuf(DV(), U8(), buf, 2, 0, realtimeNs());\n return 0;\n }\n const follow = (flags & 1) !== 0;\n if (!follow && s.fs.symlinks.has(raw)) {\n statBuf(DV(), U8(), buf, 7, te.encode(s.fs.symlinks.get(raw)).length, mtimeNs(s, raw));\n return 0;\n }\n const r = followPath(s, raw);\n if (r.errno) return r.errno;\n const path = r.path;\n const trav = checkTraversal(s, path);\n if (trav) return trav;\n const f = fileLookup(s, path);\n if (f) {\n statBuf(DV(), U8(), buf, 4, f.bytes.length, mtimeNs(s, path));\n return 0;\n }\n if (isCoreutil(s, path)) {\n statBuf(DV(), U8(), buf, 4, 1024, mtimeNs(s, path));\n return 0;\n }\n if (isDir(s, path)) {\n statBuf(DV(), U8(), buf, 3, 0, mtimeNs(s, path));\n return 0;\n }\n if (fileExists(s, path)) {\n statBuf(DV(), U8(), buf, 4, 0, mtimeNs(s, path));\n return 0;\n }\n return E.NOENT;\n },\n // fstflags: ATIM=1, ATIM_NOW=2, MTIM=4, MTIM_NOW=8. Only the mtime half is\n // recorded — this layer keeps one timestamp per inode, and mtime is the\n // one every build tool reads.\n path_filestat_set_times: (dirfd, _flags, pathPtr, pathLen, _atim, mtim, fstflags) => {\n const path = resolve(td.decode(U8().subarray(pathPtr, pathPtr + pathLen)));\n if (!inodeExists(s, path) && !s.fs.symlinks.has(path)) return E.NOENT;\n if (fstflags & 8) s.fs.times.set(path, realtimeNs());\n else if (fstflags & 4) s.fs.times.set(path, typeof mtim === \"bigint\" ? mtim : BigInt(mtim >>> 0));\n return 0;\n },\n path_unlink_file: (dirfd, pathPtr, pathLen) => {\n const r = followParents(s, resolve(td.decode(U8().subarray(pathPtr, pathPtr + pathLen))));\n if (r.errno) return r.errno;\n const path = r.path;\n const trav = checkTraversal(s, path);\n if (trav) return trav;\n if (isDir(s, path)) return E.ISDIR;\n if (!fileExists(s, path) && !s.fs.symlinks.has(path)) return E.NOENT;\n const denied = checkParentWritable(s, path);\n if (denied) return denied;\n recordFileRemoved(s, path);\n return 0;\n },\n path_rename: (fd1, oldPtr, oldLen, fd2, newPtr, newLen) => {\n const from = resolve(td.decode(U8().subarray(oldPtr, oldPtr + oldLen)));\n const to = resolve(td.decode(U8().subarray(newPtr, newPtr + newLen)));\n let trav = checkTraversal(s, from);\n if (trav) return trav;\n trav = checkTraversal(s, to);\n if (trav) return trav;\n const fromIsDir = from !== \"\" && isDir(s, from);\n const f = fileLookup(s, from);\n if (!fromIsDir && !f && !fileExists(s, from)) return E.NOENT;\n if (from === to) return 0;\n let denied = checkParentWritable(s, from);\n if (!denied) denied = checkParentWritable(s, to);\n if (denied) return denied;\n const moveMode = (a, b) => {\n const m = s.fs.modes.get(a);\n s.fs.modes.delete(a);\n if (m !== void 0) s.fs.modes.set(b, m);\n };\n if (isDir(s, to)) {\n if (!fromIsDir) return E.ISDIR;\n const prefix = to + \"/\";\n for (const p of s.fs.files.keys()) if (p.startsWith(prefix)) return E.NOTEMPTY;\n for (const p of s.fs.dirs) if (p.startsWith(prefix)) return E.NOTEMPTY;\n recordDirRemoved(s, to);\n } else if (fileExists(s, to)) {\n if (fromIsDir) return E.NOTDIR;\n recordFileRemoved(s, to);\n }\n if (fromIsDir) {\n const mode2 = s.fs.modes.get(from);\n recordDirRemoved(s, from);\n recordDirAdded(s, to);\n if (mode2 !== void 0) s.fs.modes.set(to, mode2);\n const prefix = from + \"/\";\n const rebase = (key) => to + \"/\" + key.slice(prefix.length);\n for (const key of [...s.fs.files.keys()]) {\n if (!key.startsWith(prefix)) continue;\n const nk = rebase(key);\n const entry2 = s.fs.files.get(key);\n recordFileRemoved(s, key);\n moveMode(key, nk);\n s.fs.files.set(nk, entry2);\n s.fs.written.add(nk);\n s.fs.deleted.delete(nk);\n if (!s.fs.modes.has(nk)) s.fs.modes.set(nk, 6);\n }\n for (const key of [...s.fs.dirs]) {\n if (!key.startsWith(prefix)) continue;\n const nk = rebase(key);\n const mode22 = s.fs.modes.get(key);\n recordDirRemoved(s, key);\n recordDirAdded(s, nk);\n if (mode22 !== void 0) s.fs.modes.set(nk, mode22);\n }\n return 0;\n }\n const entry = f || { bytes: new Uint8Array(0) };\n const mode = s.fs.modes.get(from);\n recordFileRemoved(s, from);\n s.fs.files.set(to, entry);\n markWritten(s, to);\n if (mode !== void 0) s.fs.modes.set(to, mode);\n return 0;\n },\n path_create_directory: (dirfd, pathPtr, pathLen) => {\n const path = resolve(td.decode(U8().subarray(pathPtr, pathPtr + pathLen)));\n const trav = checkTraversal(s, path);\n if (trav) return trav;\n if (isDir(s, path) || fileExists(s, path)) return E.EXIST;\n const denied = checkParentWritable(s, path);\n if (denied) return denied;\n recordDirAdded(s, path);\n return 0;\n },\n // path_readlink(fd, path, path_len, buf, buf_len, *bufused). Never follows\n // the last component — reading a link is the one op that is about the link.\n path_readlink: (dirfd, pathPtr, pathLen, bufPtr, bufLen, bufUsedPtr) => {\n const r = followParents(s, resolve(td.decode(U8().subarray(pathPtr, pathPtr + pathLen))));\n if (r.errno) return r.errno;\n const target = s.fs.symlinks.get(r.path);\n if (target === void 0) return inodeExists(s, r.path) ? E.INVAL : E.NOENT;\n const bytes = te.encode(target);\n const n = Math.min(bytes.length, bufLen);\n U8().set(bytes.subarray(0, n), bufPtr);\n DV().setUint32(bufUsedPtr, n, true);\n return 0;\n },\n // path_symlink(old_path, old_path_len, fd, new_path, new_path_len) — the\n // target comes FIRST and is not a path in this filesystem: it is stored\n // verbatim and may dangle, exactly as ln -s allows.\n path_symlink: (oldPtr, oldLen, newFd, newPtr, newLen) => {\n const target = td.decode(U8().subarray(oldPtr, oldPtr + oldLen));\n const r = followParents(s, resolve(td.decode(U8().subarray(newPtr, newPtr + newLen))));\n if (r.errno) return r.errno;\n const link = r.path;\n const trav = checkTraversal(s, link);\n if (trav) return trav;\n if (inodeExists(s, link) || s.fs.symlinks.has(link)) return E.EXIST;\n const denied = checkParentWritable(s, link);\n if (denied) return denied;\n s.fs.symlinks.set(link, target);\n s.fs.symlinksCreated.set(link, target);\n s.fs.modes.set(link, 7);\n s.fs.deleted.delete(link);\n touchPath(s, link);\n return 0;\n },\n path_link: (fd1, _lookupFlags, oldPtr, oldLen, fd2, newPtr, newLen) => {\n const from = resolve(td.decode(U8().subarray(oldPtr, oldPtr + oldLen)));\n const to = resolve(td.decode(U8().subarray(newPtr, newPtr + newLen)));\n let trav = checkTraversal(s, from);\n if (trav) return trav;\n trav = checkTraversal(s, to);\n if (trav) return trav;\n if (isDir(s, from)) return E.PERM;\n const f = fileLookup(s, from);\n if (!f) return fileExists(s, from) ? E.ACCES : E.NOENT;\n if (fileExists(s, to) || isDir(s, to)) return E.EXIST;\n const denied = checkParentWritable(s, to);\n if (denied) return denied;\n s.fs.files.set(to, f);\n markWritten(s, to);\n s.fs.modes.set(to, effMode(s, from));\n return 0;\n },\n path_remove_directory: (dirfd, pathPtr, pathLen) => {\n const path = resolve(td.decode(U8().subarray(pathPtr, pathPtr + pathLen)));\n const trav = checkTraversal(s, path);\n if (trav) return trav;\n if (path === \"\" || path === \"etc\" || path === \"dev\" || path === \"bin\" || path === \"usr\" || path === \"usr/bin\") return E.ACCES;\n if (!isDir(s, path)) return fileExists(s, path) ? E.NOTDIR : E.NOENT;\n const denied = checkParentWritable(s, path);\n if (denied) return denied;\n const prefix = path + \"/\";\n for (const p of s.fs.files.keys()) if (p.startsWith(prefix)) return E.NOTEMPTY;\n for (const p of s.fs.dirs) if (p.startsWith(prefix)) return E.NOTEMPTY;\n for (const p of s.fs.modes.keys()) if (p.startsWith(prefix)) return E.NOTEMPTY;\n recordDirRemoved(s, path);\n return 0;\n },\n fd_renumber: (from, to) => {\n const e = proc.fds.get(from);\n if (!e) return E.BADF;\n if (from !== to) {\n closeFd(s, proc, to);\n proc.fds.set(to, e);\n proc.fds.delete(from);\n }\n return 0;\n },\n fd_readdir: (fd, buf, bufLen, cookie, retPtr) => {\n const e = proc.fds.get(fd);\n if (!e || e.kind !== \"dir\" && e.kind !== \"preopen\") return E.BADF;\n const dir = e.kind === \"preopen\" ? \"\" : e.path;\n if ((effMode(s, dir) & 4) === 0) return E.ACCES;\n const prefix = dir ? dir + \"/\" : \"\";\n const seen = /* @__PURE__ */ new Set([\".\", \"..\"]);\n const children = [];\n const push = (name, type) => {\n if (!seen.has(name)) {\n seen.add(name);\n children.push({ name, type });\n }\n };\n for (const p of s.fs.dirs) {\n if (p !== dir && p.startsWith(prefix) && !p.slice(prefix.length).includes(\"/\")) push(p.slice(prefix.length), 3);\n }\n for (const p of s.fs.files.keys()) {\n if (p.startsWith(prefix) && !p.slice(prefix.length).includes(\"/\")) push(p.slice(prefix.length), 4);\n }\n for (const p of s.fs.symlinks.keys()) {\n if (p.startsWith(prefix) && !p.slice(prefix.length).includes(\"/\")) push(p.slice(prefix.length), 7);\n }\n for (const p of s.fs.modes.keys()) {\n if (p !== dir && p.startsWith(prefix) && !p.slice(prefix.length).includes(\"/\") && !s.fs.dirs.has(p)) push(p.slice(prefix.length), 4);\n }\n if (dir === \"bin\" || dir === \"usr/bin\") for (const name of s.coreutils.keys()) push(name, 4);\n children.sort((a, b) => a.name < b.name ? -1 : 1);\n const entries = [{ name: \".\", type: 3 }, { name: \"..\", type: 3 }, ...children];\n const dv = DV(), u8 = U8();\n let off = 0;\n for (let i = Number(cookie); i < entries.length; i++) {\n const nb = te.encode(entries[i].name);\n const need = 24 + nb.length;\n const record = new Uint8Array(need);\n const rdv = new DataView(record.buffer);\n rdv.setBigUint64(0, BigInt(i + 1), true);\n rdv.setBigUint64(8, 1n, true);\n rdv.setUint32(16, nb.length, true);\n record[20] = entries[i].type;\n record.set(nb, 24);\n const room = bufLen - off;\n if (need > room) {\n u8.set(record.subarray(0, room), buf + off);\n off = bufLen;\n break;\n }\n u8.set(record, buf + off);\n off += need;\n }\n dv.setUint32(retPtr, off, true);\n return 0;\n },\n // An fd that was never opened is EBADF everywhere below. It used to be\n // ESPIPE from fd_seek and plain SUCCESS from fd_tell and fd_filestat_get,\n // while fd_close, fd_fdstat_get and fd_write in this same table already\n // answered EBADF — one descriptor table, five different opinions about what\n // a closed descriptor is. ESPIPE in particular is a yes: it means \"open, and\n // not seekable\", so a caller using lseek to test validity read it as valid.\n fd_seek: (fd, offset, whence, retPtr) => {\n const e = proc.fds.get(fd);\n if (!e) return E.BADF;\n if (e.kind === \"file\") {\n const f = fileLookup(s, e.path);\n const len = f ? f.bytes.length : 0;\n const off = Number(offset);\n const next = whence === 0 ? off : whence === 1 ? e.pos + off : len + off;\n if (next < 0) return E.INVAL;\n e.pos = next;\n DV().setBigUint64(retPtr, BigInt(next), true);\n return 0;\n }\n DV().setBigUint64(retPtr, 0n, true);\n return E.SPIPE;\n },\n fd_tell: (fd, retPtr) => {\n const e = proc.fds.get(fd);\n if (!e) return E.BADF;\n DV().setBigUint64(retPtr, BigInt(e.kind === \"file\" ? e.pos : 0), true);\n return 0;\n },\n fd_close: (fd) => {\n if (!proc.fds.has(fd)) return E.BADF;\n closeFd(s, proc, fd);\n return 0;\n },\n fd_fdstat_get: (fd, st) => {\n const dv = DV();\n const e = proc.fds.get(fd);\n let ft = 4;\n if (e && e.kind === \"pipe\") ft = 0;\n else if (e && (e.kind === \"stdin\" || e.kind === \"stdout\" || e.kind === \"stderr\")) ft = s.stdinTty || fd !== 0 ? 2 : 0;\n else if (e && e.kind === \"tty\") ft = 2;\n else if (e && (e.kind === \"dir\" || e.kind === \"preopen\")) ft = 3;\n else if (!e) return E.BADF;\n dv.setUint8(st, ft);\n dv.setUint16(st + 2, 0, true);\n dv.setBigUint64(st + 8, 0xffffffffffffffffn, true);\n dv.setBigUint64(st + 16, 0xffffffffffffffffn, true);\n return 0;\n },\n fd_fdstat_set_flags: () => 0,\n fd_read: (fd, iovs, n, nread) => io.read(fd, readIovs(DV(), iovs, n), nread),\n fd_write: (fd, iovs, n, nw) => {\n if (!proc.fds.has(fd)) return E.BADF;\n const dv = DV(), u8 = U8();\n let w = 0;\n for (let i = 0; i < n; i++) {\n const p = dv.getUint32(iovs + i * 8, true), l = dv.getUint32(iovs + i * 8 + 4, true);\n const n2 = io.write(fd, u8.subarray(p, p + l));\n if (n2 === null) return E.BADF;\n w += n2;\n }\n dv.setUint32(nw, w, true);\n return 0;\n },\n poll_oneoff: (inPtr, outPtr, nsubs, retPtr) => io.poll(inPtr, outPtr, nsubs, retPtr),\n clock_time_get: (id, _pr, t) => {\n const ns = clockNs(id);\n if (ns === null) return E.INVAL;\n DV().setBigUint64(t, ns, true);\n return 0;\n },\n clock_res_get: (id, r) => {\n if (clockNs(id) === null) return E.INVAL;\n DV().setBigUint64(r, id === CLOCK_REALTIME ? 1000000n : 1000n, true);\n return 0;\n },\n // getRandomValues caps at 65536 bytes per call; a guest asking for more\n // gets the same CSPRNG, chunked, never a weaker one.\n random_get: (b, l) => {\n const u = U8();\n for (let off = 0; off < l; off += 65536) {\n crypto.getRandomValues(u.subarray(b + off, b + off + Math.min(l - off, 65536)));\n }\n return 0;\n },\n proc_exit: (code) => {\n throw new Exit(code);\n }\n };\n }\n function writeThroughFd(s, proc, fd, bytes) {\n const e = proc.fds.get(fd);\n if (e && e.kind === \"pipe\") {\n if (e.end === \"r\") return null;\n const pp = s.pipes.get(e.pipeId);\n pp.chunks.push(bytes.slice());\n pp.queued += bytes.length;\n wakePipe(s, pp);\n return bytes.length;\n }\n if (e && e.kind === \"file\") {\n const f = fileLookup(s, e.path) || { bytes: new Uint8Array(0) };\n if (!s.fs.files.has(e.path)) s.fs.files.set(e.path, f);\n const pos = e.append ? f.bytes.length : e.pos;\n fileWrite(s, f, e.path, pos, bytes);\n e.pos = pos + bytes.length;\n return bytes.length;\n }\n if (e && (e.kind === \"null\" || e.kind === \"tty\")) return bytes.length;\n const text = td.decode(bytes);\n if (e.kind === \"stderr\") s.err += text;\n else s.out += text;\n return bytes.length;\n }\n function tryReadFd(s, proc, fd, dv, u8, iov, nreadPtr) {\n const e = proc.fds.get(fd);\n const deliver = (bytes) => {\n dv.setUint32(nreadPtr, scatter(u8, iov, bytes), true);\n return { errno: 0 };\n };\n if (e && e.kind === \"file\") {\n const f = fileLookup(s, e.path);\n const bytes = f ? f.bytes.subarray(e.pos, e.pos + iov.total) : new Uint8Array(0);\n e.pos += bytes.length;\n return deliver(bytes);\n }\n if (e && (e.kind === \"null\" || e.kind === \"tty\")) {\n dv.setUint32(nreadPtr, 0, true);\n return { errno: 0 };\n }\n if (e && e.kind === \"pipe\") {\n const pp = s.pipes.get(e.pipeId);\n if (pp.queued > 0) return deliver(takeUpTo(pp, iov.total));\n if (pp.writers === 0) {\n dv.setUint32(nreadPtr, 0, true);\n return { errno: 0 };\n }\n return null;\n }\n if (e && e.kind === \"stdin\") {\n const st = s.stdin;\n if (st.queued > 0) return deliver(takeUpTo(st, iov.total));\n if (st.closed) {\n dv.setUint32(nreadPtr, 0, true);\n return { errno: 0 };\n }\n return null;\n }\n if (e) {\n dv.setUint32(nreadPtr, 0, true);\n return { errno: 0 };\n }\n return { errno: E.BADF };\n }\n function readSubs(dv, inPtr, nsubs) {\n const subs = [];\n for (let i = 0; i < nsubs; i++) {\n const base = inPtr + i * 48;\n const userdata = dv.getBigUint64(base, true);\n const tag = dv.getUint8(base + 8);\n if (tag === 0) {\n const id = dv.getUint32(base + 16, true);\n const timeout = dv.getBigUint64(base + 24, true);\n const abs = (dv.getUint16(base + 40, true) & 1) !== 0;\n const now = clockNs(id);\n subs.push(now === null ? { tag, userdata, id, bad: true } : { tag, userdata, id, deadline: abs ? timeout : now + timeout });\n } else {\n subs.push({ tag, userdata, fd: dv.getUint32(base + 16, true) });\n }\n }\n return subs;\n }\n function clockExpired(sub) {\n const now = clockNs(sub.id);\n return now !== null && now >= sub.deadline;\n }\n function writeEvent(dv, outPtr, slot, sub, errno, nbytes) {\n const ev = outPtr + slot * 32;\n dv.setBigUint64(ev, sub.userdata, true);\n dv.setUint16(ev + 8, errno, true);\n dv.setUint8(ev + 10, sub.tag);\n dv.setBigUint64(ev + 16, BigInt(nbytes), true);\n dv.setUint16(ev + 24, 0, true);\n }\n function fdReadReady(s, proc, fd) {\n const e = proc.fds.get(fd);\n if (!e) return null;\n if (e.kind === \"pipe\") {\n const pp = s.pipes.get(e.pipeId);\n return { ready: pp.queued > 0 || pp.writers === 0, avail: pp.queued };\n }\n if (e.kind === \"stdin\") return { ready: s.stdin.queued > 0 || s.stdin.closed, avail: s.stdin.queued };\n return { ready: true, avail: 0 };\n }\n function emitReady(s, proc, dv, outPtr, subs) {\n let n = 0;\n for (const sub of subs) {\n if (sub.tag === 0) {\n if (sub.bad) {\n writeEvent(dv, outPtr, n++, sub, E.INVAL, 0);\n continue;\n }\n if (clockExpired(sub)) writeEvent(dv, outPtr, n++, sub, 0, 0);\n continue;\n }\n const st = fdReadReady(s, proc, sub.fd);\n if (!st) {\n writeEvent(dv, outPtr, n++, sub, E.BADF, 0);\n continue;\n }\n if (sub.tag === 2 || st.ready) writeEvent(dv, outPtr, n++, sub, 0, st.avail);\n }\n return n;\n }\n var FROZEN_PROBE = 2e5;\n function waitForDeadline(s, proc, subs) {\n const clocks = subs.filter((x) => x.tag === 0 && !x.bad);\n if (!clocks.length) return;\n const startedNs = realtimeNs();\n let spins = 0;\n while (!clocks.some(clockExpired)) {\n if (s.rootExit !== null) return;\n if (subs.some((x) => x.tag === 1 && (fdReadReady(s, proc, x.fd) || { ready: true }).ready)) return;\n if (s.runnable.length) {\n pumpOne(s);\n continue;\n }\n if (++spins > FROZEN_PROBE && realtimeNs() === startedNs) return;\n }\n }\n function emitClocks(dv, outPtr, subs) {\n let n = 0;\n for (const sub of subs) if (sub.tag === 0 && !sub.bad) writeEvent(dv, outPtr, n++, sub, 0, 0);\n return n;\n }\n function blockTarget(s, proc, fd) {\n const e = proc.fds.get(fd);\n if (e && e.kind === \"pipe\") return { list: s.pipes.get(e.pipeId).readW, wake: () => wakePipe(s, s.pipes.get(e.pipeId)) };\n if (e && e.kind === \"stdin\") return { list: s.stdin.waiters, wake: () => wakeStdin(s) };\n return null;\n }\n function makeProc(s, pid, ppid, fds) {\n const proc = {\n pid,\n ppid,\n fds,\n inst: null,\n __s: s,\n ctx: { reason: null, rewinding: false, captureEnv: 0, ljEnv: 0, ljVal: 0, nextSlot: 0, resume: 0 },\n MAIN_BUF: 0,\n SLOT0: 0,\n pendingRead: null,\n slotByEnv: /* @__PURE__ */ new Map(),\n freeSlots: []\n };\n const DV = () => new DataView(proc.inst.exports.memory.buffer);\n const U8 = () => new Uint8Array(proc.inst.exports.memory.buffer);\n proc.DV = DV;\n proc.U8 = U8;\n const slotAddr = (i) => proc.SLOT0 + i * SLOT_SIZE;\n proc.slotAddr = slotAddr;\n const initHdr = (a, sz) => {\n const dv = DV();\n dv.setUint32(a, a + 8, true);\n dv.setUint32(a + 4, a + sz, true);\n };\n proc.initHdr = initHdr;\n const wstr = (p, str) => {\n const b = te.encode(str);\n U8().set(b, p);\n return b.length;\n };\n const c = proc.ctx;\n const io = {\n read: (fd, iov, nread) => {\n if (proc.pendingRead) {\n proc.inst.exports.asyncify_stop_rewind();\n c.rewinding = false;\n const pr = proc.pendingRead;\n proc.pendingRead = null;\n DV().setUint32(pr.nreadPtr, scatter(U8(), pr.iov, pr.bytes), true);\n return 0;\n }\n const dv = DV();\n const sync = tryReadFd(s, proc, fd, dv, U8(), iov, nread);\n if (sync) return sync.errno;\n dv.setUint32(nread, 0, true);\n c.reason = \"blockread\";\n c.pipeReq = { fd, iov, nreadPtr: nread };\n initHdr(proc.MAIN_BUF, MAIN_SIZE);\n proc.inst.exports.asyncify_start_unwind(proc.MAIN_BUF);\n return 0;\n },\n write: (fd, bytes) => writeThroughFd(s, proc, fd, bytes),\n poll: (inPtr, outPtr, nsubs, retPtr) => {\n if (proc.pendingRead) {\n proc.inst.exports.asyncify_stop_rewind();\n c.rewinding = false;\n const pr = proc.pendingRead;\n proc.pendingRead = null;\n const dv2 = DV();\n dv2.setBigUint64(outPtr, pr.pollUserdata ?? 0n, true);\n dv2.setUint16(outPtr + 8, 0, true);\n dv2.setUint8(outPtr + 10, 1);\n dv2.setBigUint64(outPtr + 16, BigInt(pr.bytes.length), true);\n dv2.setUint16(outPtr + 24, 0, true);\n dv2.setUint32(retPtr, 1, true);\n return 0;\n }\n const dv = DV();\n const subs = readSubs(dv, inPtr, nsubs);\n let emitted = emitReady(s, proc, dv, outPtr, subs);\n if (emitted > 0) {\n dv.setUint32(retPtr, emitted, true);\n return 0;\n }\n const blockSub = subs.find((x) => x.tag === 1 && blockTarget(s, proc, x.fd));\n if (!blockSub) {\n waitForDeadline(s, proc, subs);\n emitted = emitReady(s, proc, dv, outPtr, subs) || emitClocks(dv, outPtr, subs);\n dv.setUint32(retPtr, emitted, true);\n return 0;\n }\n dv.setUint32(retPtr, 0, true);\n c.reason = \"blockread\";\n c.pipeReq = { fd: blockSub.fd, iov: EMPTY_IOV, nreadPtr: 0, pollUserdata: blockSub.userdata };\n initHdr(proc.MAIN_BUF, MAIN_SIZE);\n proc.inst.exports.asyncify_start_unwind(proc.MAIN_BUF);\n return 0;\n }\n };\n const wasiBase = makeWasiFs(s, proc, DV, U8, io);\n const wasi = {\n ...wasiBase,\n args_sizes_get: (a, b) => {\n const dv = DV();\n dv.setUint32(a, s.argv.length, true);\n dv.setUint32(b, s.argv.reduce((x, v) => x + te.encode(v).length + 1, 0), true);\n return 0;\n },\n args_get: (ptrs, buf) => {\n const dv = DV();\n let p = buf;\n for (const a of s.argv) {\n dv.setUint32(ptrs, p, true);\n ptrs += 4;\n p += wstr(p, a);\n U8()[p++] = 0;\n }\n return 0;\n },\n environ_sizes_get: (a, b) => {\n const dv = DV();\n dv.setUint32(a, s.environ.length, true);\n dv.setUint32(b, s.environ.reduce((x, v) => x + te.encode(v).length + 1, 0), true);\n return 0;\n },\n environ_get: (ptrs, buf) => {\n const dv = DV();\n let p = buf;\n for (const v of s.environ) {\n dv.setUint32(ptrs, p, true);\n ptrs += 4;\n p += wstr(p, v);\n U8()[p++] = 0;\n }\n return 0;\n }\n };\n const allocSlot = (env) => {\n const prev = proc.slotByEnv.get(env);\n if (prev !== void 0) {\n proc.slotByEnv.delete(env);\n proc.freeSlots.push(prev);\n }\n let idx;\n if (proc.freeSlots.length > 1) idx = proc.freeSlots.shift();\n else if (c.nextSlot < NSLOT) idx = c.nextSlot++;\n else idx = proc.freeSlots.shift();\n if (idx === void 0) throw new Error(\"bash-runner: setjmp slot budget exceeded (\" + NSLOT + \")\");\n proc.slotByEnv.set(env, idx);\n return idx;\n };\n const nimbus_proc = {\n setjmp: (env) => {\n if (c.rewinding) {\n proc.inst.exports.asyncify_stop_rewind();\n c.rewinding = false;\n return;\n }\n c.reason = \"capture\";\n c.captureEnv = env;\n const idx = allocSlot(env);\n const dv = DV();\n dv.setInt32(env, idx, true);\n dv.setInt32(env + 4, 0, true);\n initHdr(slotAddr(idx), SLOT_SIZE);\n proc.inst.exports.asyncify_start_unwind(slotAddr(idx));\n },\n longjmp: (env, val) => {\n if (c.rewinding) {\n proc.inst.exports.asyncify_stop_rewind();\n c.rewinding = false;\n return;\n }\n c.reason = \"longjmp\";\n c.ljEnv = env;\n c.ljVal = val;\n initHdr(proc.MAIN_BUF, MAIN_SIZE);\n proc.inst.exports.asyncify_start_unwind(proc.MAIN_BUF);\n },\n fork: () => {\n if (c.rewinding) {\n proc.inst.exports.asyncify_stop_rewind();\n c.rewinding = false;\n return c.resume;\n }\n c.reason = \"fork\";\n initHdr(proc.MAIN_BUF, MAIN_SIZE);\n proc.inst.exports.asyncify_start_unwind(proc.MAIN_BUF);\n return 0;\n },\n vfork: () => nimbus_proc.fork(),\n waitpid: (pid2, statusPtr, _opt) => {\n if (c.rewinding) {\n proc.inst.exports.asyncify_stop_rewind();\n c.rewinding = false;\n if (c.waitStatusPtr != null) DV().setInt32(c.waitStatusPtr, c.resumeStatus, true);\n return c.resume;\n }\n c.reason = \"waitpid\";\n c.waitTarget = pid2;\n c.waitStatusPtr = statusPtr;\n initHdr(proc.MAIN_BUF, MAIN_SIZE);\n proc.inst.exports.asyncify_start_unwind(proc.MAIN_BUF);\n return 0;\n },\n execve: (pathPtr, argvFlatPtr, argvLen, envFlatPtr, envLen) => {\n if (c.rewinding) {\n proc.inst.exports.asyncify_stop_rewind();\n c.rewinding = false;\n return c.resume;\n }\n const u8 = U8();\n let e = pathPtr;\n while (u8[e]) e++;\n c.reason = \"exec\";\n c.execPath = td.decode(u8.subarray(pathPtr, e));\n c.execArgv = td.decode(u8.subarray(argvFlatPtr, argvFlatPtr + argvLen)).split(\"\\0\").filter((x) => x.length);\n c.execEnv = td.decode(u8.subarray(envFlatPtr, envFlatPtr + envLen)).split(\"\\0\").filter((x) => x.length);\n initHdr(proc.MAIN_BUF, MAIN_SIZE);\n proc.inst.exports.asyncify_start_unwind(proc.MAIN_BUF);\n return 0;\n },\n pipe: (fdsPtr) => {\n const id = newPipe(s);\n const rfd = lowestFd(proc);\n proc.fds.set(rfd, { kind: \"pipe\", pipeId: id, end: \"r\" });\n const wfd = lowestFd(proc);\n proc.fds.set(wfd, { kind: \"pipe\", pipeId: id, end: \"w\" });\n const dv = DV();\n dv.setInt32(fdsPtr, rfd, true);\n dv.setInt32(fdsPtr + 4, wfd, true);\n return 0;\n },\n dup: (o) => {\n const e = proc.fds.get(o);\n if (!e) return -E.BADF;\n const nf = lowestFd(proc);\n proc.fds.set(nf, { ...e });\n if (e.kind === \"pipe\") bumpPipe(s, e, 1);\n return nf;\n },\n dup2: (o, n) => {\n const e = proc.fds.get(o);\n if (!e) return -E.BADF;\n if (o === n) return n;\n if (proc.fds.has(n)) closeFd(s, proc, n);\n proc.fds.set(n, { ...e });\n if (e.kind === \"pipe\") bumpPipe(s, e, 1);\n return n;\n },\n kill: () => 0,\n setpgid: () => 0,\n getpgid: () => proc.pid,\n getppid: () => proc.ppid,\n tcsetpgrp: () => 0,\n tcgetpgrp: () => proc.pid,\n tcgetattr: () => -1,\n tcsetattr: () => 0\n };\n const envImports = {\n getpid: () => proc.pid,\n getuid: () => 0,\n geteuid: () => 0,\n getgid: () => 0,\n getegid: () => 0,\n setuid: () => 0,\n setgid: () => 0,\n umask: () => 18,\n gethostname: (p, _l) => {\n U8().set(te.encode(\"nimbus\"), p);\n return 0;\n },\n dlopen: () => 0,\n dlsym: () => 0,\n dlclose: () => 0,\n dlerror: () => 0\n };\n proc.inst = new WebAssembly.Instance(s.mod, { wasi_snapshot_preview1: wasi, nimbus_proc, env: envImports });\n s.stats.instances++;\n s.procs.set(pid, proc);\n return proc;\n }\n function setupArena(proc) {\n const base = proc.inst.exports.memory.buffer.byteLength;\n const need = MAIN_SIZE + NSLOT * SLOT_SIZE;\n proc.inst.exports.memory.grow(Math.ceil(need / PAGE));\n proc.MAIN_BUF = base;\n proc.SLOT0 = proc.MAIN_BUF + MAIN_SIZE;\n }\n function resumeProc(proc) {\n proc.ctx.rewinding = true;\n proc.inst.exports.asyncify_start_rewind(proc.MAIN_BUF);\n proc.__s.runnable.push(proc);\n }\n function trackArena(s, proc, bufAddr, size, isSlot) {\n const used = proc.DV().getUint32(bufAddr, true) - (bufAddr + 8);\n if (isSlot) {\n if (used > s.stats.slotHi) s.stats.slotHi = used;\n } else if (used > s.stats.mainHi) s.stats.mainHi = used;\n }\n function step(s, proc) {\n const c = proc.ctx, ex = proc.inst.exports;\n try {\n ex._start();\n } catch (e) {\n if (e instanceof Exit) {\n finishProc(s, proc, e.code);\n return;\n }\n throw e;\n }\n if (c.reason === null) {\n finishProc(s, proc, 0);\n return;\n }\n ex.asyncify_stop_unwind();\n const r = c.reason;\n c.reason = null;\n const dv = proc.DV();\n const mem = proc.inst.exports.memory.buffer.byteLength;\n if (mem > s.stats.memPeak) s.stats.memPeak = mem;\n if (r === \"capture\") {\n const idx = dv.getInt32(c.captureEnv, true);\n trackArena(s, proc, proc.slotAddr(idx), SLOT_SIZE, true);\n dv.setUint32(c.captureEnv + 8, dv.getUint32(proc.slotAddr(idx), true), true);\n c.rewinding = true;\n ex.asyncify_start_rewind(proc.slotAddr(idx));\n s.runnable.push(proc);\n } else if (r === \"longjmp\") {\n trackArena(s, proc, proc.MAIN_BUF, MAIN_SIZE, false);\n const idx = dv.getInt32(c.ljEnv, true), hw = dv.getUint32(c.ljEnv + 8, true);\n dv.setInt32(c.ljEnv + 4, c.ljVal, true);\n dv.setUint32(proc.slotAddr(idx), hw, true);\n c.rewinding = true;\n ex.asyncify_start_rewind(proc.slotAddr(idx));\n s.runnable.push(proc);\n } else if (r === \"fork\") {\n trackArena(s, proc, proc.MAIN_BUF, MAIN_SIZE, false);\n doFork(s, proc);\n } else if (r === \"waitpid\") {\n trackArena(s, proc, proc.MAIN_BUF, MAIN_SIZE, false);\n doWait(s, proc);\n } else if (r === \"blockread\") {\n trackArena(s, proc, proc.MAIN_BUF, MAIN_SIZE, false);\n const target = blockTarget(s, proc, c.pipeReq.fd);\n if (!target) {\n proc.pendingRead = { iov: c.pipeReq.iov, bytes: new Uint8Array(0), nreadPtr: c.pipeReq.nreadPtr, pollUserdata: c.pipeReq.pollUserdata };\n resumeProc(proc);\n } else {\n target.list.push({ proc });\n target.wake();\n }\n } else if (r === \"exec\") {\n trackArena(s, proc, proc.MAIN_BUF, MAIN_SIZE, false);\n doExec(s, proc);\n } else {\n throw new Error(\"bash-runner: unknown unwind reason \" + r);\n }\n }\n function pumpOne(s) {\n if (!s.runnable.length) return false;\n step(s, s.runnable.shift());\n return true;\n }\n function doExec(s, proc) {\n const name = proc.ctx.execPath.split(\"/\").pop();\n const env = proc.ctx.execEnv && proc.ctx.execEnv.length ? proc.ctx.execEnv : s.environ;\n const pwdVar = env.find((x) => x.startsWith(\"PWD=\"));\n const childCwd = pwdVar ? norm(pwdVar.slice(4)) : s.cwd;\n const path = makeResolve(s, childCwd)(proc.ctx.execPath);\n const viaPath = path.startsWith(\"bin/\") || path.startsWith(\"usr/bin/\");\n const m = viaPath ? s.coreutils.get(name) : null;\n if (!m) {\n proc.ctx.resume = fileLookup(s, path) ? -45 : fileExists(s, path) ? -2 : -44;\n proc.ctx.rewinding = true;\n proc.inst.exports.asyncify_start_rewind(proc.MAIN_BUF);\n s.runnable.push(proc);\n return;\n }\n let inst2;\n const DV = () => new DataView(inst2.exports.memory.buffer);\n const U8 = () => new Uint8Array(inst2.exports.memory.buffer);\n const tv = proc.ctx.execArgv;\n const absRoots = /* @__PURE__ */ new Set();\n for (let i = 1; i < tv.length; i++) if (tv[i].startsWith(\"/\")) absRoots.add(norm(tv[i]));\n const wstr = (p, str) => {\n const b = te.encode(str);\n U8().set(b, p);\n return b.length;\n };\n let code = 0;\n const io = {\n read: (fd, iov, nread) => {\n const dv = DV();\n for (; ; ) {\n const sync = tryReadFd(s, proc, fd, dv, U8(), iov, nread);\n if (sync) return sync.errno;\n if (!pumpOne(s)) {\n dv.setUint32(nread, 0, true);\n return 0;\n }\n }\n },\n write: (fd, bytes) => writeThroughFd(s, proc, fd, bytes),\n // An exec'd tool cannot park, so a wait it cannot satisfy locally is spent\n // pumping the scheduler — the same discipline its blocking reads use.\n poll: (inPtr, outPtr, nsubs, retPtr) => {\n const dv = DV();\n const subs = readSubs(dv, inPtr, nsubs);\n let emitted = emitReady(s, proc, dv, outPtr, subs);\n if (emitted === 0) {\n waitForDeadline(s, proc, subs);\n emitted = emitReady(s, proc, dv, outPtr, subs) || emitClocks(dv, outPtr, subs);\n }\n dv.setUint32(retPtr, emitted, true);\n return 0;\n }\n };\n const base = makeWasiFs(s, proc, DV, U8, io, childCwd, absRoots);\n const w = {\n ...base,\n args_sizes_get: (a, b) => {\n const dv = DV();\n dv.setUint32(a, tv.length, true);\n dv.setUint32(b, tv.reduce((x, v) => x + te.encode(v).length + 1, 0), true);\n return 0;\n },\n args_get: (ptrs, buf) => {\n const dv = DV();\n let p = buf;\n for (const a of tv) {\n dv.setUint32(ptrs, p, true);\n ptrs += 4;\n p += wstr(p, a);\n U8()[p++] = 0;\n }\n return 0;\n },\n // POSIX environment inheritance: the child env bash passed to execve.\n environ_sizes_get: (a, b) => {\n const dv = DV();\n dv.setUint32(a, env.length, true);\n dv.setUint32(b, env.reduce((x, v) => x + te.encode(v).length + 1, 0), true);\n return 0;\n },\n environ_get: (ptrs, buf) => {\n const dv = DV();\n let p = buf;\n for (const v of env) {\n dv.setUint32(ptrs, p, true);\n ptrs += 4;\n p += wstr(p, v);\n U8()[p++] = 0;\n }\n return 0;\n },\n proc_exit: (ec) => {\n code = ec;\n throw new Exit(ec);\n }\n };\n const chmodResolve = makeResolve(s, childCwd, absRoots);\n const nimbus_proc = {\n chmod: (pathPtr, pathLen, mode) => {\n const p = chmodResolve(td.decode(U8().subarray(pathPtr, pathPtr + pathLen)));\n const trav = checkTraversal(s, p);\n if (trav) return trav;\n if (!inodeExists(s, p) && !(p !== \"\" && isDir(s, p))) return E.NOENT;\n const bits = mode & 511;\n s.fs.modes.set(p, bits >> 6 & 7);\n s.fs.modesChanged.set(p, bits);\n return 0;\n }\n };\n inst2 = new WebAssembly.Instance(m, { wasi_snapshot_preview1: w, nimbus_proc });\n s.stats.instances++;\n const mem2 = inst2.exports.memory.buffer.byteLength;\n if (mem2 > s.stats.memPeak) s.stats.memPeak = mem2;\n try {\n inst2.exports._start();\n } catch (e) {\n if (e instanceof Exit) code = e.code;\n else throw e;\n }\n finishProc(s, proc, code);\n }\n function doFork(s, parent) {\n const childPid = s.pidNext++;\n const childFds = /* @__PURE__ */ new Map();\n for (const [fd, e] of parent.fds) {\n childFds.set(fd, { ...e });\n if (e.kind === \"pipe\") bumpPipe(s, e, 1);\n }\n const child = makeProc(s, childPid, parent.pid, childFds);\n const pmem = parent.inst.exports.memory, cmem = child.inst.exports.memory;\n if (cmem.buffer.byteLength < pmem.buffer.byteLength) cmem.grow((pmem.buffer.byteLength - cmem.buffer.byteLength) / PAGE);\n new Uint8Array(cmem.buffer).set(new Uint8Array(pmem.buffer));\n for (const [k, v] of Object.entries(parent.inst.exports)) if (v instanceof WebAssembly.Global) child.inst.exports[k].value = v.value;\n child.MAIN_BUF = parent.MAIN_BUF;\n child.SLOT0 = parent.SLOT0;\n child.ctx.nextSlot = parent.ctx.nextSlot;\n child.slotByEnv = new Map(parent.slotByEnv);\n child.freeSlots = parent.freeSlots.slice();\n const total = s.procs.size;\n if (total * cmem.buffer.byteLength > s.stats.memPeak) s.stats.memPeak = total * cmem.buffer.byteLength;\n child.ctx.resume = 0;\n child.ctx.rewinding = true;\n child.inst.exports.asyncify_start_rewind(child.MAIN_BUF);\n s.runnable.push(child);\n parent.ctx.resume = childPid;\n parent.ctx.rewinding = true;\n parent.inst.exports.asyncify_start_rewind(parent.MAIN_BUF);\n s.runnable.push(parent);\n }\n function doWait(s, proc) {\n const t = proc.ctx.waitTarget;\n let pid = null;\n if (t > 0) {\n const e = s.exitStatus.get(t);\n if (e && e.ppid === proc.pid) pid = t;\n } else {\n for (const [p, e] of s.exitStatus) {\n if (e.ppid === proc.pid) {\n pid = p;\n break;\n }\n }\n }\n if (pid != null) {\n const st = s.exitStatus.get(pid).status;\n s.exitStatus.delete(pid);\n proc.ctx.resume = pid;\n proc.ctx.resumeStatus = st;\n resumeProc(proc);\n } else {\n s.waiters.push({ proc, targetPid: t });\n }\n }\n function finishProc(s, proc, code) {\n const st = (code & 255) << 8;\n s.procs.delete(proc.pid);\n for (const fd of [...proc.fds.keys()]) closeFd(s, proc, fd);\n if (proc.ppid === 0) s.rootExit = code;\n s.exitStatus.set(proc.pid, { status: st, ppid: proc.ppid });\n for (let i = 0; i < s.waiters.length; i++) {\n const w = s.waiters[i];\n if (w.proc.pid === proc.ppid && (w.targetPid === proc.pid || w.targetPid <= 0)) {\n s.waiters.splice(i, 1);\n w.proc.ctx.resume = proc.pid;\n w.proc.ctx.resumeStatus = st;\n s.exitStatus.delete(proc.pid);\n resumeProc(w.proc);\n break;\n }\n }\n }\n function composeFsDiff(s) {\n const filesWritten = {};\n for (const path of s.fs.written) {\n if (path.startsWith(\"etc/nimbus.bashrc\")) continue;\n const f = s.fs.files.get(path);\n if (f) filesWritten[path] = bytesToB64(f.bytes);\n }\n return {\n filesWritten,\n filesDeleted: [...s.fs.deleted],\n dirsCreated: [...s.fs.dirsCreated],\n // Deepest-first so the flush rmdirs children before their parents.\n dirsDeleted: [...s.fs.dirsDeleted].sort((a, b) => b.length - a.length),\n modesChanged: Object.fromEntries(s.fs.modesChanged),\n symlinksCreated: Object.fromEntries(s.fs.symlinksCreated)\n };\n }\n function pump(s) {\n try {\n while (s.runnable.length) {\n if (++s.steps > 5e6) throw new Error(\"bash-runner: runaway scheduler (>5M steps)\");\n step(s, s.runnable.shift());\n if (s.rootExit !== null) break;\n }\n } catch (e) {\n s.error = String(e && e.stack || e && e.message || e);\n }\n const out = s.out, err = s.err;\n s.out = \"\";\n s.err = \"\";\n const stats = { ...s.stats, steps: s.steps, missingWasi: [...s.missingWasi] };\n if (s.error) {\n S = null;\n return { state: \"error\", exitCode: 1, stdout: out, stderr: err, error: s.error, stats };\n }\n if (s.rootExit !== null || s.procs.size === 0) {\n const code = s.rootExit === null ? 0 : s.rootExit;\n const fsDiff = composeFsDiff(s);\n S = null;\n return { state: \"exited\", exitCode: code, stdout: out, stderr: err, fsDiff, stats };\n }\n if (s.stdin.waiters.length > 0) {\n return { state: \"need-input\", exitCode: 0, stdout: out, stderr: err, stats };\n }\n S = null;\n return { state: \"error\", exitCode: 1, stdout: out, stderr: err, error: \"bash-runner: deadlock \\u2014 live procs with empty run queue\", stats };\n }\n globalThis.__bashBoot = function __bashBoot(args) {\n try {\n S = newSession(args);\n initStdinQueued(S);\n const root = makeProc(S, S.pidNext++, 0, /* @__PURE__ */ new Map([\n [0, { kind: \"stdin\" }],\n [1, { kind: \"stdout\" }],\n [2, { kind: \"stderr\" }],\n [3, { kind: \"preopen\" }]\n ]));\n S.rootPid = root.pid;\n setupArena(root);\n S.runnable.push(root);\n return pump(S);\n } catch (e) {\n S = null;\n return { state: \"error\", exitCode: 1, stdout: \"\", stderr: \"\", error: \"boot failed: \" + String(e && e.message || e) };\n }\n };\n globalThis.__bashFeed = function __bashFeed(args) {\n if (!S) {\n return { state: \"error\", exitCode: 1, stdout: \"\", stderr: \"\", error: \"bash facet has no active session (warm isolate recycled?)\" };\n }\n try {\n if (args.data) {\n const b = te.encode(args.data);\n S.stdin.chunks.push(b);\n S.stdin.queued += b.length;\n }\n if (args.eof) S.stdin.closed = true;\n wakeStdin(S);\n return pump(S);\n } catch (e) {\n const s = S;\n S = null;\n return { state: \"error\", exitCode: 1, stdout: s ? s.out : \"\", stderr: s ? s.err : \"\", error: \"feed failed: \" + String(e && e.message || e) };\n }\n };\n})();\n";