@nimbus-sh/worker 0.1.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.
- package/README.md +219 -0
- package/dist/_shared/bytes.d.ts +18 -0
- package/dist/_shared/bytes.d.ts.map +1 -0
- package/dist/_shared/bytes.js +17 -0
- package/dist/_shared/cache-stats.d.ts +146 -0
- package/dist/_shared/cache-stats.d.ts.map +1 -0
- package/dist/_shared/cache-stats.js +162 -0
- package/dist/_shared/exports-resolver.d.ts +76 -0
- package/dist/_shared/exports-resolver.d.ts.map +1 -0
- package/dist/_shared/exports-resolver.js +284 -0
- package/dist/_shared/install-phase.d.ts +47 -0
- package/dist/_shared/install-phase.d.ts.map +1 -0
- package/dist/_shared/install-phase.js +1 -0
- package/dist/_shared/real-node-imports.d.ts +38 -0
- package/dist/_shared/real-node-imports.d.ts.map +1 -0
- package/dist/_shared/real-node-imports.js +46 -0
- package/dist/_shared/retry.d.ts +93 -0
- package/dist/_shared/retry.d.ts.map +1 -0
- package/dist/_shared/retry.js +161 -0
- package/dist/_shared/session-id.d.ts +39 -0
- package/dist/_shared/session-id.d.ts.map +1 -0
- package/dist/_shared/session-id.js +127 -0
- package/dist/_shared/session-router.d.ts +104 -0
- package/dist/_shared/session-router.d.ts.map +1 -0
- package/dist/_shared/session-router.js +165 -0
- package/dist/_shared/w7-frame.d.ts +93 -0
- package/dist/_shared/w7-frame.d.ts.map +1 -0
- package/dist/_shared/w7-frame.js +402 -0
- package/dist/auth/index.d.ts +16 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +15 -0
- package/dist/auth/middleware.d.ts +85 -0
- package/dist/auth/middleware.d.ts.map +1 -0
- package/dist/auth/middleware.js +150 -0
- package/dist/auth/token.d.ts +96 -0
- package/dist/auth/token.d.ts.map +1 -0
- package/dist/auth/token.js +226 -0
- package/dist/auth/types.d.ts +141 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +114 -0
- package/dist/bindings/d1.d.ts +102 -0
- package/dist/bindings/d1.d.ts.map +1 -0
- package/dist/bindings/d1.js +549 -0
- package/dist/bindings/kv.d.ts +82 -0
- package/dist/bindings/kv.d.ts.map +1 -0
- package/dist/bindings/kv.js +290 -0
- package/dist/bindings/r2.d.ts +137 -0
- package/dist/bindings/r2.d.ts.map +1 -0
- package/dist/bindings/r2.js +379 -0
- package/dist/cirrus-npm-cjs.generated.d.ts +17 -0
- package/dist/cirrus-npm-cjs.generated.d.ts.map +1 -0
- package/dist/cirrus-npm-cjs.generated.js +41 -0
- package/dist/cirrus-plugin-react.generated.d.ts +34 -0
- package/dist/cirrus-plugin-react.generated.d.ts.map +1 -0
- package/dist/cirrus-plugin-react.generated.js +33 -0
- package/dist/constants.d.ts +34 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +116 -0
- package/dist/esbuild-wasm-bundle.generated.d.ts +41 -0
- package/dist/esbuild-wasm-bundle.generated.d.ts.map +1 -0
- package/dist/esbuild-wasm-bundle.generated.js +40 -0
- package/dist/facets/cirrus-real.d.ts +147 -0
- package/dist/facets/cirrus-real.d.ts.map +1 -0
- package/dist/facets/cirrus-real.js +981 -0
- package/dist/facets/inner-do-registry.d.ts +41 -0
- package/dist/facets/inner-do-registry.d.ts.map +1 -0
- package/dist/facets/inner-do-registry.js +51 -0
- package/dist/facets/manager.d.ts +314 -0
- package/dist/facets/manager.d.ts.map +1 -0
- package/dist/facets/manager.js +2681 -0
- package/dist/facets/process.d.ts +318 -0
- package/dist/facets/process.d.ts.map +1 -0
- package/dist/facets/process.js +567 -0
- package/dist/facets/real-vite-fs-shim.d.ts +151 -0
- package/dist/facets/real-vite-fs-shim.d.ts.map +1 -0
- package/dist/facets/real-vite-fs-shim.js +1279 -0
- package/dist/facets/real-vite-hmr.d.ts +142 -0
- package/dist/facets/real-vite-hmr.d.ts.map +1 -0
- package/dist/facets/real-vite-hmr.js +476 -0
- package/dist/facets/vite-dev-server.d.ts +293 -0
- package/dist/facets/vite-dev-server.d.ts.map +1 -0
- package/dist/facets/vite-dev-server.js +2580 -0
- package/dist/facets/wasm-swap-registry.d.ts +261 -0
- package/dist/facets/wasm-swap-registry.d.ts.map +1 -0
- package/dist/facets/wasm-swap-registry.js +551 -0
- package/dist/facets/ws-terminal.d.ts +107 -0
- package/dist/facets/ws-terminal.d.ts.map +1 -0
- package/dist/facets/ws-terminal.js +207 -0
- package/dist/git/commands.d.ts +13 -0
- package/dist/git/commands.d.ts.map +1 -0
- package/dist/git/commands.js +633 -0
- package/dist/git/network-facet.d.ts +61 -0
- package/dist/git/network-facet.d.ts.map +1 -0
- package/dist/git/network-facet.js +700 -0
- package/dist/git-bundle.generated.d.ts +16 -0
- package/dist/git-bundle.generated.d.ts.map +1 -0
- package/dist/git-bundle.generated.js +15 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +73 -0
- package/dist/loaders/child-process/spawn-facet.d.ts +90 -0
- package/dist/loaders/child-process/spawn-facet.d.ts.map +1 -0
- package/dist/loaders/child-process/spawn-facet.js +103 -0
- package/dist/loaders/child-process/spawn-pool.d.ts +110 -0
- package/dist/loaders/child-process/spawn-pool.d.ts.map +1 -0
- package/dist/loaders/child-process/spawn-pool.js +161 -0
- package/dist/loaders/fanout-pool.d.ts +195 -0
- package/dist/loaders/fanout-pool.d.ts.map +1 -0
- package/dist/loaders/fanout-pool.js +326 -0
- package/dist/loaders/generated-workers.d.ts +26 -0
- package/dist/loaders/generated-workers.d.ts.map +1 -0
- package/dist/loaders/generated-workers.js +25 -0
- package/dist/loaders/index.d.ts +15 -0
- package/dist/loaders/index.d.ts.map +1 -0
- package/dist/loaders/index.js +13 -0
- package/dist/loaders/loader-pool.d.ts +273 -0
- package/dist/loaders/loader-pool.d.ts.map +1 -0
- package/dist/loaders/loader-pool.js +624 -0
- package/dist/loaders/npm-resolve-preamble.d.ts +34 -0
- package/dist/loaders/npm-resolve-preamble.d.ts.map +1 -0
- package/dist/loaders/npm-resolve-preamble.js +237 -0
- package/dist/loaders/pre-bundle-preamble.d.ts +41 -0
- package/dist/loaders/pre-bundle-preamble.d.ts.map +1 -0
- package/dist/loaders/pre-bundle-preamble.js +114 -0
- package/dist/loaders/vendor/codegen.d.ts +24 -0
- package/dist/loaders/vendor/codegen.d.ts.map +1 -0
- package/dist/loaders/vendor/codegen.js +44 -0
- package/dist/loaders/vendor/errors.d.ts +24 -0
- package/dist/loaders/vendor/errors.d.ts.map +1 -0
- package/dist/loaders/vendor/errors.js +46 -0
- package/dist/loaders/vendor/index.d.ts +16 -0
- package/dist/loaders/vendor/index.d.ts.map +1 -0
- package/dist/loaders/vendor/index.js +11 -0
- package/dist/loaders/vendor/pool.d.ts +88 -0
- package/dist/loaders/vendor/pool.d.ts.map +1 -0
- package/dist/loaders/vendor/pool.js +353 -0
- package/dist/loaders/vendor/primitives.d.ts +11 -0
- package/dist/loaders/vendor/primitives.d.ts.map +1 -0
- package/dist/loaders/vendor/primitives.js +33 -0
- package/dist/loaders/vendor/serialize.d.ts +3 -0
- package/dist/loaders/vendor/serialize.d.ts.map +1 -0
- package/dist/loaders/vendor/serialize.js +25 -0
- package/dist/loaders/vendor/types.d.ts +60 -0
- package/dist/loaders/vendor/types.d.ts.map +1 -0
- package/dist/loaders/vendor/types.js +4 -0
- package/dist/npm/cache.d.ts +136 -0
- package/dist/npm/cache.d.ts.map +1 -0
- package/dist/npm/cache.js +429 -0
- package/dist/npm/install-batch-facet.d.ts +129 -0
- package/dist/npm/install-batch-facet.d.ts.map +1 -0
- package/dist/npm/install-batch-facet.js +598 -0
- package/dist/npm/install-facet.d.ts +29 -0
- package/dist/npm/install-facet.d.ts.map +1 -0
- package/dist/npm/install-facet.js +9 -0
- package/dist/npm/installer.d.ts +239 -0
- package/dist/npm/installer.d.ts.map +1 -0
- package/dist/npm/installer.js +2022 -0
- package/dist/npm/npx-install.d.ts +74 -0
- package/dist/npm/npx-install.d.ts.map +1 -0
- package/dist/npm/npx-install.js +302 -0
- package/dist/npm/pre-bundle-facet.d.ts +168 -0
- package/dist/npm/pre-bundle-facet.d.ts.map +1 -0
- package/dist/npm/pre-bundle-facet.js +653 -0
- package/dist/npm/r2-cache.d.ts +286 -0
- package/dist/npm/r2-cache.d.ts.map +1 -0
- package/dist/npm/r2-cache.js +506 -0
- package/dist/npm/resolve-facet.d.ts +202 -0
- package/dist/npm/resolve-facet.d.ts.map +1 -0
- package/dist/npm/resolve-facet.js +833 -0
- package/dist/npm/resolve-one-facet.d.ts +204 -0
- package/dist/npm/resolve-one-facet.d.ts.map +1 -0
- package/dist/npm/resolve-one-facet.js +437 -0
- package/dist/npm/resolver.d.ts +109 -0
- package/dist/npm/resolver.d.ts.map +1 -0
- package/dist/npm/resolver.js +899 -0
- package/dist/npm/tarball-stream.d.ts +85 -0
- package/dist/npm/tarball-stream.d.ts.map +1 -0
- package/dist/npm/tarball-stream.js +205 -0
- package/dist/npm/tarball.d.ts +51 -0
- package/dist/npm/tarball.d.ts.map +1 -0
- package/dist/npm/tarball.js +185 -0
- package/dist/observability/diag-counters.d.ts +292 -0
- package/dist/observability/diag-counters.d.ts.map +1 -0
- package/dist/observability/diag-counters.js +302 -0
- package/dist/observability/heap-estimate.d.ts +166 -0
- package/dist/observability/heap-estimate.d.ts.map +1 -0
- package/dist/observability/heap-estimate.js +192 -0
- package/dist/observability/heavy-alloc-coord.d.ts +95 -0
- package/dist/observability/heavy-alloc-coord.d.ts.map +1 -0
- package/dist/observability/heavy-alloc-coord.js +149 -0
- package/dist/observability/oom-classify.d.ts +56 -0
- package/dist/observability/oom-classify.d.ts.map +1 -0
- package/dist/observability/oom-classify.js +118 -0
- package/dist/observability/oom-discriminator.d.ts +192 -0
- package/dist/observability/oom-discriminator.d.ts.map +1 -0
- package/dist/observability/oom-discriminator.js +233 -0
- package/dist/real-vite-bundle.generated.d.ts +46 -0
- package/dist/real-vite-bundle.generated.d.ts.map +1 -0
- package/dist/real-vite-bundle.generated.js +53 -0
- package/dist/replica/routing.d.ts +171 -0
- package/dist/replica/routing.d.ts.map +1 -0
- package/dist/replica/routing.js +288 -0
- package/dist/replica/suspension.d.ts +36 -0
- package/dist/replica/suspension.d.ts.map +1 -0
- package/dist/replica/suspension.js +49 -0
- package/dist/router/index.d.ts +156 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/index.js +220 -0
- package/dist/runtime/assets-loader.d.ts +71 -0
- package/dist/runtime/assets-loader.d.ts.map +1 -0
- package/dist/runtime/assets-loader.js +128 -0
- package/dist/runtime/barrel-detect.d.ts +46 -0
- package/dist/runtime/barrel-detect.d.ts.map +1 -0
- package/dist/runtime/barrel-detect.js +73 -0
- package/dist/runtime/barrel-synthesizer.d.ts +181 -0
- package/dist/runtime/barrel-synthesizer.d.ts.map +1 -0
- package/dist/runtime/barrel-synthesizer.js +541 -0
- package/dist/runtime/bun-repl.d.ts +46 -0
- package/dist/runtime/bun-repl.d.ts.map +1 -0
- package/dist/runtime/bun-repl.js +326 -0
- package/dist/runtime/bun-runner.d.ts +66 -0
- package/dist/runtime/bun-runner.d.ts.map +1 -0
- package/dist/runtime/bun-runner.js +219 -0
- package/dist/runtime/clang-runner.d.ts +38 -0
- package/dist/runtime/clang-runner.d.ts.map +1 -0
- package/dist/runtime/clang-runner.js +1033 -0
- package/dist/runtime/comment-strip.d.ts +37 -0
- package/dist/runtime/comment-strip.d.ts.map +1 -0
- package/dist/runtime/comment-strip.js +74 -0
- package/dist/runtime/esbuild-service.d.ts +201 -0
- package/dist/runtime/esbuild-service.d.ts.map +1 -0
- package/dist/runtime/esbuild-service.js +1444 -0
- package/dist/runtime/esbuild-wasm-bytes.d.ts +87 -0
- package/dist/runtime/esbuild-wasm-bytes.d.ts.map +1 -0
- package/dist/runtime/esbuild-wasm-bytes.js +131 -0
- package/dist/runtime/framework-detect.d.ts +71 -0
- package/dist/runtime/framework-detect.d.ts.map +1 -0
- package/dist/runtime/framework-detect.js +178 -0
- package/dist/runtime/long-running-handle.d.ts +133 -0
- package/dist/runtime/long-running-handle.d.ts.map +1 -0
- package/dist/runtime/long-running-handle.js +192 -0
- package/dist/runtime/node-repl.d.ts +36 -0
- package/dist/runtime/node-repl.d.ts.map +1 -0
- package/dist/runtime/node-repl.js +293 -0
- package/dist/runtime/node-runner.d.ts +111 -0
- package/dist/runtime/node-runner.d.ts.map +1 -0
- package/dist/runtime/node-runner.js +194 -0
- package/dist/runtime/node-shims.d.ts +24 -0
- package/dist/runtime/node-shims.d.ts.map +1 -0
- package/dist/runtime/node-shims.js +3019 -0
- package/dist/runtime/package-manager.d.ts +118 -0
- package/dist/runtime/package-manager.d.ts.map +1 -0
- package/dist/runtime/package-manager.js +420 -0
- package/dist/runtime/port-registry.d.ts +80 -0
- package/dist/runtime/port-registry.d.ts.map +1 -0
- package/dist/runtime/port-registry.js +166 -0
- package/dist/runtime/process-logs-api.d.ts +102 -0
- package/dist/runtime/process-logs-api.d.ts.map +1 -0
- package/dist/runtime/process-logs-api.js +253 -0
- package/dist/runtime/process-logs.d.ts +311 -0
- package/dist/runtime/process-logs.d.ts.map +1 -0
- package/dist/runtime/process-logs.js +656 -0
- package/dist/runtime/process-table.d.ts +65 -0
- package/dist/runtime/process-table.d.ts.map +1 -0
- package/dist/runtime/process-table.js +107 -0
- package/dist/runtime/project-detect.d.ts +21 -0
- package/dist/runtime/project-detect.d.ts.map +1 -0
- package/dist/runtime/project-detect.js +46 -0
- package/dist/runtime/python-repl.d.ts +66 -0
- package/dist/runtime/python-repl.d.ts.map +1 -0
- package/dist/runtime/python-repl.js +563 -0
- package/dist/runtime/python-runner.d.ts +50 -0
- package/dist/runtime/python-runner.d.ts.map +1 -0
- package/dist/runtime/python-runner.js +870 -0
- package/dist/runtime/repl-session.d.ts +160 -0
- package/dist/runtime/repl-session.d.ts.map +1 -0
- package/dist/runtime/repl-session.js +393 -0
- package/dist/runtime/require-resolver.d.ts +52 -0
- package/dist/runtime/require-resolver.d.ts.map +1 -0
- package/dist/runtime/require-resolver.js +690 -0
- package/dist/runtime/router-basename.d.ts +36 -0
- package/dist/runtime/router-basename.d.ts.map +1 -0
- package/dist/runtime/router-basename.js +310 -0
- package/dist/runtime/ruby-repl.d.ts +43 -0
- package/dist/runtime/ruby-repl.d.ts.map +1 -0
- package/dist/runtime/ruby-repl.js +229 -0
- package/dist/runtime/ruby-runner.d.ts +62 -0
- package/dist/runtime/ruby-runner.d.ts.map +1 -0
- package/dist/runtime/ruby-runner.js +737 -0
- package/dist/runtime/runtime-catalog.d.ts +99 -0
- package/dist/runtime/runtime-catalog.d.ts.map +1 -0
- package/dist/runtime/runtime-catalog.js +183 -0
- package/dist/runtime/runtime-registry.d.ts +136 -0
- package/dist/runtime/runtime-registry.d.ts.map +1 -0
- package/dist/runtime/runtime-registry.js +318 -0
- package/dist/runtime/streams.d.ts +13 -0
- package/dist/runtime/streams.d.ts.map +1 -0
- package/dist/runtime/streams.js +402 -0
- package/dist/runtime/wasi-instance.d.ts +190 -0
- package/dist/runtime/wasi-instance.d.ts.map +1 -0
- package/dist/runtime/wasi-instance.js +2044 -0
- package/dist/runtime/wasm-runner.d.ts +117 -0
- package/dist/runtime/wasm-runner.d.ts.map +1 -0
- package/dist/runtime/wasm-runner.js +705 -0
- package/dist/session/bindings.d.ts +168 -0
- package/dist/session/bindings.d.ts.map +1 -0
- package/dist/session/bindings.js +524 -0
- package/dist/session/ctx-exports.d.ts +16 -0
- package/dist/session/ctx-exports.d.ts.map +1 -0
- package/dist/session/ctx-exports.js +22 -0
- package/dist/session/diag.d.ts +62 -0
- package/dist/session/diag.d.ts.map +1 -0
- package/dist/session/diag.js +118 -0
- package/dist/session/fs-watch.d.ts +132 -0
- package/dist/session/fs-watch.d.ts.map +1 -0
- package/dist/session/fs-watch.js +255 -0
- package/dist/session/helpers.d.ts +150 -0
- package/dist/session/helpers.d.ts.map +1 -0
- package/dist/session/helpers.js +472 -0
- package/dist/session/hibernation.d.ts +145 -0
- package/dist/session/hibernation.d.ts.map +1 -0
- package/dist/session/hibernation.js +385 -0
- package/dist/session/init-phases.d.ts +87 -0
- package/dist/session/init-phases.d.ts.map +1 -0
- package/dist/session/init-phases.js +137 -0
- package/dist/session/init.d.ts +46 -0
- package/dist/session/init.d.ts.map +1 -0
- package/dist/session/init.js +3579 -0
- package/dist/session/keys.d.ts +56 -0
- package/dist/session/keys.d.ts.map +1 -0
- package/dist/session/keys.js +55 -0
- package/dist/session/nimbus-session.d.ts +434 -0
- package/dist/session/nimbus-session.d.ts.map +1 -0
- package/dist/session/nimbus-session.js +1084 -0
- package/dist/session/programmatic.d.ts +105 -0
- package/dist/session/programmatic.d.ts.map +1 -0
- package/dist/session/programmatic.js +326 -0
- package/dist/session/replica-routes.d.ts +68 -0
- package/dist/session/replica-routes.d.ts.map +1 -0
- package/dist/session/replica-routes.js +77 -0
- package/dist/session/routes.d.ts +29 -0
- package/dist/session/routes.d.ts.map +1 -0
- package/dist/session/routes.js +1331 -0
- package/dist/session/rpc.d.ts +323 -0
- package/dist/session/rpc.d.ts.map +1 -0
- package/dist/session/rpc.js +808 -0
- package/dist/session/state-store.d.ts +210 -0
- package/dist/session/state-store.d.ts.map +1 -0
- package/dist/session/state-store.js +459 -0
- package/dist/session/supervisor-rpc.d.ts +269 -0
- package/dist/session/supervisor-rpc.d.ts.map +1 -0
- package/dist/session/supervisor-rpc.js +437 -0
- package/dist/session/ws-hibernation-config.d.ts +64 -0
- package/dist/session/ws-hibernation-config.d.ts.map +1 -0
- package/dist/session/ws-hibernation-config.js +89 -0
- package/dist/session/ws.d.ts +95 -0
- package/dist/session/ws.d.ts.map +1 -0
- package/dist/session/ws.js +395 -0
- package/dist/shell/features.d.ts +410 -0
- package/dist/shell/features.d.ts.map +1 -0
- package/dist/shell/features.js +1885 -0
- package/dist/shell/unix-commands.d.ts +15 -0
- package/dist/shell/unix-commands.d.ts.map +1 -0
- package/dist/shell/unix-commands.js +3703 -0
- package/dist/stubs/lifo-ui.d.ts +10 -0
- package/dist/stubs/lifo-ui.d.ts.map +1 -0
- package/dist/stubs/lifo-ui.js +1 -0
- package/dist/tailwind-play.generated.d.ts +19 -0
- package/dist/tailwind-play.generated.d.ts.map +1 -0
- package/dist/tailwind-play.generated.js +18 -0
- package/dist/vfs/dev-provider.d.ts +64 -0
- package/dist/vfs/dev-provider.d.ts.map +1 -0
- package/dist/vfs/dev-provider.js +191 -0
- package/dist/vfs/events.d.ts +51 -0
- package/dist/vfs/events.d.ts.map +1 -0
- package/dist/vfs/events.js +127 -0
- package/dist/vfs/path.d.ts +30 -0
- package/dist/vfs/path.d.ts.map +1 -0
- package/dist/vfs/path.js +41 -0
- package/dist/vfs/seed-project.d.ts +62 -0
- package/dist/vfs/seed-project.d.ts.map +1 -0
- package/dist/vfs/seed-project.js +1045 -0
- package/dist/vfs/sqlite-vfs.d.ts +435 -0
- package/dist/vfs/sqlite-vfs.d.ts.map +1 -0
- package/dist/vfs/sqlite-vfs.js +1670 -0
- package/dist/vfs/symlink-registry.d.ts +64 -0
- package/dist/vfs/symlink-registry.d.ts.map +1 -0
- package/dist/vfs/symlink-registry.js +137 -0
- package/dist/wrangler/nimbus-wrangler.d.ts +157 -0
- package/dist/wrangler/nimbus-wrangler.d.ts.map +1 -0
- package/dist/wrangler/nimbus-wrangler.js +931 -0
- package/package.json +77 -0
- package/public/_assets/cirrus-plugin-react.bundle.js +70740 -0
- package/public/_assets/esbuild-0.24.2.wasm +0 -0
- package/public/_assets/real-vite-bundle.js +101478 -0
- package/public/_assets/rollup.wasm +0 -0
- package/public/favicon.svg +8 -0
- package/public/index.html +565 -0
- package/public/s/index.html +2379 -0
- package/scripts/bundle-esbuild-wasm.mjs +244 -0
- package/scripts/bundle-facet-workers.mjs +137 -0
- package/scripts/bundle-git.mjs +118 -0
- package/scripts/bundle-npm-cjs.mjs +363 -0
- package/scripts/bundle-plugin-react.mjs +480 -0
- package/scripts/bundle-real-vite.mjs +959 -0
- package/scripts/bundle-runtime.mjs +600 -0
- package/scripts/bundle-tailwind-play.mjs +130 -0
- package/scripts/patch-lifo-ui.mjs +131 -0
|
@@ -0,0 +1,1084 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nimbus-session.ts — NimbusSession Durable Object (v2.0).
|
|
3
|
+
*
|
|
4
|
+
* The supervisor DO that owns the VFS, shell, and all commands.
|
|
5
|
+
* `node` execution is delegated to dynamic workers via LOADER.load().
|
|
6
|
+
* IPC between facets and the supervisor flows through SupervisorRPC.
|
|
7
|
+
*/
|
|
8
|
+
import { DurableObject as CloudflareDurableObject } from 'cloudflare:workers';
|
|
9
|
+
import { SqliteVFS } from '../vfs/sqlite-vfs.js';
|
|
10
|
+
import { FacetManager } from '../facets/manager.js';
|
|
11
|
+
import { FacetProcessManager } from '../facets/process.js';
|
|
12
|
+
import { ChildProcessSpawnPool } from '../loaders/child-process/spawn-pool.js';
|
|
13
|
+
import { ProcessTable } from '../runtime/process-table.js';
|
|
14
|
+
import { ProcessLogStore } from '../runtime/process-logs.js';
|
|
15
|
+
import { PortRegistry } from '../runtime/port-registry.js';
|
|
16
|
+
import { EsbuildService } from '../runtime/esbuild-service.js';
|
|
17
|
+
import { registerAllocObserver } from '../observability/heavy-alloc-coord.js';
|
|
18
|
+
import { NpmInstaller } from '../npm/installer.js';
|
|
19
|
+
// S10: oom-discriminator helpers (recordFailure, getFailures,
|
|
20
|
+
// getLastRpcFrame, getLastFacetId, snapshotForStorage, rehydrateFromStorage)
|
|
21
|
+
// moved to sibling modules (-rpc uses recordFailure for _reportExternalExit;
|
|
22
|
+
// -ws uses recordFailure for webSocketError; -diag uses getFailures +
|
|
23
|
+
// snapshotForStorage + rehydrateFromStorage; -routes uses getFailures +
|
|
24
|
+
// getLastRpcFrame + getLastFacetId for /api/_diag/memory). Class file
|
|
25
|
+
// no longer references any of them directly.
|
|
26
|
+
// S10: classifyError, LRU_MAX_ENTRIES, fetchEsbuildWasmBytes moved to
|
|
27
|
+
// sibling modules (-rpc, -routes, esbuild-wasm-bytes); class file no
|
|
28
|
+
// longer references them directly. Phase 2 A'.5 renamed the function
|
|
29
|
+
// (was getEsbuildWasmBytes; cached) to fetchEsbuildWasmBytes (no
|
|
30
|
+
// supervisor cache; goes through env.ASSETS on demand).
|
|
31
|
+
import { setCtxExports } from './ctx-exports.js';
|
|
32
|
+
import { NIMBUS_VERSION, DEFAULT_HOSTNAME, CF_COMPAT_DATE } from '../constants.js';
|
|
33
|
+
import { seedProject } from '../vfs/seed-project.js';
|
|
34
|
+
import { BASE_PATH_HEADER } from '../_shared/session-router.js';
|
|
35
|
+
import { notifyTerminalEvent } from '../runtime/process-logs-api.js';
|
|
36
|
+
// S3: tryEnableReplicas + getReplicaState extracted to ./nimbus-session-replica.ts.
|
|
37
|
+
import { wireReplicasOnConstruct as _w12WireReplicasOnConstruct, getReplicaState as _w12GetReplicaState, } from './replica-routes.js';
|
|
38
|
+
// S5: storage-key constants moved to ./nimbus-session-keys.ts; consumed by
|
|
39
|
+
// sibling modules (-hib, -diag, -ws). The class file itself no longer
|
|
40
|
+
// references any storage key directly.
|
|
41
|
+
// S4: W9 hibernation surface extracted.
|
|
42
|
+
import { wireHibernationOnConstruct as _w9WireHibernationOnConstruct, wireProcessLogPersist as _w9DoWireProcessLogPersist, ensureHibSchema as _w9DoEnsureHibSchema, scheduleHibFlush as _w9DoScheduleHibFlush, dispatchAlarm as _w9DoDispatchAlarm, maybeBumpIsolateGen as _w9DoMaybeBumpIsolateGen, flushOnClose as _w9DoFlushOnClose, } from './hibernation.js';
|
|
43
|
+
// S6: initSession (1875 LOC of cmd registrations + boot wiring) extracted.
|
|
44
|
+
import { initSession as _w11InitSession } from './init.js';
|
|
45
|
+
// S7: webSocket lifecycle (message, close, error, F1 discriminator,
|
|
46
|
+
// _w5SafePersistRing) extracted.
|
|
47
|
+
import { wsMessage as _wsDoMessage, wsClose as _wsDoClose, wsError as _wsDoError, safePersistRing as _wsDoSafePersistRing, } from './ws.js';
|
|
48
|
+
// S8: Supervisor RPC + W8 cp* + legacy VFS impls extracted.
|
|
49
|
+
import * as _rpc from './rpc.js';
|
|
50
|
+
// S9: HTTP fetch routing extracted (combined S9a + S9b).
|
|
51
|
+
import * as _routes from './routes.js';
|
|
52
|
+
// Programmatic SDK RPC surface.
|
|
53
|
+
import * as _programmatic from './programmatic.js';
|
|
54
|
+
// S10: heap probe + W5 OOM-ring persistence extracted.
|
|
55
|
+
import * as _diag from './diag.js';
|
|
56
|
+
// ── Pure helpers extracted to ./nimbus-session-helpers.ts (S1) ────────
|
|
57
|
+
//
|
|
58
|
+
// renderNoDevServerHtml, BUNDLER_BIN_PREFIXES, NIMBUS_UNSUPPORTED_BINS,
|
|
59
|
+
// WRANGLER_IGNORED_FLAGS{,_WITH_VALUE}, WRANGLER_UNSUPPORTED_CONFIG_FIELDS,
|
|
60
|
+
// filterWranglerFlags, detectUnsupportedWranglerConfig, _CP_FACET_DIRECT,
|
|
61
|
+
// _CP_PURE_BUILTIN, _classifyCommand, detectBundlerBin, checkNodeModulesGuard
|
|
62
|
+
// all live in the helpers module now.
|
|
63
|
+
//
|
|
64
|
+
// They are imported here (so call sites in this file work unchanged) and
|
|
65
|
+
// re-exported (so external callers importing them from
|
|
66
|
+
// `./nimbus-session.js` keep working — back-compat).
|
|
67
|
+
//
|
|
68
|
+
// (esbuild wasm bytes are fetched from env.ASSETS by
|
|
69
|
+
// src/esbuild-wasm-bytes.ts at pool-construction time; A'.5 dropped
|
|
70
|
+
// the supervisor-resident cache + the SUPERVISOR.getEsbuildWasm RPC.)
|
|
71
|
+
// Helpers needed by this class file's own logic (not just re-export).
|
|
72
|
+
import { _classifyCommand, } from './helpers.js';
|
|
73
|
+
// Re-exports preserved for callers that import from nimbus-session
|
|
74
|
+
// directly (the historical entry point). Each one has a dedicated
|
|
75
|
+
// import site elsewhere in the codebase.
|
|
76
|
+
export { filterWranglerFlags, detectBundlerBin, checkNodeModulesGuard, detectUnsupportedWranglerConfig, renderNoDevServerHtml, BUNDLER_BIN_PREFIXES, NIMBUS_UNSUPPORTED_BINS, WRANGLER_IGNORED_FLAGS, WRANGLER_IGNORED_FLAGS_WITH_VALUE, WRANGLER_UNSUPPORTED_CONFIG_FIELDS, } from './helpers.js';
|
|
77
|
+
// W10: detectCloudflareWorkersProject lives in src/project-detect.ts so
|
|
78
|
+
// unit-level tests can import it without pulling in cloudflare:workers.
|
|
79
|
+
// Re-export here so the existing import surface (callers that already
|
|
80
|
+
// import from nimbus-session) continues to work.
|
|
81
|
+
export { detectCloudflareWorkersProject } from '../runtime/project-detect.js';
|
|
82
|
+
/**
|
|
83
|
+
* Render the welcome MOTD banner with column-counted padding so every
|
|
84
|
+
* line lands the right ║ on the same column regardless of how many
|
|
85
|
+
* non-ASCII characters (em-dash, middle-dot) the content contains.
|
|
86
|
+
*
|
|
87
|
+
* Width model: each "row" is built by left-aligning content into a
|
|
88
|
+
* fixed inner-width slot, then bracketed by ║ and padded with spaces.
|
|
89
|
+
* Codepoint count is used as the column count. This is correct for
|
|
90
|
+
* all characters in the current banner — box-drawing (U+2500-U+257F),
|
|
91
|
+
* em-dash (U+2014), and middle-dot (U+00B7) are all defined as
|
|
92
|
+
* 1-cell-wide in the Unicode East Asian Width table (`Na`/`A`/`N`).
|
|
93
|
+
*
|
|
94
|
+
* Why this exists: pre-fix, the banner used hand-counted padding
|
|
95
|
+
* literals. Adding/removing a single character (e.g. version bumping
|
|
96
|
+
* from "v2.0" to "v2.0.0") shifted line 2 by one column, breaking
|
|
97
|
+
* the right boundary. See user-debug-transcript.txt lines 1-5.
|
|
98
|
+
*/
|
|
99
|
+
export function renderMotdBanner(version) {
|
|
100
|
+
const INNER_WIDTH = 48; // columns between the two ║
|
|
101
|
+
const lines = [
|
|
102
|
+
`Nimbus v${version} — Cloud Dev Environment`,
|
|
103
|
+
'node · npm · vite · clang · python · ruby',
|
|
104
|
+
'10 GB VFS · REPLs · nimbus install · HMR',
|
|
105
|
+
];
|
|
106
|
+
const padLine = (content) => {
|
|
107
|
+
// 2-space left margin inside the box, then content, then trailing
|
|
108
|
+
// spaces to reach INNER_WIDTH columns total.
|
|
109
|
+
const indented = ' ' + content;
|
|
110
|
+
const used = [...indented].length;
|
|
111
|
+
const padding = Math.max(0, INNER_WIDTH - used);
|
|
112
|
+
return `║${indented}${' '.repeat(padding)}║`;
|
|
113
|
+
};
|
|
114
|
+
const top = '╔' + '═'.repeat(INNER_WIDTH) + '╗';
|
|
115
|
+
const bot = '╚' + '═'.repeat(INNER_WIDTH) + '╝';
|
|
116
|
+
return ('\x1b[1;36m' +
|
|
117
|
+
top + '\r\n' +
|
|
118
|
+
lines.map(padLine).join('\r\n') + '\r\n' +
|
|
119
|
+
bot +
|
|
120
|
+
'\x1b[0m\r\n');
|
|
121
|
+
}
|
|
122
|
+
export class NimbusSession extends CloudflareDurableObject {
|
|
123
|
+
// this.ctx and this.env are provided by the DurableObject base class
|
|
124
|
+
sqliteFs = null;
|
|
125
|
+
kernel = null;
|
|
126
|
+
shell = null;
|
|
127
|
+
terminal = null;
|
|
128
|
+
facetManager = null;
|
|
129
|
+
/** W8: child_process broker. Lazy — only constructed when first cp* RPC arrives. */
|
|
130
|
+
facetProcessManager = null;
|
|
131
|
+
esbuildService = null;
|
|
132
|
+
viteDevServer = null;
|
|
133
|
+
/**
|
|
134
|
+
* Primitives wave (P5): PID + port the default-Cirrus vite shim is
|
|
135
|
+
* registered under. Cleared on `vite stop`. See
|
|
136
|
+
* `runtime/long-running-handle.ts` and the vite handler in
|
|
137
|
+
* `session/init.ts`.
|
|
138
|
+
*/
|
|
139
|
+
_viteShimPid = null;
|
|
140
|
+
_viteShimPort = null;
|
|
141
|
+
/**
|
|
142
|
+
* Opt-in real-vite mode (Phase 0 spike). Activated when the user sets
|
|
143
|
+
* NIMBUS_REAL_VITE=1 in the shell env or `nimbusDevServer: 'real'` in
|
|
144
|
+
* vite.config.ts. Runs real Vite in a dynamic-worker facet, bypassing
|
|
145
|
+
* the in-process Cirrus shim. Coexists with viteDevServer — only one
|
|
146
|
+
* is live per session.
|
|
147
|
+
*/
|
|
148
|
+
cirrusReal = null;
|
|
149
|
+
/**
|
|
150
|
+
* Map of HMR WebSocket (server-side) → clientId. Populated when the
|
|
151
|
+
* browser's @vite/client opens a connection at /preview/__nimbus_hmr;
|
|
152
|
+
* consumed by the message+close handlers on the WS itself.
|
|
153
|
+
* Non-hibernatable (we use server.accept(), not ctx.acceptWebSocket).
|
|
154
|
+
*/
|
|
155
|
+
_cirrusHmrWsClients = null;
|
|
156
|
+
/** file-tree-watch (2026-05-15): per-WS fs-watch subscriptions.
|
|
157
|
+
* Lazily created on first fs-watch-subscribe by src/session/fs-watch.ts;
|
|
158
|
+
* cleaned up unconditionally in src/session/ws.ts on wsClose / wsError.
|
|
159
|
+
* Optional (undefined) until first subscribe so memory stays at 0
|
|
160
|
+
* for terminal-only sessions. */
|
|
161
|
+
_fsWatchSubs;
|
|
162
|
+
nimbusWrangler = null;
|
|
163
|
+
npmInstaller = null;
|
|
164
|
+
/** Singleton fetch proxy entrypoint — created once, reused for all npm fetches. */
|
|
165
|
+
fetchProxyEntrypoint = null;
|
|
166
|
+
processTable;
|
|
167
|
+
portRegistry;
|
|
168
|
+
// S4: visibility relaxed (was `private`) so ./nimbus-session-hib.ts's
|
|
169
|
+
// HibHost interface can declare it. Per plan §IX.1.
|
|
170
|
+
processLogs = new ProcessLogStore();
|
|
171
|
+
/** W1: idempotency flag for the alarm-driven log-janitor bootstrap.
|
|
172
|
+
* Replaces the pre-W1 `processLogsTimer` setTimeout handle (which
|
|
173
|
+
* prevented hibernation per CF DO docs). The alarm itself lives in
|
|
174
|
+
* DO storage at key `w1_next_alarm_reasons`. */
|
|
175
|
+
processLogsJanitorWired = false;
|
|
176
|
+
// ── W9 — hibernation persistence + auto-response config ───────────────
|
|
177
|
+
/**
|
|
178
|
+
* Result of `configureWsHibernation` at constructor time. Exposed via
|
|
179
|
+
* `/api/_diag/memory` under `hib.autoResponseConfigured`,
|
|
180
|
+
* `hib.timeoutSetMs` etc. `null` until the constructor's wiring runs
|
|
181
|
+
* (which it does unconditionally — left null only on a defensive
|
|
182
|
+
* catch-all).
|
|
183
|
+
*/
|
|
184
|
+
// S4: visibility relaxed (was `private`) so ./nimbus-session-hib.ts's
|
|
185
|
+
// free functions can read/write via the HibHost interface. Per plan
|
|
186
|
+
// §IX.1 (refined option b'). Sigil `_w9*` flags it as internal.
|
|
187
|
+
_w9WsConfig = null;
|
|
188
|
+
/**
|
|
189
|
+
* Monotonic isolate generation counter. Each fresh isolate (cold start
|
|
190
|
+
* or post-hibernation wake) increments this and persists to storage.
|
|
191
|
+
* Lets `/api/_diag/memory` confirm whether a wake actually happened
|
|
192
|
+
* between two probe calls.
|
|
193
|
+
*/
|
|
194
|
+
_w9IsolateGen = 0;
|
|
195
|
+
/** True once we've persisted the bumped gen counter to storage. */
|
|
196
|
+
_w9IsolateGenPersisted = false;
|
|
197
|
+
/** SQL DDL — idempotent; run on first fetch. */
|
|
198
|
+
_w9SchemaInit = false;
|
|
199
|
+
/** Have we wired the persist adapter into ProcessLogStore yet? */
|
|
200
|
+
_w9PersistWired = false;
|
|
201
|
+
/**
|
|
202
|
+
* Debounced flush state. Append marks the timer; the timer fires
|
|
203
|
+
* after W9_FLUSH_DEBOUNCE_MS and calls `processLogs.flush()`. We
|
|
204
|
+
* also flush eagerly when `dirtyChunks * pidCount` crosses a threshold
|
|
205
|
+
* — but the debounce handles the steady-state case.
|
|
206
|
+
*
|
|
207
|
+
* S5: storage keys + the debounce constant moved to ./nimbus-session-keys.ts.
|
|
208
|
+
*/
|
|
209
|
+
_w9FlushTimer = null;
|
|
210
|
+
// ── Heap-pressure probe state ───────────────────────────────────────────
|
|
211
|
+
/**
|
|
212
|
+
* Peak supervisor heap (rss + heapUsed) seen since process start. Updated
|
|
213
|
+
* by `_diagSampleMemory()` on every call to `/api/_diag/memory`. Used to
|
|
214
|
+
* confirm OOM hypotheses without re-running the failure: a peak that
|
|
215
|
+
* grew toward 128 MB during pre-bundle is direct evidence the supervisor
|
|
216
|
+
* isolate is the one being killed. Survives the lifetime of THIS isolate
|
|
217
|
+
* only — a DO reboot resets it to 0, which is itself a useful signal
|
|
218
|
+
* (peak == 0 immediately after the banner re-printed = the killed
|
|
219
|
+
* isolate took its peak with it).
|
|
220
|
+
*/
|
|
221
|
+
_diagPeakRss = 0;
|
|
222
|
+
_diagPeakHeapUsed = 0;
|
|
223
|
+
_diagPeakAt = 0;
|
|
224
|
+
_diagSampleCount = 0;
|
|
225
|
+
/**
|
|
226
|
+
* Fix 5: toggled by env NIMBUS_DEBUG=1 (checked each call; cheap enough).
|
|
227
|
+
* When true: spawn banners and exit traces are unconditional (not just
|
|
228
|
+
* long-running facets), RPC envelope errors are surfaced to the terminal
|
|
229
|
+
* with a [rpc-error] prefix, and the exit trace includes duration_ms.
|
|
230
|
+
*
|
|
231
|
+
* The flag is derived from `this.env.NIMBUS_DEBUG` — the binding comes
|
|
232
|
+
* from wrangler's var declaration or a test harness.
|
|
233
|
+
*/
|
|
234
|
+
get nimbusDebug() {
|
|
235
|
+
try {
|
|
236
|
+
const e = this.env;
|
|
237
|
+
return e?.NIMBUS_DEBUG === '1' || e?.NIMBUS_DEBUG === 'true';
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// ── W12 — DO read replicas state ────────────────────────────────────
|
|
244
|
+
/**
|
|
245
|
+
* Result of `tryEnableReplicas(this.ctx)` at constructor time. Surfaced
|
|
246
|
+
* via `/api/_diag/memory.replica` so operators can confirm whether the
|
|
247
|
+
* runtime accepted the SPEC API. Stays `null` if the constructor's
|
|
248
|
+
* call ran before this assignment (defensive — should never happen).
|
|
249
|
+
*/
|
|
250
|
+
// S3: visibility relaxed (was `private`) so ./nimbus-session-replica.ts's
|
|
251
|
+
// free functions can read/write via the ReplicaHost interface. Per plan
|
|
252
|
+
// §IX.1 (refined option b'). Sigil `_w12*` still flags it as internal.
|
|
253
|
+
_w12EnableResult = null;
|
|
254
|
+
/**
|
|
255
|
+
* Public URL prefix this DO is mounted at (e.g. `/s/nimble-otter-4271`).
|
|
256
|
+
* Set from the `X-Nimbus-Base` request header on the first forwarded
|
|
257
|
+
* request and persisted so it survives hibernation. Empty string means
|
|
258
|
+
* "unknown" (e.g. direct DO stub call from legacy callers) — in that
|
|
259
|
+
* case ViteDevServer falls back to the bare `/preview` default.
|
|
260
|
+
*
|
|
261
|
+
* NOTE: this is the SESSION prefix, not the vite preview prefix. The
|
|
262
|
+
* full vite basePath is `sessionBasePath + '/preview'`.
|
|
263
|
+
*/
|
|
264
|
+
sessionBasePath = '';
|
|
265
|
+
/** Have we attempted to hydrate sessionBasePath from storage yet? */
|
|
266
|
+
sessionBasePathHydrated = false;
|
|
267
|
+
/**
|
|
268
|
+
* Has the "wrangler is aliased to nimbus-wrangler" banner been shown
|
|
269
|
+
* this session? Reset on WebSocket close/reopen so a reconnecting user
|
|
270
|
+
* sees it once per terminal attach. Purely cosmetic; no persistence.
|
|
271
|
+
*/
|
|
272
|
+
wranglerAliasBannerShown = false;
|
|
273
|
+
constructor(ctx, env) {
|
|
274
|
+
super(ctx, env); // enables RPC on the DO
|
|
275
|
+
// In `wrangler dev`, the outer Worker and this DO share a single
|
|
276
|
+
// workerd process, so the `setCtxExports(ctx.exports)` call in the
|
|
277
|
+
// outer fetch handler (src/index.ts) is visible here via the
|
|
278
|
+
// module-level singleton in ctx-exports.ts. In PROD they run in
|
|
279
|
+
// separate isolates — that outer setter never reaches us, so
|
|
280
|
+
// getCtxExports() returns null, the facet pool falls back silently,
|
|
281
|
+
// and facets get `env.SUPERVISOR === undefined` → writeBatch throws.
|
|
282
|
+
//
|
|
283
|
+
// DurableObjectState.exports is exposed at compat date ≥ 2025-11-17.
|
|
284
|
+
// We're on 2026-04-01, so `ctx.exports` is present. Capture it here
|
|
285
|
+
// so the DO's own loopback bindings are available to the facet-pool
|
|
286
|
+
// and facet-manager in prod as well as dev.
|
|
287
|
+
const ctxExports = ctx?.exports;
|
|
288
|
+
if (ctxExports)
|
|
289
|
+
setCtxExports(ctxExports);
|
|
290
|
+
this.processTable = new ProcessTable();
|
|
291
|
+
this.portRegistry = new PortRegistry();
|
|
292
|
+
// W1: log-janitor moved to alarm-driven scheduling (was a
|
|
293
|
+
// self-renewing setTimeout chain at rpc.ts:_ensureLogJanitor;
|
|
294
|
+
// per CF DO docs that prevented hibernation, billing duration
|
|
295
|
+
// continuously). The delegator below forwards `this.ctx` so
|
|
296
|
+
// scheduleAlarm can write to ctx.storage.
|
|
297
|
+
this._ensureLogJanitor();
|
|
298
|
+
// ── W9 (CF research §C.3 + §C.4) ──────────────────────────────────
|
|
299
|
+
//
|
|
300
|
+
// Configure WS auto-response (`ping`/`pong`) so vite HMR + xterm
|
|
301
|
+
// idle pings don't wake the actor from hibernation. ~95% drop in
|
|
302
|
+
// billable wakes per the research doc. Auto-response config and
|
|
303
|
+
// hibernation event timeout both survive hibernation per the
|
|
304
|
+
// STOR/Durable Objects WebSocket Primer ("Survives: Auto-response
|
|
305
|
+
// configuration") — set once, forget.
|
|
306
|
+
//
|
|
307
|
+
// Failures are non-fatal — older workerd builds may lack the APIs.
|
|
308
|
+
// The result lands in /api/_diag/memory.hib for verification.
|
|
309
|
+
// S4: extracted to ./nimbus-session-hib.ts.
|
|
310
|
+
this._w9WsConfig = _w9WireHibernationOnConstruct(this.ctx);
|
|
311
|
+
// Wire the ProcessLogStore to its persist adapter (CF research §C.2,
|
|
312
|
+
// Lever 11). Idempotent: only runs once per isolate. The DDL +
|
|
313
|
+
// adapter run lazily on first append/read, but the wiring itself
|
|
314
|
+
// happens here so any subsequent call (including initSession) sees
|
|
315
|
+
// the adapter in place.
|
|
316
|
+
this._w9WireProcessLogPersist();
|
|
317
|
+
// ── W12 — Lever 12 / G3 / H1 — DO read replicas (best-effort).
|
|
318
|
+
//
|
|
319
|
+
// Constructor runs on EVERY isolate (primary + replica alike). Replicas
|
|
320
|
+
// get a stub at `ctx.storage.primary` from the runtime; primaries get
|
|
321
|
+
// `undefined`. We call `enableReplicas()` defensively — pre-GA runtimes
|
|
322
|
+
// lacking the API surface get `state: 'unsupported'` and the DO behaves
|
|
323
|
+
// exactly as pre-W12 (single-primary). Result is captured for the
|
|
324
|
+
// `/api/_diag/memory.replica` block.
|
|
325
|
+
// S3: extracted to ./nimbus-session-replica.ts. Passes ctx
|
|
326
|
+
// explicitly because `protected ctx` can't be put on a public
|
|
327
|
+
// interface (per nimbus-session-replica.ts ReplicaHost docs).
|
|
328
|
+
this._w12EnableResult = _w12WireReplicasOnConstruct(this.ctx);
|
|
329
|
+
}
|
|
330
|
+
// ── W9 hibernation methods extracted to ./nimbus-session-hib.ts (S4) ──
|
|
331
|
+
//
|
|
332
|
+
// The class retains the public method NAMES (delegator pattern per plan
|
|
333
|
+
// §IX.4 R1: delegators are immortal until S13). Bodies live in -hib.ts.
|
|
334
|
+
// Methods _w9*-prefixed dropped `private` because the diag handler at
|
|
335
|
+
// /api/_diag/memory + sibling modules need to call them (or because the
|
|
336
|
+
// free function signature requires `host` typed as HibHost which can't
|
|
337
|
+
// see `private` members).
|
|
338
|
+
_w9WireProcessLogPersist() {
|
|
339
|
+
return _w9DoWireProcessLogPersist(this, this.ctx);
|
|
340
|
+
}
|
|
341
|
+
_w9EnsureSchema() {
|
|
342
|
+
return _w9DoEnsureHibSchema(this, this.ctx);
|
|
343
|
+
}
|
|
344
|
+
_w9ScheduleFlush() {
|
|
345
|
+
return _w9DoScheduleHibFlush(this, this.ctx);
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* W1: multi-reason alarm handler. Routes via a `w1_next_alarm_reasons`
|
|
349
|
+
* storage map managed by `scheduleAlarm` (see ./hibernation.ts).
|
|
350
|
+
* Dispatches every pending reason whose deadline has passed, then
|
|
351
|
+
* re-arms `ctx.storage.setAlarm` at the earliest remaining deadline.
|
|
352
|
+
* Today's reasons: 'w9-flush' (process-log SQL drain) and
|
|
353
|
+
* 'log-janitor' (dropOlderThan sweep). The janitor body needs an
|
|
354
|
+
* orphan-pid predicate so we close over `processTable` here.
|
|
355
|
+
*/
|
|
356
|
+
async alarm() {
|
|
357
|
+
return _w9DoDispatchAlarm(this, this.ctx, _rpc._logJanitorOrphanCheck(this));
|
|
358
|
+
}
|
|
359
|
+
/** W9: increment + persist isolate-gen counter once per fresh isolate. */
|
|
360
|
+
async _w9MaybeBumpIsolateGen() {
|
|
361
|
+
return _w9DoMaybeBumpIsolateGen(this, this.ctx);
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Convenience: the full URL prefix for the Vite dev server inside this
|
|
365
|
+
* session (e.g. `/s/nimble-otter-4271/preview`). Falls back to the
|
|
366
|
+
* historical default when sessionBasePath is unknown so legacy callers
|
|
367
|
+
* and unit tests keep working.
|
|
368
|
+
*/
|
|
369
|
+
get viteBasePath() {
|
|
370
|
+
return (this.sessionBasePath || '') + '/preview';
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Lazily hydrate sessionBasePath from storage, then overwrite with the
|
|
374
|
+
* current request's `X-Nimbus-Base` header if present. Call at the top
|
|
375
|
+
* of `_handleFetch` before any HTML rendering or ViteDevServer spawn.
|
|
376
|
+
*
|
|
377
|
+
* We always trust the most recent header over storage, because a DO
|
|
378
|
+
* instance is always pinned to one session ID (via idFromName) but the
|
|
379
|
+
* URL prefix COULD change across deploys (e.g. if we ever rename `/s/`).
|
|
380
|
+
*/
|
|
381
|
+
async hydrateSessionBasePath(request) {
|
|
382
|
+
if (!this.sessionBasePathHydrated) {
|
|
383
|
+
try {
|
|
384
|
+
const saved = await this.ctx.storage.get('session-base-path');
|
|
385
|
+
if (typeof saved === 'string')
|
|
386
|
+
this.sessionBasePath = saved;
|
|
387
|
+
}
|
|
388
|
+
catch { /* storage unavailable — stay empty */ }
|
|
389
|
+
this.sessionBasePathHydrated = true;
|
|
390
|
+
}
|
|
391
|
+
const fromHeader = request.headers.get(BASE_PATH_HEADER);
|
|
392
|
+
if (fromHeader && fromHeader !== this.sessionBasePath) {
|
|
393
|
+
this.sessionBasePath = fromHeader;
|
|
394
|
+
try {
|
|
395
|
+
await this.ctx.storage.put('session-base-path', fromHeader);
|
|
396
|
+
}
|
|
397
|
+
catch { }
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
// ── Supervisor RPC + W8 cp* + legacy VFS extracted to
|
|
401
|
+
// ── ./nimbus-session-rpc.ts (S8). See plan §B.3.4.
|
|
402
|
+
//
|
|
403
|
+
// The class retains every method NAME so the DO RPC fabric (which
|
|
404
|
+
// dispatches by name from the stub) keeps working. Method bodies are
|
|
405
|
+
// 1-line delegators that pass `this as any` (per plan §IX rec 1 +
|
|
406
|
+
// DEFECT-D1: ctx is protected and not on a public interface).
|
|
407
|
+
// Supervisor RPC (file/log/HMR/batch)
|
|
408
|
+
async _rpcReadFile(path) { return _rpc._rpcReadFile(this, path); }
|
|
409
|
+
async _rpcReadFileBytes(path) { return _rpc._rpcReadFileBytes(this, path); }
|
|
410
|
+
async _rpcInnerDoFetch(req) { return _rpc._rpcInnerDoFetch(this, req); }
|
|
411
|
+
async _rpcWriteFile(path, content) { return _rpc._rpcWriteFile(this, path, content); }
|
|
412
|
+
async _rpcStat(path) { return _rpc._rpcStat(this, path); }
|
|
413
|
+
async _rpcReaddir(path) { return _rpc._rpcReaddir(this, path); }
|
|
414
|
+
async _rpcExists(path) { return _rpc._rpcExists(this, path); }
|
|
415
|
+
async _rpcMkdir(path) { return _rpc._rpcMkdir(this, path); }
|
|
416
|
+
async _rpcHmrRelay(clientId, msg) { return _rpc._rpcHmrRelay(this, clientId, msg); }
|
|
417
|
+
async _rpcUnlink(path) { return _rpc._rpcUnlink(this, path); }
|
|
418
|
+
async _rpcWriteBatch(payload) { return _rpc._rpcWriteBatch(this, payload); }
|
|
419
|
+
async _rpcWriteBatchStream(stream) { return _rpc._rpcWriteBatchStream(this, stream); }
|
|
420
|
+
async _rpcPutRegistryEntries(entries) { return _rpc._rpcPutRegistryEntries(this, entries); }
|
|
421
|
+
async _rpcRecordCacheStats(events) { return _rpc._rpcRecordCacheStats(this, events); }
|
|
422
|
+
async _rpcStdout(pid, data) { return _rpc._rpcStdout(this, pid, data); }
|
|
423
|
+
async _rpcStderr(pid, data) { return _rpc._rpcStderr(this, pid, data); }
|
|
424
|
+
async _rpcReportExit(pid, code, tail) { return _rpc._rpcReportExit(this, pid, code, tail); }
|
|
425
|
+
// W3 emitters / external-exit / log janitor
|
|
426
|
+
_emitExitDump(pid, code) { return _rpc._emitExitDump(this, pid, code); }
|
|
427
|
+
_emitShellExecDone(pid, cmd, code, durationMs) { return _rpc._emitShellExecDone(this, pid, cmd, code, durationMs); }
|
|
428
|
+
_reportExternalExit(pid, code, reason) { return _rpc._reportExternalExit(this, pid, code, reason); }
|
|
429
|
+
_ensureLogJanitor() { return _rpc._ensureLogJanitor(this, this.ctx); }
|
|
430
|
+
// Misc supervisor RPC
|
|
431
|
+
async _rpcPrefetch(cwd, entryCode) { return _rpc._rpcPrefetch(this, cwd, entryCode); }
|
|
432
|
+
async _rpcRegisterPort(pid, port) { return _rpc._rpcRegisterPort(this, pid, port); }
|
|
433
|
+
async _rpcUnregisterPort(port) { return _rpc._rpcUnregisterPort(this, port); }
|
|
434
|
+
async _rpcTransform(code, loader) { return _rpc._rpcTransform(this, code, loader); }
|
|
435
|
+
// two-tier-fanout: peer-DO execute leg of NimbusFanoutPool's POC B topology.
|
|
436
|
+
async _rpcFanoutExecute(fnSource, args, poolOpts) {
|
|
437
|
+
return _rpc._rpcFanoutExecute(this, fnSource, args, poolOpts);
|
|
438
|
+
}
|
|
439
|
+
// W8 child_process RPC
|
|
440
|
+
async _rpcCpSpawn(req) { return _rpc._rpcCpSpawn(this, req); }
|
|
441
|
+
async _rpcCpStdinWrite(childPid, data) { return _rpc._rpcCpStdinWrite(this, childPid, data); }
|
|
442
|
+
async _rpcCpStdinEnd(childPid) { return _rpc._rpcCpStdinEnd(this, childPid); }
|
|
443
|
+
async _rpcCpReadOutput(childPid, fd, sinceSeq, waitMs) { return _rpc._rpcCpReadOutput(this, childPid, fd, sinceSeq, waitMs); }
|
|
444
|
+
async _rpcCpDrainOutput(childPid) { return _rpc._rpcCpDrainOutput(this, childPid); }
|
|
445
|
+
async _rpcCpKill(childPid, signal) { return _rpc._rpcCpKill(this, childPid, signal); }
|
|
446
|
+
async _rpcCpWait(childPid, waitMs) { return _rpc._rpcCpWait(this, childPid, waitMs); }
|
|
447
|
+
// arch-gaps gap #1: per-spawn fresh-isolate dispatch.
|
|
448
|
+
async _rpcCpDispatchInline(req, kind) { return _rpc._rpcCpDispatchInline(this, req, kind); }
|
|
449
|
+
// Programmatic sandbox SDK RPC
|
|
450
|
+
async _rpcReady(options) { return _programmatic.ensureProgrammaticReady(this, options); }
|
|
451
|
+
async _rpcExec(command, options) { return _programmatic.rpcExec(this, command, options); }
|
|
452
|
+
async _rpcStartProcess(command, options) { return _programmatic.rpcStartProcess(this, command, options); }
|
|
453
|
+
async _rpcRunCode(code, options) {
|
|
454
|
+
return _programmatic.rpcRunCode(this, code, options);
|
|
455
|
+
}
|
|
456
|
+
async _rpcInstallRuntime(spec, options) { return _programmatic.rpcInstallRuntime(this, spec, options); }
|
|
457
|
+
async _rpcEnsureRuntimes(specs, options) { return _programmatic.rpcEnsureRuntimes(this, specs, options); }
|
|
458
|
+
async _rpcListRuntimes() { return _programmatic.rpcListRuntimes(this); }
|
|
459
|
+
async _rpcListProcesses() { return _programmatic.rpcListProcesses(this); }
|
|
460
|
+
async _rpcKillProcess(pid) { return _programmatic.rpcKillProcess(this, pid); }
|
|
461
|
+
async _rpcProcessLogs(pid, options) { return _programmatic.rpcProcessLogs(this, pid, options); }
|
|
462
|
+
async _rpcListPorts() { return _programmatic.rpcListPorts(this); }
|
|
463
|
+
async _rpcExposePort(port) { return _programmatic.rpcExposePort(this, port); }
|
|
464
|
+
async _rpcUnexposePort(port) { return _programmatic.rpcUnexposePort(this, port); }
|
|
465
|
+
async _rpcDeleteFile(path, options) { return _programmatic.rpcDeleteFile(this, path, options); }
|
|
466
|
+
// Legacy VFS (direct method calls)
|
|
467
|
+
vfsReadFile(path) { return _rpc.vfsReadFile(this, path); }
|
|
468
|
+
vfsReadFileString(path) { return _rpc.vfsReadFileString(this, path); }
|
|
469
|
+
vfsStat(path) { return _rpc.vfsStat(this, path); }
|
|
470
|
+
vfsExists(path) { return _rpc.vfsExists(this, path); }
|
|
471
|
+
vfsReaddir(path) { return _rpc.vfsReaddir(this, path); }
|
|
472
|
+
vfsWriteFile(path, data) { return _rpc.vfsWriteFile(this, path, data); }
|
|
473
|
+
// ── HTTP handler ──────────────────────────────────────────────────────
|
|
474
|
+
async fetch(request) {
|
|
475
|
+
try {
|
|
476
|
+
return await this._handleFetch(request);
|
|
477
|
+
}
|
|
478
|
+
catch (e) {
|
|
479
|
+
console.error('[nimbus] Unhandled fetch error:', e?.message, e?.stack);
|
|
480
|
+
return new Response(`Internal Error: ${e?.message}`, { status: 500 });
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
// ── HTTP fetch routing extracted to ./nimbus-session-routes.ts (S9).
|
|
484
|
+
// The class retains the DO-contract `fetch` method + `_handleFetch`
|
|
485
|
+
// delegator (per plan §IX.4 R1).
|
|
486
|
+
async _handleFetch(request) {
|
|
487
|
+
return _routes.handleFetch(this, request);
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Read process.memoryUsage() if nodejs_compat exposes it. Returns null
|
|
491
|
+
* on environments where the binding is absent (older compat dates,
|
|
492
|
+
* non-Workers test harnesses). Never throws — heap probes must be
|
|
493
|
+
* fault-tolerant so a probe that fails in prod doesn't take the
|
|
494
|
+
* request handler down with it.
|
|
495
|
+
*/
|
|
496
|
+
// ── W12 — getReplicaState() — exposed via /api/_diag/memory.replica.
|
|
497
|
+
//
|
|
498
|
+
// Delegator. Impl extracted to ./nimbus-session-replica.ts (S3).
|
|
499
|
+
// Stays public-by-name so the diag handler at /api/_diag/memory keeps
|
|
500
|
+
// working unchanged. The CT1 drift detector reads the result.
|
|
501
|
+
getReplicaState() {
|
|
502
|
+
return _w12GetReplicaState(this, this.ctx);
|
|
503
|
+
}
|
|
504
|
+
// Heap probe + W5 ring helpers extracted to ./nimbus-session-diag.ts (S10).
|
|
505
|
+
// The class retains delegator methods because the diag handler at
|
|
506
|
+
// /api/_diag/memory + the ws lifecycle + ensureSqliteFs all call them.
|
|
507
|
+
_diagReadNodeMem() {
|
|
508
|
+
return _diag.readNodeMem();
|
|
509
|
+
}
|
|
510
|
+
_diagReadPerfMem() {
|
|
511
|
+
return _diag.readPerfMem();
|
|
512
|
+
}
|
|
513
|
+
_diagSampleMemory() {
|
|
514
|
+
return _diag.sampleMemory(this);
|
|
515
|
+
}
|
|
516
|
+
ensureSqliteFs() {
|
|
517
|
+
if (!this.sqliteFs) {
|
|
518
|
+
this.sqliteFs = new SqliteVFS(this.ctx.storage.sql, this.ctx);
|
|
519
|
+
// Audit C1: surface deferred-flush failures. SqliteVFS.writeFile()
|
|
520
|
+
// is synchronous (fire-and-forget by design — see LIFO MountProvider
|
|
521
|
+
// contract) so it can't return an error to the caller. Instead the
|
|
522
|
+
// VFS retries once and then calls these handlers for chunks that
|
|
523
|
+
// failed twice. We log to the user's terminal (non-spammy — the
|
|
524
|
+
// VFS also logs to the Worker console for operator triage) and
|
|
525
|
+
// make the error visible in /api/stats via getStats().
|
|
526
|
+
this.sqliteFs.onWriteError((err) => {
|
|
527
|
+
try {
|
|
528
|
+
if (this.terminal) {
|
|
529
|
+
this.terminal.write(`\x1b[31m[vfs] write failed: ${err.path} chunk ${err.chunkId} ` +
|
|
530
|
+
`(attempts=${err.attempts}): ${err.error}\x1b[0m\r\n`);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
catch { /* handler must not throw back into the flush path */ }
|
|
534
|
+
});
|
|
535
|
+
// W5 Lever 8: shrinkForInstall() during heavy-alloc windows.
|
|
536
|
+
// The observer fires only on 0→1 / ≥1→0 edges (registerAllocObserver
|
|
537
|
+
// in heavy-alloc-coord.ts handles the refcount). Default shrink
|
|
538
|
+
// target 128 entries × 64 KB = 8 MiB; +24 MiB heap headroom
|
|
539
|
+
// during install / clone / pre-bundle. See W5-plan.md §2.
|
|
540
|
+
const vfs = this.sqliteFs;
|
|
541
|
+
registerAllocObserver({
|
|
542
|
+
onAcquire: () => {
|
|
543
|
+
try {
|
|
544
|
+
vfs.shrinkForInstall();
|
|
545
|
+
}
|
|
546
|
+
catch (e) {
|
|
547
|
+
console.warn('[nimbus/W5] shrinkForInstall threw:', e?.message);
|
|
548
|
+
}
|
|
549
|
+
},
|
|
550
|
+
onRelease: () => {
|
|
551
|
+
try {
|
|
552
|
+
vfs.restoreAfterInstall();
|
|
553
|
+
}
|
|
554
|
+
catch (e) {
|
|
555
|
+
console.warn('[nimbus/W5] restoreAfterInstall threw:', e?.message);
|
|
556
|
+
}
|
|
557
|
+
},
|
|
558
|
+
});
|
|
559
|
+
// W5 Lever 5: rehydrate the OOM ring from storage (best-effort).
|
|
560
|
+
// Survives DO hibernation; lets cf-tail-style forensics include
|
|
561
|
+
// pre-hibernate failures. Fail-soft on garbage / missing — the
|
|
562
|
+
// rehydrate function's own contract.
|
|
563
|
+
this._w5RehydrateRingFromStorage().catch((e) => {
|
|
564
|
+
console.warn('[nimbus/W5] ring rehydrate failed:', e?.message);
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
// ── W5 Lever 5: ring buffer persistence on DO storage ─────────────────
|
|
569
|
+
// Storage key W5_RING_STORAGE_KEY lives in ./nimbus-session-keys.ts (S5).
|
|
570
|
+
// Bounded ≤20 KB by oom-discriminator.ts; one async put per
|
|
571
|
+
// webSocketClose where the ring is non-empty.
|
|
572
|
+
/** Track when we last persisted to avoid redundant writes. */
|
|
573
|
+
_w5LastPersistAt = 0;
|
|
574
|
+
/** Track ring size at last persist; skip write if unchanged. */
|
|
575
|
+
_w5LastPersistRingSize = -1;
|
|
576
|
+
/** B'.4 — live initSession phase. Surfaced via
|
|
577
|
+
* /api/_diag/session.phase. null pre-first-init. */
|
|
578
|
+
_b4Phase = null;
|
|
579
|
+
/** B'.5 — count of warm-rejoin /ws upgrades. Increments each
|
|
580
|
+
* time the join path is taken (Phase B skipped). 0 means no
|
|
581
|
+
* warm rejoins yet. Surfaced via /api/_diag/session.warmJoinCount. */
|
|
582
|
+
_b4WarmJoinCount = 0;
|
|
583
|
+
async _w5RehydrateRingFromStorage() {
|
|
584
|
+
return _diag.rehydrateRingFromStorage(this, this.ctx);
|
|
585
|
+
}
|
|
586
|
+
/** Snapshot + persist OOM ring. Delegator → ./nimbus-session-diag.ts (S10). */
|
|
587
|
+
_w5PersistRing() {
|
|
588
|
+
return _diag.persistRing(this, this.ctx);
|
|
589
|
+
}
|
|
590
|
+
ensureFacetManager() {
|
|
591
|
+
if (!this.facetManager) {
|
|
592
|
+
this.facetManager = new FacetManager(this.ctx, this.env, this.processTable, this.portRegistry, {
|
|
593
|
+
onExternalExit: (pid, code, reason) => this._reportExternalExit(pid, code, reason),
|
|
594
|
+
onSpawn: (pid, command, longRunning) => {
|
|
595
|
+
// Only surface long-running / user-visible spawns to keep
|
|
596
|
+
// the terminal uncluttered. Short `node <file>` evals also
|
|
597
|
+
// get a line because users want the pid for `logs`/`kill`.
|
|
598
|
+
if (!this.terminal)
|
|
599
|
+
return;
|
|
600
|
+
const label = longRunning ? 'started (long-running)' : 'started';
|
|
601
|
+
this.terminal.write(`\x1b[2m[facet ${label}: pid=${pid} cmd="${command}"]\x1b[0m\r\n`);
|
|
602
|
+
// Structured event so the tabs UI can auto-open a log tab
|
|
603
|
+
// for long-running processes (vite, wrangler dev, etc.).
|
|
604
|
+
notifyTerminalEvent(this.terminal, { type: 'spawn', pid, command, longRunning });
|
|
605
|
+
},
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
if (this.facetManager && this.sqliteFs) {
|
|
609
|
+
this.facetManager.setVfs(this.sqliteFs);
|
|
610
|
+
// W3.5 Fix B: share the session's lazy esbuildService with the
|
|
611
|
+
// FacetManager so the bundle's ESM→CJS pre-pass doesn't pay
|
|
612
|
+
// wasm-init twice. If the session hasn't constructed one yet,
|
|
613
|
+
// FacetManager will lazy-create its own on first exec — same
|
|
614
|
+
// wasm bytes, same ~10ms init cost, just paid once per surface.
|
|
615
|
+
if (this.esbuildService) {
|
|
616
|
+
this.facetManager.setEsbuildService(this.esbuildService);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* W8: lazily construct the FacetProcessManager when the first cp* RPC
|
|
622
|
+
* arrives. Wired with adapters that bridge the Nimbus shell command
|
|
623
|
+
* registry to the FacetProcessManager's CommandRegistryLike contract.
|
|
624
|
+
*/
|
|
625
|
+
_ensureFacetProcessManager() {
|
|
626
|
+
if (this.facetProcessManager)
|
|
627
|
+
return this.facetProcessManager;
|
|
628
|
+
this.ensureSqliteFs();
|
|
629
|
+
this.ensureFacetManager();
|
|
630
|
+
// FacetProcessManager is statically imported at top-of-file (W8).
|
|
631
|
+
// No lazy-import: workerd doesn't ship CJS require, and the dynamic
|
|
632
|
+
// import would be async — making _ensureFacetProcessManager async
|
|
633
|
+
// would force every cp* RPC entry point to also be async on the
|
|
634
|
+
// promise-resolution path, which is fine but uglier. Compile-time
|
|
635
|
+
// tree-shaking handles unused-when-no-cp-RPC paths.
|
|
636
|
+
// Adapter for FacetManagerLike — wraps the existing FacetManager.exec
|
|
637
|
+
// with a streaming surface. Phase 1 simplification: facet-direct
|
|
638
|
+
// commands are dispatched through the shell registry the same way
|
|
639
|
+
// shell.execute does, but with the per-PID hooks routed.
|
|
640
|
+
const facetMgrAdapter = {
|
|
641
|
+
execStream: async (codeJson, opts, hooks) => {
|
|
642
|
+
// codeJson is a payload from FacetProcessManager._dispatch facet-direct
|
|
643
|
+
// path: {command, args, env, cwd, stdin}. We dispatch through the
|
|
644
|
+
// existing shell registry by resolving the command and invoking
|
|
645
|
+
// it with synthesized output streams that route to hooks.
|
|
646
|
+
let payload;
|
|
647
|
+
try {
|
|
648
|
+
payload = JSON.parse(codeJson);
|
|
649
|
+
}
|
|
650
|
+
catch {
|
|
651
|
+
payload = { command: '', args: [], env: {}, cwd: '/' };
|
|
652
|
+
}
|
|
653
|
+
const registry = this._cpRegistry;
|
|
654
|
+
if (!registry) {
|
|
655
|
+
hooks.onStderr('child_process: command registry unavailable\n');
|
|
656
|
+
return 127;
|
|
657
|
+
}
|
|
658
|
+
const cmd = await registry.resolve(payload.command);
|
|
659
|
+
if (!cmd) {
|
|
660
|
+
hooks.onStderr(`${payload.command}: command not found\n`);
|
|
661
|
+
return 127;
|
|
662
|
+
}
|
|
663
|
+
// Synthesize a CommandContext compatible with @lifo-sh/core.
|
|
664
|
+
const stdoutStream = { write: (d) => hooks.onStdout(String(d)) };
|
|
665
|
+
const stderrStream = { write: (d) => hooks.onStderr(String(d)) };
|
|
666
|
+
const ac = new AbortController();
|
|
667
|
+
const ctx = {
|
|
668
|
+
args: payload.args || [],
|
|
669
|
+
env: payload.env || {},
|
|
670
|
+
cwd: payload.cwd || '/home/user',
|
|
671
|
+
vfs: this.sqliteFs,
|
|
672
|
+
stdout: stdoutStream,
|
|
673
|
+
stderr: stderrStream,
|
|
674
|
+
signal: ac.signal,
|
|
675
|
+
// For commands that need stdin we pass a tiny adapter.
|
|
676
|
+
stdin: {
|
|
677
|
+
read: async () => null,
|
|
678
|
+
readAll: async () => payload.stdin || '',
|
|
679
|
+
},
|
|
680
|
+
};
|
|
681
|
+
try {
|
|
682
|
+
const code = await cmd(ctx);
|
|
683
|
+
return typeof code === 'number' ? code : 0;
|
|
684
|
+
}
|
|
685
|
+
catch (e) {
|
|
686
|
+
hooks.onStderr(`${payload.command}: ${e?.message || String(e)}\n`);
|
|
687
|
+
return 1;
|
|
688
|
+
}
|
|
689
|
+
},
|
|
690
|
+
abort: (facetName) => {
|
|
691
|
+
// Best-effort: relay to ctx.facets.abort, mirroring FacetManager.kill.
|
|
692
|
+
try {
|
|
693
|
+
this.ctx.facets?.abort?.(facetName, new Error('SIGKILL'));
|
|
694
|
+
}
|
|
695
|
+
catch { }
|
|
696
|
+
return true;
|
|
697
|
+
},
|
|
698
|
+
};
|
|
699
|
+
// Adapter for CommandRegistryLike. The shared shell registry is
|
|
700
|
+
// attached to `this._cpRegistry` by the shell-init path (see
|
|
701
|
+
// construction near line 2058 — registry passed as ctor arg there).
|
|
702
|
+
const cmdRegistryAdapter = {
|
|
703
|
+
// Consult the live shell registry FIRST so dynamically-registered
|
|
704
|
+
// commands (registerUnixCommands / registerGitCommands / npm /
|
|
705
|
+
// wrangler etc.) are seen even if they're not in the static
|
|
706
|
+
// _CP_PURE_BUILTIN allow-list. Falls back to the static
|
|
707
|
+
// facet-direct table for known facet-only commands. Returns null
|
|
708
|
+
// (→ exit 127) for everything unknown.
|
|
709
|
+
resolve: (name) => {
|
|
710
|
+
const registry = this._cpRegistry;
|
|
711
|
+
if (registry && typeof registry.has === 'function' && registry.has(name)) {
|
|
712
|
+
// Registered — classify by name. Reuse the static table so
|
|
713
|
+
// facet-direct commands (node/npm/git/...) keep their kind
|
|
714
|
+
// even when they ALSO happen to be registry entries.
|
|
715
|
+
return _classifyCommand(name) || { kind: 'pure-builtin' };
|
|
716
|
+
}
|
|
717
|
+
return _classifyCommand(name);
|
|
718
|
+
},
|
|
719
|
+
runPureBuiltin: async (name, args, env, cwd, stdin, hooks) => {
|
|
720
|
+
const registry = this._cpRegistry;
|
|
721
|
+
if (!registry) {
|
|
722
|
+
hooks.onStderr('cp: registry unavailable\n');
|
|
723
|
+
return 127;
|
|
724
|
+
}
|
|
725
|
+
const cmd = await registry.resolve(name);
|
|
726
|
+
if (!cmd) {
|
|
727
|
+
hooks.onStderr(`${name}: command not found\n`);
|
|
728
|
+
return 127;
|
|
729
|
+
}
|
|
730
|
+
const ac = new AbortController();
|
|
731
|
+
const ctx = {
|
|
732
|
+
args, env, cwd,
|
|
733
|
+
vfs: this.sqliteFs,
|
|
734
|
+
stdout: { write: (d) => hooks.onStdout(String(d)) },
|
|
735
|
+
stderr: { write: (d) => hooks.onStderr(String(d)) },
|
|
736
|
+
signal: ac.signal,
|
|
737
|
+
stdin: { read: async () => null, readAll: async () => stdin },
|
|
738
|
+
};
|
|
739
|
+
try {
|
|
740
|
+
const code = await cmd(ctx);
|
|
741
|
+
return typeof code === 'number' ? code : 0;
|
|
742
|
+
}
|
|
743
|
+
catch (e) {
|
|
744
|
+
hooks.onStderr(`${name}: ${e?.message || String(e)}\n`);
|
|
745
|
+
return 1;
|
|
746
|
+
}
|
|
747
|
+
},
|
|
748
|
+
};
|
|
749
|
+
// arch-gaps gap #1: per-spawn fresh-isolate envelope. The pool wraps
|
|
750
|
+
// NimbusFanoutPool — auto-routes <5 → POC C in-DO, ≥5 → POC B
|
|
751
|
+
// peer-DO. Hard-fails at construction if env.LOADER missing (no
|
|
752
|
+
// fallback). The pool is constructed lazily on the same path that
|
|
753
|
+
// builds FacetProcessManager so unit tests that don't supply
|
|
754
|
+
// env.LOADER still work via the legacy in-supervisor dispatch.
|
|
755
|
+
let spawnPool;
|
|
756
|
+
try {
|
|
757
|
+
const envAny = this.env;
|
|
758
|
+
if (envAny?.LOADER && typeof envAny.LOADER.get === 'function') {
|
|
759
|
+
spawnPool = new ChildProcessSpawnPool(this.env, this.ctx);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
catch {
|
|
763
|
+
spawnPool = undefined;
|
|
764
|
+
}
|
|
765
|
+
this.facetProcessManager = new FacetProcessManager({
|
|
766
|
+
facetMgr: facetMgrAdapter,
|
|
767
|
+
processTable: this.processTable,
|
|
768
|
+
processLogs: this.processLogs,
|
|
769
|
+
vfs: this.sqliteFs,
|
|
770
|
+
commandRegistry: cmdRegistryAdapter,
|
|
771
|
+
ctx: this.ctx,
|
|
772
|
+
spawnPool,
|
|
773
|
+
});
|
|
774
|
+
return this.facetProcessManager;
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* Set the shell command registry for the W8 broker to dispatch
|
|
778
|
+
* resolved commands. Called from the shell-init path right after
|
|
779
|
+
* `registerUnixCommands(registry, sqliteFs)`.
|
|
780
|
+
*/
|
|
781
|
+
_cpRegistry = null;
|
|
782
|
+
_setCpRegistry(r) { this._cpRegistry = r; }
|
|
783
|
+
/**
|
|
784
|
+
* Get or create the singleton fetch proxy entrypoint.
|
|
785
|
+
* ONE dynamic worker is created via LOADER.load() and reused for ALL npm
|
|
786
|
+
* fetch calls across the lifetime of this DO instance. This prevents
|
|
787
|
+
* ephemeral port exhaustion from creating a new worker per fetch.
|
|
788
|
+
*/
|
|
789
|
+
ensureFetchProxy(log) {
|
|
790
|
+
if (this.fetchProxyEntrypoint)
|
|
791
|
+
return this.fetchProxyEntrypoint;
|
|
792
|
+
try {
|
|
793
|
+
const env = this.env;
|
|
794
|
+
if (!env?.LOADER?.load) {
|
|
795
|
+
log?.('LOADER.load not available — using global fetch');
|
|
796
|
+
return null;
|
|
797
|
+
}
|
|
798
|
+
// Buffered proxy: reads the entire response body into an ArrayBuffer
|
|
799
|
+
// and returns it in ONE message instead of forwarding a ReadableStream.
|
|
800
|
+
// In workerd local dev, streaming responses across a service-binding
|
|
801
|
+
// RPC fabric opens a separate loopback socket PER chunk (~16KB), which
|
|
802
|
+
// exhausts ephemeral ports for larger installs (npm registry packuments
|
|
803
|
+
// are 500KB-3MB, tarballs up to 5MB). Buffering to arrayBuffer means
|
|
804
|
+
// 1 stub call = 1 loopback connection, not N connections.
|
|
805
|
+
//
|
|
806
|
+
// 32MB cap prevents a malformed giant response from OOMing the proxy
|
|
807
|
+
// isolate. Packages with tarballs larger than 32MB will fail to install
|
|
808
|
+
// cleanly (returned as 413 → caller treats as failed fetch).
|
|
809
|
+
const proxyCode = [
|
|
810
|
+
'const MAX_BYTES = 32 * 1024 * 1024;',
|
|
811
|
+
'export default {',
|
|
812
|
+
' async fetch(request, workerEnv) {',
|
|
813
|
+
' try {',
|
|
814
|
+
' const body = await request.json();',
|
|
815
|
+
' const resp = await fetch(body.url, {',
|
|
816
|
+
' method: body.method || "GET",',
|
|
817
|
+
' headers: body.headers || {},',
|
|
818
|
+
' });',
|
|
819
|
+
' // Check advertised Content-Length before buffering',
|
|
820
|
+
' const clStr = resp.headers.get("content-length");',
|
|
821
|
+
' if (clStr) {',
|
|
822
|
+
' const cl = parseInt(clStr, 10);',
|
|
823
|
+
' if (cl > MAX_BYTES) {',
|
|
824
|
+
' return new Response(',
|
|
825
|
+
' JSON.stringify({ error: "response too large: " + cl + " bytes (cap " + MAX_BYTES + ")" }),',
|
|
826
|
+
' { status: 413, headers: { "Content-Type": "application/json" } }',
|
|
827
|
+
' );',
|
|
828
|
+
' }',
|
|
829
|
+
' }',
|
|
830
|
+
' // Buffer entire body — ONE message, not streamed chunks',
|
|
831
|
+
' const buf = await resp.arrayBuffer();',
|
|
832
|
+
' if (buf.byteLength > MAX_BYTES) {',
|
|
833
|
+
' return new Response(',
|
|
834
|
+
' JSON.stringify({ error: "response exceeded cap: " + buf.byteLength + " bytes" }),',
|
|
835
|
+
' { status: 413, headers: { "Content-Type": "application/json" } }',
|
|
836
|
+
' );',
|
|
837
|
+
' }',
|
|
838
|
+
' return new Response(buf, {',
|
|
839
|
+
' status: resp.status,',
|
|
840
|
+
' statusText: resp.statusText,',
|
|
841
|
+
' headers: Object.fromEntries(resp.headers.entries()),',
|
|
842
|
+
' });',
|
|
843
|
+
' } catch (e) {',
|
|
844
|
+
' return new Response(JSON.stringify({ error: e.message }), {',
|
|
845
|
+
' status: 502,',
|
|
846
|
+
' headers: { "Content-Type": "application/json" },',
|
|
847
|
+
' });',
|
|
848
|
+
' }',
|
|
849
|
+
' }',
|
|
850
|
+
'};',
|
|
851
|
+
].join('\n');
|
|
852
|
+
const worker = env.LOADER.load({
|
|
853
|
+
compatibilityDate: CF_COMPAT_DATE,
|
|
854
|
+
compatibilityFlags: ['nodejs_compat'],
|
|
855
|
+
mainModule: 'fetch-proxy.js',
|
|
856
|
+
modules: { 'fetch-proxy.js': proxyCode },
|
|
857
|
+
});
|
|
858
|
+
this.fetchProxyEntrypoint = worker.getEntrypoint();
|
|
859
|
+
log?.('Fetch proxy worker created (singleton)');
|
|
860
|
+
return this.fetchProxyEntrypoint;
|
|
861
|
+
}
|
|
862
|
+
catch (e) {
|
|
863
|
+
log?.(`Fetch proxy creation failed: ${e?.message}`);
|
|
864
|
+
return null;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
/**
|
|
868
|
+
* Build a FetchFn that routes through the singleton proxy entrypoint.
|
|
869
|
+
* All concurrent fetches share ONE worker — no port exhaustion.
|
|
870
|
+
*/
|
|
871
|
+
buildFetchFn(log) {
|
|
872
|
+
const entrypoint = this.ensureFetchProxy(log);
|
|
873
|
+
if (!entrypoint)
|
|
874
|
+
return undefined;
|
|
875
|
+
return async (url, init) => {
|
|
876
|
+
const headers = {};
|
|
877
|
+
if (init?.headers) {
|
|
878
|
+
if (init.headers instanceof Headers) {
|
|
879
|
+
init.headers.forEach((v, k) => { headers[k] = v; });
|
|
880
|
+
}
|
|
881
|
+
else if (typeof init.headers === 'object') {
|
|
882
|
+
Object.assign(headers, init.headers);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
return entrypoint.fetch(new Request('http://fetch-proxy/do-fetch', {
|
|
886
|
+
method: 'POST',
|
|
887
|
+
body: JSON.stringify({ url, method: init?.method || 'GET', headers }),
|
|
888
|
+
}));
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
ensureNpmInstaller(onProgress) {
|
|
892
|
+
this.ensureSqliteFs();
|
|
893
|
+
if (!this.esbuildService) {
|
|
894
|
+
this.esbuildService = new EsbuildService(this.sqliteFs);
|
|
895
|
+
}
|
|
896
|
+
// ── Lazy fetch-proxy ────────────────────────────────────────────
|
|
897
|
+
// The fetch-proxy is a singleton dynamic worker (LOADER.load) that
|
|
898
|
+
// buffers registry responses to dodge wrangler-local-dev port
|
|
899
|
+
// exhaustion. It is ONLY needed by the legacy in-supervisor
|
|
900
|
+
// resolveTree path (src/npm-resolver.ts). With NIMBUS_FACET_RESOLVER
|
|
901
|
+
// default-on (commit 9194998) the resolver runs in a facet that
|
|
902
|
+
// uses bare globalThis.fetch — no proxy needed. Same for the
|
|
903
|
+
// install-batch-facet path (commit c285025) — uses bare fetch.
|
|
904
|
+
//
|
|
905
|
+
// workerd has a per-DO cap on concurrent dynamic workers (~5-6
|
|
906
|
+
// empirically). A permanent live proxy worker eats one of those
|
|
907
|
+
// slots for the entire DO lifetime. Skipping the proxy in the
|
|
908
|
+
// default-on configuration buys us back a slot.
|
|
909
|
+
//
|
|
910
|
+
// Proxy is only built when ANY legacy fallback is active — this
|
|
911
|
+
// way `NIMBUS_FACET_RESOLVER=0` (rolling back the resolver) or
|
|
912
|
+
// `NIMBUS_FACET_NPM_INSTALL_BATCH=0` (rolling back install-batch)
|
|
913
|
+
// still works exactly as before. Same emergency-rollback contract.
|
|
914
|
+
const useFacetResolver = this._envFlagDefaultOn('NIMBUS_FACET_RESOLVER');
|
|
915
|
+
const useFacetInstall = this._envFlagDefaultOn('NIMBUS_FACET_NPM_INSTALL');
|
|
916
|
+
const useBatchFacet = this._envFlagDefaultOn('NIMBUS_FACET_NPM_INSTALL_BATCH');
|
|
917
|
+
const needProxy = !(useFacetResolver && useFacetInstall && useBatchFacet);
|
|
918
|
+
const fetchFn = needProxy ? this.buildFetchFn(onProgress) : undefined;
|
|
919
|
+
if (!needProxy) {
|
|
920
|
+
onProgress?.(`[npm] Lazy fetch-proxy: skipped (all facet paths default-on)`);
|
|
921
|
+
}
|
|
922
|
+
this.npmInstaller = new NpmInstaller(this.sqliteFs, this.ctx.storage.sql, {
|
|
923
|
+
esbuild: this.esbuildService,
|
|
924
|
+
ctx: this.ctx,
|
|
925
|
+
env: this.env,
|
|
926
|
+
onProgress,
|
|
927
|
+
fetchFn,
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Read an environment flag with default-on semantics. Mirrors the
|
|
932
|
+
* shouldUseFacetPool / shouldUseFacetResolver / shouldUseBatchFacet
|
|
933
|
+
* gates inside NpmInstaller — kept here as a private helper so the
|
|
934
|
+
* lazy-proxy decision uses identical semantics without leaking that
|
|
935
|
+
* private API across modules.
|
|
936
|
+
*/
|
|
937
|
+
_envFlagDefaultOn(name) {
|
|
938
|
+
const raw = this.env?.[name];
|
|
939
|
+
if (raw === undefined || raw === null)
|
|
940
|
+
return true;
|
|
941
|
+
const s = String(raw).toLowerCase();
|
|
942
|
+
if (s === '0' || s === '' || s === 'false' || s === 'off' || s === 'no')
|
|
943
|
+
return false;
|
|
944
|
+
return true;
|
|
945
|
+
}
|
|
946
|
+
// ── Session initialization ────────────────────────────────────────────
|
|
947
|
+
// ── Session initialization ────────────────────────────────────────────
|
|
948
|
+
//
|
|
949
|
+
// S6: body extracted to ./nimbus-session-init.ts (1875 LOC of registry
|
|
950
|
+
// command registrations + boot wiring). The class retains `initSession`
|
|
951
|
+
// as a delegator per plan §IX.4 R1. Visibility relaxed (was `private`)
|
|
952
|
+
// so the SessionInternal interface declares it.
|
|
953
|
+
initSession(ws) {
|
|
954
|
+
// Cast pattern (per plan §IX recommendation 1, used here only because
|
|
955
|
+
// initSession reads this.ctx + this.env extensively; siblings that need
|
|
956
|
+
// ctx/env take them as separate explicit args per DEFECT-D1).
|
|
957
|
+
return _w11InitSession(this, ws);
|
|
958
|
+
}
|
|
959
|
+
// ── Filesystem seeding ────────────────────────────────────────────────
|
|
960
|
+
seedFilesystem() {
|
|
961
|
+
const fs = this.sqliteFs;
|
|
962
|
+
const dirs = [
|
|
963
|
+
'bin', 'etc', 'home', 'home/user', 'home/user/.config',
|
|
964
|
+
'tmp', 'var', 'var/log', 'usr', 'usr/bin', 'usr/lib',
|
|
965
|
+
'usr/lib/node_modules', 'usr/share', 'usr/share/pkg',
|
|
966
|
+
'usr/share/pkg/node_modules', 'opt',
|
|
967
|
+
'home/user/projects',
|
|
968
|
+
];
|
|
969
|
+
for (const dir of dirs) {
|
|
970
|
+
if (!fs.exists(dir))
|
|
971
|
+
fs.mkdir(dir, { recursive: true });
|
|
972
|
+
}
|
|
973
|
+
if (!fs.exists('etc/hostname')) {
|
|
974
|
+
fs.writeFile('etc/hostname', DEFAULT_HOSTNAME + '\n');
|
|
975
|
+
}
|
|
976
|
+
if (!fs.exists('etc/os-release')) {
|
|
977
|
+
fs.writeFile('etc/os-release', `NAME="Nimbus"\nVERSION="${NIMBUS_VERSION}"\nID=nimbus\n` +
|
|
978
|
+
'PRETTY_NAME="Nimbus — Cloud Dev Environment"\n');
|
|
979
|
+
}
|
|
980
|
+
if (!fs.exists('etc/profile')) {
|
|
981
|
+
fs.writeFile('etc/profile', 'export PATH=/usr/bin:/bin\nexport EDITOR=nano\n');
|
|
982
|
+
}
|
|
983
|
+
if (!fs.exists('home/user/.nimbusrc')) {
|
|
984
|
+
fs.writeFile('home/user/.nimbusrc', '# Nimbus shell config\nalias ll="ls -la"\nalias la="ls -a"\nalias l="ls -1"\n');
|
|
985
|
+
}
|
|
986
|
+
{
|
|
987
|
+
// [BANNER ALIGNMENT FIX] Always re-render the motd so existing
|
|
988
|
+
// sessions whose VFS has the pre-fix mis-aligned banner get the
|
|
989
|
+
// corrected version on next reconnect. Cheap (~200 bytes); no
|
|
990
|
+
// user-edit collision concern (the file isn't user-owned).
|
|
991
|
+
const expectedMotd = renderMotdBanner(NIMBUS_VERSION);
|
|
992
|
+
let needsWrite = !fs.exists('etc/motd');
|
|
993
|
+
if (!needsWrite) {
|
|
994
|
+
try {
|
|
995
|
+
const existing = fs.readFileString('etc/motd');
|
|
996
|
+
if (existing !== expectedMotd)
|
|
997
|
+
needsWrite = true;
|
|
998
|
+
}
|
|
999
|
+
catch {
|
|
1000
|
+
needsWrite = true;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
if (needsWrite)
|
|
1004
|
+
fs.writeFile('etc/motd', expectedMotd);
|
|
1005
|
+
}
|
|
1006
|
+
if (!fs.exists('home/user/hello.js')) {
|
|
1007
|
+
fs.writeFile('home/user/hello.js', 'console.log("Hello from Nimbus!");\n' +
|
|
1008
|
+
'console.log("Executed in a Dynamic Worker isolate");\n' +
|
|
1009
|
+
'console.log("Platform:", process.platform, "| PID:", process.pid);\n' +
|
|
1010
|
+
'console.log("2 + 2 =", 2 + 2);\n');
|
|
1011
|
+
}
|
|
1012
|
+
// seed-refresh (2026-05-15): symmetric to hello.js — give new users
|
|
1013
|
+
// a starter C file so the clang demo in README.md / Docs.tsx works
|
|
1014
|
+
// without typing the source by hand.
|
|
1015
|
+
if (!fs.exists('home/user/hello.c')) {
|
|
1016
|
+
fs.writeFile('home/user/hello.c', '#include <stdio.h>\n' +
|
|
1017
|
+
'\n' +
|
|
1018
|
+
'int main(void) {\n' +
|
|
1019
|
+
' printf("Hello from Nimbus C!\\n");\n' +
|
|
1020
|
+
' return 0;\n' +
|
|
1021
|
+
'}\n');
|
|
1022
|
+
}
|
|
1023
|
+
if (!fs.exists('home/user/welcome.txt')) {
|
|
1024
|
+
fs.writeFile('home/user/welcome.txt', `Welcome to Nimbus v${NIMBUS_VERSION}!\n\n` +
|
|
1025
|
+
'Cloud-native dev environment on Cloudflare Workers.\n\n' +
|
|
1026
|
+
'JavaScript / TypeScript:\n' +
|
|
1027
|
+
' node hello.js — run in isolated dynamic worker\n' +
|
|
1028
|
+
' npm install <pkg> — install npm packages\n' +
|
|
1029
|
+
' vite — start dev server with HMR\n' +
|
|
1030
|
+
' nimbus-wrangler dev — run Cloudflare Worker locally\n' +
|
|
1031
|
+
'\n' +
|
|
1032
|
+
'More languages (install on demand):\n' +
|
|
1033
|
+
" nimbus install clang — then: clang hello.c -o hello && ./hello\n" +
|
|
1034
|
+
" nimbus install python — then: python -c 'print(\"hi\")'\n" +
|
|
1035
|
+
" nimbus install ruby — then: ruby -e 'puts \"hi\"'\n" +
|
|
1036
|
+
'\n' +
|
|
1037
|
+
'Interactive REPLs (after install):\n' +
|
|
1038
|
+
' python — Pyodide CPython 3.13\n' +
|
|
1039
|
+
' ruby — ruby.wasm 3.3\n' +
|
|
1040
|
+
' node — bare REPL\n' +
|
|
1041
|
+
'\n' +
|
|
1042
|
+
'System:\n' +
|
|
1043
|
+
' nimbus install --list — show installed runtimes\n' +
|
|
1044
|
+
' nimbus install --available — show catalog\n' +
|
|
1045
|
+
' df — filesystem + cache stats\n');
|
|
1046
|
+
}
|
|
1047
|
+
// ── Starter app (Vite + React + TS + Tailwind + Router) ──
|
|
1048
|
+
// Idempotent: guarded by shouldSeedProject() which checks both a
|
|
1049
|
+
// sentinel file and the project dir. Safe to call on every boot.
|
|
1050
|
+
try {
|
|
1051
|
+
seedProject(fs, { log: (msg) => console.log(msg) });
|
|
1052
|
+
}
|
|
1053
|
+
catch (e) {
|
|
1054
|
+
console.error('[nimbus] seedProject failed:', e?.message || e);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
// ── WebSocket lifecycle ───────────────────────────────────────────────
|
|
1058
|
+
//
|
|
1059
|
+
// S7: bodies extracted to ./nimbus-session-ws.ts. Class retains the
|
|
1060
|
+
// method NAMES per plan §IX.4 R1 (DO RPC fabric needs them). Visibility
|
|
1061
|
+
// relaxed for the methods (default-public; was async/private).
|
|
1062
|
+
async webSocketMessage(ws, message) {
|
|
1063
|
+
return _wsDoMessage(this, ws, message);
|
|
1064
|
+
}
|
|
1065
|
+
async webSocketClose(ws, code, reason, wasClean) {
|
|
1066
|
+
return _wsDoClose(this, ws, code, reason, wasClean);
|
|
1067
|
+
}
|
|
1068
|
+
async webSocketError(ws, error) {
|
|
1069
|
+
return _wsDoError(this, ws, error);
|
|
1070
|
+
}
|
|
1071
|
+
/** W9: synchronous flush on close. Delegator → ./nimbus-session-hib.ts (S4). */
|
|
1072
|
+
_w9FlushOnClose() {
|
|
1073
|
+
return _w9DoFlushOnClose(this);
|
|
1074
|
+
}
|
|
1075
|
+
/** W5: bridge _w5PersistRing → ctx.waitUntil. Delegator → ./nimbus-session-ws.ts (S7). */
|
|
1076
|
+
_w5SafePersistRing() {
|
|
1077
|
+
return _wsDoSafePersistRing(this);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
// ── W10 Inner-Worker + assets bindings extracted to
|
|
1081
|
+
// ── ./nimbus-session-bindings.ts (S2). See plan §B.3.9.
|
|
1082
|
+
// ── Re-exported here so src/index.ts's wrangler entry-graph
|
|
1083
|
+
// ── reachability is preserved.
|
|
1084
|
+
export { NimbusAssetsRPC, NimbusLoaderRPC, NimbusLoadedWorker, NimbusLoadedEntrypoint, NimbusDurableObjectNamespace, NimbusDOStub, } from './bindings.js';
|