@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,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* port-registry.ts — Maps virtual ports to facet stubs.
|
|
3
|
+
*
|
|
4
|
+
* When a facet calls http.createServer().listen(3000), it notifies the
|
|
5
|
+
* supervisor via SupervisorRPC.registerPort(3000). The supervisor stores
|
|
6
|
+
* the facet stub here. When the DO receives a request to /port/3000/*,
|
|
7
|
+
* it looks up the facet and forwards the request.
|
|
8
|
+
*
|
|
9
|
+
* Transport (audit C2):
|
|
10
|
+
* The proxy forwards a real Request and returns a real Response.
|
|
11
|
+
* Workers RPC natively transfers Request/Response values between
|
|
12
|
+
* isolates with streaming bodies and flow control (they are NOT
|
|
13
|
+
* structured-cloneable — RPC has its own transfer mechanism for
|
|
14
|
+
* these types). Binary payloads (images, wasm, gzip, multipart
|
|
15
|
+
* uploads, audio/video) go through byte-for-byte with no UTF-8
|
|
16
|
+
* coercion or JSON envelope anywhere in the path.
|
|
17
|
+
*
|
|
18
|
+
* Facet contract for handleHttpRequest:
|
|
19
|
+
* async handleHttpRequest(request: Request): Promise<Response>
|
|
20
|
+
*
|
|
21
|
+
* where `request.url` is the inner URL the user's HTTP server
|
|
22
|
+
* expects (pathname + search, absolute against a synthetic origin),
|
|
23
|
+
* `request.method`/`headers` mirror the outer request, and
|
|
24
|
+
* `request.body` is a ReadableStream (or null for GET/HEAD) that
|
|
25
|
+
* the facet can consume once. The returned Response is returned
|
|
26
|
+
* to the outer fetch as-is; its body is streamed directly.
|
|
27
|
+
*/
|
|
28
|
+
export class PortRegistry {
|
|
29
|
+
ports = new Map();
|
|
30
|
+
/** Register a facet as listening on a port. */
|
|
31
|
+
register(port, pid, facetStub) {
|
|
32
|
+
this.ports.set(port, { port, pid, facetStub, registeredAt: Date.now() });
|
|
33
|
+
}
|
|
34
|
+
/** Unregister a port. */
|
|
35
|
+
unregister(port) {
|
|
36
|
+
return this.ports.delete(port);
|
|
37
|
+
}
|
|
38
|
+
/** Unregister all ports owned by a specific PID. */
|
|
39
|
+
unregisterByPid(pid) {
|
|
40
|
+
let count = 0;
|
|
41
|
+
for (const [port, entry] of this.ports) {
|
|
42
|
+
if (entry.pid === pid) {
|
|
43
|
+
this.ports.delete(port);
|
|
44
|
+
count++;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return count;
|
|
48
|
+
}
|
|
49
|
+
/** Look up a port entry. */
|
|
50
|
+
get(port) {
|
|
51
|
+
return this.ports.get(port);
|
|
52
|
+
}
|
|
53
|
+
/** Check if a port is registered. */
|
|
54
|
+
has(port) {
|
|
55
|
+
return this.ports.has(port);
|
|
56
|
+
}
|
|
57
|
+
/** Get all registered ports. */
|
|
58
|
+
getAll() {
|
|
59
|
+
return [...this.ports.values()];
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Forward an HTTP request to the facet owning a port.
|
|
63
|
+
*
|
|
64
|
+
* Returns null if no facet is listening on this port (or if a port
|
|
65
|
+
* is registered but its stub is not yet attached — see PortEntry).
|
|
66
|
+
*
|
|
67
|
+
* The Request passed to the facet has a rewritten URL whose pathname
|
|
68
|
+
* is the inner path (without the `/port/<n>` prefix) and whose origin
|
|
69
|
+
* matches the outer request, so user code reading `request.url`
|
|
70
|
+
* sees a URL shape consistent with "my server is running at this
|
|
71
|
+
* port". Headers and body are forwarded unchanged; the body is a
|
|
72
|
+
* ReadableStream so binary payloads aren't materialised in memory.
|
|
73
|
+
*
|
|
74
|
+
* Binary safety: both directions use structured-cloneable Request/
|
|
75
|
+
* Response values over Workers RPC. No UTF-8 round-trip anywhere.
|
|
76
|
+
* A user-facet serving a PNG will return the exact same bytes the
|
|
77
|
+
* client receives.
|
|
78
|
+
*/
|
|
79
|
+
async routeRequest(port, request, pathname) {
|
|
80
|
+
const entry = this.ports.get(port);
|
|
81
|
+
// Some short-lived/foreground paths can reserve a port through
|
|
82
|
+
// SupervisorRPC.registerPort before a routeable WorkerEntrypoint
|
|
83
|
+
// exists. Explicit long-running processes (for example
|
|
84
|
+
// `node --watch server.js`) register a non-null facet stub and route
|
|
85
|
+
// below. For a null-stub reservation, return an honest 501 instead
|
|
86
|
+
// of a misleading "no process listening" 502.
|
|
87
|
+
if (!entry?.facetStub) {
|
|
88
|
+
return new Response(JSON.stringify({
|
|
89
|
+
error: 'port is registered but has no routeable facet handler',
|
|
90
|
+
port,
|
|
91
|
+
}), {
|
|
92
|
+
status: 501,
|
|
93
|
+
headers: { 'Content-Type': 'application/json' },
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const outerUrl = new URL(request.url);
|
|
98
|
+
// Preserve query; replace the pathname with the inner path so
|
|
99
|
+
// the user's HTTP server sees its own URL shape. (Fragments
|
|
100
|
+
// never make it onto the wire, so there's nothing to forward.)
|
|
101
|
+
const innerUrl = new URL(pathname + outerUrl.search, outerUrl.origin);
|
|
102
|
+
// Rebuild a Request targeting the inner URL. Body is forwarded as
|
|
103
|
+
// a stream — we do NOT await `.text()`/`.arrayBuffer()`, which
|
|
104
|
+
// would corrupt any binary payload and block large uploads.
|
|
105
|
+
//
|
|
106
|
+
// Construct via (url, init) so we can override the URL while
|
|
107
|
+
// inheriting method/headers/body from the original. For GET/HEAD
|
|
108
|
+
// we must omit `body` entirely — the Request constructor throws
|
|
109
|
+
// if a body is supplied on those methods.
|
|
110
|
+
const hasBody = request.method !== 'GET' && request.method !== 'HEAD';
|
|
111
|
+
const headers = new Headers(request.headers);
|
|
112
|
+
headers.set('X-Nimbus-Port', String(port));
|
|
113
|
+
// `duplex: 'half'` is required by workerd when body is a
|
|
114
|
+
// ReadableStream — otherwise `new Request(…)` throws. It's not
|
|
115
|
+
// part of the published @cloudflare/workers-types RequestInit,
|
|
116
|
+
// so extend the type locally rather than casting the whole
|
|
117
|
+
// init — that keeps typos in other fields caught by the compiler.
|
|
118
|
+
const init = {
|
|
119
|
+
method: request.method,
|
|
120
|
+
headers,
|
|
121
|
+
body: hasBody ? request.body : undefined,
|
|
122
|
+
};
|
|
123
|
+
if (hasBody)
|
|
124
|
+
init.duplex = 'half';
|
|
125
|
+
const forwarded = new Request(innerUrl.toString(), init);
|
|
126
|
+
// RPC: the facet receives the Request and returns a Response.
|
|
127
|
+
// Both cross the isolate boundary via Workers RPC's native
|
|
128
|
+
// Request/Response transport — bytes are streamed with
|
|
129
|
+
// flow-control, never materialised.
|
|
130
|
+
const response = await entry.facetStub.handleHttpRequest(forwarded);
|
|
131
|
+
if (!(response instanceof Response)) {
|
|
132
|
+
// Defensive: if a facet ever returns something else (JSON
|
|
133
|
+
// envelope, string, etc.), treat it as a 502 so the client
|
|
134
|
+
// sees a clear signal rather than a confusing surprise.
|
|
135
|
+
return new Response(JSON.stringify({
|
|
136
|
+
error: 'port proxy: facet returned non-Response value',
|
|
137
|
+
type: typeof response,
|
|
138
|
+
}), {
|
|
139
|
+
status: 502,
|
|
140
|
+
headers: { 'Content-Type': 'application/json' },
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
// Return the facet's Response as-is. Body is streamed; headers,
|
|
144
|
+
// status, and status-text pass through unchanged. We do NOT
|
|
145
|
+
// inject Access-Control-Allow-Origin — a port proxy forwards
|
|
146
|
+
// whatever CORS policy the user's HTTP server chose (audit C3
|
|
147
|
+
// discourages gratuitous wildcards on non-static routes).
|
|
148
|
+
return response;
|
|
149
|
+
}
|
|
150
|
+
catch (e) {
|
|
151
|
+
// Server-side triage — users see only the 502 body, operators
|
|
152
|
+
// see the full error + stack in Worker logs.
|
|
153
|
+
console.error('[port-registry] routeRequest failed for port', port, ':', e);
|
|
154
|
+
return new Response(JSON.stringify({ error: e?.message || String(e) }), {
|
|
155
|
+
status: 502,
|
|
156
|
+
headers: { 'Content-Type': 'application/json' },
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
get stats() {
|
|
161
|
+
return {
|
|
162
|
+
activePorts: this.ports.size,
|
|
163
|
+
ports: [...this.ports.entries()].map(([port, e]) => ({ port, pid: e.pid })),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* process-logs-api.ts — HTTP/WS surface for the per-PID process log store.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from nimbus-session.ts so this file owns every byte of the
|
|
5
|
+
* log-tabs feature (WS handler, processes list, terminal event helper).
|
|
6
|
+
* nimbus-session.ts only calls these at 4 small hook sites:
|
|
7
|
+
* - onSpawn (notify)
|
|
8
|
+
* - _rpcReportExit / _reportExternalExit / shellExecuteTracked finally (notify)
|
|
9
|
+
* - Two route branches in _handleFetch (serve)
|
|
10
|
+
*
|
|
11
|
+
* The WebSocket protocol (server → client):
|
|
12
|
+
* { type: 'backlog', pid, chunks: [{ stream, data, ts, binary? }] } — once on open
|
|
13
|
+
* { type: 'chunk', stream, data, ts, binary? } — per append
|
|
14
|
+
* { type: 'exit', code, at, reason? } — on exit
|
|
15
|
+
* { type: 'notfound', pid } — pid unknown; socket closes
|
|
16
|
+
*
|
|
17
|
+
* Client → server: ignored. Clients are output-only; close the socket to
|
|
18
|
+
* unsubscribe. The ring buffer keeps state for 10 min post-exit so a tab
|
|
19
|
+
* that's still open after a crash continues to show the final output.
|
|
20
|
+
*
|
|
21
|
+
* W9 (CF research §C.2, Lever 11): the WS now uses `ctx.acceptWebSocket`
|
|
22
|
+
* (hibernatable) when a `ctx` is provided. Why the switch:
|
|
23
|
+
* - The pre-W9 `server.accept()` call pinned the actor for the full
|
|
24
|
+
* duration of the log tail. A user opening a long-running log tab
|
|
25
|
+
* and walking away kept the DO awake — accumulating co-residency-
|
|
26
|
+
* OOM risk per Section A.1 of the research doc.
|
|
27
|
+
* - With hibernatable WS, the actor sleeps when nothing else holds it.
|
|
28
|
+
* The `pid` is captured in the serialized attachment so a wake-up
|
|
29
|
+
* dispatch can re-resolve. Subscribers are NOT preserved across
|
|
30
|
+
* hibernation (per the STOR Primer: "Does not survive: All JS
|
|
31
|
+
* in-memory state"), but the client typically reconnects via a
|
|
32
|
+
* fresh WS open which triggers a new backlog frame from the now-
|
|
33
|
+
* hydrated ring (W9 hib-persist) — equivalent UX, fewer wakes.
|
|
34
|
+
* - Falls back to `server.accept()` when `ctx` is omitted (legacy
|
|
35
|
+
* callers / unit tests without a DurableObjectState).
|
|
36
|
+
*/
|
|
37
|
+
import type { ProcessLogStore } from './process-logs.js';
|
|
38
|
+
import type { ProcessTable } from './process-table.js';
|
|
39
|
+
/**
|
|
40
|
+
* Parameters for `handleLogsWebSocketRequest`. We accept the process
|
|
41
|
+
* table so the handler can distinguish "brand-new pid, not yet written
|
|
42
|
+
* to" from "pid never existed". The former is common — a client that
|
|
43
|
+
* opens a log WS immediately on the `{type:'spawn'}` frame races with
|
|
44
|
+
* the first `_rpcStdout` RPC call and would otherwise get `notfound`.
|
|
45
|
+
*/
|
|
46
|
+
export interface LogsWebSocketDeps {
|
|
47
|
+
processLogs: ProcessLogStore;
|
|
48
|
+
processTable: ProcessTable;
|
|
49
|
+
/**
|
|
50
|
+
* W9: optional `DurableObjectState`. When provided, the upgrade uses
|
|
51
|
+
* `ctx.acceptWebSocket` (hibernatable) and serializes a process-logs
|
|
52
|
+
* attachment so post-hibernate dispatches can resolve the pid. When
|
|
53
|
+
* omitted, falls back to `server.accept()` (non-hibernatable; pre-W9
|
|
54
|
+
* behaviour, kept for unit tests).
|
|
55
|
+
*/
|
|
56
|
+
ctx?: {
|
|
57
|
+
acceptWebSocket(ws: WebSocket, tags?: string[]): void;
|
|
58
|
+
} | null;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Minimum interface this module needs from the terminal. The real type
|
|
62
|
+
* is WebSocketTerminal but typing by shape avoids a cross-file import
|
|
63
|
+
* cycle and lets tests pass a plain stub.
|
|
64
|
+
*/
|
|
65
|
+
export interface TerminalLike {
|
|
66
|
+
ws: WebSocket;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Send a structured JSON event to the main terminal WebSocket, if one is
|
|
70
|
+
* attached. Used for out-of-band process lifecycle notifications
|
|
71
|
+
* (`spawn`, `exit`) that the UI's tabs panel listens for — so it can
|
|
72
|
+
* auto-open a log tab when a long-running process starts and stamp an
|
|
73
|
+
* exit banner when it finishes.
|
|
74
|
+
*
|
|
75
|
+
* Unlike WebSocketTerminal.write (which buffers + emits `{type:'output'}`
|
|
76
|
+
* after a 5 ms flush), this bypasses the buffer so spawn/exit events
|
|
77
|
+
* arrive immediately — the UI's tab auto-open feels snappier, and a
|
|
78
|
+
* crash-and-exit race can't drop the spawn frame.
|
|
79
|
+
*/
|
|
80
|
+
export declare function notifyTerminalEvent(terminal: TerminalLike | null, event: Record<string, unknown>): void;
|
|
81
|
+
/**
|
|
82
|
+
* Handle an incoming `/api/logs/<pid>` upgrade request. On success
|
|
83
|
+
* returns the 101 Response; caller forwards it unchanged. Subscribes
|
|
84
|
+
* to the ring buffer and streams until the client (or we) closes.
|
|
85
|
+
*
|
|
86
|
+
* Guarantees:
|
|
87
|
+
* - Backlog frame is always sent first if the pid exists, even if
|
|
88
|
+
* the buffer is empty (so clients can render "empty but attached").
|
|
89
|
+
* - If the process already exited, an `exit` frame is included in
|
|
90
|
+
* the handshake so UIs don't need to wait for a live event.
|
|
91
|
+
* - Subscribers are torn down on close OR error — no leaks if the
|
|
92
|
+
* client disconnects abruptly.
|
|
93
|
+
*/
|
|
94
|
+
export declare function handleLogsWebSocketRequest(request: Request, pid: number, deps: LogsWebSocketDeps): Response;
|
|
95
|
+
export declare function handleProcessesListRequest(processTable: ProcessTable, processLogs: ProcessLogStore): Response;
|
|
96
|
+
/**
|
|
97
|
+
* Utility: does this pathname match `/api/logs/<pid>`? Returns the pid
|
|
98
|
+
* or null. Kept alongside the handler so the routing regex lives in
|
|
99
|
+
* exactly one place.
|
|
100
|
+
*/
|
|
101
|
+
export declare function matchLogsPath(pathname: string): number | null;
|
|
102
|
+
//# sourceMappingURL=process-logs-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-logs-api.d.ts","sourceRoot":"","sources":["../../src/runtime/process-logs-api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAY,MAAM,mBAAmB,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,eAAe,CAAC;IAC7B,YAAY,EAAE,YAAY,CAAC;IAC3B;;;;;;OAMG;IACH,GAAG,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;CACxE;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,SAAS,CAAC;CACf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,YAAY,GAAG,IAAI,EAC7B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,IAAI,CAON;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,iBAAiB,GACtB,QAAQ,CAiHV;AAmBD,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,YAAY,EAC1B,WAAW,EAAE,eAAe,GAC3B,QAAQ,CAuCV;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK7D"}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* process-logs-api.ts — HTTP/WS surface for the per-PID process log store.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from nimbus-session.ts so this file owns every byte of the
|
|
5
|
+
* log-tabs feature (WS handler, processes list, terminal event helper).
|
|
6
|
+
* nimbus-session.ts only calls these at 4 small hook sites:
|
|
7
|
+
* - onSpawn (notify)
|
|
8
|
+
* - _rpcReportExit / _reportExternalExit / shellExecuteTracked finally (notify)
|
|
9
|
+
* - Two route branches in _handleFetch (serve)
|
|
10
|
+
*
|
|
11
|
+
* The WebSocket protocol (server → client):
|
|
12
|
+
* { type: 'backlog', pid, chunks: [{ stream, data, ts, binary? }] } — once on open
|
|
13
|
+
* { type: 'chunk', stream, data, ts, binary? } — per append
|
|
14
|
+
* { type: 'exit', code, at, reason? } — on exit
|
|
15
|
+
* { type: 'notfound', pid } — pid unknown; socket closes
|
|
16
|
+
*
|
|
17
|
+
* Client → server: ignored. Clients are output-only; close the socket to
|
|
18
|
+
* unsubscribe. The ring buffer keeps state for 10 min post-exit so a tab
|
|
19
|
+
* that's still open after a crash continues to show the final output.
|
|
20
|
+
*
|
|
21
|
+
* W9 (CF research §C.2, Lever 11): the WS now uses `ctx.acceptWebSocket`
|
|
22
|
+
* (hibernatable) when a `ctx` is provided. Why the switch:
|
|
23
|
+
* - The pre-W9 `server.accept()` call pinned the actor for the full
|
|
24
|
+
* duration of the log tail. A user opening a long-running log tab
|
|
25
|
+
* and walking away kept the DO awake — accumulating co-residency-
|
|
26
|
+
* OOM risk per Section A.1 of the research doc.
|
|
27
|
+
* - With hibernatable WS, the actor sleeps when nothing else holds it.
|
|
28
|
+
* The `pid` is captured in the serialized attachment so a wake-up
|
|
29
|
+
* dispatch can re-resolve. Subscribers are NOT preserved across
|
|
30
|
+
* hibernation (per the STOR Primer: "Does not survive: All JS
|
|
31
|
+
* in-memory state"), but the client typically reconnects via a
|
|
32
|
+
* fresh WS open which triggers a new backlog frame from the now-
|
|
33
|
+
* hydrated ring (W9 hib-persist) — equivalent UX, fewer wakes.
|
|
34
|
+
* - Falls back to `server.accept()` when `ctx` is omitted (legacy
|
|
35
|
+
* callers / unit tests without a DurableObjectState).
|
|
36
|
+
*/
|
|
37
|
+
/**
|
|
38
|
+
* Send a structured JSON event to the main terminal WebSocket, if one is
|
|
39
|
+
* attached. Used for out-of-band process lifecycle notifications
|
|
40
|
+
* (`spawn`, `exit`) that the UI's tabs panel listens for — so it can
|
|
41
|
+
* auto-open a log tab when a long-running process starts and stamp an
|
|
42
|
+
* exit banner when it finishes.
|
|
43
|
+
*
|
|
44
|
+
* Unlike WebSocketTerminal.write (which buffers + emits `{type:'output'}`
|
|
45
|
+
* after a 5 ms flush), this bypasses the buffer so spawn/exit events
|
|
46
|
+
* arrive immediately — the UI's tab auto-open feels snappier, and a
|
|
47
|
+
* crash-and-exit race can't drop the spawn frame.
|
|
48
|
+
*/
|
|
49
|
+
export function notifyTerminalEvent(terminal, event) {
|
|
50
|
+
if (!terminal)
|
|
51
|
+
return;
|
|
52
|
+
try {
|
|
53
|
+
terminal.ws.send(JSON.stringify(event));
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
/* socket closed or congested — dropping is the right behavior here */
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Handle an incoming `/api/logs/<pid>` upgrade request. On success
|
|
61
|
+
* returns the 101 Response; caller forwards it unchanged. Subscribes
|
|
62
|
+
* to the ring buffer and streams until the client (or we) closes.
|
|
63
|
+
*
|
|
64
|
+
* Guarantees:
|
|
65
|
+
* - Backlog frame is always sent first if the pid exists, even if
|
|
66
|
+
* the buffer is empty (so clients can render "empty but attached").
|
|
67
|
+
* - If the process already exited, an `exit` frame is included in
|
|
68
|
+
* the handshake so UIs don't need to wait for a live event.
|
|
69
|
+
* - Subscribers are torn down on close OR error — no leaks if the
|
|
70
|
+
* client disconnects abruptly.
|
|
71
|
+
*/
|
|
72
|
+
export function handleLogsWebSocketRequest(request, pid, deps) {
|
|
73
|
+
const { processLogs, processTable, ctx } = deps;
|
|
74
|
+
if (request.headers.get('Upgrade') !== 'websocket') {
|
|
75
|
+
return new Response('Expected WebSocket', { status: 426 });
|
|
76
|
+
}
|
|
77
|
+
const pair = new WebSocketPair();
|
|
78
|
+
const [client, server] = Object.values(pair);
|
|
79
|
+
// W9: prefer hibernatable WS so an idle log-tail tab doesn't pin the
|
|
80
|
+
// actor. Fallback to server.accept when ctx is absent (e.g., unit
|
|
81
|
+
// tests). Either way, tag the socket with `{kind:'process-logs', pid}`
|
|
82
|
+
// so the close handler can discriminate.
|
|
83
|
+
if (ctx && typeof ctx.acceptWebSocket === 'function') {
|
|
84
|
+
try {
|
|
85
|
+
ctx.acceptWebSocket(server, ['process-logs']);
|
|
86
|
+
try {
|
|
87
|
+
server.serializeAttachment?.({ kind: 'process-logs', pid });
|
|
88
|
+
}
|
|
89
|
+
catch { /* attachment is best-effort */ }
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
// If acceptWebSocket throws (e.g., older runtime), fall back.
|
|
93
|
+
console.warn('[nimbus/W9] ctx.acceptWebSocket failed; falling back:', e?.message);
|
|
94
|
+
server.accept();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
server.accept();
|
|
99
|
+
}
|
|
100
|
+
// A pid is "truly unknown" only if neither the log store nor the
|
|
101
|
+
// process table has ever heard of it. The log store lags slightly
|
|
102
|
+
// behind the process table — facet stdout/stderr arrives via async
|
|
103
|
+
// RPC, so a log WS opened the instant the {spawn} event fires will
|
|
104
|
+
// usually see `processLogs.has(pid)===false` even though the pid is
|
|
105
|
+
// perfectly valid and about to start producing output.
|
|
106
|
+
//
|
|
107
|
+
// Subscribing in that window is safe: `subscribe` creates state via
|
|
108
|
+
// _getOrCreate, so when the first chunk arrives we fan it out to
|
|
109
|
+
// this client too. The only downside is a client that opens a log
|
|
110
|
+
// WS for a typo'd pid gets an empty live stream instead of an
|
|
111
|
+
// immediate error — acceptable tradeoff for removing the racy
|
|
112
|
+
// "no log buffer for pid N" banner users saw on EVERY short-lived
|
|
113
|
+
// process.
|
|
114
|
+
const pidKnown = processLogs.has(pid) || !!processTable.get(pid);
|
|
115
|
+
if (!pidKnown) {
|
|
116
|
+
try {
|
|
117
|
+
server.send(JSON.stringify({ type: 'notfound', pid }));
|
|
118
|
+
}
|
|
119
|
+
catch { }
|
|
120
|
+
try {
|
|
121
|
+
server.close(1000, 'unknown pid');
|
|
122
|
+
}
|
|
123
|
+
catch { }
|
|
124
|
+
return new Response(null, { status: 101, webSocket: client });
|
|
125
|
+
}
|
|
126
|
+
// 1. Backlog — one frame, so the client has a snapshot before any
|
|
127
|
+
// live chunks arrive. Bounded by the ring's 64 KB cap.
|
|
128
|
+
const chunks = processLogs.all(pid).map((c) => ({
|
|
129
|
+
stream: c.stream,
|
|
130
|
+
data: c.data,
|
|
131
|
+
ts: c.ts,
|
|
132
|
+
binary: c.binary,
|
|
133
|
+
}));
|
|
134
|
+
try {
|
|
135
|
+
server.send(JSON.stringify({ type: 'backlog', pid, chunks }));
|
|
136
|
+
}
|
|
137
|
+
catch { /* socket died during handshake — fall through, cleanup below */ }
|
|
138
|
+
// 2. If the process already exited, tell the client now. Idempotent
|
|
139
|
+
// with the subscribeExit callback below (client tolerates duplicates).
|
|
140
|
+
const existingExit = processLogs.getExit(pid);
|
|
141
|
+
if (existingExit) {
|
|
142
|
+
try {
|
|
143
|
+
server.send(JSON.stringify({
|
|
144
|
+
type: 'exit',
|
|
145
|
+
code: existingExit.code,
|
|
146
|
+
at: existingExit.at,
|
|
147
|
+
reason: existingExit.reason,
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
catch { }
|
|
151
|
+
}
|
|
152
|
+
// 3. Live stream. `subscribe` fires synchronously from append — no
|
|
153
|
+
// buffering fights with WebSocketTerminal's 5 ms flush timer.
|
|
154
|
+
let unsubChunk = null;
|
|
155
|
+
let unsubExit = null;
|
|
156
|
+
const cleanup = () => {
|
|
157
|
+
try {
|
|
158
|
+
unsubChunk?.();
|
|
159
|
+
}
|
|
160
|
+
catch { }
|
|
161
|
+
try {
|
|
162
|
+
unsubExit?.();
|
|
163
|
+
}
|
|
164
|
+
catch { }
|
|
165
|
+
unsubChunk = null;
|
|
166
|
+
unsubExit = null;
|
|
167
|
+
};
|
|
168
|
+
unsubChunk = processLogs.subscribe(pid, (c) => {
|
|
169
|
+
try {
|
|
170
|
+
server.send(JSON.stringify({
|
|
171
|
+
type: 'chunk',
|
|
172
|
+
stream: c.stream,
|
|
173
|
+
data: c.data,
|
|
174
|
+
ts: c.ts,
|
|
175
|
+
binary: c.binary,
|
|
176
|
+
}));
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// Socket is dead — cleanup so the subscriber set doesn't leak.
|
|
180
|
+
cleanup();
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
unsubExit = processLogs.subscribeExit(pid, (e) => {
|
|
184
|
+
try {
|
|
185
|
+
server.send(JSON.stringify({
|
|
186
|
+
type: 'exit',
|
|
187
|
+
code: e.code,
|
|
188
|
+
at: e.at,
|
|
189
|
+
reason: e.reason,
|
|
190
|
+
}));
|
|
191
|
+
}
|
|
192
|
+
catch { }
|
|
193
|
+
});
|
|
194
|
+
server.addEventListener('close', cleanup);
|
|
195
|
+
server.addEventListener('error', cleanup);
|
|
196
|
+
return new Response(null, { status: 101, webSocket: client });
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* GET /api/processes — lightweight listing for the tabs UI's hydrate-
|
|
200
|
+
* on-refresh path. Returns every process the DO currently knows about
|
|
201
|
+
* (running + recently exited, bounded by the ring buffer's 10 min
|
|
202
|
+
* post-exit retention).
|
|
203
|
+
*
|
|
204
|
+
* The `longRunning` flag is derived from the command string so the
|
|
205
|
+
* client can filter to "likely user-visible dev servers" without
|
|
206
|
+
* needing ProcessTable to expose the FacetManager/Shell-level spawn
|
|
207
|
+
* options (which it doesn't — the `longRunning` decision is made by
|
|
208
|
+
* FacetManager for facets and by shellExecuteTracked opts for scripts).
|
|
209
|
+
* A regex match is good enough because false positives cost nothing
|
|
210
|
+
* (they just show an extra tab the user can close).
|
|
211
|
+
*/
|
|
212
|
+
const LONG_RUNNING_CMD_RE = /^(vite|wrangler|next|nuxt|astro|remix|dev|serve|start|watch|npm\s+run\s+dev)\b/;
|
|
213
|
+
export function handleProcessesListRequest(processTable, processLogs) {
|
|
214
|
+
const processes = [];
|
|
215
|
+
for (const p of processTable.getAll()) {
|
|
216
|
+
const snap = processLogs.snapshot(p.pid);
|
|
217
|
+
processes.push({
|
|
218
|
+
pid: p.pid,
|
|
219
|
+
command: p.command,
|
|
220
|
+
state: p.state,
|
|
221
|
+
exitCode: p.exitCode,
|
|
222
|
+
// arch-gaps gap #2: prefer the explicit longRunning flag set by
|
|
223
|
+
// FacetManager.spawn; fall back to the command-string heuristic
|
|
224
|
+
// for legacy entries that didn't go through that primitive.
|
|
225
|
+
longRunning: p.longRunning === true || LONG_RUNNING_CMD_RE.test(p.command),
|
|
226
|
+
hasLogs: !!snap && snap.chunks > 0,
|
|
227
|
+
logBytes: snap?.bytes ?? 0,
|
|
228
|
+
startTime: p.startTime,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
// Reaped processes with lingering log buffers (exited >60s ago, not
|
|
232
|
+
// yet past the 10 min retention) are intentionally NOT listed here —
|
|
233
|
+
// the ProcessTable has already purged them and ProcessLogStore has no
|
|
234
|
+
// key-iteration API. Users can still access those logs via the `logs
|
|
235
|
+
// <pid>` shell command, which reads directly from ProcessLogStore.
|
|
236
|
+
// Audit C3: same-origin only. The session shell at /s/<id>/ polls
|
|
237
|
+
// this from its own origin; no cross-origin reader is intended.
|
|
238
|
+
return Response.json({ processes }, {
|
|
239
|
+
headers: { 'Cache-Control': 'no-store' },
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Utility: does this pathname match `/api/logs/<pid>`? Returns the pid
|
|
244
|
+
* or null. Kept alongside the handler so the routing regex lives in
|
|
245
|
+
* exactly one place.
|
|
246
|
+
*/
|
|
247
|
+
export function matchLogsPath(pathname) {
|
|
248
|
+
const m = pathname.match(/^\/api\/logs\/(\d+)$/);
|
|
249
|
+
if (!m)
|
|
250
|
+
return null;
|
|
251
|
+
const n = parseInt(m[1], 10);
|
|
252
|
+
return Number.isFinite(n) && n > 0 ? n : null;
|
|
253
|
+
}
|