@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,567 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* facet-process.ts — supervisor-side broker for child_process.spawn.
|
|
3
|
+
*
|
|
4
|
+
* W8 Phase 1: facet-mapped pseudo-process. Each child_process.spawn call from
|
|
5
|
+
* a parent facet routes through here:
|
|
6
|
+
*
|
|
7
|
+
* parent facet ── SUPERVISOR.cpSpawn(req) ─→ FacetProcessManager.spawn
|
|
8
|
+
* │
|
|
9
|
+
* ▼
|
|
10
|
+
* one of two execution kinds:
|
|
11
|
+
*
|
|
12
|
+
* pure-builtin — run inline in supervisor isolate via the command
|
|
13
|
+
* registry (echo, cat, true, false, ls, env, sleep,
|
|
14
|
+
* exit-code, …). No facet hop. Fast.
|
|
15
|
+
*
|
|
16
|
+
* facet-direct — mint a child facet that runs the command directly
|
|
17
|
+
* via FacetManager.execStream(). The facet IS the
|
|
18
|
+
* command's runtime — no nested cpRunBuiltinCommand
|
|
19
|
+
* recursion (that was the BLOCKER-2 deadlock vector
|
|
20
|
+
* in the initial plan; see W8-plan.md §8.5).
|
|
21
|
+
*
|
|
22
|
+
* stdin / stdout / stderr stream through per-child queues maintained on
|
|
23
|
+
* this manager instance. cpReadOutput long-polls for incremental delivery
|
|
24
|
+
* to the parent; cpDrainOutput is a one-shot full-flush invoked from the
|
|
25
|
+
* parent's exit path so unawaited children don't lose output.
|
|
26
|
+
*
|
|
27
|
+
* Lifecycle invariants:
|
|
28
|
+
* - exitCode is stamped exactly once (first writer wins). kill() and
|
|
29
|
+
* reportExit() race-free.
|
|
30
|
+
* - kill() resolves all pending waiters BEFORE invoking facets.abort,
|
|
31
|
+
* so cpWait/cpReadOutput don't hang on a torn-down facet.
|
|
32
|
+
* - facets.delete is deferred to a microtask after abort to give any
|
|
33
|
+
* in-flight reportExit RPC a chance to land (and be no-op'd by the
|
|
34
|
+
* idempotent guard).
|
|
35
|
+
*/
|
|
36
|
+
/** Cap recursion depth to defend against runaway spawn loops. */
|
|
37
|
+
export const CHILD_PROCESS_MAX_DEPTH = 8;
|
|
38
|
+
/**
|
|
39
|
+
* Cap stdin queue per child to avoid unbounded memory consumption from a
|
|
40
|
+
* fast parent against a slow child. cpStdinWrite returns ok=false past
|
|
41
|
+
* the cap; the parent's Writable will then surface a 'drain'-needed
|
|
42
|
+
* signal (real Node would return false from .write).
|
|
43
|
+
*/
|
|
44
|
+
const STDIN_QUEUE_MAX_BYTES = 256 * 1024; // 256 KiB
|
|
45
|
+
/**
|
|
46
|
+
* How long the parent's cpReadOutput long-poll waits for new chunks
|
|
47
|
+
* before returning empty. 250ms is the plan §3 target.
|
|
48
|
+
*/
|
|
49
|
+
const READ_OUTPUT_DEFAULT_WAIT_MS = 250;
|
|
50
|
+
/**
|
|
51
|
+
* Cap on cpWait long-poll. Anything longer should be split into multiple
|
|
52
|
+
* polls by the caller.
|
|
53
|
+
*/
|
|
54
|
+
const WAIT_MAX_MS = 30_000;
|
|
55
|
+
export class FacetProcessManager {
|
|
56
|
+
children = new Map();
|
|
57
|
+
nextPid = 10_000; // child PIDs start above ProcessTable's range
|
|
58
|
+
deps;
|
|
59
|
+
constructor(deps) {
|
|
60
|
+
this.deps = deps;
|
|
61
|
+
}
|
|
62
|
+
// ── spawn ───────────────────────────────────────────────────────────────
|
|
63
|
+
/**
|
|
64
|
+
* Allocate a child PID, classify the command, dispatch to inline runner
|
|
65
|
+
* or facet-direct runner. Returns immediately with the child PID; the
|
|
66
|
+
* actual command executes asynchronously and pushes output via the
|
|
67
|
+
* per-child hooks.
|
|
68
|
+
*/
|
|
69
|
+
async spawn(req) {
|
|
70
|
+
// Recursion-depth cap (env-propagated).
|
|
71
|
+
const depthIn = parseInt(req.env?.NIMBUS_CP_DEPTH || '0', 10) || 0;
|
|
72
|
+
if (depthIn >= CHILD_PROCESS_MAX_DEPTH) {
|
|
73
|
+
throw new Error(`EAGAIN: child_process spawn depth ${depthIn} exceeds ` +
|
|
74
|
+
`CHILD_PROCESS_MAX_DEPTH=${CHILD_PROCESS_MAX_DEPTH}`);
|
|
75
|
+
}
|
|
76
|
+
const childEnv = {
|
|
77
|
+
...req.env,
|
|
78
|
+
NIMBUS_CP_DEPTH: String(depthIn + 1),
|
|
79
|
+
};
|
|
80
|
+
const pid = this.nextPid++;
|
|
81
|
+
const facetName = `cp-proc-${pid}`;
|
|
82
|
+
const child = {
|
|
83
|
+
pid,
|
|
84
|
+
command: req.command,
|
|
85
|
+
args: req.args || [],
|
|
86
|
+
cwd: req.cwd,
|
|
87
|
+
env: childEnv,
|
|
88
|
+
facetName,
|
|
89
|
+
startedAt: Date.now(),
|
|
90
|
+
endedAt: null,
|
|
91
|
+
stdinChunks: [],
|
|
92
|
+
stdinClosed: false,
|
|
93
|
+
stdinTotalBytes: 0,
|
|
94
|
+
stdinWaiters: [],
|
|
95
|
+
outputs: { 1: [], 2: [] },
|
|
96
|
+
outputSeq: { 1: 0, 2: 0 },
|
|
97
|
+
outputWaiters: [],
|
|
98
|
+
exitCode: null,
|
|
99
|
+
signal: null,
|
|
100
|
+
killed: false,
|
|
101
|
+
exitWaiters: [],
|
|
102
|
+
facetSlot: null,
|
|
103
|
+
};
|
|
104
|
+
this.children.set(pid, child);
|
|
105
|
+
// ProcessTable side: register so `ps`/`logs` see the child.
|
|
106
|
+
try {
|
|
107
|
+
this.deps.processTable.spawn(`${req.command} ${req.args.join(' ')}`.trim(), req.args, req.cwd);
|
|
108
|
+
}
|
|
109
|
+
catch { /* ignore */ }
|
|
110
|
+
// Resolve command kind. Resolution failure → exit 127 (command not
|
|
111
|
+
// found), no facet at all. Same shell semantics.
|
|
112
|
+
const reg = this.deps.commandRegistry.resolve(req.command);
|
|
113
|
+
const kind = reg ? reg.kind : 'unknown';
|
|
114
|
+
// Dispatch — fire-and-forget. The promise resolves when the command
|
|
115
|
+
// completes, at which point we stamp the exit slot.
|
|
116
|
+
void this._dispatch(child, kind, req).catch((e) => {
|
|
117
|
+
// Last-resort: if both runners somehow throw, exit 1 with the error
|
|
118
|
+
// on stderr.
|
|
119
|
+
this._appendOutput(child, 2, `Error: ${e?.message || String(e)}\n`);
|
|
120
|
+
this._stampExit(child, 1, null);
|
|
121
|
+
});
|
|
122
|
+
return { childPid: pid };
|
|
123
|
+
}
|
|
124
|
+
/** Dispatch by kind. */
|
|
125
|
+
async _dispatch(child, kind, req) {
|
|
126
|
+
if (kind === 'unknown') {
|
|
127
|
+
this._appendOutput(child, 2, `${req.command}: command not found\n`);
|
|
128
|
+
this._stampExit(child, 127, null);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const hooks = {
|
|
132
|
+
onStdout: (d) => this._appendOutput(child, 1, d),
|
|
133
|
+
onStderr: (d) => this._appendOutput(child, 2, d),
|
|
134
|
+
};
|
|
135
|
+
// arch-gaps gap #1: if a spawnPool is configured (production
|
|
136
|
+
// wiring), route the dispatch through a fresh Worker Loader
|
|
137
|
+
// isolate via NimbusFanoutPool. The pool's task body emits a
|
|
138
|
+
// per-isolate marker token + delegates the actual command back
|
|
139
|
+
// to the supervisor via env.SUPERVISOR.cpDispatchInline (preserves
|
|
140
|
+
// pure-builtin / facet-direct correctness; only the dispatch
|
|
141
|
+
// envelope moves to a fresh isolate).
|
|
142
|
+
if (this.deps.spawnPool) {
|
|
143
|
+
// Pure-builtins consume stdin; drain before dispatch. facet-direct
|
|
144
|
+
// reads stdin lazily via cpReadStdin so we don't block on it here.
|
|
145
|
+
const stdin = kind === 'pure-builtin'
|
|
146
|
+
? await this._drainStdinForBuiltin(child)
|
|
147
|
+
: '';
|
|
148
|
+
// Single-ownership: build a fresh request payload (not a reference
|
|
149
|
+
// to the caller's req) at the boundary.
|
|
150
|
+
const reqCopy = {
|
|
151
|
+
command: String(req.command),
|
|
152
|
+
args: Array.isArray(req.args) ? [...req.args] : [],
|
|
153
|
+
env: { ...child.env },
|
|
154
|
+
cwd: String(req.cwd),
|
|
155
|
+
stdio: req.stdio,
|
|
156
|
+
detached: !!req.detached,
|
|
157
|
+
shell: req.shell ?? false,
|
|
158
|
+
stdin,
|
|
159
|
+
};
|
|
160
|
+
// Register the facet-slot so kill() can find the abort handle.
|
|
161
|
+
child.facetSlot = { abort: undefined, killed: false };
|
|
162
|
+
try {
|
|
163
|
+
const code = await this.deps.spawnPool.runOne(reqCopy, kind, hooks, child.pid);
|
|
164
|
+
this._stampExit(child, code, null);
|
|
165
|
+
}
|
|
166
|
+
catch (e) {
|
|
167
|
+
this._appendOutput(child, 2, `spawn-pool error: ${e?.message || String(e)}\n`);
|
|
168
|
+
this._stampExit(child, 1, null);
|
|
169
|
+
}
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
// ── Legacy in-supervisor dispatch (unit-test path) ─────────────
|
|
173
|
+
if (kind === 'pure-builtin') {
|
|
174
|
+
// Drain stdin synchronously — pure builtins are sync-style; they
|
|
175
|
+
// expect a complete stdin string. The parent must call stdinEnd()
|
|
176
|
+
// before this resolves. If the parent hasn't ended, we wait up to
|
|
177
|
+
// 50ms for stdin then proceed with whatever's queued.
|
|
178
|
+
const stdin = await this._drainStdinForBuiltin(child);
|
|
179
|
+
try {
|
|
180
|
+
const code = await this.deps.commandRegistry.runPureBuiltin(req.command, req.args, child.env, req.cwd, stdin, hooks);
|
|
181
|
+
this._stampExit(child, code, null);
|
|
182
|
+
}
|
|
183
|
+
catch (e) {
|
|
184
|
+
this._appendOutput(child, 2, `Error: ${e?.message || String(e)}\n`);
|
|
185
|
+
this._stampExit(child, 1, null);
|
|
186
|
+
}
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
// facet-direct: ship a payload to the FacetManager.execStream that
|
|
190
|
+
// describes the command. In production execStream wraps a generated
|
|
191
|
+
// facet template that imports node:child_process internally; in the
|
|
192
|
+
// unit-test mock the payload is interpreted by the test interpreter.
|
|
193
|
+
const payload = JSON.stringify({
|
|
194
|
+
command: req.command,
|
|
195
|
+
args: req.args,
|
|
196
|
+
env: child.env,
|
|
197
|
+
cwd: req.cwd,
|
|
198
|
+
stdin: '', // facet-direct reads stdin via cpReadStdin RPC at runtime
|
|
199
|
+
});
|
|
200
|
+
// Register the facet-slot so kill() can find the abort handle.
|
|
201
|
+
child.facetSlot = { abort: undefined, killed: false };
|
|
202
|
+
try {
|
|
203
|
+
const code = await this.deps.facetMgr.execStream(payload, { facetName: child.facetName, cwd: req.cwd, env: child.env, argv: req.args }, hooks);
|
|
204
|
+
this._stampExit(child, code, null);
|
|
205
|
+
}
|
|
206
|
+
catch (e) {
|
|
207
|
+
this._appendOutput(child, 2, `facet error: ${e?.message || String(e)}\n`);
|
|
208
|
+
this._stampExit(child, 1, null);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* arch-gaps gap #1: inline dispatch — runs the existing
|
|
213
|
+
* pure-builtin / facet-direct logic with string-collecting hooks
|
|
214
|
+
* and returns the final {exitCode, stdout, stderr} envelope.
|
|
215
|
+
*
|
|
216
|
+
* Called by _rpcCpDispatchInline (src/session/rpc.ts) which is in
|
|
217
|
+
* turn called by the spawn-facet running inside a fresh Worker
|
|
218
|
+
* Loader isolate. The dispatch envelope is in a fresh isolate; the
|
|
219
|
+
* actual command logic still uses the existing registry paths.
|
|
220
|
+
*
|
|
221
|
+
* Single-ownership: stdin/stdout/stderr returned as strings; no
|
|
222
|
+
* shared buffers cross the RPC boundary.
|
|
223
|
+
*/
|
|
224
|
+
async dispatchInline(req, kind) {
|
|
225
|
+
if (kind === 'unknown') {
|
|
226
|
+
return { exitCode: 127, stdout: '', stderr: `${req.command}: command not found\n` };
|
|
227
|
+
}
|
|
228
|
+
let stdoutBuf = '';
|
|
229
|
+
let stderrBuf = '';
|
|
230
|
+
const hooks = {
|
|
231
|
+
onStdout: (d) => { stdoutBuf += d; },
|
|
232
|
+
onStderr: (d) => { stderrBuf += d; },
|
|
233
|
+
};
|
|
234
|
+
const childEnv = {
|
|
235
|
+
...(req.env || {}),
|
|
236
|
+
};
|
|
237
|
+
if (kind === 'pure-builtin') {
|
|
238
|
+
try {
|
|
239
|
+
const code = await this.deps.commandRegistry.runPureBuiltin(req.command, req.args, childEnv, String(req.cwd || '/home/user'), typeof req.stdin === 'string' ? req.stdin : '', hooks);
|
|
240
|
+
return { exitCode: typeof code === 'number' ? code : 0, stdout: stdoutBuf, stderr: stderrBuf };
|
|
241
|
+
}
|
|
242
|
+
catch (e) {
|
|
243
|
+
stderrBuf += `Error: ${e?.message || String(e)}\n`;
|
|
244
|
+
return { exitCode: 1, stdout: stdoutBuf, stderr: stderrBuf };
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
// facet-direct
|
|
248
|
+
const payload = JSON.stringify({
|
|
249
|
+
command: req.command,
|
|
250
|
+
args: req.args,
|
|
251
|
+
env: childEnv,
|
|
252
|
+
cwd: String(req.cwd || '/home/user'),
|
|
253
|
+
stdin: typeof req.stdin === 'string' ? req.stdin : '',
|
|
254
|
+
});
|
|
255
|
+
try {
|
|
256
|
+
const code = await this.deps.facetMgr.execStream(payload,
|
|
257
|
+
// facetName: synthetic identity so adapter callers that key off
|
|
258
|
+
// it don't collide; not used by the inline path.
|
|
259
|
+
{ facetName: `cp-inline-${Date.now().toString(36)}`, cwd: req.cwd, env: childEnv, argv: req.args }, hooks);
|
|
260
|
+
return { exitCode: typeof code === 'number' ? code : 0, stdout: stdoutBuf, stderr: stderrBuf };
|
|
261
|
+
}
|
|
262
|
+
catch (e) {
|
|
263
|
+
stderrBuf += `facet error: ${e?.message || String(e)}\n`;
|
|
264
|
+
return { exitCode: 1, stdout: stdoutBuf, stderr: stderrBuf };
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Synchronously drain the stdin queue for a pure-builtin. Waits up to
|
|
269
|
+
* 50ms for stdinClosed if data is still flowing. Pure-builtins block
|
|
270
|
+
* on full stdin so we have to commit upfront — the parent should have
|
|
271
|
+
* called stdinEnd() before the wait ticks expire.
|
|
272
|
+
*/
|
|
273
|
+
async _drainStdinForBuiltin(child) {
|
|
274
|
+
const t0 = Date.now();
|
|
275
|
+
while (!child.stdinClosed && Date.now() - t0 < 50) {
|
|
276
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
277
|
+
}
|
|
278
|
+
return child.stdinChunks.join('');
|
|
279
|
+
}
|
|
280
|
+
// ── stdin queue ─────────────────────────────────────────────────────────
|
|
281
|
+
stdinWrite(childPid, data) {
|
|
282
|
+
const child = this.children.get(childPid);
|
|
283
|
+
if (!child || child.stdinClosed || child.exitCode !== null)
|
|
284
|
+
return { ok: false };
|
|
285
|
+
if (child.stdinTotalBytes + data.length > STDIN_QUEUE_MAX_BYTES) {
|
|
286
|
+
return { ok: false };
|
|
287
|
+
}
|
|
288
|
+
child.stdinChunks.push(data);
|
|
289
|
+
child.stdinTotalBytes += data.length;
|
|
290
|
+
// Flush any waiters
|
|
291
|
+
for (const w of child.stdinWaiters.splice(0)) {
|
|
292
|
+
w({ data, ended: false });
|
|
293
|
+
}
|
|
294
|
+
return { ok: true };
|
|
295
|
+
}
|
|
296
|
+
stdinEnd(childPid) {
|
|
297
|
+
const child = this.children.get(childPid);
|
|
298
|
+
if (!child)
|
|
299
|
+
return;
|
|
300
|
+
child.stdinClosed = true;
|
|
301
|
+
for (const w of child.stdinWaiters.splice(0)) {
|
|
302
|
+
w({ data: '', ended: true });
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Long-poll: child facet asks the supervisor for its next stdin chunk.
|
|
307
|
+
* Returns immediately if data is already queued OR if stdin is closed.
|
|
308
|
+
*/
|
|
309
|
+
async cpReadStdin(childPid, waitMs) {
|
|
310
|
+
const child = this.children.get(childPid);
|
|
311
|
+
if (!child)
|
|
312
|
+
return { data: '', ended: true };
|
|
313
|
+
if (child.stdinChunks.length > 0) {
|
|
314
|
+
const data = child.stdinChunks.shift();
|
|
315
|
+
child.stdinTotalBytes -= data.length;
|
|
316
|
+
return { data, ended: false };
|
|
317
|
+
}
|
|
318
|
+
if (child.stdinClosed)
|
|
319
|
+
return { data: '', ended: true };
|
|
320
|
+
// Long-poll
|
|
321
|
+
return new Promise((resolve) => {
|
|
322
|
+
const timer = setTimeout(() => {
|
|
323
|
+
const idx = child.stdinWaiters.indexOf(wrapped);
|
|
324
|
+
if (idx >= 0)
|
|
325
|
+
child.stdinWaiters.splice(idx, 1);
|
|
326
|
+
resolve({ data: '', ended: false });
|
|
327
|
+
}, Math.min(waitMs, 5000));
|
|
328
|
+
const wrapped = (r) => {
|
|
329
|
+
clearTimeout(timer);
|
|
330
|
+
resolve(r);
|
|
331
|
+
};
|
|
332
|
+
child.stdinWaiters.push(wrapped);
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
// ── output queue ────────────────────────────────────────────────────────
|
|
336
|
+
/** Internal: push a chunk to fd 1 or 2, fire log-store + waiters. */
|
|
337
|
+
_appendOutput(child, fd, data) {
|
|
338
|
+
if (!data)
|
|
339
|
+
return;
|
|
340
|
+
child.outputSeq[fd]++;
|
|
341
|
+
const chunk = { seq: child.outputSeq[fd], data };
|
|
342
|
+
child.outputs[fd].push(chunk);
|
|
343
|
+
// Tee to ProcessLogStore for `logs <pid>` parity with facet processes.
|
|
344
|
+
try {
|
|
345
|
+
this.deps.processLogs.append(child.pid, fd === 1 ? 'stdout' : 'stderr', data);
|
|
346
|
+
}
|
|
347
|
+
catch { /* ignore */ }
|
|
348
|
+
// Resolve waiters whose fd matches and whose sinceSeq is now satisfied.
|
|
349
|
+
for (let i = child.outputWaiters.length - 1; i >= 0; i--) {
|
|
350
|
+
const w = child.outputWaiters[i];
|
|
351
|
+
if (w.fd !== fd)
|
|
352
|
+
continue;
|
|
353
|
+
const fresh = child.outputs[fd].filter((c) => c.seq > w.sinceSeq);
|
|
354
|
+
if (fresh.length > 0) {
|
|
355
|
+
child.outputWaiters.splice(i, 1);
|
|
356
|
+
w.resolve({
|
|
357
|
+
chunks: fresh,
|
|
358
|
+
closed: child.exitCode !== null,
|
|
359
|
+
maxSeq: child.outputSeq[fd],
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Long-poll read for fd 1 or 2. Returns immediately if there are
|
|
366
|
+
* chunks > sinceSeq OR if the child has already exited.
|
|
367
|
+
*/
|
|
368
|
+
async readOutput(childPid, fd, sinceSeq, waitMs = READ_OUTPUT_DEFAULT_WAIT_MS) {
|
|
369
|
+
const child = this.children.get(childPid);
|
|
370
|
+
if (!child) {
|
|
371
|
+
return { chunks: [], closed: true, maxSeq: 0 };
|
|
372
|
+
}
|
|
373
|
+
const fresh = child.outputs[fd].filter((c) => c.seq > sinceSeq);
|
|
374
|
+
if (fresh.length > 0 || child.exitCode !== null) {
|
|
375
|
+
return {
|
|
376
|
+
chunks: fresh,
|
|
377
|
+
closed: child.exitCode !== null,
|
|
378
|
+
maxSeq: child.outputSeq[fd],
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
return new Promise((resolve) => {
|
|
382
|
+
const expiresAt = Date.now() + Math.min(waitMs, 5000);
|
|
383
|
+
const waiter = {
|
|
384
|
+
fd,
|
|
385
|
+
sinceSeq,
|
|
386
|
+
resolve: (r) => {
|
|
387
|
+
clearTimeout(timer);
|
|
388
|
+
resolve(r);
|
|
389
|
+
},
|
|
390
|
+
expiresAt,
|
|
391
|
+
};
|
|
392
|
+
const timer = setTimeout(() => {
|
|
393
|
+
const idx = child.outputWaiters.indexOf(waiter);
|
|
394
|
+
if (idx >= 0)
|
|
395
|
+
child.outputWaiters.splice(idx, 1);
|
|
396
|
+
// Re-snapshot at resolution time
|
|
397
|
+
const fresh2 = child.outputs[fd].filter((c) => c.seq > sinceSeq);
|
|
398
|
+
resolve({
|
|
399
|
+
chunks: fresh2,
|
|
400
|
+
closed: child.exitCode !== null,
|
|
401
|
+
maxSeq: child.outputSeq[fd],
|
|
402
|
+
});
|
|
403
|
+
}, expiresAt - Date.now());
|
|
404
|
+
child.outputWaiters.push(waiter);
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* One-shot final flush. Used by the parent's exit-time drain (BLOCKER-1
|
|
409
|
+
* fix in W8-plan §8.5). Returns ALL pending output for both fds plus
|
|
410
|
+
* the closed state. Does NOT wait — caller is the parent shutting down.
|
|
411
|
+
*/
|
|
412
|
+
async drainOutput(childPid) {
|
|
413
|
+
const child = this.children.get(childPid);
|
|
414
|
+
if (!child) {
|
|
415
|
+
return { stdout: '', stderr: '', stdoutClosed: true, stderrClosed: true };
|
|
416
|
+
}
|
|
417
|
+
// Wait briefly (up to 50ms) for the dispatch to settle if the child
|
|
418
|
+
// hasn't exited yet — without this, drain races against the spawn's
|
|
419
|
+
// queueMicrotask in the test interpreter / real facet startup.
|
|
420
|
+
const t0 = Date.now();
|
|
421
|
+
while (child.exitCode === null && Date.now() - t0 < 100) {
|
|
422
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
423
|
+
}
|
|
424
|
+
return {
|
|
425
|
+
stdout: child.outputs[1].map((c) => c.data).join(''),
|
|
426
|
+
stderr: child.outputs[2].map((c) => c.data).join(''),
|
|
427
|
+
stdoutClosed: child.exitCode !== null,
|
|
428
|
+
stderrClosed: child.exitCode !== null,
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
// ── kill / wait / reportExit ────────────────────────────────────────────
|
|
432
|
+
/**
|
|
433
|
+
* Synchronous kill. First-writer-wins on exit slot. Resolves all
|
|
434
|
+
* pending waiters BEFORE invoking facets.abort so cpWait/cpReadOutput
|
|
435
|
+
* don't hang on a torn-down facet.
|
|
436
|
+
*/
|
|
437
|
+
kill(childPid, signal = 'SIGTERM') {
|
|
438
|
+
const child = this.children.get(childPid);
|
|
439
|
+
if (!child || child.exitCode !== null)
|
|
440
|
+
return false;
|
|
441
|
+
const exitCode = signal === 'SIGKILL' ? 137 : 143; // POSIX 128+9 / 128+15
|
|
442
|
+
child.signal = signal;
|
|
443
|
+
child.killed = true;
|
|
444
|
+
// Stamp + wake waiters atomically.
|
|
445
|
+
this._stampExit(child, exitCode, signal);
|
|
446
|
+
// Tell the facet runtime to abort, best-effort. The mock FacetManager
|
|
447
|
+
// and real FacetManager both expose an `abort(name)` method.
|
|
448
|
+
try {
|
|
449
|
+
if (this.deps.facetMgr.abort) {
|
|
450
|
+
this.deps.facetMgr.abort(child.facetName, signal);
|
|
451
|
+
}
|
|
452
|
+
// Also try the ctx.facets path used by FacetManager.kill (for
|
|
453
|
+
// production where facets are actual DO facets).
|
|
454
|
+
if (this.deps.ctx?.facets?.abort) {
|
|
455
|
+
this.deps.ctx.facets.abort(child.facetName, new Error(signal));
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
catch { /* best-effort */ }
|
|
459
|
+
// Defer delete by a microtask so any in-flight reportExit RPC lands
|
|
460
|
+
// and is no-op'd by the idempotent guard in _stampExit.
|
|
461
|
+
queueMicrotask(() => {
|
|
462
|
+
try {
|
|
463
|
+
if (this.deps.ctx?.facets?.delete) {
|
|
464
|
+
this.deps.ctx.facets.delete(child.facetName);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
catch { /* best-effort */ }
|
|
468
|
+
});
|
|
469
|
+
return true;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Stamp the exit slot. Idempotent — first call wins.
|
|
473
|
+
* Wakes all waiters (exit, output, stdin) so callers don't hang.
|
|
474
|
+
*/
|
|
475
|
+
_stampExit(child, exitCode, signal) {
|
|
476
|
+
if (child.exitCode !== null)
|
|
477
|
+
return; // first writer wins
|
|
478
|
+
child.exitCode = exitCode;
|
|
479
|
+
child.signal = signal;
|
|
480
|
+
child.endedAt = Date.now();
|
|
481
|
+
// Tell the ProcessTable + LogStore so `ps` and `logs <pid>` line up.
|
|
482
|
+
try {
|
|
483
|
+
this.deps.processTable.exit(child.pid, exitCode);
|
|
484
|
+
}
|
|
485
|
+
catch { }
|
|
486
|
+
try {
|
|
487
|
+
this.deps.processLogs.markExit(child.pid, exitCode);
|
|
488
|
+
}
|
|
489
|
+
catch { }
|
|
490
|
+
// Wake exit waiters.
|
|
491
|
+
for (const w of child.exitWaiters.splice(0)) {
|
|
492
|
+
w({ done: true, exitCode, signal });
|
|
493
|
+
}
|
|
494
|
+
// Wake output waiters with closed=true so polling parents stop.
|
|
495
|
+
for (const w of child.outputWaiters.splice(0)) {
|
|
496
|
+
const fresh = child.outputs[w.fd].filter((c) => c.seq > w.sinceSeq);
|
|
497
|
+
w.resolve({ chunks: fresh, closed: true, maxSeq: child.outputSeq[w.fd] });
|
|
498
|
+
}
|
|
499
|
+
// Wake stdin waiters with ended=true so a child blocked on cpReadStdin
|
|
500
|
+
// unblocks and exits cleanly.
|
|
501
|
+
for (const w of child.stdinWaiters.splice(0)) {
|
|
502
|
+
w({ data: '', ended: true });
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Late-arriving reportExit from the facet. Idempotent; if kill() or
|
|
507
|
+
* an earlier reportExit already stamped, this is a no-op.
|
|
508
|
+
*/
|
|
509
|
+
reportExit(childPid, exitCode, signal) {
|
|
510
|
+
const child = this.children.get(childPid);
|
|
511
|
+
if (!child)
|
|
512
|
+
return;
|
|
513
|
+
this._stampExit(child, exitCode, signal);
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Long-poll wait. Returns immediately if already exited; otherwise
|
|
517
|
+
* registers a waiter that resolves on the next exit-slot stamp.
|
|
518
|
+
*/
|
|
519
|
+
async wait(childPid, waitMs = WAIT_MAX_MS) {
|
|
520
|
+
const child = this.children.get(childPid);
|
|
521
|
+
if (!child) {
|
|
522
|
+
return { done: true, exitCode: 1, signal: null };
|
|
523
|
+
}
|
|
524
|
+
if (child.exitCode !== null) {
|
|
525
|
+
return { done: true, exitCode: child.exitCode, signal: child.signal };
|
|
526
|
+
}
|
|
527
|
+
return new Promise((resolve) => {
|
|
528
|
+
const timer = setTimeout(() => {
|
|
529
|
+
const idx = child.exitWaiters.indexOf(wrapped);
|
|
530
|
+
if (idx >= 0)
|
|
531
|
+
child.exitWaiters.splice(idx, 1);
|
|
532
|
+
resolve({ done: false, exitCode: null, signal: null });
|
|
533
|
+
}, Math.min(waitMs, WAIT_MAX_MS));
|
|
534
|
+
const wrapped = (r) => {
|
|
535
|
+
clearTimeout(timer);
|
|
536
|
+
resolve(r);
|
|
537
|
+
};
|
|
538
|
+
child.exitWaiters.push(wrapped);
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
// ── housekeeping ────────────────────────────────────────────────────────
|
|
542
|
+
/** Reap entries older than maxAgeMs whose exit slot is stamped. */
|
|
543
|
+
reap(maxAgeMs = 60_000) {
|
|
544
|
+
const now = Date.now();
|
|
545
|
+
let n = 0;
|
|
546
|
+
for (const [pid, child] of this.children) {
|
|
547
|
+
if (child.exitCode !== null && child.endedAt && now - child.endedAt > maxAgeMs) {
|
|
548
|
+
this.children.delete(pid);
|
|
549
|
+
n++;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
return n;
|
|
553
|
+
}
|
|
554
|
+
get stats() {
|
|
555
|
+
const all = [...this.children.values()];
|
|
556
|
+
return {
|
|
557
|
+
total: all.length,
|
|
558
|
+
running: all.filter((c) => c.exitCode === null).length,
|
|
559
|
+
exited: all.filter((c) => c.exitCode !== null && !c.killed).length,
|
|
560
|
+
killed: all.filter((c) => c.killed).length,
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
/** Test/diagnostic introspection. */
|
|
564
|
+
_getChildEntry(pid) {
|
|
565
|
+
return this.children.get(pid);
|
|
566
|
+
}
|
|
567
|
+
}
|