@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,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* state-store.ts — DO-SQLite-backed persistence for session state
|
|
3
|
+
* that must survive isolate teardown [Phase 3 Track B'].
|
|
4
|
+
*
|
|
5
|
+
* Why this exists
|
|
6
|
+
* ───────────────
|
|
7
|
+
* Pre-Phase-3 the supervisor's session state lived entirely in
|
|
8
|
+
* isolate memory: the LIFO Shell instance owned cwd + env vars; the
|
|
9
|
+
* LIFO Kernel owned mount points; the WebSocketTerminal had no
|
|
10
|
+
* persistence at all. A wsClose / wsError nulled all three
|
|
11
|
+
* (src/nimbus-session-ws.ts:165-167, :221-223), and the next /ws
|
|
12
|
+
* upgrade rebuilt them from defaults. Result: cwd reset to ~,
|
|
13
|
+
* env vars lost, scrollback gone, MOTD reprinted — the user-
|
|
14
|
+
* visible Bug C symptom.
|
|
15
|
+
*
|
|
16
|
+
* The architectural fix is to move every observable session-state
|
|
17
|
+
* field into DO SQLite. The in-memory fields become CACHES of the
|
|
18
|
+
* SQL row, not the master copy. A wsClose flushes the cache and
|
|
19
|
+
* clears the in-memory copy; a /ws upgrade reads SQL and seeds the
|
|
20
|
+
* fresh Shell/Kernel/Terminal.
|
|
21
|
+
*
|
|
22
|
+
* Schema overview
|
|
23
|
+
* ───────────────
|
|
24
|
+
* Three tables, all keyed by single-row sentinels:
|
|
25
|
+
*
|
|
26
|
+
* nimbus_session_kv (k TEXT PRIMARY KEY, v TEXT)
|
|
27
|
+
* — generic key/value bag for primitives. Today stores 'cwd'
|
|
28
|
+
* and 'env' (the env is JSON-serialised). One row per key.
|
|
29
|
+
*
|
|
30
|
+
* nimbus_kernel_mounts (mount_point TEXT PRIMARY KEY)
|
|
31
|
+
* — explicit mount-point list (B'.2). Empty until B'.2 lands.
|
|
32
|
+
*
|
|
33
|
+
* nimbus_terminal_scrollback (seq INTEGER PRIMARY KEY, ts INTEGER, data TEXT)
|
|
34
|
+
* — bounded ring of recent terminal output (B'.3). Empty until
|
|
35
|
+
* B'.3 lands.
|
|
36
|
+
*
|
|
37
|
+
* Stability contract
|
|
38
|
+
* ──────────────────
|
|
39
|
+
* Every storage key + table name in this module is part of the DO's
|
|
40
|
+
* persistent schema. Renaming any of them is a storage migration —
|
|
41
|
+
* never do it without an explicit migration plan. New tables can be
|
|
42
|
+
* added; existing tables can be ALTER-ADDed columns (schema is
|
|
43
|
+
* forward-compatible if the rehydrate path reads-with-default for
|
|
44
|
+
* missing columns).
|
|
45
|
+
*/
|
|
46
|
+
/** Module-level cap on the env-row JSON length. The whole env is one
|
|
47
|
+
* row in nimbus_session_kv; if the user's env explodes (eg every
|
|
48
|
+
* npm-install side-effects 100 vars), we want to surface that as a
|
|
49
|
+
* clear error rather than a silent storage-row-too-long failure.
|
|
50
|
+
*
|
|
51
|
+
* Per docs/research/cf-internal-dossier.md §9 the per-row cap is
|
|
52
|
+
* 2 MB; we set a much tighter 256 KiB ceiling so a misbehaving
|
|
53
|
+
* session can't approach the platform limit. */
|
|
54
|
+
export declare const SESSION_ENV_MAX_BYTES: number;
|
|
55
|
+
/** Module-level cap on the per-session terminal scrollback in DO
|
|
56
|
+
* SQLite. Each WS `output` frame written by WebSocketTerminal is
|
|
57
|
+
* persisted as one row; on every insert we evict the oldest rows
|
|
58
|
+
* until total bytes ≤ this cap.
|
|
59
|
+
*
|
|
60
|
+
* 1 MiB ≈ ~10,000 lines of ANSI-coloured prompt+output text — many
|
|
61
|
+
* multiples of typical xterm.js scrollback default (1000 rows ×
|
|
62
|
+
* ~100B = ~100KB). The headroom matters: real workloads commonly
|
|
63
|
+
* produce a single huge frame (eg `cat package-lock.json` ≈ 200 KiB)
|
|
64
|
+
* followed by many small frames (prompt updates, cd output). With
|
|
65
|
+
* the cap at 256 KiB, the big frame consumes the entire budget and
|
|
66
|
+
* every subsequent prompt update displaces it via eviction. With
|
|
67
|
+
* 1 MiB we have room for the big frame plus the next dozens of
|
|
68
|
+
* command outputs.
|
|
69
|
+
*
|
|
70
|
+
* This is a SOFT cap on persistence. The live terminal still gets
|
|
71
|
+
* every byte the shell writes; only the rehydrate-on-reconnect
|
|
72
|
+
* replay is bounded. A user who exceeds 1 MiB mid-session sees
|
|
73
|
+
* full output live, but on reconnect only the most recent ~1 MiB
|
|
74
|
+
* replays. That's the right trade-off — losing early-session output
|
|
75
|
+
* > losing recent output, and DO storage is cheap relative to a
|
|
76
|
+
* good UX.
|
|
77
|
+
*
|
|
78
|
+
* Per row, individual frames > MAX_FRAME_BYTES are TRUNCATED to
|
|
79
|
+
* their last MAX_FRAME_BYTES — same trade-off applied at the
|
|
80
|
+
* single-frame granularity for pathological multi-MB cat output. */
|
|
81
|
+
export declare const SCROLLBACK_MAX_BYTES: number;
|
|
82
|
+
/** Per-row cap. A single coalesced WS frame larger than this is
|
|
83
|
+
* trimmed to its trailing MAX_FRAME_BYTES bytes before insert. Set
|
|
84
|
+
* smaller than SCROLLBACK_MAX_BYTES so a single huge frame can't
|
|
85
|
+
* consume the whole budget; with this gap, the eviction loop never
|
|
86
|
+
* has to delete the just-inserted row to fit a subsequent small
|
|
87
|
+
* one. 256 KiB per frame is generous (a screenful of dense text +
|
|
88
|
+
* ANSI). */
|
|
89
|
+
export declare const SCROLLBACK_MAX_FRAME_BYTES: number;
|
|
90
|
+
/** Storage key names. Module-scope constants so a future migration
|
|
91
|
+
* can find every site by grep. */
|
|
92
|
+
export declare const KEY_CWD = "cwd";
|
|
93
|
+
export declare const KEY_ENV_JSON = "env";
|
|
94
|
+
export declare const KEY_HYDRATED_AT = "hydrated_at";
|
|
95
|
+
/** A snapshot of the persisted shell state, returned by loadShellState. */
|
|
96
|
+
export interface ShellStateSnapshot {
|
|
97
|
+
cwd: string | null;
|
|
98
|
+
env: Record<string, string> | null;
|
|
99
|
+
hydratedAt: number | null;
|
|
100
|
+
/** True iff at least one of cwd/env was present in SQL. The caller
|
|
101
|
+
* uses this to decide cold-start (no row) vs. rehydrate (row present),
|
|
102
|
+
* which gates one-shot UI like the MOTD. */
|
|
103
|
+
hasPersistedState: boolean;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Idempotent CREATE TABLE. Safe to call on every initSession; the
|
|
107
|
+
* IF NOT EXISTS clause makes repeats free. Inlines all three Track
|
|
108
|
+
* B' tables; B'.2 / B'.3 land their callers, not their schema.
|
|
109
|
+
*/
|
|
110
|
+
export declare function ensureSessionStateSchema(ctx: any): void;
|
|
111
|
+
/**
|
|
112
|
+
* Load the persisted shell state. Returns a snapshot with `null`
|
|
113
|
+
* fields when no row exists. Never throws — a corrupt env row
|
|
114
|
+
* (failed JSON parse) returns null env and we treat it as cold;
|
|
115
|
+
* better to start fresh than crash the rehydrate path.
|
|
116
|
+
*
|
|
117
|
+
* Auto-ensures the schema. Calling this from a route handler that
|
|
118
|
+
* runs BEFORE any WS upgrade (e.g. /api/_diag/session on a freshly-
|
|
119
|
+
* minted session) used to fail with SQLITE_ERROR because no prior
|
|
120
|
+
* code path had run CREATE TABLE. The IF NOT EXISTS guarantees the
|
|
121
|
+
* extra ensure is microseconds when tables already exist.
|
|
122
|
+
*/
|
|
123
|
+
export declare function loadShellState(ctx: any): ShellStateSnapshot;
|
|
124
|
+
/**
|
|
125
|
+
* Atomically write the shell state. Called from the snapshot
|
|
126
|
+
* mechanism (periodic + on wsClose). Uses INSERT-OR-REPLACE so
|
|
127
|
+
* repeated writes are idempotent.
|
|
128
|
+
*
|
|
129
|
+
* `cwd` and `env` are both required parameters — the caller (the
|
|
130
|
+
* snapshot loop in initSession) reads them from the live Shell
|
|
131
|
+
* BEFORE calling. If either is null, the corresponding row is
|
|
132
|
+
* REMOVED rather than re-written; this keeps the table clean if a
|
|
133
|
+
* future code path explicitly clears state.
|
|
134
|
+
*
|
|
135
|
+
* Throws if the env JSON would exceed SESSION_ENV_MAX_BYTES — that's
|
|
136
|
+
* a session-misuse signal worth surfacing rather than silently
|
|
137
|
+
* truncating.
|
|
138
|
+
*/
|
|
139
|
+
export declare function persistShellState(ctx: any, state: {
|
|
140
|
+
cwd: string | null;
|
|
141
|
+
env: Record<string, string> | null;
|
|
142
|
+
}): void;
|
|
143
|
+
/**
|
|
144
|
+
* Stamp the time of the most recent successful hydrate. Used by the
|
|
145
|
+
* /api/_diag/session debug endpoint to show "the last cold-start
|
|
146
|
+
* found a row at <ts>". Cheap; one row write per initSession call.
|
|
147
|
+
*/
|
|
148
|
+
export declare function stampHydratedAt(ctx: any, atMs: number): void;
|
|
149
|
+
/**
|
|
150
|
+
* Drop ALL session-state rows. Used by /api/_test/session/reset for
|
|
151
|
+
* test-only flows; never called from prod paths. Equivalent to
|
|
152
|
+
* "this DO has never seen a session" — the next initSession runs
|
|
153
|
+
* the cold-start path.
|
|
154
|
+
*/
|
|
155
|
+
export declare function clearSessionState(ctx: any): void;
|
|
156
|
+
/** Count rows in nimbus_session_kv — used by the recovery_event
|
|
157
|
+
* recorder to populate snapshotKeysRehydrated. Cheap; bounded by
|
|
158
|
+
* the small key set above. */
|
|
159
|
+
export declare function countSessionStateKeys(ctx: any): number;
|
|
160
|
+
/**
|
|
161
|
+
* Load the persisted mount-point list. Returns a plain string[] of
|
|
162
|
+
* mount point names (without leading slash — same shape as
|
|
163
|
+
* DEFAULT_MOUNT_POINTS). Empty array when no rows exist.
|
|
164
|
+
*/
|
|
165
|
+
export declare function loadKernelMounts(ctx: any): string[];
|
|
166
|
+
/**
|
|
167
|
+
* Persist a mount-point list. Idempotent — replaces the entire
|
|
168
|
+
* nimbus_kernel_mounts contents in a single transaction. Caller
|
|
169
|
+
* provides the full desired set; we don't merge with existing rows.
|
|
170
|
+
*
|
|
171
|
+
* `mounts` should be plain names without leading slash
|
|
172
|
+
* ('bin', 'etc', ...) — same shape DEFAULT_MOUNT_POINTS uses.
|
|
173
|
+
*/
|
|
174
|
+
export declare function persistKernelMounts(ctx: any, mounts: string[]): void;
|
|
175
|
+
/**
|
|
176
|
+
* Append one coalesced output frame. Inserts a row, then evicts the
|
|
177
|
+
* oldest rows until total bytes ≤ SCROLLBACK_MAX_BYTES.
|
|
178
|
+
*
|
|
179
|
+
* `data` is the UTF-8 string written to the WS (typically including
|
|
180
|
+
* ANSI escape codes); empty strings are no-ops.
|
|
181
|
+
*
|
|
182
|
+
* Frames larger than SCROLLBACK_MAX_BYTES are TRUNCATED to their
|
|
183
|
+
* last cap bytes before insert. Reasoning: a single shell command
|
|
184
|
+
* (eg `cat huge-file`) can produce ≫ cap bytes in one coalesced WS
|
|
185
|
+
* frame; we want to preserve the most recent portion of that
|
|
186
|
+
* output (the user's "what just happened") rather than dropping it
|
|
187
|
+
* entirely. Truncation of UTF-8 at byte boundaries can split a
|
|
188
|
+
* multi-byte sequence; we accept that minor cosmetic risk vs. the
|
|
189
|
+
* complexity of a code-point-aware slice.
|
|
190
|
+
*
|
|
191
|
+
* Fail-soft: any thrown error is swallowed by the caller — losing
|
|
192
|
+
* scrollback is annoying but not session-fatal.
|
|
193
|
+
*/
|
|
194
|
+
export declare function appendScrollback(ctx: any, data: string, atMs: number): void;
|
|
195
|
+
/**
|
|
196
|
+
* Read all scrollback rows in seq (chronological) order and return
|
|
197
|
+
* the concatenated payload. Used by the rehydrate path to emit a
|
|
198
|
+
* single batched replay frame.
|
|
199
|
+
*
|
|
200
|
+
* Returns empty string when the table is empty (cold start, or
|
|
201
|
+
* after explicit reset).
|
|
202
|
+
*/
|
|
203
|
+
export declare function loadScrollback(ctx: any): string;
|
|
204
|
+
/** Stats for /api/_diag/session: row count + total bytes + cap. */
|
|
205
|
+
export declare function getScrollbackStats(ctx: any): {
|
|
206
|
+
rows: number;
|
|
207
|
+
bytes: number;
|
|
208
|
+
maxBytes: number;
|
|
209
|
+
};
|
|
210
|
+
//# sourceMappingURL=state-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-store.d.ts","sourceRoot":"","sources":["../../src/session/state-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH;;;;;;;iDAOiD;AACjD,eAAO,MAAM,qBAAqB,QAAa,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;;;;;;;qEAyBqE;AACrE,eAAO,MAAM,oBAAoB,QAAc,CAAC;AAChD;;;;;;aAMa;AACb,eAAO,MAAM,0BAA0B,QAAa,CAAC;AAErD;mCACmC;AACnC,eAAO,MAAM,OAAO,QAAQ,CAAC;AAC7B,eAAO,MAAM,YAAY,QAAQ,CAAC;AAClC,eAAO,MAAM,eAAe,gBAAgB,CAAC;AAE7C,2EAA2E;AAC3E,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACnC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;iDAE6C;IAC7C,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAiCvD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,GAAG,kBAAkB,CAqC3D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,GAAG,EACR,KAAK,EAAE;IAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAA;CAAE,GAChE,IAAI,CA6BN;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAQ5D;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAOhD;AAED;;+BAE+B;AAC/B,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAQtD;AAaD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,EAAE,CAWnD;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAcpE;AAwBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CA6E3E;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAW/C;AAED,mEAAmE;AACnE,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,GAAG,GAAG;IAC5C,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;CAC/C,CAaA"}
|
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* state-store.ts — DO-SQLite-backed persistence for session state
|
|
3
|
+
* that must survive isolate teardown [Phase 3 Track B'].
|
|
4
|
+
*
|
|
5
|
+
* Why this exists
|
|
6
|
+
* ───────────────
|
|
7
|
+
* Pre-Phase-3 the supervisor's session state lived entirely in
|
|
8
|
+
* isolate memory: the LIFO Shell instance owned cwd + env vars; the
|
|
9
|
+
* LIFO Kernel owned mount points; the WebSocketTerminal had no
|
|
10
|
+
* persistence at all. A wsClose / wsError nulled all three
|
|
11
|
+
* (src/nimbus-session-ws.ts:165-167, :221-223), and the next /ws
|
|
12
|
+
* upgrade rebuilt them from defaults. Result: cwd reset to ~,
|
|
13
|
+
* env vars lost, scrollback gone, MOTD reprinted — the user-
|
|
14
|
+
* visible Bug C symptom.
|
|
15
|
+
*
|
|
16
|
+
* The architectural fix is to move every observable session-state
|
|
17
|
+
* field into DO SQLite. The in-memory fields become CACHES of the
|
|
18
|
+
* SQL row, not the master copy. A wsClose flushes the cache and
|
|
19
|
+
* clears the in-memory copy; a /ws upgrade reads SQL and seeds the
|
|
20
|
+
* fresh Shell/Kernel/Terminal.
|
|
21
|
+
*
|
|
22
|
+
* Schema overview
|
|
23
|
+
* ───────────────
|
|
24
|
+
* Three tables, all keyed by single-row sentinels:
|
|
25
|
+
*
|
|
26
|
+
* nimbus_session_kv (k TEXT PRIMARY KEY, v TEXT)
|
|
27
|
+
* — generic key/value bag for primitives. Today stores 'cwd'
|
|
28
|
+
* and 'env' (the env is JSON-serialised). One row per key.
|
|
29
|
+
*
|
|
30
|
+
* nimbus_kernel_mounts (mount_point TEXT PRIMARY KEY)
|
|
31
|
+
* — explicit mount-point list (B'.2). Empty until B'.2 lands.
|
|
32
|
+
*
|
|
33
|
+
* nimbus_terminal_scrollback (seq INTEGER PRIMARY KEY, ts INTEGER, data TEXT)
|
|
34
|
+
* — bounded ring of recent terminal output (B'.3). Empty until
|
|
35
|
+
* B'.3 lands.
|
|
36
|
+
*
|
|
37
|
+
* Stability contract
|
|
38
|
+
* ──────────────────
|
|
39
|
+
* Every storage key + table name in this module is part of the DO's
|
|
40
|
+
* persistent schema. Renaming any of them is a storage migration —
|
|
41
|
+
* never do it without an explicit migration plan. New tables can be
|
|
42
|
+
* added; existing tables can be ALTER-ADDed columns (schema is
|
|
43
|
+
* forward-compatible if the rehydrate path reads-with-default for
|
|
44
|
+
* missing columns).
|
|
45
|
+
*/
|
|
46
|
+
/** Module-level cap on the env-row JSON length. The whole env is one
|
|
47
|
+
* row in nimbus_session_kv; if the user's env explodes (eg every
|
|
48
|
+
* npm-install side-effects 100 vars), we want to surface that as a
|
|
49
|
+
* clear error rather than a silent storage-row-too-long failure.
|
|
50
|
+
*
|
|
51
|
+
* Per docs/research/cf-internal-dossier.md §9 the per-row cap is
|
|
52
|
+
* 2 MB; we set a much tighter 256 KiB ceiling so a misbehaving
|
|
53
|
+
* session can't approach the platform limit. */
|
|
54
|
+
export const SESSION_ENV_MAX_BYTES = 256 * 1024;
|
|
55
|
+
/** Module-level cap on the per-session terminal scrollback in DO
|
|
56
|
+
* SQLite. Each WS `output` frame written by WebSocketTerminal is
|
|
57
|
+
* persisted as one row; on every insert we evict the oldest rows
|
|
58
|
+
* until total bytes ≤ this cap.
|
|
59
|
+
*
|
|
60
|
+
* 1 MiB ≈ ~10,000 lines of ANSI-coloured prompt+output text — many
|
|
61
|
+
* multiples of typical xterm.js scrollback default (1000 rows ×
|
|
62
|
+
* ~100B = ~100KB). The headroom matters: real workloads commonly
|
|
63
|
+
* produce a single huge frame (eg `cat package-lock.json` ≈ 200 KiB)
|
|
64
|
+
* followed by many small frames (prompt updates, cd output). With
|
|
65
|
+
* the cap at 256 KiB, the big frame consumes the entire budget and
|
|
66
|
+
* every subsequent prompt update displaces it via eviction. With
|
|
67
|
+
* 1 MiB we have room for the big frame plus the next dozens of
|
|
68
|
+
* command outputs.
|
|
69
|
+
*
|
|
70
|
+
* This is a SOFT cap on persistence. The live terminal still gets
|
|
71
|
+
* every byte the shell writes; only the rehydrate-on-reconnect
|
|
72
|
+
* replay is bounded. A user who exceeds 1 MiB mid-session sees
|
|
73
|
+
* full output live, but on reconnect only the most recent ~1 MiB
|
|
74
|
+
* replays. That's the right trade-off — losing early-session output
|
|
75
|
+
* > losing recent output, and DO storage is cheap relative to a
|
|
76
|
+
* good UX.
|
|
77
|
+
*
|
|
78
|
+
* Per row, individual frames > MAX_FRAME_BYTES are TRUNCATED to
|
|
79
|
+
* their last MAX_FRAME_BYTES — same trade-off applied at the
|
|
80
|
+
* single-frame granularity for pathological multi-MB cat output. */
|
|
81
|
+
export const SCROLLBACK_MAX_BYTES = 1024 * 1024;
|
|
82
|
+
/** Per-row cap. A single coalesced WS frame larger than this is
|
|
83
|
+
* trimmed to its trailing MAX_FRAME_BYTES bytes before insert. Set
|
|
84
|
+
* smaller than SCROLLBACK_MAX_BYTES so a single huge frame can't
|
|
85
|
+
* consume the whole budget; with this gap, the eviction loop never
|
|
86
|
+
* has to delete the just-inserted row to fit a subsequent small
|
|
87
|
+
* one. 256 KiB per frame is generous (a screenful of dense text +
|
|
88
|
+
* ANSI). */
|
|
89
|
+
export const SCROLLBACK_MAX_FRAME_BYTES = 256 * 1024;
|
|
90
|
+
/** Storage key names. Module-scope constants so a future migration
|
|
91
|
+
* can find every site by grep. */
|
|
92
|
+
export const KEY_CWD = 'cwd';
|
|
93
|
+
export const KEY_ENV_JSON = 'env';
|
|
94
|
+
export const KEY_HYDRATED_AT = 'hydrated_at';
|
|
95
|
+
/**
|
|
96
|
+
* Idempotent CREATE TABLE. Safe to call on every initSession; the
|
|
97
|
+
* IF NOT EXISTS clause makes repeats free. Inlines all three Track
|
|
98
|
+
* B' tables; B'.2 / B'.3 land their callers, not their schema.
|
|
99
|
+
*/
|
|
100
|
+
export function ensureSessionStateSchema(ctx) {
|
|
101
|
+
const sql = ctx?.storage?.sql;
|
|
102
|
+
if (!sql)
|
|
103
|
+
return;
|
|
104
|
+
sql.exec('CREATE TABLE IF NOT EXISTS nimbus_session_kv (' +
|
|
105
|
+
'k TEXT PRIMARY KEY, ' +
|
|
106
|
+
'v TEXT NOT NULL)');
|
|
107
|
+
sql.exec('CREATE TABLE IF NOT EXISTS nimbus_kernel_mounts (' +
|
|
108
|
+
'mount_point TEXT PRIMARY KEY)');
|
|
109
|
+
sql.exec('CREATE TABLE IF NOT EXISTS nimbus_terminal_scrollback (' +
|
|
110
|
+
'seq INTEGER PRIMARY KEY AUTOINCREMENT, ' +
|
|
111
|
+
'ts INTEGER NOT NULL, ' +
|
|
112
|
+
'data TEXT NOT NULL, ' +
|
|
113
|
+
'bytes INTEGER NOT NULL DEFAULT 0)');
|
|
114
|
+
// [B'.3] Migration guard: B'.1 created the table without a `bytes`
|
|
115
|
+
// column. If a DO already ran B'.1/B'.2 with that schema, the
|
|
116
|
+
// CREATE TABLE IF NOT EXISTS above is a no-op and we'd hit
|
|
117
|
+
// 'no such column: bytes' on the first INSERT. Detect via
|
|
118
|
+
// PRAGMA table_info and ALTER if the column is missing.
|
|
119
|
+
let hasBytes = false;
|
|
120
|
+
for (const row of sql.exec('PRAGMA table_info(nimbus_terminal_scrollback)')) {
|
|
121
|
+
if (String(row.name) === 'bytes') {
|
|
122
|
+
hasBytes = true;
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (!hasBytes) {
|
|
127
|
+
try {
|
|
128
|
+
sql.exec('ALTER TABLE nimbus_terminal_scrollback ADD COLUMN bytes INTEGER NOT NULL DEFAULT 0');
|
|
129
|
+
}
|
|
130
|
+
catch { /* race: concurrent ensure already added it */ }
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Load the persisted shell state. Returns a snapshot with `null`
|
|
135
|
+
* fields when no row exists. Never throws — a corrupt env row
|
|
136
|
+
* (failed JSON parse) returns null env and we treat it as cold;
|
|
137
|
+
* better to start fresh than crash the rehydrate path.
|
|
138
|
+
*
|
|
139
|
+
* Auto-ensures the schema. Calling this from a route handler that
|
|
140
|
+
* runs BEFORE any WS upgrade (e.g. /api/_diag/session on a freshly-
|
|
141
|
+
* minted session) used to fail with SQLITE_ERROR because no prior
|
|
142
|
+
* code path had run CREATE TABLE. The IF NOT EXISTS guarantees the
|
|
143
|
+
* extra ensure is microseconds when tables already exist.
|
|
144
|
+
*/
|
|
145
|
+
export function loadShellState(ctx) {
|
|
146
|
+
const sql = ctx?.storage?.sql;
|
|
147
|
+
const empty = {
|
|
148
|
+
cwd: null, env: null, hydratedAt: null, hasPersistedState: false,
|
|
149
|
+
};
|
|
150
|
+
if (!sql)
|
|
151
|
+
return empty;
|
|
152
|
+
ensureSessionStateSchema(ctx);
|
|
153
|
+
// Single row per key — the table is intentionally tiny so we read
|
|
154
|
+
// all interesting keys in one query.
|
|
155
|
+
let cwd = null;
|
|
156
|
+
let env = null;
|
|
157
|
+
let hydratedAt = null;
|
|
158
|
+
let any = false;
|
|
159
|
+
for (const row of sql.exec('SELECT k, v FROM nimbus_session_kv WHERE k IN (?, ?, ?)', KEY_CWD, KEY_ENV_JSON, KEY_HYDRATED_AT)) {
|
|
160
|
+
any = true;
|
|
161
|
+
if (row.k === KEY_CWD) {
|
|
162
|
+
cwd = String(row.v);
|
|
163
|
+
}
|
|
164
|
+
else if (row.k === KEY_ENV_JSON) {
|
|
165
|
+
try {
|
|
166
|
+
const parsed = JSON.parse(String(row.v));
|
|
167
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
168
|
+
env = parsed;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
// Corrupt env JSON → treat as no env (still rehydrates cwd
|
|
173
|
+
// from the cwd row above). Don't throw.
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else if (row.k === KEY_HYDRATED_AT) {
|
|
177
|
+
const n = Number(row.v);
|
|
178
|
+
hydratedAt = Number.isFinite(n) ? n : null;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return { cwd, env, hydratedAt, hasPersistedState: any };
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Atomically write the shell state. Called from the snapshot
|
|
185
|
+
* mechanism (periodic + on wsClose). Uses INSERT-OR-REPLACE so
|
|
186
|
+
* repeated writes are idempotent.
|
|
187
|
+
*
|
|
188
|
+
* `cwd` and `env` are both required parameters — the caller (the
|
|
189
|
+
* snapshot loop in initSession) reads them from the live Shell
|
|
190
|
+
* BEFORE calling. If either is null, the corresponding row is
|
|
191
|
+
* REMOVED rather than re-written; this keeps the table clean if a
|
|
192
|
+
* future code path explicitly clears state.
|
|
193
|
+
*
|
|
194
|
+
* Throws if the env JSON would exceed SESSION_ENV_MAX_BYTES — that's
|
|
195
|
+
* a session-misuse signal worth surfacing rather than silently
|
|
196
|
+
* truncating.
|
|
197
|
+
*/
|
|
198
|
+
export function persistShellState(ctx, state) {
|
|
199
|
+
const sql = ctx?.storage?.sql;
|
|
200
|
+
if (!sql)
|
|
201
|
+
return;
|
|
202
|
+
ensureSessionStateSchema(ctx);
|
|
203
|
+
if (state.cwd !== null) {
|
|
204
|
+
sql.exec('INSERT OR REPLACE INTO nimbus_session_kv (k, v) VALUES (?, ?)', KEY_CWD, state.cwd);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
sql.exec('DELETE FROM nimbus_session_kv WHERE k = ?', KEY_CWD);
|
|
208
|
+
}
|
|
209
|
+
if (state.env !== null) {
|
|
210
|
+
const json = JSON.stringify(state.env);
|
|
211
|
+
if (json.length > SESSION_ENV_MAX_BYTES) {
|
|
212
|
+
throw new Error(`[state-store] env JSON exceeds ${SESSION_ENV_MAX_BYTES} bytes ` +
|
|
213
|
+
`(got ${json.length}); refusing to persist`);
|
|
214
|
+
}
|
|
215
|
+
sql.exec('INSERT OR REPLACE INTO nimbus_session_kv (k, v) VALUES (?, ?)', KEY_ENV_JSON, json);
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
sql.exec('DELETE FROM nimbus_session_kv WHERE k = ?', KEY_ENV_JSON);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Stamp the time of the most recent successful hydrate. Used by the
|
|
223
|
+
* /api/_diag/session debug endpoint to show "the last cold-start
|
|
224
|
+
* found a row at <ts>". Cheap; one row write per initSession call.
|
|
225
|
+
*/
|
|
226
|
+
export function stampHydratedAt(ctx, atMs) {
|
|
227
|
+
const sql = ctx?.storage?.sql;
|
|
228
|
+
if (!sql)
|
|
229
|
+
return;
|
|
230
|
+
ensureSessionStateSchema(ctx);
|
|
231
|
+
sql.exec('INSERT OR REPLACE INTO nimbus_session_kv (k, v) VALUES (?, ?)', KEY_HYDRATED_AT, String(atMs));
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Drop ALL session-state rows. Used by /api/_test/session/reset for
|
|
235
|
+
* test-only flows; never called from prod paths. Equivalent to
|
|
236
|
+
* "this DO has never seen a session" — the next initSession runs
|
|
237
|
+
* the cold-start path.
|
|
238
|
+
*/
|
|
239
|
+
export function clearSessionState(ctx) {
|
|
240
|
+
const sql = ctx?.storage?.sql;
|
|
241
|
+
if (!sql)
|
|
242
|
+
return;
|
|
243
|
+
ensureSessionStateSchema(ctx);
|
|
244
|
+
sql.exec('DELETE FROM nimbus_session_kv');
|
|
245
|
+
sql.exec('DELETE FROM nimbus_kernel_mounts');
|
|
246
|
+
sql.exec('DELETE FROM nimbus_terminal_scrollback');
|
|
247
|
+
}
|
|
248
|
+
/** Count rows in nimbus_session_kv — used by the recovery_event
|
|
249
|
+
* recorder to populate snapshotKeysRehydrated. Cheap; bounded by
|
|
250
|
+
* the small key set above. */
|
|
251
|
+
export function countSessionStateKeys(ctx) {
|
|
252
|
+
const sql = ctx?.storage?.sql;
|
|
253
|
+
if (!sql)
|
|
254
|
+
return 0;
|
|
255
|
+
ensureSessionStateSchema(ctx);
|
|
256
|
+
for (const row of sql.exec('SELECT COUNT(*) AS n FROM nimbus_session_kv')) {
|
|
257
|
+
return Number(row.n) || 0;
|
|
258
|
+
}
|
|
259
|
+
return 0;
|
|
260
|
+
}
|
|
261
|
+
// ── Kernel mount tree [B'.2] ───────────────────────────────────────────
|
|
262
|
+
//
|
|
263
|
+
// The kernel mount list lives in nimbus_kernel_mounts. Today the list
|
|
264
|
+
// is exactly DEFAULT_MOUNT_POINTS (a static constant in src/constants.ts),
|
|
265
|
+
// so persistence is functionally redundant — every initSession would
|
|
266
|
+
// rebuild the same list from the constant. The architectural reason to
|
|
267
|
+
// route mount-list reads through SQL anyway is to give a future
|
|
268
|
+
// custom-mount feature a stable storage surface: when `mount /r2-cache`
|
|
269
|
+
// is added later, it just inserts a row, and the rehydrate path picks
|
|
270
|
+
// it up without any further refactor.
|
|
271
|
+
/**
|
|
272
|
+
* Load the persisted mount-point list. Returns a plain string[] of
|
|
273
|
+
* mount point names (without leading slash — same shape as
|
|
274
|
+
* DEFAULT_MOUNT_POINTS). Empty array when no rows exist.
|
|
275
|
+
*/
|
|
276
|
+
export function loadKernelMounts(ctx) {
|
|
277
|
+
const sql = ctx?.storage?.sql;
|
|
278
|
+
if (!sql)
|
|
279
|
+
return [];
|
|
280
|
+
ensureSessionStateSchema(ctx);
|
|
281
|
+
const out = [];
|
|
282
|
+
for (const row of sql.exec('SELECT mount_point FROM nimbus_kernel_mounts ORDER BY mount_point ASC')) {
|
|
283
|
+
out.push(String(row.mount_point));
|
|
284
|
+
}
|
|
285
|
+
return out;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Persist a mount-point list. Idempotent — replaces the entire
|
|
289
|
+
* nimbus_kernel_mounts contents in a single transaction. Caller
|
|
290
|
+
* provides the full desired set; we don't merge with existing rows.
|
|
291
|
+
*
|
|
292
|
+
* `mounts` should be plain names without leading slash
|
|
293
|
+
* ('bin', 'etc', ...) — same shape DEFAULT_MOUNT_POINTS uses.
|
|
294
|
+
*/
|
|
295
|
+
export function persistKernelMounts(ctx, mounts) {
|
|
296
|
+
const sql = ctx?.storage?.sql;
|
|
297
|
+
if (!sql)
|
|
298
|
+
return;
|
|
299
|
+
ensureSessionStateSchema(ctx);
|
|
300
|
+
// Replace-all semantics: clear, then insert. The whole table is
|
|
301
|
+
// bounded by O(10) rows so a full rewrite per persist is cheap.
|
|
302
|
+
sql.exec('DELETE FROM nimbus_kernel_mounts');
|
|
303
|
+
for (const mp of mounts) {
|
|
304
|
+
if (typeof mp !== 'string' || mp.length === 0)
|
|
305
|
+
continue;
|
|
306
|
+
sql.exec('INSERT OR REPLACE INTO nimbus_kernel_mounts (mount_point) VALUES (?)', mp);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// ── Terminal scrollback [B'.3] ─────────────────────────────────────────
|
|
310
|
+
//
|
|
311
|
+
// Every coalesced WS `output` frame (post-flush) is appended as one
|
|
312
|
+
// row in nimbus_terminal_scrollback. On reconnect, the rehydrate path
|
|
313
|
+
// reads all rows in seq order, concatenates the data column, and
|
|
314
|
+
// emits a single replay frame so the user sees their pre-close
|
|
315
|
+
// terminal contents above the fresh prompt.
|
|
316
|
+
//
|
|
317
|
+
// Cap policy: byte-budget eviction. After every append, if total
|
|
318
|
+
// bytes > SCROLLBACK_MAX_BYTES, delete oldest-seq rows until the
|
|
319
|
+
// total fits. Per-row count is unbounded — frames are coalesced at
|
|
320
|
+
// the WebSocketTerminal flush layer (5 ms timer) so a chatty shell
|
|
321
|
+
// produces O(N_frames) rows, not O(N_writes).
|
|
322
|
+
//
|
|
323
|
+
// The byte length of each frame is computed by the caller (cheap
|
|
324
|
+
// TextEncoder().encode().length) and stored in the `bytes` column.
|
|
325
|
+
// Computing per-row length on the read side would also work
|
|
326
|
+
// (length(CAST(data AS BLOB))) but storing it makes the eviction
|
|
327
|
+
// query trivial.
|
|
328
|
+
const _scrollbackEnc = new TextEncoder();
|
|
329
|
+
/**
|
|
330
|
+
* Append one coalesced output frame. Inserts a row, then evicts the
|
|
331
|
+
* oldest rows until total bytes ≤ SCROLLBACK_MAX_BYTES.
|
|
332
|
+
*
|
|
333
|
+
* `data` is the UTF-8 string written to the WS (typically including
|
|
334
|
+
* ANSI escape codes); empty strings are no-ops.
|
|
335
|
+
*
|
|
336
|
+
* Frames larger than SCROLLBACK_MAX_BYTES are TRUNCATED to their
|
|
337
|
+
* last cap bytes before insert. Reasoning: a single shell command
|
|
338
|
+
* (eg `cat huge-file`) can produce ≫ cap bytes in one coalesced WS
|
|
339
|
+
* frame; we want to preserve the most recent portion of that
|
|
340
|
+
* output (the user's "what just happened") rather than dropping it
|
|
341
|
+
* entirely. Truncation of UTF-8 at byte boundaries can split a
|
|
342
|
+
* multi-byte sequence; we accept that minor cosmetic risk vs. the
|
|
343
|
+
* complexity of a code-point-aware slice.
|
|
344
|
+
*
|
|
345
|
+
* Fail-soft: any thrown error is swallowed by the caller — losing
|
|
346
|
+
* scrollback is annoying but not session-fatal.
|
|
347
|
+
*/
|
|
348
|
+
export function appendScrollback(ctx, data, atMs) {
|
|
349
|
+
if (!data || data.length === 0)
|
|
350
|
+
return;
|
|
351
|
+
const sql = ctx?.storage?.sql;
|
|
352
|
+
if (!sql)
|
|
353
|
+
return;
|
|
354
|
+
ensureSessionStateSchema(ctx);
|
|
355
|
+
// Per-frame truncation: oversized single frames keep only the
|
|
356
|
+
// trailing MAX_FRAME_BYTES bytes. The "trailing" choice matches
|
|
357
|
+
// user intuition — when scrollback shows a big cat output, the
|
|
358
|
+
// user cares about the END of that output (where the prompt is)
|
|
359
|
+
// not the beginning.
|
|
360
|
+
let payload = data;
|
|
361
|
+
let bytes = _scrollbackEnc.encode(payload).length;
|
|
362
|
+
if (bytes > SCROLLBACK_MAX_FRAME_BYTES) {
|
|
363
|
+
const buf = _scrollbackEnc.encode(payload);
|
|
364
|
+
const start = buf.length - SCROLLBACK_MAX_FRAME_BYTES;
|
|
365
|
+
payload = new TextDecoder().decode(buf.subarray(start));
|
|
366
|
+
bytes = _scrollbackEnc.encode(payload).length;
|
|
367
|
+
}
|
|
368
|
+
let insertedSeq = null;
|
|
369
|
+
try {
|
|
370
|
+
sql.exec('INSERT INTO nimbus_terminal_scrollback (ts, data, bytes) VALUES (?, ?, ?)', atMs, payload, bytes);
|
|
371
|
+
// Capture the seq we just wrote so the eviction loop can avoid
|
|
372
|
+
// deleting it. SQLite's last_insert_rowid() returns the row id of
|
|
373
|
+
// the most recent insert on this connection.
|
|
374
|
+
for (const row of sql.exec('SELECT last_insert_rowid() AS id')) {
|
|
375
|
+
insertedSeq = Number(row.id);
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
catch (e) {
|
|
380
|
+
try {
|
|
381
|
+
console.warn('[B\'.3] scrollback INSERT failed bytes=' + bytes + ' err=' + (e?.message || e));
|
|
382
|
+
}
|
|
383
|
+
catch { }
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
// Eviction. Read total bytes; if over cap, delete oldest-seq rows
|
|
387
|
+
// in a loop. The table is bounded so a select-min/delete loop is
|
|
388
|
+
// O(rows-evicted) and runs only when needed.
|
|
389
|
+
//
|
|
390
|
+
// Invariant: we never delete the row we JUST inserted. With a
|
|
391
|
+
// sensibly-sized MAX_FRAME_BYTES (256 KiB) << MAX_BYTES (1 MiB),
|
|
392
|
+
// a single insert can never single-handedly violate the cap by
|
|
393
|
+
// more than MAX_FRAME_BYTES, and the eviction loop always has
|
|
394
|
+
// older rows to delete first. The "never the new row" guard is
|
|
395
|
+
// a belt-and-suspenders defence against future mis-tuning of the
|
|
396
|
+
// two constants.
|
|
397
|
+
let total = 0;
|
|
398
|
+
for (const row of sql.exec('SELECT COALESCE(SUM(bytes), 0) AS n FROM nimbus_terminal_scrollback')) {
|
|
399
|
+
total = Number(row.n) || 0;
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
if (total <= SCROLLBACK_MAX_BYTES)
|
|
403
|
+
return;
|
|
404
|
+
for (;;) {
|
|
405
|
+
let oldestSeq = null;
|
|
406
|
+
let oldestBytes = 0;
|
|
407
|
+
for (const row of sql.exec('SELECT seq, bytes FROM nimbus_terminal_scrollback ORDER BY seq ASC LIMIT 1')) {
|
|
408
|
+
oldestSeq = Number(row.seq);
|
|
409
|
+
oldestBytes = Number(row.bytes) || 0;
|
|
410
|
+
break;
|
|
411
|
+
}
|
|
412
|
+
if (oldestSeq === null)
|
|
413
|
+
break;
|
|
414
|
+
if (insertedSeq !== null && oldestSeq === insertedSeq) {
|
|
415
|
+
// The row we just inserted is now the only row → can't evict
|
|
416
|
+
// it; cap is briefly exceeded, accept it. With MAX_FRAME_BYTES
|
|
417
|
+
// ≤ MAX_BYTES the over-by amount is bounded.
|
|
418
|
+
break;
|
|
419
|
+
}
|
|
420
|
+
sql.exec('DELETE FROM nimbus_terminal_scrollback WHERE seq = ?', oldestSeq);
|
|
421
|
+
total -= oldestBytes;
|
|
422
|
+
if (total <= SCROLLBACK_MAX_BYTES)
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Read all scrollback rows in seq (chronological) order and return
|
|
428
|
+
* the concatenated payload. Used by the rehydrate path to emit a
|
|
429
|
+
* single batched replay frame.
|
|
430
|
+
*
|
|
431
|
+
* Returns empty string when the table is empty (cold start, or
|
|
432
|
+
* after explicit reset).
|
|
433
|
+
*/
|
|
434
|
+
export function loadScrollback(ctx) {
|
|
435
|
+
const sql = ctx?.storage?.sql;
|
|
436
|
+
if (!sql)
|
|
437
|
+
return '';
|
|
438
|
+
ensureSessionStateSchema(ctx);
|
|
439
|
+
const parts = [];
|
|
440
|
+
for (const row of sql.exec('SELECT data FROM nimbus_terminal_scrollback ORDER BY seq ASC')) {
|
|
441
|
+
parts.push(String(row.data));
|
|
442
|
+
}
|
|
443
|
+
return parts.join('');
|
|
444
|
+
}
|
|
445
|
+
/** Stats for /api/_diag/session: row count + total bytes + cap. */
|
|
446
|
+
export function getScrollbackStats(ctx) {
|
|
447
|
+
const sql = ctx?.storage?.sql;
|
|
448
|
+
if (!sql)
|
|
449
|
+
return { rows: 0, bytes: 0, maxBytes: SCROLLBACK_MAX_BYTES };
|
|
450
|
+
ensureSessionStateSchema(ctx);
|
|
451
|
+
let rows = 0;
|
|
452
|
+
let bytes = 0;
|
|
453
|
+
for (const row of sql.exec('SELECT COUNT(*) AS n, COALESCE(SUM(bytes), 0) AS b FROM nimbus_terminal_scrollback')) {
|
|
454
|
+
rows = Number(row.n) || 0;
|
|
455
|
+
bytes = Number(row.b) || 0;
|
|
456
|
+
break;
|
|
457
|
+
}
|
|
458
|
+
return { rows, bytes, maxBytes: SCROLLBACK_MAX_BYTES };
|
|
459
|
+
}
|