@nimbus-sh/core 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/dist/_shared/ai-egress.d.ts +73 -0
- package/dist/_shared/ai-egress.d.ts.map +1 -0
- package/dist/_shared/ai-egress.js +88 -0
- package/dist/_shared/byte-stream.d.ts +59 -0
- package/dist/_shared/byte-stream.d.ts.map +1 -0
- package/dist/_shared/byte-stream.js +77 -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 +145 -0
- package/dist/_shared/cache-stats.d.ts.map +1 -0
- package/dist/_shared/cache-stats.js +161 -0
- package/dist/_shared/crypto.d.ts +17 -0
- package/dist/_shared/crypto.d.ts.map +1 -0
- package/dist/_shared/crypto.js +131 -0
- package/dist/_shared/esbuild-facet-shim.d.ts +21 -0
- package/dist/_shared/esbuild-facet-shim.d.ts.map +1 -0
- package/dist/_shared/esbuild-facet-shim.js +22 -0
- package/dist/_shared/exports-resolver.d.ts +74 -0
- package/dist/_shared/exports-resolver.d.ts.map +1 -0
- package/dist/_shared/exports-resolver.js +284 -0
- package/dist/_shared/facet-vfs-cursor.d.ts +48 -0
- package/dist/_shared/facet-vfs-cursor.d.ts.map +1 -0
- package/dist/_shared/facet-vfs-cursor.js +51 -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/lru-map.d.ts +25 -0
- package/dist/_shared/lru-map.d.ts.map +1 -0
- package/dist/_shared/lru-map.js +57 -0
- package/dist/_shared/real-node-imports.d.ts +43 -0
- package/dist/_shared/real-node-imports.d.ts.map +1 -0
- package/dist/_shared/real-node-imports.js +52 -0
- package/dist/_shared/retry.d.ts +92 -0
- package/dist/_shared/retry.d.ts.map +1 -0
- package/dist/_shared/retry.js +155 -0
- package/dist/_shared/rpc-dispose.d.ts +4 -0
- package/dist/_shared/rpc-dispose.d.ts.map +1 -0
- package/dist/_shared/rpc-dispose.js +30 -0
- package/dist/_shared/terminal.d.ts +2 -0
- package/dist/_shared/terminal.d.ts.map +1 -0
- package/dist/_shared/terminal.js +3 -0
- package/dist/_shared/typescript-specifiers.d.ts +58 -0
- package/dist/_shared/typescript-specifiers.d.ts.map +1 -0
- package/dist/_shared/typescript-specifiers.js +84 -0
- package/dist/_shared/untrusted-request.d.ts +34 -0
- package/dist/_shared/untrusted-request.d.ts.map +1 -0
- package/dist/_shared/untrusted-request.js +74 -0
- package/dist/_shared/vfs-write-ledger.d.ts +3 -0
- package/dist/_shared/vfs-write-ledger.d.ts.map +1 -0
- package/dist/_shared/vfs-write-ledger.js +439 -0
- package/dist/_shared/w7-frame.d.ts +79 -0
- package/dist/_shared/w7-frame.d.ts.map +1 -0
- package/dist/_shared/w7-frame.js +839 -0
- package/dist/_shared/weighted-credit-pool.d.ts +61 -0
- package/dist/_shared/weighted-credit-pool.d.ts.map +1 -0
- package/dist/_shared/weighted-credit-pool.js +196 -0
- package/dist/constants.d.ts +64 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +264 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/observability/diag-counters.d.ts +236 -0
- package/dist/observability/diag-counters.d.ts.map +1 -0
- package/dist/observability/diag-counters.js +195 -0
- package/dist/observability/heap-estimate.d.ts +225 -0
- package/dist/observability/heap-estimate.d.ts.map +1 -0
- package/dist/observability/heap-estimate.js +185 -0
- package/dist/observability/heavy-alloc-coord.d.ts +93 -0
- package/dist/observability/heavy-alloc-coord.d.ts.map +1 -0
- package/dist/observability/heavy-alloc-coord.js +181 -0
- package/dist/observability/oom-classify.d.ts +138 -0
- package/dist/observability/oom-classify.d.ts.map +1 -0
- package/dist/observability/oom-classify.js +261 -0
- package/dist/observability/oom-discriminator.d.ts +176 -0
- package/dist/observability/oom-discriminator.d.ts.map +1 -0
- package/dist/observability/oom-discriminator.js +272 -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 +139 -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/bash/preamble.d.ts +2 -0
- package/dist/runtime/bash/preamble.d.ts.map +1 -0
- package/dist/runtime/bash/preamble.js +1849 -0
- package/dist/runtime/bash/types.d.ts +433 -0
- package/dist/runtime/bash/types.d.ts.map +1 -0
- package/dist/runtime/bash/types.js +1 -0
- package/dist/runtime/bundle-profile.d.ts +14 -0
- package/dist/runtime/bundle-profile.d.ts.map +1 -0
- package/dist/runtime/bundle-profile.js +20 -0
- package/dist/runtime/cirrus-vite-config-rewriter.d.ts +2 -0
- package/dist/runtime/cirrus-vite-config-rewriter.d.ts.map +1 -0
- package/dist/runtime/cirrus-vite-config-rewriter.js +28 -0
- package/dist/runtime/cli-flags.d.ts +2 -0
- package/dist/runtime/cli-flags.d.ts.map +1 -0
- package/dist/runtime/cli-flags.js +11 -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/cpython-preamble.d.ts +32 -0
- package/dist/runtime/cpython-preamble.d.ts.map +1 -0
- package/dist/runtime/cpython-preamble.js +296 -0
- package/dist/runtime/esbuild-service.d.ts +242 -0
- package/dist/runtime/esbuild-service.d.ts.map +1 -0
- package/dist/runtime/esbuild-service.js +1452 -0
- package/dist/runtime/framework-detect.d.ts +70 -0
- package/dist/runtime/framework-detect.d.ts.map +1 -0
- package/dist/runtime/framework-detect.js +177 -0
- package/dist/runtime/import-meta-transform.d.ts +3 -0
- package/dist/runtime/import-meta-transform.d.ts.map +1 -0
- package/dist/runtime/import-meta-transform.js +15 -0
- package/dist/runtime/javascript-ast.d.ts +13 -0
- package/dist/runtime/javascript-ast.d.ts.map +1 -0
- package/dist/runtime/javascript-ast.js +57 -0
- package/dist/runtime/long-running-handle.d.ts +119 -0
- package/dist/runtime/long-running-handle.d.ts.map +1 -0
- package/dist/runtime/long-running-handle.js +169 -0
- package/dist/runtime/module-source-rewriter.d.ts +11 -0
- package/dist/runtime/module-source-rewriter.d.ts.map +1 -0
- package/dist/runtime/module-source-rewriter.js +61 -0
- package/dist/runtime/opentui-wasm-backend.d.ts +160 -0
- package/dist/runtime/opentui-wasm-backend.d.ts.map +1 -0
- package/dist/runtime/opentui-wasm-backend.js +416 -0
- package/dist/runtime/os-contracts.d.ts +436 -0
- package/dist/runtime/os-contracts.d.ts.map +1 -0
- package/dist/runtime/os-contracts.js +101 -0
- package/dist/runtime/port-registry.d.ts +100 -0
- package/dist/runtime/port-registry.d.ts.map +1 -0
- package/dist/runtime/port-registry.js +265 -0
- package/dist/runtime/process-input-routing.d.ts +33 -0
- package/dist/runtime/process-input-routing.d.ts.map +1 -0
- package/dist/runtime/process-input-routing.js +59 -0
- package/dist/runtime/process-input.d.ts +39 -0
- package/dist/runtime/process-input.d.ts.map +1 -0
- package/dist/runtime/process-input.js +117 -0
- package/dist/runtime/process-io-protocol.d.ts +90 -0
- package/dist/runtime/process-io-protocol.d.ts.map +1 -0
- package/dist/runtime/process-io-protocol.js +81 -0
- package/dist/runtime/process-logs.d.ts +352 -0
- package/dist/runtime/process-logs.d.ts.map +1 -0
- package/dist/runtime/process-logs.js +750 -0
- package/dist/runtime/process-table.d.ts +101 -0
- package/dist/runtime/process-table.d.ts.map +1 -0
- package/dist/runtime/process-table.js +175 -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-server-adapter.d.ts +33 -0
- package/dist/runtime/python-server-adapter.d.ts.map +1 -0
- package/dist/runtime/python-server-adapter.js +132 -0
- package/dist/runtime/require-resolver.d.ts +44 -0
- package/dist/runtime/require-resolver.d.ts.map +1 -0
- package/dist/runtime/require-resolver.js +698 -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-green-threads.d.ts +136 -0
- package/dist/runtime/ruby-green-threads.d.ts.map +1 -0
- package/dist/runtime/ruby-green-threads.js +904 -0
- package/dist/runtime/ruby-socket-shim.d.ts +2 -0
- package/dist/runtime/ruby-socket-shim.d.ts.map +1 -0
- package/dist/runtime/ruby-socket-shim.js +548 -0
- package/dist/runtime/session-process-supervisor.d.ts +156 -0
- package/dist/runtime/session-process-supervisor.d.ts.map +1 -0
- package/dist/runtime/session-process-supervisor.js +238 -0
- package/dist/runtime/sqlite-runtime-fs-bridge.d.ts +70 -0
- package/dist/runtime/sqlite-runtime-fs-bridge.d.ts.map +1 -0
- package/dist/runtime/sqlite-runtime-fs-bridge.js +425 -0
- package/dist/runtime/streams.d.ts +13 -0
- package/dist/runtime/streams.d.ts.map +1 -0
- package/dist/runtime/streams.js +546 -0
- package/dist/runtime/undici-shim.d.ts +62 -0
- package/dist/runtime/undici-shim.d.ts.map +1 -0
- package/dist/runtime/undici-shim.js +516 -0
- package/dist/runtime/vfs-manifest.d.ts +63 -0
- package/dist/runtime/vfs-manifest.d.ts.map +1 -0
- package/dist/runtime/vfs-manifest.js +141 -0
- package/dist/runtime/vfs-snapshot.d.ts +75 -0
- package/dist/runtime/vfs-snapshot.d.ts.map +1 -0
- package/dist/runtime/vfs-snapshot.js +220 -0
- package/dist/runtime/virtual-socket-kernel.d.ts +252 -0
- package/dist/runtime/virtual-socket-kernel.d.ts.map +1 -0
- package/dist/runtime/virtual-socket-kernel.js +1143 -0
- package/dist/runtime/vite-config-parser.d.ts +13 -0
- package/dist/runtime/vite-config-parser.d.ts.map +1 -0
- package/dist/runtime/vite-config-parser.js +258 -0
- package/dist/runtime/wasi/types.d.ts +347 -0
- package/dist/runtime/wasi/types.d.ts.map +1 -0
- package/dist/runtime/wasi/types.js +1 -0
- package/dist/runtime/wasi-instance.d.ts +215 -0
- package/dist/runtime/wasi-instance.d.ts.map +1 -0
- package/dist/runtime/wasi-instance.generated.d.ts +14 -0
- package/dist/runtime/wasi-instance.generated.d.ts.map +1 -0
- package/dist/runtime/wasi-instance.generated.js +13 -0
- package/dist/runtime/wasi-instance.js +179 -0
- package/dist/runtime/wasi-threads.d.ts +169 -0
- package/dist/runtime/wasi-threads.d.ts.map +1 -0
- package/dist/runtime/wasi-threads.js +669 -0
- package/dist/runtime/wasm-memory.d.ts +84 -0
- package/dist/runtime/wasm-memory.d.ts.map +1 -0
- package/dist/runtime/wasm-memory.js +235 -0
- package/dist/shell/elevation-commands.d.ts +5 -0
- package/dist/shell/elevation-commands.d.ts.map +1 -0
- package/dist/shell/elevation-commands.js +109 -0
- package/dist/shell/exec-dispatch.d.ts +57 -0
- package/dist/shell/exec-dispatch.d.ts.map +1 -0
- package/dist/shell/exec-dispatch.js +181 -0
- package/dist/shell/features.d.ts +154 -0
- package/dist/shell/features.d.ts.map +1 -0
- package/dist/shell/features.js +792 -0
- package/dist/shell/shell-entrypoints.d.ts +55 -0
- package/dist/shell/shell-entrypoints.d.ts.map +1 -0
- package/dist/shell/shell-entrypoints.js +230 -0
- package/dist/shell/shell-invocation.d.ts +36 -0
- package/dist/shell/shell-invocation.d.ts.map +1 -0
- package/dist/shell/shell-invocation.js +148 -0
- package/dist/shell/unix-accounts.d.ts +27 -0
- package/dist/shell/unix-accounts.d.ts.map +1 -0
- package/dist/shell/unix-accounts.js +105 -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 +4702 -0
- package/dist/substrate/lifo/commands/archive/gunzip.d.ts +4 -0
- package/dist/substrate/lifo/commands/archive/gunzip.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/archive/gunzip.js +58 -0
- package/dist/substrate/lifo/commands/archive/gzip.d.ts +4 -0
- package/dist/substrate/lifo/commands/archive/gzip.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/archive/gzip.js +73 -0
- package/dist/substrate/lifo/commands/archive/tar.d.ts +4 -0
- package/dist/substrate/lifo/commands/archive/tar.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/archive/tar.js +158 -0
- package/dist/substrate/lifo/commands/archive/unzip.d.ts +4 -0
- package/dist/substrate/lifo/commands/archive/unzip.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/archive/unzip.js +114 -0
- package/dist/substrate/lifo/commands/archive/zip.d.ts +4 -0
- package/dist/substrate/lifo/commands/archive/zip.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/archive/zip.js +58 -0
- package/dist/substrate/lifo/commands/fs/basename.d.ts +4 -0
- package/dist/substrate/lifo/commands/fs/basename.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/fs/basename.js +12 -0
- package/dist/substrate/lifo/commands/fs/cat.d.ts +4 -0
- package/dist/substrate/lifo/commands/fs/cat.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/fs/cat.js +52 -0
- package/dist/substrate/lifo/commands/fs/chmod.d.ts +25 -0
- package/dist/substrate/lifo/commands/fs/chmod.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/fs/chmod.js +97 -0
- package/dist/substrate/lifo/commands/fs/chown.d.ts +4 -0
- package/dist/substrate/lifo/commands/fs/chown.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/fs/chown.js +41 -0
- package/dist/substrate/lifo/commands/fs/cp.d.ts +4 -0
- package/dist/substrate/lifo/commands/fs/cp.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/fs/cp.js +29 -0
- package/dist/substrate/lifo/commands/fs/df.d.ts +4 -0
- package/dist/substrate/lifo/commands/fs/df.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/fs/df.js +52 -0
- package/dist/substrate/lifo/commands/fs/dirname.d.ts +4 -0
- package/dist/substrate/lifo/commands/fs/dirname.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/fs/dirname.js +12 -0
- package/dist/substrate/lifo/commands/fs/du.d.ts +4 -0
- package/dist/substrate/lifo/commands/fs/du.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/fs/du.js +104 -0
- package/dist/substrate/lifo/commands/fs/file.d.ts +4 -0
- package/dist/substrate/lifo/commands/fs/file.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/fs/file.js +84 -0
- package/dist/substrate/lifo/commands/fs/find.d.ts +4 -0
- package/dist/substrate/lifo/commands/fs/find.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/fs/find.js +72 -0
- package/dist/substrate/lifo/commands/fs/ln.d.ts +4 -0
- package/dist/substrate/lifo/commands/fs/ln.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/fs/ln.js +15 -0
- package/dist/substrate/lifo/commands/fs/ls.d.ts +4 -0
- package/dist/substrate/lifo/commands/fs/ls.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/fs/ls.js +146 -0
- package/dist/substrate/lifo/commands/fs/mkdir.d.ts +4 -0
- package/dist/substrate/lifo/commands/fs/mkdir.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/fs/mkdir.js +31 -0
- package/dist/substrate/lifo/commands/fs/mktemp.d.ts +4 -0
- package/dist/substrate/lifo/commands/fs/mktemp.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/fs/mktemp.js +48 -0
- package/dist/substrate/lifo/commands/fs/mv.d.ts +4 -0
- package/dist/substrate/lifo/commands/fs/mv.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/fs/mv.js +113 -0
- package/dist/substrate/lifo/commands/fs/realpath.d.ts +4 -0
- package/dist/substrate/lifo/commands/fs/realpath.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/fs/realpath.js +27 -0
- package/dist/substrate/lifo/commands/fs/rm.d.ts +4 -0
- package/dist/substrate/lifo/commands/fs/rm.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/fs/rm.js +48 -0
- package/dist/substrate/lifo/commands/fs/rmdir.d.ts +4 -0
- package/dist/substrate/lifo/commands/fs/rmdir.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/fs/rmdir.js +44 -0
- package/dist/substrate/lifo/commands/fs/stat.d.ts +4 -0
- package/dist/substrate/lifo/commands/fs/stat.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/fs/stat.js +35 -0
- package/dist/substrate/lifo/commands/fs/touch.d.ts +4 -0
- package/dist/substrate/lifo/commands/fs/touch.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/fs/touch.js +26 -0
- package/dist/substrate/lifo/commands/fs/tree.d.ts +4 -0
- package/dist/substrate/lifo/commands/fs/tree.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/fs/tree.js +69 -0
- package/dist/substrate/lifo/commands/index.d.ts +3 -0
- package/dist/substrate/lifo/commands/index.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/index.js +1 -0
- package/dist/substrate/lifo/commands/io/dd.d.ts +4 -0
- package/dist/substrate/lifo/commands/io/dd.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/io/dd.js +256 -0
- package/dist/substrate/lifo/commands/io/printf.d.ts +4 -0
- package/dist/substrate/lifo/commands/io/printf.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/io/printf.js +82 -0
- package/dist/substrate/lifo/commands/io/tee.d.ts +4 -0
- package/dist/substrate/lifo/commands/io/tee.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/io/tee.js +31 -0
- package/dist/substrate/lifo/commands/io/xargs.d.ts +4 -0
- package/dist/substrate/lifo/commands/io/xargs.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/io/xargs.js +44 -0
- package/dist/substrate/lifo/commands/io/yes.d.ts +4 -0
- package/dist/substrate/lifo/commands/io/yes.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/io/yes.js +10 -0
- package/dist/substrate/lifo/commands/net/curl.d.ts +6 -0
- package/dist/substrate/lifo/commands/net/curl.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/net/curl.js +617 -0
- package/dist/substrate/lifo/commands/net/dig.d.ts +4 -0
- package/dist/substrate/lifo/commands/net/dig.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/net/dig.js +77 -0
- package/dist/substrate/lifo/commands/net/forward.d.ts +14 -0
- package/dist/substrate/lifo/commands/net/forward.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/net/forward.js +80 -0
- package/dist/substrate/lifo/commands/net/host.d.ts +14 -0
- package/dist/substrate/lifo/commands/net/host.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/net/host.js +160 -0
- package/dist/substrate/lifo/commands/net/ifconfig.d.ts +8 -0
- package/dist/substrate/lifo/commands/net/ifconfig.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/net/ifconfig.js +120 -0
- package/dist/substrate/lifo/commands/net/ip.d.ts +14 -0
- package/dist/substrate/lifo/commands/net/ip.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/net/ip.js +508 -0
- package/dist/substrate/lifo/commands/net/netstat.d.ts +8 -0
- package/dist/substrate/lifo/commands/net/netstat.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/net/netstat.js +94 -0
- package/dist/substrate/lifo/commands/net/ping.d.ts +4 -0
- package/dist/substrate/lifo/commands/net/ping.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/net/ping.js +64 -0
- package/dist/substrate/lifo/commands/net/ports.d.ts +8 -0
- package/dist/substrate/lifo/commands/net/ports.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/net/ports.js +36 -0
- package/dist/substrate/lifo/commands/net/route.d.ts +8 -0
- package/dist/substrate/lifo/commands/net/route.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/net/route.js +128 -0
- package/dist/substrate/lifo/commands/net/tunnel-v2.d.ts +4 -0
- package/dist/substrate/lifo/commands/net/tunnel-v2.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/net/tunnel-v2.js +141 -0
- package/dist/substrate/lifo/commands/net/tunnel.d.ts +6 -0
- package/dist/substrate/lifo/commands/net/tunnel.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/net/tunnel.js +232 -0
- package/dist/substrate/lifo/commands/net/wget.d.ts +4 -0
- package/dist/substrate/lifo/commands/net/wget.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/net/wget.js +78 -0
- package/dist/substrate/lifo/commands/registry.d.ts +15 -0
- package/dist/substrate/lifo/commands/registry.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/registry.js +120 -0
- package/dist/substrate/lifo/commands/signal.d.ts +12 -0
- package/dist/substrate/lifo/commands/signal.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/signal.js +47 -0
- package/dist/substrate/lifo/commands/system/bc.d.ts +4 -0
- package/dist/substrate/lifo/commands/system/bc.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/bc.js +243 -0
- package/dist/substrate/lifo/commands/system/cal.d.ts +4 -0
- package/dist/substrate/lifo/commands/system/cal.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/cal.js +81 -0
- package/dist/substrate/lifo/commands/system/date.d.ts +4 -0
- package/dist/substrate/lifo/commands/system/date.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/date.js +79 -0
- package/dist/substrate/lifo/commands/system/env.d.ts +4 -0
- package/dist/substrate/lifo/commands/system/env.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/env.js +7 -0
- package/dist/substrate/lifo/commands/system/fastfetch.d.ts +4 -0
- package/dist/substrate/lifo/commands/system/fastfetch.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/fastfetch.js +385 -0
- package/dist/substrate/lifo/commands/system/free.d.ts +4 -0
- package/dist/substrate/lifo/commands/system/free.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/free.js +36 -0
- package/dist/substrate/lifo/commands/system/help.d.ts +4 -0
- package/dist/substrate/lifo/commands/system/help.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/help.js +46 -0
- package/dist/substrate/lifo/commands/system/hostname.d.ts +4 -0
- package/dist/substrate/lifo/commands/system/hostname.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/hostname.js +6 -0
- package/dist/substrate/lifo/commands/system/kill.d.ts +4 -0
- package/dist/substrate/lifo/commands/system/kill.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/kill.js +76 -0
- package/dist/substrate/lifo/commands/system/lifo.d.ts +41 -0
- package/dist/substrate/lifo/commands/system/lifo.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/lifo.js +446 -0
- package/dist/substrate/lifo/commands/system/logout.d.ts +5 -0
- package/dist/substrate/lifo/commands/system/logout.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/logout.js +15 -0
- package/dist/substrate/lifo/commands/system/man.d.ts +4 -0
- package/dist/substrate/lifo/commands/system/man.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/man.js +144 -0
- package/dist/substrate/lifo/commands/system/node.d.ts +6 -0
- package/dist/substrate/lifo/commands/system/node.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/node.js +1241 -0
- package/dist/substrate/lifo/commands/system/npm.d.ts +26 -0
- package/dist/substrate/lifo/commands/system/npm.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/npm.js +934 -0
- package/dist/substrate/lifo/commands/system/pkg.d.ts +4 -0
- package/dist/substrate/lifo/commands/system/pkg.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/pkg.js +97 -0
- package/dist/substrate/lifo/commands/system/ps.d.ts +5 -0
- package/dist/substrate/lifo/commands/system/ps.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/ps.js +35 -0
- package/dist/substrate/lifo/commands/system/registry-schemas.d.ts +44 -0
- package/dist/substrate/lifo/commands/system/registry-schemas.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/registry-schemas.js +28 -0
- package/dist/substrate/lifo/commands/system/sha256sum.d.ts +4 -0
- package/dist/substrate/lifo/commands/system/sha256sum.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/sha256sum.js +86 -0
- package/dist/substrate/lifo/commands/system/sl.d.ts +4 -0
- package/dist/substrate/lifo/commands/system/sl.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/sl.js +204 -0
- package/dist/substrate/lifo/commands/system/sleep.d.ts +4 -0
- package/dist/substrate/lifo/commands/system/sleep.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/sleep.js +27 -0
- package/dist/substrate/lifo/commands/system/stty.d.ts +4 -0
- package/dist/substrate/lifo/commands/system/stty.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/stty.js +61 -0
- package/dist/substrate/lifo/commands/system/systemctl.d.ts +4 -0
- package/dist/substrate/lifo/commands/system/systemctl.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/systemctl.js +164 -0
- package/dist/substrate/lifo/commands/system/top.d.ts +4 -0
- package/dist/substrate/lifo/commands/system/top.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/top.js +38 -0
- package/dist/substrate/lifo/commands/system/uname.d.ts +4 -0
- package/dist/substrate/lifo/commands/system/uname.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/uname.js +55 -0
- package/dist/substrate/lifo/commands/system/uptime.d.ts +4 -0
- package/dist/substrate/lifo/commands/system/uptime.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/uptime.js +16 -0
- package/dist/substrate/lifo/commands/system/watch.d.ts +4 -0
- package/dist/substrate/lifo/commands/system/watch.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/watch.js +53 -0
- package/dist/substrate/lifo/commands/system/which.d.ts +4 -0
- package/dist/substrate/lifo/commands/system/which.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/which.js +45 -0
- package/dist/substrate/lifo/commands/system/whoami.d.ts +4 -0
- package/dist/substrate/lifo/commands/system/whoami.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/system/whoami.js +6 -0
- package/dist/substrate/lifo/commands/text/awk.d.ts +4 -0
- package/dist/substrate/lifo/commands/text/awk.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/text/awk.js +200 -0
- package/dist/substrate/lifo/commands/text/base64.d.ts +4 -0
- package/dist/substrate/lifo/commands/text/base64.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/text/base64.js +102 -0
- package/dist/substrate/lifo/commands/text/cut.d.ts +4 -0
- package/dist/substrate/lifo/commands/text/cut.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/text/cut.js +91 -0
- package/dist/substrate/lifo/commands/text/diff.d.ts +4 -0
- package/dist/substrate/lifo/commands/text/diff.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/text/diff.js +292 -0
- package/dist/substrate/lifo/commands/text/grep.d.ts +4 -0
- package/dist/substrate/lifo/commands/text/grep.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/text/grep.js +189 -0
- package/dist/substrate/lifo/commands/text/head.d.ts +4 -0
- package/dist/substrate/lifo/commands/text/head.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/text/head.js +111 -0
- package/dist/substrate/lifo/commands/text/less.d.ts +4 -0
- package/dist/substrate/lifo/commands/text/less.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/text/less.js +335 -0
- package/dist/substrate/lifo/commands/text/nano.d.ts +4 -0
- package/dist/substrate/lifo/commands/text/nano.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/text/nano.js +510 -0
- package/dist/substrate/lifo/commands/text/nl.d.ts +4 -0
- package/dist/substrate/lifo/commands/text/nl.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/text/nl.js +64 -0
- package/dist/substrate/lifo/commands/text/rev.d.ts +4 -0
- package/dist/substrate/lifo/commands/text/rev.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/text/rev.js +41 -0
- package/dist/substrate/lifo/commands/text/sed.d.ts +21 -0
- package/dist/substrate/lifo/commands/text/sed.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/text/sed.js +248 -0
- package/dist/substrate/lifo/commands/text/seq.d.ts +4 -0
- package/dist/substrate/lifo/commands/text/seq.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/text/seq.js +69 -0
- package/dist/substrate/lifo/commands/text/sort.d.ts +4 -0
- package/dist/substrate/lifo/commands/text/sort.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/text/sort.js +90 -0
- package/dist/substrate/lifo/commands/text/strings.d.ts +4 -0
- package/dist/substrate/lifo/commands/text/strings.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/text/strings.js +69 -0
- package/dist/substrate/lifo/commands/text/tac.d.ts +4 -0
- package/dist/substrate/lifo/commands/text/tac.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/text/tac.js +32 -0
- package/dist/substrate/lifo/commands/text/tail.d.ts +4 -0
- package/dist/substrate/lifo/commands/text/tail.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/text/tail.js +64 -0
- package/dist/substrate/lifo/commands/text/tr.d.ts +4 -0
- package/dist/substrate/lifo/commands/text/tr.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/text/tr.js +180 -0
- package/dist/substrate/lifo/commands/text/uniq.d.ts +4 -0
- package/dist/substrate/lifo/commands/text/uniq.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/text/uniq.js +82 -0
- package/dist/substrate/lifo/commands/text/wc.d.ts +4 -0
- package/dist/substrate/lifo/commands/text/wc.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/text/wc.js +107 -0
- package/dist/substrate/lifo/commands/types.d.ts +49 -0
- package/dist/substrate/lifo/commands/types.d.ts.map +1 -0
- package/dist/substrate/lifo/commands/types.js +1 -0
- package/dist/substrate/lifo/index.d.ts +67 -0
- package/dist/substrate/lifo/index.d.ts.map +1 -0
- package/dist/substrate/lifo/index.js +66 -0
- package/dist/substrate/lifo/kernel/ServiceManager.d.ts +51 -0
- package/dist/substrate/lifo/kernel/ServiceManager.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/ServiceManager.js +302 -0
- package/dist/substrate/lifo/kernel/index.d.ts +40 -0
- package/dist/substrate/lifo/kernel/index.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/index.js +170 -0
- package/dist/substrate/lifo/kernel/network/Bridge.d.ts +110 -0
- package/dist/substrate/lifo/kernel/network/Bridge.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/network/Bridge.js +278 -0
- package/dist/substrate/lifo/kernel/network/DNSResolver.d.ts +50 -0
- package/dist/substrate/lifo/kernel/network/DNSResolver.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/network/DNSResolver.js +135 -0
- package/dist/substrate/lifo/kernel/network/NetworkInterface.d.ts +83 -0
- package/dist/substrate/lifo/kernel/network/NetworkInterface.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/network/NetworkInterface.js +183 -0
- package/dist/substrate/lifo/kernel/network/NetworkNamespace.d.ts +98 -0
- package/dist/substrate/lifo/kernel/network/NetworkNamespace.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/network/NetworkNamespace.js +149 -0
- package/dist/substrate/lifo/kernel/network/NetworkStack.d.ts +178 -0
- package/dist/substrate/lifo/kernel/network/NetworkStack.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/network/NetworkStack.js +460 -0
- package/dist/substrate/lifo/kernel/network/PortBridge.d.ts +44 -0
- package/dist/substrate/lifo/kernel/network/PortBridge.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/network/PortBridge.js +158 -0
- package/dist/substrate/lifo/kernel/network/RoutingTable.d.ts +55 -0
- package/dist/substrate/lifo/kernel/network/RoutingTable.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/network/RoutingTable.js +167 -0
- package/dist/substrate/lifo/kernel/network/Socket.d.ts +65 -0
- package/dist/substrate/lifo/kernel/network/Socket.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/network/Socket.js +201 -0
- package/dist/substrate/lifo/kernel/network/index.d.ts +11 -0
- package/dist/substrate/lifo/kernel/network/index.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/network/index.js +9 -0
- package/dist/substrate/lifo/kernel/network/tunnel/BaseTunnel.d.ts +55 -0
- package/dist/substrate/lifo/kernel/network/tunnel/BaseTunnel.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/network/tunnel/BaseTunnel.js +88 -0
- package/dist/substrate/lifo/kernel/network/tunnel/VETHPair.d.ts +71 -0
- package/dist/substrate/lifo/kernel/network/tunnel/VETHPair.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/network/tunnel/VETHPair.js +191 -0
- package/dist/substrate/lifo/kernel/network/tunnel/WebSocketTunnel.d.ts +89 -0
- package/dist/substrate/lifo/kernel/network/tunnel/WebSocketTunnel.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/network/tunnel/WebSocketTunnel.js +324 -0
- package/dist/substrate/lifo/kernel/network/types.d.ts +159 -0
- package/dist/substrate/lifo/kernel/network/types.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/network/types.js +5 -0
- package/dist/substrate/lifo/kernel/persistence/PersistenceManager.d.ts +12 -0
- package/dist/substrate/lifo/kernel/persistence/PersistenceManager.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/persistence/PersistenceManager.js +47 -0
- package/dist/substrate/lifo/kernel/persistence/backends.d.ts +15 -0
- package/dist/substrate/lifo/kernel/persistence/backends.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/persistence/backends.js +18 -0
- package/dist/substrate/lifo/kernel/persistence/index.d.ts +6 -0
- package/dist/substrate/lifo/kernel/persistence/index.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/persistence/index.js +3 -0
- package/dist/substrate/lifo/kernel/persistence/serializer.d.ts +21 -0
- package/dist/substrate/lifo/kernel/persistence/serializer.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/persistence/serializer.js +99 -0
- package/dist/substrate/lifo/kernel/samples.d.ts +3 -0
- package/dist/substrate/lifo/kernel/samples.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/samples.js +930 -0
- package/dist/substrate/lifo/kernel/storage/BlobStore.d.ts +22 -0
- package/dist/substrate/lifo/kernel/storage/BlobStore.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/storage/BlobStore.js +84 -0
- package/dist/substrate/lifo/kernel/storage/ContentStore.d.ts +54 -0
- package/dist/substrate/lifo/kernel/storage/ContentStore.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/storage/ContentStore.js +120 -0
- package/dist/substrate/lifo/kernel/storage/index.d.ts +6 -0
- package/dist/substrate/lifo/kernel/storage/index.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/storage/index.js +3 -0
- package/dist/substrate/lifo/kernel/unit-parser.d.ts +22 -0
- package/dist/substrate/lifo/kernel/unit-parser.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/unit-parser.js +80 -0
- package/dist/substrate/lifo/kernel/vfs/VFS.d.ts +99 -0
- package/dist/substrate/lifo/kernel/vfs/VFS.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/vfs/VFS.js +679 -0
- package/dist/substrate/lifo/kernel/vfs/index.d.ts +8 -0
- package/dist/substrate/lifo/kernel/vfs/index.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/vfs/index.js +5 -0
- package/dist/substrate/lifo/kernel/vfs/providers/DevProvider.d.ts +21 -0
- package/dist/substrate/lifo/kernel/vfs/providers/DevProvider.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/vfs/providers/DevProvider.js +137 -0
- package/dist/substrate/lifo/kernel/vfs/providers/NativeFsProvider.d.ts +63 -0
- package/dist/substrate/lifo/kernel/vfs/providers/NativeFsProvider.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/vfs/providers/NativeFsProvider.js +175 -0
- package/dist/substrate/lifo/kernel/vfs/providers/ProcProvider.d.ts +14 -0
- package/dist/substrate/lifo/kernel/vfs/providers/ProcProvider.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/vfs/providers/ProcProvider.js +126 -0
- package/dist/substrate/lifo/kernel/vfs/types.d.ts +117 -0
- package/dist/substrate/lifo/kernel/vfs/types.d.ts.map +1 -0
- package/dist/substrate/lifo/kernel/vfs/types.js +42 -0
- package/dist/substrate/lifo/node-compat/buffer.d.ts +44 -0
- package/dist/substrate/lifo/node-compat/buffer.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/buffer.js +331 -0
- package/dist/substrate/lifo/node-compat/child_process.d.ts +10 -0
- package/dist/substrate/lifo/node-compat/child_process.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/child_process.js +42 -0
- package/dist/substrate/lifo/node-compat/console.d.ts +21 -0
- package/dist/substrate/lifo/node-compat/console.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/console.js +68 -0
- package/dist/substrate/lifo/node-compat/crypto.d.ts +24 -0
- package/dist/substrate/lifo/node-compat/crypto.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/crypto.js +216 -0
- package/dist/substrate/lifo/node-compat/dns.d.ts +117 -0
- package/dist/substrate/lifo/node-compat/dns.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/dns.js +144 -0
- package/dist/substrate/lifo/node-compat/esbuild.d.ts +10 -0
- package/dist/substrate/lifo/node-compat/esbuild.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/esbuild.js +70 -0
- package/dist/substrate/lifo/node-compat/events.d.ts +19 -0
- package/dist/substrate/lifo/node-compat/events.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/events.js +83 -0
- package/dist/substrate/lifo/node-compat/fs.d.ts +195 -0
- package/dist/substrate/lifo/node-compat/fs.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/fs.js +624 -0
- package/dist/substrate/lifo/node-compat/http.d.ts +133 -0
- package/dist/substrate/lifo/node-compat/http.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/http.js +452 -0
- package/dist/substrate/lifo/node-compat/index.d.ts +20 -0
- package/dist/substrate/lifo/node-compat/index.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/index.js +291 -0
- package/dist/substrate/lifo/node-compat/module.d.ts +55 -0
- package/dist/substrate/lifo/node-compat/module.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/module.js +124 -0
- package/dist/substrate/lifo/node-compat/os.d.ts +39 -0
- package/dist/substrate/lifo/node-compat/os.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/os.js +45 -0
- package/dist/substrate/lifo/node-compat/path.d.ts +86 -0
- package/dist/substrate/lifo/node-compat/path.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/path.js +49 -0
- package/dist/substrate/lifo/node-compat/process.d.ts +127 -0
- package/dist/substrate/lifo/node-compat/process.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/process.js +173 -0
- package/dist/substrate/lifo/node-compat/readline.d.ts +85 -0
- package/dist/substrate/lifo/node-compat/readline.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/readline.js +205 -0
- package/dist/substrate/lifo/node-compat/rimraf.d.ts +30 -0
- package/dist/substrate/lifo/node-compat/rimraf.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/rimraf.js +63 -0
- package/dist/substrate/lifo/node-compat/stream.d.ts +36 -0
- package/dist/substrate/lifo/node-compat/stream.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/stream.js +90 -0
- package/dist/substrate/lifo/node-compat/string_decoder.d.ts +11 -0
- package/dist/substrate/lifo/node-compat/string_decoder.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/string_decoder.js +23 -0
- package/dist/substrate/lifo/node-compat/timers.d.ts +16 -0
- package/dist/substrate/lifo/node-compat/timers.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/timers.js +18 -0
- package/dist/substrate/lifo/node-compat/tty.d.ts +32 -0
- package/dist/substrate/lifo/node-compat/tty.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/tty.js +76 -0
- package/dist/substrate/lifo/node-compat/url.d.ts +37 -0
- package/dist/substrate/lifo/node-compat/url.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/url.js +65 -0
- package/dist/substrate/lifo/node-compat/util.d.ts +42 -0
- package/dist/substrate/lifo/node-compat/util.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/util.js +132 -0
- package/dist/substrate/lifo/node-compat/zlib.d.ts +66 -0
- package/dist/substrate/lifo/node-compat/zlib.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/zlib.js +80 -0
- package/dist/substrate/lifo/pkg/PackageManager.d.ts +19 -0
- package/dist/substrate/lifo/pkg/PackageManager.d.ts.map +1 -0
- package/dist/substrate/lifo/pkg/PackageManager.js +97 -0
- package/dist/substrate/lifo/pkg/lifo-dev.d.ts +38 -0
- package/dist/substrate/lifo/pkg/lifo-dev.d.ts.map +1 -0
- package/dist/substrate/lifo/pkg/lifo-dev.js +98 -0
- package/dist/substrate/lifo/pkg/lifo-runtime.d.ts +44 -0
- package/dist/substrate/lifo/pkg/lifo-runtime.d.ts.map +1 -0
- package/dist/substrate/lifo/pkg/lifo-runtime.js +372 -0
- package/dist/substrate/lifo/pkg/loader.d.ts +4 -0
- package/dist/substrate/lifo/pkg/loader.d.ts.map +1 -0
- package/dist/substrate/lifo/pkg/loader.js +24 -0
- package/dist/substrate/lifo/sandbox/HeadlessTerminal.d.ts +18 -0
- package/dist/substrate/lifo/sandbox/HeadlessTerminal.d.ts.map +1 -0
- package/dist/substrate/lifo/sandbox/HeadlessTerminal.js +28 -0
- package/dist/substrate/lifo/sandbox/Sandbox.d.ts +59 -0
- package/dist/substrate/lifo/sandbox/Sandbox.d.ts.map +1 -0
- package/dist/substrate/lifo/sandbox/Sandbox.js +234 -0
- package/dist/substrate/lifo/sandbox/SandboxCommands.d.ts +18 -0
- package/dist/substrate/lifo/sandbox/SandboxCommands.d.ts.map +1 -0
- package/dist/substrate/lifo/sandbox/SandboxCommands.js +64 -0
- package/dist/substrate/lifo/sandbox/SandboxFs.d.ts +43 -0
- package/dist/substrate/lifo/sandbox/SandboxFs.d.ts.map +1 -0
- package/dist/substrate/lifo/sandbox/SandboxFs.js +136 -0
- package/dist/substrate/lifo/sandbox/index.d.ts +3 -0
- package/dist/substrate/lifo/sandbox/index.d.ts.map +1 -0
- package/dist/substrate/lifo/sandbox/index.js +1 -0
- package/dist/substrate/lifo/sandbox/types.d.ts +116 -0
- package/dist/substrate/lifo/sandbox/types.d.ts.map +1 -0
- package/dist/substrate/lifo/sandbox/types.js +1 -0
- package/dist/substrate/lifo/shell/ProcessRegistry.d.ts +142 -0
- package/dist/substrate/lifo/shell/ProcessRegistry.d.ts.map +1 -0
- package/dist/substrate/lifo/shell/ProcessRegistry.js +256 -0
- package/dist/substrate/lifo/shell/Shell.d.ts +170 -0
- package/dist/substrate/lifo/shell/Shell.d.ts.map +1 -0
- package/dist/substrate/lifo/shell/Shell.js +1863 -0
- package/dist/substrate/lifo/shell/completer.d.ts +19 -0
- package/dist/substrate/lifo/shell/completer.d.ts.map +1 -0
- package/dist/substrate/lifo/shell/completer.js +164 -0
- package/dist/substrate/lifo/shell/default-shell.d.ts +9 -0
- package/dist/substrate/lifo/shell/default-shell.d.ts.map +1 -0
- package/dist/substrate/lifo/shell/default-shell.js +53 -0
- package/dist/substrate/lifo/shell/expander.d.ts +48 -0
- package/dist/substrate/lifo/shell/expander.d.ts.map +1 -0
- package/dist/substrate/lifo/shell/expander.js +1204 -0
- package/dist/substrate/lifo/shell/history.d.ts +22 -0
- package/dist/substrate/lifo/shell/history.d.ts.map +1 -0
- package/dist/substrate/lifo/shell/history.js +121 -0
- package/dist/substrate/lifo/shell/interpreter.d.ts +257 -0
- package/dist/substrate/lifo/shell/interpreter.d.ts.map +1 -0
- package/dist/substrate/lifo/shell/interpreter.js +1490 -0
- package/dist/substrate/lifo/shell/jobs.d.ts +21 -0
- package/dist/substrate/lifo/shell/jobs.d.ts.map +1 -0
- package/dist/substrate/lifo/shell/jobs.js +48 -0
- package/dist/substrate/lifo/shell/lexer.d.ts +13 -0
- package/dist/substrate/lifo/shell/lexer.d.ts.map +1 -0
- package/dist/substrate/lifo/shell/lexer.js +614 -0
- package/dist/substrate/lifo/shell/parser.d.ts +7 -0
- package/dist/substrate/lifo/shell/parser.d.ts.map +1 -0
- package/dist/substrate/lifo/shell/parser.js +573 -0
- package/dist/substrate/lifo/shell/pipe.d.ts +15 -0
- package/dist/substrate/lifo/shell/pipe.d.ts.map +1 -0
- package/dist/substrate/lifo/shell/pipe.js +86 -0
- package/dist/substrate/lifo/shell/signals.d.ts +11 -0
- package/dist/substrate/lifo/shell/signals.d.ts.map +1 -0
- package/dist/substrate/lifo/shell/signals.js +57 -0
- package/dist/substrate/lifo/shell/terminal-stdin.d.ts +28 -0
- package/dist/substrate/lifo/shell/terminal-stdin.d.ts.map +1 -0
- package/dist/substrate/lifo/shell/terminal-stdin.js +100 -0
- package/dist/substrate/lifo/shell/test-builtin.d.ts +16 -0
- package/dist/substrate/lifo/shell/test-builtin.d.ts.map +1 -0
- package/dist/substrate/lifo/shell/test-builtin.js +237 -0
- package/dist/substrate/lifo/shell/types.d.ts +140 -0
- package/dist/substrate/lifo/shell/types.d.ts.map +1 -0
- package/dist/substrate/lifo/shell/types.js +25 -0
- package/dist/substrate/lifo/terminal/ITerminal.d.ts +10 -0
- package/dist/substrate/lifo/terminal/ITerminal.d.ts.map +1 -0
- package/dist/substrate/lifo/terminal/ITerminal.js +1 -0
- package/dist/substrate/lifo/utils/archive.d.ts +28 -0
- package/dist/substrate/lifo/utils/archive.d.ts.map +1 -0
- package/dist/substrate/lifo/utils/archive.js +323 -0
- package/dist/substrate/lifo/utils/args.d.ts +18 -0
- package/dist/substrate/lifo/utils/args.d.ts.map +1 -0
- package/dist/substrate/lifo/utils/args.js +73 -0
- package/dist/substrate/lifo/utils/colors.d.ts +27 -0
- package/dist/substrate/lifo/utils/colors.d.ts.map +1 -0
- package/dist/substrate/lifo/utils/colors.js +26 -0
- package/dist/substrate/lifo/utils/encoding.d.ts +4 -0
- package/dist/substrate/lifo/utils/encoding.d.ts.map +1 -0
- package/dist/substrate/lifo/utils/encoding.js +18 -0
- package/dist/substrate/lifo/utils/glob.d.ts +12 -0
- package/dist/substrate/lifo/utils/glob.d.ts.map +1 -0
- package/dist/substrate/lifo/utils/glob.js +150 -0
- package/dist/substrate/lifo/utils/mime.d.ts +4 -0
- package/dist/substrate/lifo/utils/mime.d.ts.map +1 -0
- package/dist/substrate/lifo/utils/mime.js +107 -0
- package/dist/substrate/lifo/utils/path.d.ts +12 -0
- package/dist/substrate/lifo/utils/path.d.ts.map +1 -0
- package/dist/substrate/lifo/utils/path.js +82 -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 +46 -0
- package/dist/vfs/path.d.ts.map +1 -0
- package/dist/vfs/path.js +69 -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 +1054 -0
- package/dist/vfs/sqlite-vfs.d.ts +821 -0
- package/dist/vfs/sqlite-vfs.d.ts.map +1 -0
- package/dist/vfs/sqlite-vfs.js +4135 -0
- package/dist/vfs/symlink-registry.d.ts +60 -0
- package/dist/vfs/symlink-registry.d.ts.map +1 -0
- package/dist/vfs/symlink-registry.js +162 -0
- package/dist/workspace/nimbus-workspace.d.ts +94 -0
- package/dist/workspace/nimbus-workspace.d.ts.map +1 -0
- package/dist/workspace/nimbus-workspace.js +167 -0
- package/package.json +69 -0
- package/src/_shared/ai-egress.ts +91 -0
- package/src/_shared/byte-stream.ts +97 -0
- package/src/_shared/bytes.ts +19 -0
- package/src/_shared/cache-stats.ts +231 -0
- package/src/_shared/crypto.ts +184 -0
- package/src/_shared/esbuild-facet-shim.ts +22 -0
- package/src/_shared/exports-resolver.ts +308 -0
- package/src/_shared/facet-vfs-cursor.ts +58 -0
- package/src/_shared/install-phase.ts +56 -0
- package/src/_shared/lru-map.ts +59 -0
- package/src/_shared/real-node-imports.ts +53 -0
- package/src/_shared/retry.ts +202 -0
- package/src/_shared/rpc-dispose.ts +31 -0
- package/src/_shared/terminal.ts +3 -0
- package/src/_shared/typescript-specifiers.ts +86 -0
- package/src/_shared/untrusted-request.ts +75 -0
- package/src/_shared/vfs-write-ledger.ts +440 -0
- package/src/_shared/w7-frame.ts +1012 -0
- package/src/_shared/weighted-credit-pool.ts +244 -0
- package/src/constants.ts +285 -0
- package/src/index.ts +22 -0
- package/src/observability/diag-counters.ts +343 -0
- package/src/observability/heap-estimate.ts +331 -0
- package/src/observability/heavy-alloc-coord.ts +241 -0
- package/src/observability/oom-classify.ts +253 -0
- package/src/observability/oom-discriminator.ts +405 -0
- package/src/runtime/assets-loader.ts +189 -0
- package/src/runtime/barrel-detect.ts +69 -0
- package/src/runtime/bash/preamble.ts +1519 -0
- package/src/runtime/bash/types.ts +497 -0
- package/src/runtime/bundle-profile.ts +32 -0
- package/src/runtime/cirrus-vite-config-rewriter.ts +28 -0
- package/src/runtime/cli-flags.ts +11 -0
- package/src/runtime/comment-strip.ts +73 -0
- package/src/runtime/cpython-preamble.ts +298 -0
- package/src/runtime/esbuild-service.ts +1470 -0
- package/src/runtime/framework-detect.ts +245 -0
- package/src/runtime/import-meta-transform.ts +16 -0
- package/src/runtime/javascript-ast.ts +66 -0
- package/src/runtime/long-running-handle.ts +185 -0
- package/src/runtime/module-source-rewriter.ts +95 -0
- package/src/runtime/opentui-wasm-backend.ts +608 -0
- package/src/runtime/os-contracts.ts +529 -0
- package/src/runtime/port-registry.ts +298 -0
- package/src/runtime/process-input-routing.ts +92 -0
- package/src/runtime/process-input.ts +148 -0
- package/src/runtime/process-io-protocol.ts +97 -0
- package/src/runtime/process-logs.ts +882 -0
- package/src/runtime/process-table.ts +225 -0
- package/src/runtime/project-detect.ts +42 -0
- package/src/runtime/python-server-adapter.ts +133 -0
- package/src/runtime/require-resolver.ts +760 -0
- package/src/runtime/router-basename.ts +279 -0
- package/src/runtime/ruby-green-threads.ts +904 -0
- package/src/runtime/ruby-socket-shim.ts +548 -0
- package/src/runtime/session-process-supervisor.ts +323 -0
- package/src/runtime/sqlite-runtime-fs-bridge.ts +468 -0
- package/src/runtime/streams.ts +547 -0
- package/src/runtime/undici-shim.ts +517 -0
- package/src/runtime/vfs-manifest.ts +170 -0
- package/src/runtime/vfs-snapshot.ts +273 -0
- package/src/runtime/virtual-socket-kernel.ts +1370 -0
- package/src/runtime/vite-config-parser.ts +273 -0
- package/src/runtime/wasi/types.ts +448 -0
- package/src/runtime/wasi-instance.generated.ts +14 -0
- package/src/runtime/wasi-instance.ts +259 -0
- package/src/runtime/wasi-threads.ts +682 -0
- package/src/runtime/wasm-memory.ts +261 -0
- package/src/shell/elevation-commands.ts +117 -0
- package/src/shell/exec-dispatch.ts +201 -0
- package/src/shell/features.ts +870 -0
- package/src/shell/shell-entrypoints.ts +323 -0
- package/src/shell/shell-invocation.ts +181 -0
- package/src/shell/unix-accounts.ts +128 -0
- package/src/shell/unix-commands.ts +4335 -0
- package/src/substrate/lifo/UPSTREAM.md +14 -0
- package/src/substrate/lifo/commands/archive/gunzip.ts +63 -0
- package/src/substrate/lifo/commands/archive/gzip.ts +76 -0
- package/src/substrate/lifo/commands/archive/tar.ts +157 -0
- package/src/substrate/lifo/commands/archive/unzip.ts +112 -0
- package/src/substrate/lifo/commands/archive/zip.ts +70 -0
- package/src/substrate/lifo/commands/fs/basename.ts +16 -0
- package/src/substrate/lifo/commands/fs/cat.ts +54 -0
- package/src/substrate/lifo/commands/fs/chmod.ts +110 -0
- package/src/substrate/lifo/commands/fs/chown.ts +45 -0
- package/src/substrate/lifo/commands/fs/cp.ts +33 -0
- package/src/substrate/lifo/commands/fs/df.ts +56 -0
- package/src/substrate/lifo/commands/fs/dirname.ts +16 -0
- package/src/substrate/lifo/commands/fs/du.ts +100 -0
- package/src/substrate/lifo/commands/fs/file.ts +90 -0
- package/src/substrate/lifo/commands/fs/find.ts +76 -0
- package/src/substrate/lifo/commands/fs/ln.ts +19 -0
- package/src/substrate/lifo/commands/fs/ls.ts +164 -0
- package/src/substrate/lifo/commands/fs/mkdir.ts +37 -0
- package/src/substrate/lifo/commands/fs/mktemp.ts +56 -0
- package/src/substrate/lifo/commands/fs/mv.ts +111 -0
- package/src/substrate/lifo/commands/fs/realpath.ts +31 -0
- package/src/substrate/lifo/commands/fs/rm.ts +53 -0
- package/src/substrate/lifo/commands/fs/rmdir.ts +50 -0
- package/src/substrate/lifo/commands/fs/stat.ts +39 -0
- package/src/substrate/lifo/commands/fs/touch.ts +30 -0
- package/src/substrate/lifo/commands/fs/tree.ts +76 -0
- package/src/substrate/lifo/commands/index.ts +2 -0
- package/src/substrate/lifo/commands/io/dd.ts +289 -0
- package/src/substrate/lifo/commands/io/printf.ts +78 -0
- package/src/substrate/lifo/commands/io/tee.ts +37 -0
- package/src/substrate/lifo/commands/io/xargs.ts +49 -0
- package/src/substrate/lifo/commands/io/yes.ts +15 -0
- package/src/substrate/lifo/commands/net/curl.ts +739 -0
- package/src/substrate/lifo/commands/net/dig.ts +99 -0
- package/src/substrate/lifo/commands/net/forward.ts +90 -0
- package/src/substrate/lifo/commands/net/host.ts +182 -0
- package/src/substrate/lifo/commands/net/ifconfig.ts +139 -0
- package/src/substrate/lifo/commands/net/ip.ts +633 -0
- package/src/substrate/lifo/commands/net/netstat.ts +106 -0
- package/src/substrate/lifo/commands/net/ping.ts +74 -0
- package/src/substrate/lifo/commands/net/ports.ts +45 -0
- package/src/substrate/lifo/commands/net/route.ts +153 -0
- package/src/substrate/lifo/commands/net/tunnel-v2.ts +183 -0
- package/src/substrate/lifo/commands/net/tunnel.ts +272 -0
- package/src/substrate/lifo/commands/net/wget.ts +88 -0
- package/src/substrate/lifo/commands/registry.ts +141 -0
- package/src/substrate/lifo/commands/signal.ts +65 -0
- package/src/substrate/lifo/commands/system/bc.ts +265 -0
- package/src/substrate/lifo/commands/system/cal.ts +90 -0
- package/src/substrate/lifo/commands/system/date.ts +59 -0
- package/src/substrate/lifo/commands/system/env.ts +10 -0
- package/src/substrate/lifo/commands/system/fastfetch.ts +428 -0
- package/src/substrate/lifo/commands/system/free.ts +38 -0
- package/src/substrate/lifo/commands/system/help.ts +53 -0
- package/src/substrate/lifo/commands/system/hostname.ts +9 -0
- package/src/substrate/lifo/commands/system/kill.ts +89 -0
- package/src/substrate/lifo/commands/system/lifo.ts +525 -0
- package/src/substrate/lifo/commands/system/logout.ts +17 -0
- package/src/substrate/lifo/commands/system/man.ts +167 -0
- package/src/substrate/lifo/commands/system/node.ts +1305 -0
- package/src/substrate/lifo/commands/system/npm.ts +1078 -0
- package/src/substrate/lifo/commands/system/pkg.ts +111 -0
- package/src/substrate/lifo/commands/system/ps.ts +46 -0
- package/src/substrate/lifo/commands/system/registry-schemas.ts +33 -0
- package/src/substrate/lifo/commands/system/sha256sum.ts +89 -0
- package/src/substrate/lifo/commands/system/sl.ts +249 -0
- package/src/substrate/lifo/commands/system/sleep.ts +36 -0
- package/src/substrate/lifo/commands/system/stty.ts +69 -0
- package/src/substrate/lifo/commands/system/systemctl.ts +184 -0
- package/src/substrate/lifo/commands/system/top.ts +53 -0
- package/src/substrate/lifo/commands/system/uname.ts +59 -0
- package/src/substrate/lifo/commands/system/uptime.ts +19 -0
- package/src/substrate/lifo/commands/system/watch.ts +67 -0
- package/src/substrate/lifo/commands/system/which.ts +51 -0
- package/src/substrate/lifo/commands/system/whoami.ts +9 -0
- package/src/substrate/lifo/commands/text/awk.ts +210 -0
- package/src/substrate/lifo/commands/text/base64.ts +102 -0
- package/src/substrate/lifo/commands/text/cut.ts +92 -0
- package/src/substrate/lifo/commands/text/diff.ts +314 -0
- package/src/substrate/lifo/commands/text/grep.ts +170 -0
- package/src/substrate/lifo/commands/text/head.ts +95 -0
- package/src/substrate/lifo/commands/text/less.ts +335 -0
- package/src/substrate/lifo/commands/text/nano.ts +513 -0
- package/src/substrate/lifo/commands/text/nl.ts +74 -0
- package/src/substrate/lifo/commands/text/rev.ts +46 -0
- package/src/substrate/lifo/commands/text/sed.ts +297 -0
- package/src/substrate/lifo/commands/text/seq.ts +76 -0
- package/src/substrate/lifo/commands/text/sort.ts +88 -0
- package/src/substrate/lifo/commands/text/strings.ts +73 -0
- package/src/substrate/lifo/commands/text/tac.ts +37 -0
- package/src/substrate/lifo/commands/text/tail.ts +63 -0
- package/src/substrate/lifo/commands/text/tr.ts +192 -0
- package/src/substrate/lifo/commands/text/uniq.ts +77 -0
- package/src/substrate/lifo/commands/text/wc.ts +104 -0
- package/src/substrate/lifo/commands/types.ts +58 -0
- package/src/substrate/lifo/index.ts +173 -0
- package/src/substrate/lifo/kernel/ServiceManager.ts +371 -0
- package/src/substrate/lifo/kernel/index.ts +209 -0
- package/src/substrate/lifo/kernel/network/Bridge.ts +336 -0
- package/src/substrate/lifo/kernel/network/DNSResolver.ts +158 -0
- package/src/substrate/lifo/kernel/network/NetworkInterface.ts +235 -0
- package/src/substrate/lifo/kernel/network/NetworkNamespace.ts +182 -0
- package/src/substrate/lifo/kernel/network/NetworkStack.ts +590 -0
- package/src/substrate/lifo/kernel/network/PortBridge.ts +182 -0
- package/src/substrate/lifo/kernel/network/RoutingTable.ts +209 -0
- package/src/substrate/lifo/kernel/network/Socket.ts +245 -0
- package/src/substrate/lifo/kernel/network/index.ts +29 -0
- package/src/substrate/lifo/kernel/network/tunnel/BaseTunnel.ts +124 -0
- package/src/substrate/lifo/kernel/network/tunnel/VETHPair.ts +227 -0
- package/src/substrate/lifo/kernel/network/tunnel/WebSocketTunnel.ts +387 -0
- package/src/substrate/lifo/kernel/network/types.ts +178 -0
- package/src/substrate/lifo/kernel/persistence/PersistenceManager.ts +50 -0
- package/src/substrate/lifo/kernel/persistence/backends.ts +36 -0
- package/src/substrate/lifo/kernel/persistence/index.ts +5 -0
- package/src/substrate/lifo/kernel/persistence/serializer.ts +127 -0
- package/src/substrate/lifo/kernel/samples.ts +966 -0
- package/src/substrate/lifo/kernel/storage/BlobStore.ts +114 -0
- package/src/substrate/lifo/kernel/storage/ContentStore.ts +150 -0
- package/src/substrate/lifo/kernel/storage/index.ts +5 -0
- package/src/substrate/lifo/kernel/unit-parser.ts +95 -0
- package/src/substrate/lifo/kernel/vfs/VFS.ts +768 -0
- package/src/substrate/lifo/kernel/vfs/index.ts +9 -0
- package/src/substrate/lifo/kernel/vfs/providers/DevProvider.ts +172 -0
- package/src/substrate/lifo/kernel/vfs/providers/NativeFsProvider.ts +198 -0
- package/src/substrate/lifo/kernel/vfs/providers/ProcProvider.ts +144 -0
- package/src/substrate/lifo/kernel/vfs/types.ts +145 -0
- package/src/substrate/lifo/node-compat/buffer.ts +357 -0
- package/src/substrate/lifo/node-compat/child_process.ts +52 -0
- package/src/substrate/lifo/node-compat/console.ts +70 -0
- package/src/substrate/lifo/node-compat/crypto.ts +186 -0
- package/src/substrate/lifo/node-compat/dns.ts +214 -0
- package/src/substrate/lifo/node-compat/esbuild.ts +85 -0
- package/src/substrate/lifo/node-compat/events.ts +94 -0
- package/src/substrate/lifo/node-compat/fs.ts +762 -0
- package/src/substrate/lifo/node-compat/http.ts +561 -0
- package/src/substrate/lifo/node-compat/index.ts +300 -0
- package/src/substrate/lifo/node-compat/module.ts +148 -0
- package/src/substrate/lifo/node-compat/os.ts +44 -0
- package/src/substrate/lifo/node-compat/path.ts +74 -0
- package/src/substrate/lifo/node-compat/process.ts +178 -0
- package/src/substrate/lifo/node-compat/readline.ts +244 -0
- package/src/substrate/lifo/node-compat/rimraf.ts +80 -0
- package/src/substrate/lifo/node-compat/stream.ts +98 -0
- package/src/substrate/lifo/node-compat/string_decoder.ts +26 -0
- package/src/substrate/lifo/node-compat/timers.ts +21 -0
- package/src/substrate/lifo/node-compat/tty.ts +83 -0
- package/src/substrate/lifo/node-compat/url.ts +76 -0
- package/src/substrate/lifo/node-compat/util.ts +133 -0
- package/src/substrate/lifo/node-compat/zlib.ts +94 -0
- package/src/substrate/lifo/pkg/PackageManager.ts +105 -0
- package/src/substrate/lifo/pkg/lifo-dev.ts +130 -0
- package/src/substrate/lifo/pkg/lifo-runtime.ts +473 -0
- package/src/substrate/lifo/pkg/loader.ts +33 -0
- package/src/substrate/lifo/platform-globals.d.ts +41 -0
- package/src/substrate/lifo/sandbox/HeadlessTerminal.ts +38 -0
- package/src/substrate/lifo/sandbox/Sandbox.ts +279 -0
- package/src/substrate/lifo/sandbox/SandboxCommands.ts +74 -0
- package/src/substrate/lifo/sandbox/SandboxFs.ts +160 -0
- package/src/substrate/lifo/sandbox/index.ts +8 -0
- package/src/substrate/lifo/sandbox/types.ts +117 -0
- package/src/substrate/lifo/shell/ProcessRegistry.ts +379 -0
- package/src/substrate/lifo/shell/Shell.ts +2048 -0
- package/src/substrate/lifo/shell/completer.ts +212 -0
- package/src/substrate/lifo/shell/default-shell.ts +63 -0
- package/src/substrate/lifo/shell/expander.ts +1349 -0
- package/src/substrate/lifo/shell/history.ts +138 -0
- package/src/substrate/lifo/shell/interpreter.ts +1754 -0
- package/src/substrate/lifo/shell/jobs.ts +64 -0
- package/src/substrate/lifo/shell/lexer.ts +698 -0
- package/src/substrate/lifo/shell/parser.ts +711 -0
- package/src/substrate/lifo/shell/pipe.ts +92 -0
- package/src/substrate/lifo/shell/signals.ts +70 -0
- package/src/substrate/lifo/shell/terminal-stdin.ts +109 -0
- package/src/substrate/lifo/shell/test-builtin.ts +316 -0
- package/src/substrate/lifo/shell/types.ts +166 -0
- package/src/substrate/lifo/terminal/ITerminal.ts +9 -0
- package/src/substrate/lifo/utils/archive.ts +389 -0
- package/src/substrate/lifo/utils/args.ts +89 -0
- package/src/substrate/lifo/utils/colors.ts +29 -0
- package/src/substrate/lifo/utils/encoding.ts +21 -0
- package/src/substrate/lifo/utils/glob.ts +172 -0
- package/src/substrate/lifo/utils/mime.ts +114 -0
- package/src/substrate/lifo/utils/path.ts +83 -0
- package/src/types/assets.d.ts +4 -0
- package/src/vfs/events.ts +134 -0
- package/src/vfs/path.ts +71 -0
- package/src/vfs/seed-project.ts +1094 -0
- package/src/vfs/sqlite-vfs.ts +5035 -0
- package/src/vfs/symlink-registry.ts +172 -0
- package/src/workspace/nimbus-workspace.ts +213 -0
|
@@ -0,0 +1,1519 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bash/preamble.ts — the facet-side bash scheduler, as real TypeScript.
|
|
3
|
+
*
|
|
4
|
+
* This file IS the runner. scripts/bundle-facet-workers.mjs esbuilds it into
|
|
5
|
+
* bash-runner.generated.ts as a self-contained IIFE, and bash-runner.ts re-exports
|
|
6
|
+
* that string as BASH_RUNNER_PREAMBLE — the loader-pool preamble every bash facet
|
|
7
|
+
* evaluates. The same pattern wasi/preamble.ts and virtual-socket-kernel.ts use.
|
|
8
|
+
*
|
|
9
|
+
* Until this move the scheduler lived inside a String.raw template literal, so no
|
|
10
|
+
* type checker had ever read it.
|
|
11
|
+
*
|
|
12
|
+
* Constraints the build imposes on this file:
|
|
13
|
+
* - It is evaluated as a FUNCTION body (new Function(...) in tests, a loader-pool
|
|
14
|
+
* preamble in production), so the emitted string may contain no import, no
|
|
15
|
+
* export and no top-level await. Bundling to an IIFE is what guarantees that.
|
|
16
|
+
* - Nothing may be imported for its value. Type-only imports are erased and are
|
|
17
|
+
* the only kind permitted.
|
|
18
|
+
* - globalThis.__bashBoot and globalThis.__bashFeed are the entry points; every
|
|
19
|
+
* other declaration is reachable only from them, which is why the bundle runs
|
|
20
|
+
* with tree shaking off.
|
|
21
|
+
*/
|
|
22
|
+
import type {
|
|
23
|
+
BashBlockTarget,
|
|
24
|
+
BashBootArgs,
|
|
25
|
+
BashByteQueue,
|
|
26
|
+
BashErrno,
|
|
27
|
+
BashExitStatus,
|
|
28
|
+
BashFdEntry,
|
|
29
|
+
BashFdReadiness,
|
|
30
|
+
BashFeedArgs,
|
|
31
|
+
BashFileEntry,
|
|
32
|
+
BashFsSeed,
|
|
33
|
+
BashFsState,
|
|
34
|
+
BashInstance,
|
|
35
|
+
BashIo,
|
|
36
|
+
BashIovs,
|
|
37
|
+
BashPathResolution,
|
|
38
|
+
BashPipe,
|
|
39
|
+
BashPollSub,
|
|
40
|
+
BashProc,
|
|
41
|
+
BashProcImports,
|
|
42
|
+
BashReadWaiter,
|
|
43
|
+
BashSession,
|
|
44
|
+
BashSlice,
|
|
45
|
+
BashUnsupportedImports,
|
|
46
|
+
BashWasiFsImports,
|
|
47
|
+
BashWasiImports,
|
|
48
|
+
} from './types.js';
|
|
49
|
+
import type { WasiFsDiff } from '../vfs-snapshot.js';
|
|
50
|
+
|
|
51
|
+
const PAGE = 65536, te = new TextEncoder(), td = new TextDecoder();
|
|
52
|
+
// Sizing is measurement-grounded (local pre-gate stats): bash's deepest
|
|
53
|
+
// observed asyncify capture is ~25 KiB (full control suite), so 8 MiB
|
|
54
|
+
// main / 256 KiB slots carry 300×/10× margin while keeping a full
|
|
55
|
+
// instance ~17 MiB — several forks fit the ~180-200 MiB facet ceiling.
|
|
56
|
+
const MAIN_SIZE = 8 << 20, SLOT_SIZE = 256 << 10, NSLOT = 32;
|
|
57
|
+
const E = { ACCES: 2, BADF: 8, EXIST: 20, INVAL: 28, ISDIR: 31, LOOP: 32, NOENT: 44, NOSYS: 52, NOTDIR: 54, NOTEMPTY: 55, PERM: 63, SPIPE: 70 } satisfies Record<string, BashErrno>;
|
|
58
|
+
// WASI clock ids. MONOTONIC and the two CPUTIME clocks are answered from a
|
|
59
|
+
// monotonic source; an id outside this set is EINVAL, never a silent realtime
|
|
60
|
+
// reading — a guest that asks for monotonic and receives wall time computes
|
|
61
|
+
// negative durations the first time the wall clock steps backwards.
|
|
62
|
+
const CLOCK_REALTIME = 0, CLOCK_MONOTONIC = 1, CLOCK_PROCESS_CPUTIME = 2, CLOCK_THREAD_CPUTIME = 3;
|
|
63
|
+
function realtimeNs() { return BigInt(Date.now()) * 1000000n; }
|
|
64
|
+
function monotonicNs() {
|
|
65
|
+
const ms = (typeof performance !== 'undefined' && performance.now) ? performance.now() : Date.now();
|
|
66
|
+
return BigInt(Math.floor(ms * 1000)) * 1000n;
|
|
67
|
+
}
|
|
68
|
+
// null for an unknown id, so callers answer EINVAL rather than inventing a time.
|
|
69
|
+
function clockNs(id: number | undefined): bigint | null {
|
|
70
|
+
if (id === CLOCK_REALTIME) return realtimeNs();
|
|
71
|
+
if (id === CLOCK_MONOTONIC || id === CLOCK_PROCESS_CPUTIME || id === CLOCK_THREAD_CPUTIME) return monotonicNs();
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
class Exit { declare code: number; constructor(c: number) { this.code = c; } }
|
|
75
|
+
|
|
76
|
+
let S: BashSession | null = null; // active session state; persists across submits on the warm isolate
|
|
77
|
+
|
|
78
|
+
function norm(p: string): string {
|
|
79
|
+
const parts = [];
|
|
80
|
+
for (const seg of String(p).split('/')) {
|
|
81
|
+
if (!seg || seg === '.') continue;
|
|
82
|
+
if (seg === '..') { parts.pop(); continue; }
|
|
83
|
+
parts.push(seg);
|
|
84
|
+
}
|
|
85
|
+
return parts.join('/');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function b64ToBytes(b64: string): Uint8Array {
|
|
89
|
+
const bin = atob(b64);
|
|
90
|
+
const out = new Uint8Array(bin.length);
|
|
91
|
+
for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
function bytesToB64(bytes: Uint8Array): string {
|
|
95
|
+
let bin = '';
|
|
96
|
+
const CH = 0x8000;
|
|
97
|
+
for (let i = 0; i < bytes.length; i += CH) bin += String.fromCharCode.apply(null, bytes.subarray(i, i + CH) as unknown as number[]);
|
|
98
|
+
return btoa(bin);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function newSession(args: BashBootArgs): BashSession {
|
|
102
|
+
const wasmTable = globalThis.__NIMBUS_WASM || {};
|
|
103
|
+
const mod = wasmTable['bash.async.wasm'];
|
|
104
|
+
if (!mod) throw new Error('bash.async.wasm missing from __NIMBUS_WASM');
|
|
105
|
+
const coreutils = new Map();
|
|
106
|
+
for (const key of Object.keys(wasmTable)) {
|
|
107
|
+
if (key.startsWith('cu_') && key.endsWith('.wasm')) coreutils.set(key.slice(3, -5), wasmTable[key]);
|
|
108
|
+
}
|
|
109
|
+
// Applet aliasing: ONE staged busybox module answers to every applet
|
|
110
|
+
// name (busybox dispatches on argv[0]), so bash's PATH lookup finds
|
|
111
|
+
// ls/cat/grep/... as executables in /bin.
|
|
112
|
+
const busybox = wasmTable['cu_busybox.wasm'];
|
|
113
|
+
if (busybox) for (const name of args.busyboxApplets || []) coreutils.set(name, busybox);
|
|
114
|
+
|
|
115
|
+
const fs: BashFsState = {
|
|
116
|
+
files: new Map(), dirs: new Set(), modes: new Map(),
|
|
117
|
+
written: new Set(), deleted: new Set(),
|
|
118
|
+
dirsCreated: new Set(), dirsDeleted: new Set(), modesChanged: new Map(),
|
|
119
|
+
// vfsPath → mtime ns. The snapshot carries no times, so an inode the
|
|
120
|
+
// session did not touch reports sessionNs: older than anything written
|
|
121
|
+
// here, which is what makes 'is the target newer than its source' — the
|
|
122
|
+
// question make(1) and every incremental build asks — answerable.
|
|
123
|
+
times: new Map(), sessionNs: realtimeNs(),
|
|
124
|
+
// vfsPath → target string, stored verbatim per POSIX: a symlink is a dumb
|
|
125
|
+
// string resolved at lookup time, not a pointer to an inode.
|
|
126
|
+
symlinks: new Map(), symlinksCreated: new Map(),
|
|
127
|
+
};
|
|
128
|
+
const snap: BashFsSeed = args.fsSnapshot || { files: {}, dirs: [], modes: {} };
|
|
129
|
+
for (const [path, b64] of Object.entries(snap.files || {})) fs.files.set(norm(path), { bytes: b64ToBytes(b64) });
|
|
130
|
+
for (const d of snap.dirs || []) fs.dirs.add(norm(d));
|
|
131
|
+
// The snapshot's modes are the S2a projection: effective rwx per path
|
|
132
|
+
// for the invoking credential. Enforced by the WASI layer below.
|
|
133
|
+
for (const [path, m] of Object.entries(snap.modes || {})) fs.modes.set(norm(path), Number(m) & 7);
|
|
134
|
+
for (const seed of ['etc', 'dev', 'bin', 'usr', 'usr/bin']) {
|
|
135
|
+
fs.dirs.add(seed);
|
|
136
|
+
if (!fs.modes.has(seed)) fs.modes.set(seed, 5);
|
|
137
|
+
}
|
|
138
|
+
// Startup rc: chdir to the session cwd (wasi-libc cwd starts at '/').
|
|
139
|
+
// BASH_ENV points here for non-interactive shells; interactive shells
|
|
140
|
+
// read ~/.bashrc, which is seeded to source this file when absent.
|
|
141
|
+
if (!fs.files.has('etc/nimbus.bashrc')) {
|
|
142
|
+
fs.files.set('etc/nimbus.bashrc', { bytes: te.encode('command cd "$' + '{NIMBUS_PWD:-/}" 2>/dev/null\n') });
|
|
143
|
+
fs.modes.set('etc/nimbus.bashrc', 4);
|
|
144
|
+
}
|
|
145
|
+
const home = norm((args.environ.find((e) => e.startsWith('HOME=')) || 'HOME=/home/user').slice(5));
|
|
146
|
+
if (args.stdinTty && home && !fs.files.has(home + '/.bashrc')) {
|
|
147
|
+
fs.files.set(home + '/.bashrc', { bytes: te.encode('. /etc/nimbus.bashrc\n') });
|
|
148
|
+
if (!fs.modes.has(home + '/.bashrc')) fs.modes.set(home + '/.bashrc', 6);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const cwd = norm((args.environ.find((e) => e.startsWith('NIMBUS_PWD=')) || 'NIMBUS_PWD=/').slice(11));
|
|
152
|
+
return {
|
|
153
|
+
mod, coreutils, fs, cwd,
|
|
154
|
+
argv: args.argv, environ: args.environ,
|
|
155
|
+
stdinTty: !!args.stdinTty,
|
|
156
|
+
stdin: { chunks: args.stdinData ? [te.encode(args.stdinData)] : [], queued: 0, closed: !!args.stdinClosed, waiters: [] },
|
|
157
|
+
procs: new Map(), pipes: new Map(), runnable: [], exitStatus: new Map(), waiters: [],
|
|
158
|
+
pidNext: 100, pipeNext: 1, rootPid: 0, rootExit: null, steps: 0,
|
|
159
|
+
out: '', err: '',
|
|
160
|
+
missingWasi: new Set(),
|
|
161
|
+
stats: { instances: 0, memPeak: 0, mainHi: 0, slotHi: 0 },
|
|
162
|
+
error: null,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function initStdinQueued(s: BashSession): void { s.stdin.queued = s.stdin.chunks.reduce((a, c) => a + c.length, 0); }
|
|
166
|
+
|
|
167
|
+
function newPipe(s: BashSession): number { const id = s.pipeNext++; s.pipes.set(id, { chunks: [], queued: 0, readers: 1, writers: 1, readW: [] }); return id; }
|
|
168
|
+
// fd 3 is the wasi-libc '/' preopen. It lives in the fd table as a
|
|
169
|
+
// real 'preopen' entry: wasi-libc's path ops fd_fdstat_get the dirfd
|
|
170
|
+
// to compute inherited rights, so it must answer (not EBADF), and
|
|
171
|
+
// lowestFd must never re-issue it for a regular file.
|
|
172
|
+
function lowestFd(proc: BashProc): number { let fd = 0; while (proc.fds.has(fd) || fd === 3) fd++; return fd; }
|
|
173
|
+
function bumpPipe(s: BashSession, e: { pipeId: number; end: 'r' | 'w' }, d: number): void { const pp = s.pipes.get(e.pipeId) as BashPipe; if (e.end === 'r') pp.readers += d; else pp.writers += d; }
|
|
174
|
+
function closeFd(s: BashSession, proc: BashProc, fd: number): void {
|
|
175
|
+
const e = proc.fds.get(fd);
|
|
176
|
+
if (!e) return;
|
|
177
|
+
proc.fds.delete(fd);
|
|
178
|
+
if (e.kind === 'pipe') { bumpPipe(s, e, -1); wakePipe(s, s.pipes.get(e.pipeId) as BashPipe); }
|
|
179
|
+
}
|
|
180
|
+
function takeUpTo(src: BashByteQueue, max: number): Uint8Array {
|
|
181
|
+
let need = max; const parts = [];
|
|
182
|
+
while (need > 0 && src.chunks.length) {
|
|
183
|
+
const ch = src.chunks[0];
|
|
184
|
+
if (ch.length <= need) { parts.push(ch); need -= ch.length; src.chunks.shift(); }
|
|
185
|
+
else { parts.push(ch.subarray(0, need)); src.chunks[0] = ch.subarray(need); need = 0; }
|
|
186
|
+
}
|
|
187
|
+
const total = max - need; src.queued -= total;
|
|
188
|
+
const o = new Uint8Array(total); let x = 0;
|
|
189
|
+
for (const p of parts) { o.set(p, x); x += p.length; }
|
|
190
|
+
return o;
|
|
191
|
+
}
|
|
192
|
+
// POSIX readv is ONE read of up to the summed length, scattered across the
|
|
193
|
+
// buffers in order — not a read of the first buffer. Zero-length entries are
|
|
194
|
+
// dropped so they never terminate the scatter early.
|
|
195
|
+
// A poll park has no destination buffer — it resumes into a fresh poll call.
|
|
196
|
+
const EMPTY_IOV: BashIovs = { list: [], total: 0 };
|
|
197
|
+
function readIovs(dv: DataView, iovs: number, n: number): BashIovs {
|
|
198
|
+
const list = [];
|
|
199
|
+
let total = 0;
|
|
200
|
+
for (let i = 0; i < n; i++) {
|
|
201
|
+
const ptr = dv.getUint32(iovs + i * 8, true), len = dv.getUint32(iovs + i * 8 + 4, true);
|
|
202
|
+
if (len > 0) { list.push({ ptr, len }); total += len; }
|
|
203
|
+
}
|
|
204
|
+
return { list, total };
|
|
205
|
+
}
|
|
206
|
+
function scatter(u8: Uint8Array, iov: BashIovs, bytes: Uint8Array): number {
|
|
207
|
+
let off = 0;
|
|
208
|
+
for (const b of iov.list) {
|
|
209
|
+
if (off >= bytes.length) break;
|
|
210
|
+
const n = Math.min(b.len, bytes.length - off);
|
|
211
|
+
u8.set(bytes.subarray(off, off + n), b.ptr);
|
|
212
|
+
off += n;
|
|
213
|
+
}
|
|
214
|
+
return off;
|
|
215
|
+
}
|
|
216
|
+
function wakePipe(s: BashSession, pp: BashPipe): void {
|
|
217
|
+
while (pp.readW.length && (pp.queued > 0 || pp.writers === 0)) {
|
|
218
|
+
const w = pp.readW.shift() as BashReadWaiter; const proc = w.proc; const req = proc.ctx.pipeReq;
|
|
219
|
+
const bytes = pp.queued > 0 ? takeUpTo(pp, req.iov.total) : new Uint8Array(0);
|
|
220
|
+
proc.pendingRead = { iov: req.iov, bytes, nreadPtr: req.nreadPtr, pollUserdata: req.pollUserdata };
|
|
221
|
+
resumeProc(proc);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function wakeStdin(s: BashSession): void {
|
|
225
|
+
const st = s.stdin;
|
|
226
|
+
while (st.waiters.length && (st.queued > 0 || st.closed)) {
|
|
227
|
+
const w = st.waiters.shift() as BashReadWaiter; const proc = w.proc; const req = proc.ctx.pipeReq;
|
|
228
|
+
const bytes = st.queued > 0 ? takeUpTo(st, req.iov.total) : new Uint8Array(0);
|
|
229
|
+
proc.pendingRead = { iov: req.iov, bytes, nreadPtr: req.nreadPtr, pollUserdata: req.pollUserdata };
|
|
230
|
+
resumeProc(proc);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ── file layer ────────────────────────────────────────────────────────
|
|
235
|
+
function fileLookup(s: BashSession, path: string): BashFileEntry | null { return s.fs.files.get(path) || null; }
|
|
236
|
+
function isDir(s: BashSession, path: string): boolean { return path === '' || s.fs.dirs.has(path); }
|
|
237
|
+
function isCoreutil(s: BashSession, path: string): boolean {
|
|
238
|
+
if (!path.startsWith('bin/') && !path.startsWith('usr/bin/')) return false;
|
|
239
|
+
return s.coreutils.has(path.split('/').pop() as string);
|
|
240
|
+
}
|
|
241
|
+
// A modes-only inode is a file the snapshot could not read (S2a): it
|
|
242
|
+
// exists, its bytes are absent, and access answers with the real bits.
|
|
243
|
+
function fileExists(s: BashSession, path: string): boolean {
|
|
244
|
+
return s.fs.files.has(path) || (s.fs.modes.has(path) && !s.fs.dirs.has(path));
|
|
245
|
+
}
|
|
246
|
+
function inodeExists(s: BashSession, path: string): boolean {
|
|
247
|
+
return s.fs.files.has(path) || s.fs.dirs.has(path) || s.fs.modes.has(path);
|
|
248
|
+
}
|
|
249
|
+
// S2a effective-mode policy (mirrors the JSPI shim): a mapped mode wins;
|
|
250
|
+
// an inode that exists without one is denied; an absent path is free to
|
|
251
|
+
// create (the durable flush re-checks as the credential).
|
|
252
|
+
function effMode(s: BashSession, path: string): number {
|
|
253
|
+
if (path === '') return 7;
|
|
254
|
+
const m = s.fs.modes.get(path);
|
|
255
|
+
if (m !== undefined) return m;
|
|
256
|
+
if (isCoreutil(s, path)) return 5;
|
|
257
|
+
return inodeExists(s, path) ? 0 : 7;
|
|
258
|
+
}
|
|
259
|
+
function parentOf(path: string): string { const i = path.lastIndexOf('/'); return i < 0 ? '' : path.slice(0, i); }
|
|
260
|
+
// Rewrite the leftmost symlink on the path and repeat, so both intermediate
|
|
261
|
+
// components and a terminal link resolve. A target starting with '/' is
|
|
262
|
+
// root-anchored; anything else is relative to the link's own directory.
|
|
263
|
+
// SYMLOOP_MAX bounds it — a cycle answers ELOOP rather than hanging.
|
|
264
|
+
function followPath(s: BashSession, path: string): BashPathResolution {
|
|
265
|
+
let cur = path;
|
|
266
|
+
for (let hops = 0; hops < 32; hops++) {
|
|
267
|
+
if (!s.fs.symlinks.size) return { path: cur, errno: 0 };
|
|
268
|
+
const parts = cur.split('/');
|
|
269
|
+
let prefix = '';
|
|
270
|
+
let rewrote = false;
|
|
271
|
+
for (let i = 0; i < parts.length; i++) {
|
|
272
|
+
prefix = prefix ? prefix + '/' + parts[i] : parts[i];
|
|
273
|
+
const target = s.fs.symlinks.get(prefix);
|
|
274
|
+
if (target === undefined) continue;
|
|
275
|
+
const base = target.startsWith('/') ? norm(target) : norm(parentOf(prefix) + '/' + target);
|
|
276
|
+
const rest = parts.slice(i + 1).join('/');
|
|
277
|
+
cur = rest ? norm(base + '/' + rest) : base;
|
|
278
|
+
rewrote = true;
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
if (!rewrote) return { path: cur, errno: 0 };
|
|
282
|
+
}
|
|
283
|
+
return { path: cur, errno: E.LOOP };
|
|
284
|
+
}
|
|
285
|
+
// Resolve only the directories leading to the final component. This is what
|
|
286
|
+
// the mutating ops want: rm dir/link must delete the link, not its target,
|
|
287
|
+
// while dir itself may still be reached through a link.
|
|
288
|
+
function followParents(s: BashSession, path: string): BashPathResolution {
|
|
289
|
+
const parent = parentOf(path);
|
|
290
|
+
if (!parent) return { path, errno: 0 };
|
|
291
|
+
const r = followPath(s, parent);
|
|
292
|
+
if (r.errno) return r;
|
|
293
|
+
const name = path.slice(parent.length + 1);
|
|
294
|
+
return { path: r.path ? r.path + '/' + name : name, errno: 0 };
|
|
295
|
+
}
|
|
296
|
+
// Every ancestor dir on the way to the target needs the search (x) bit.
|
|
297
|
+
function checkTraversal(s: BashSession, path: string): BashErrno {
|
|
298
|
+
const parts = path.split('/');
|
|
299
|
+
let anc = '';
|
|
300
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
301
|
+
anc = anc ? anc + '/' + parts[i] : parts[i];
|
|
302
|
+
if (!isDir(s, anc)) return inodeExists(s, anc) ? E.NOTDIR : E.NOENT;
|
|
303
|
+
if ((effMode(s, anc) & 1) === 0) return E.ACCES;
|
|
304
|
+
}
|
|
305
|
+
return 0;
|
|
306
|
+
}
|
|
307
|
+
function checkParentWritable(s: BashSession, path: string): BashErrno {
|
|
308
|
+
const parent = parentOf(path);
|
|
309
|
+
if (parent && (effMode(s, parent) & 3) !== 3) return E.ACCES;
|
|
310
|
+
return 0;
|
|
311
|
+
}
|
|
312
|
+
function recordDirAdded(s: BashSession, path: string): void {
|
|
313
|
+
s.fs.dirs.add(path); s.fs.dirsDeleted.delete(path); s.fs.dirsCreated.add(path);
|
|
314
|
+
if (!s.fs.modes.has(path)) s.fs.modes.set(path, 7);
|
|
315
|
+
touchPath(s, path);
|
|
316
|
+
}
|
|
317
|
+
function recordDirRemoved(s: BashSession, path: string): void {
|
|
318
|
+
s.fs.dirs.delete(path); s.fs.modes.delete(path); s.fs.times.delete(path);
|
|
319
|
+
if (s.fs.dirsCreated.has(path)) s.fs.dirsCreated.delete(path);
|
|
320
|
+
else s.fs.dirsDeleted.add(path);
|
|
321
|
+
}
|
|
322
|
+
function recordFileRemoved(s: BashSession, path: string): void {
|
|
323
|
+
s.fs.files.delete(path); s.fs.written.delete(path); s.fs.modes.delete(path);
|
|
324
|
+
s.fs.times.delete(path); s.fs.symlinks.delete(path); s.fs.symlinksCreated.delete(path);
|
|
325
|
+
s.fs.deleted.add(path);
|
|
326
|
+
}
|
|
327
|
+
function markWritten(s: BashSession, path: string): void {
|
|
328
|
+
s.fs.written.add(path); s.fs.deleted.delete(path);
|
|
329
|
+
if (!s.fs.modes.has(path)) s.fs.modes.set(path, 6);
|
|
330
|
+
touchPath(s, path);
|
|
331
|
+
const parts = path.split('/');
|
|
332
|
+
for (let i = 1; i < parts.length; i++) {
|
|
333
|
+
const dir = parts.slice(0, i).join('/');
|
|
334
|
+
if (!s.fs.dirs.has(dir)) recordDirAdded(s, dir);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
function fileWrite(s: BashSession, entry: BashFileEntry, path: string, pos: number, bytes: Uint8Array): void {
|
|
338
|
+
const need = pos + bytes.length;
|
|
339
|
+
if (need > entry.bytes.length) {
|
|
340
|
+
const grown = new Uint8Array(need);
|
|
341
|
+
grown.set(entry.bytes); entry.bytes = grown;
|
|
342
|
+
}
|
|
343
|
+
entry.bytes.set(bytes, pos);
|
|
344
|
+
markWritten(s, path);
|
|
345
|
+
}
|
|
346
|
+
function mtimeNs(s: BashSession, path: string): bigint {
|
|
347
|
+
const t = s.fs.times.get(path);
|
|
348
|
+
return t === undefined ? s.fs.sessionNs : t;
|
|
349
|
+
}
|
|
350
|
+
function touchPath(s: BashSession, path: string): void { s.fs.times.set(path, realtimeNs()); }
|
|
351
|
+
function statBuf(dv: DataView, u8: Uint8Array, buf: number, filetype: number, size: number, timeNs: bigint): void {
|
|
352
|
+
u8.fill(0, buf, buf + 64);
|
|
353
|
+
dv.setUint8(buf + 16, filetype);
|
|
354
|
+
dv.setBigUint64(buf + 24, 1n, true);
|
|
355
|
+
dv.setBigUint64(buf + 32, BigInt(size), true);
|
|
356
|
+
dv.setBigUint64(buf + 40, timeNs, true);
|
|
357
|
+
dv.setBigUint64(buf + 48, timeNs, true);
|
|
358
|
+
dv.setBigUint64(buf + 56, timeNs, true);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Shared WASI surface over the process fd table + file layer. The io
|
|
362
|
+
// argument abstracts the blocking discipline: bash instances
|
|
363
|
+
// asyncify-park, exec'd plain-WASI tools synchronously pump the scheduler.
|
|
364
|
+
//
|
|
365
|
+
// pathBase (exec'd coreutils only) emulates the child's inherited cwd. An
|
|
366
|
+
// exec'd tool's wasi-libc has cwd '/', so it absolutizes BOTH its own
|
|
367
|
+
// relative args and absolute args against '/' — they reach path_open
|
|
368
|
+
// IDENTICALLY un-prefixed (e.g. 'ls tmp' and 'ls /tmp' both arrive 'tmp';
|
|
369
|
+
// 'ls' and 'ls /' both arrive ''). The lost absolute-vs-relative bit is
|
|
370
|
+
// recovered from absRoots: the normalized set of absolute argv paths bash
|
|
371
|
+
// passed the tool. A path that IS (or descends from) one of those is
|
|
372
|
+
// absolute-origin — the arriving string is already the correct
|
|
373
|
+
// root-relative target, so it is NEVER re-anchored. Everything else is
|
|
374
|
+
// relative-origin (a relative arg, or a child name synthesized while
|
|
375
|
+
// listing a directory): re-anchor against the inherited cwd, preferring
|
|
376
|
+
// the cwd-joined path, then the bare one, then whichever parent dir
|
|
377
|
+
// exists (touch newfile in the session cwd vs a '/'-listing's children).
|
|
378
|
+
function isUnderAbsRoot(p: string, absRoots: Set<string> | undefined): boolean {
|
|
379
|
+
if (!absRoots) return false;
|
|
380
|
+
for (const a of absRoots) {
|
|
381
|
+
if (p === a) return true;
|
|
382
|
+
if (a !== '' && p.startsWith(a + '/')) return true;
|
|
383
|
+
}
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
function makeResolve(s: BashSession, pathBase?: string, absRoots?: Set<string>): (raw: string) => string {
|
|
387
|
+
return (raw) => {
|
|
388
|
+
const p = norm(raw);
|
|
389
|
+
if (!pathBase) return p;
|
|
390
|
+
if (isUnderAbsRoot(p, absRoots)) return p;
|
|
391
|
+
const joined = norm(pathBase + '/' + p);
|
|
392
|
+
if (inodeExists(s, joined)) return joined;
|
|
393
|
+
if (inodeExists(s, p)) return p;
|
|
394
|
+
if (isDir(s, parentOf(joined))) return joined;
|
|
395
|
+
if (isDir(s, parentOf(p))) return p;
|
|
396
|
+
return joined;
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
// The rest of the preview1 surface. These are NOT stubs that claim success:
|
|
400
|
+
// each one either is genuinely a no-op on an in-memory filesystem, or answers
|
|
401
|
+
// ENOSYS. The old blanket fallback returned ESUCCESS for all of them without
|
|
402
|
+
// writing their out-params, so a guest read uninitialised memory and believed
|
|
403
|
+
// the call had worked — silent wrong data, the worst failure available.
|
|
404
|
+
function makeUnsupported(s: BashSession): BashUnsupportedImports {
|
|
405
|
+
const nosys = (name: string) => (): BashErrno => { s.missingWasi.add(name); return E.NOSYS; };
|
|
406
|
+
return {
|
|
407
|
+
// Every write is already durable in the session's own heap, so there is
|
|
408
|
+
// nothing to flush and success is the honest answer.
|
|
409
|
+
fd_sync: () => 0,
|
|
410
|
+
fd_datasync: () => 0,
|
|
411
|
+
fd_advise: () => 0,
|
|
412
|
+
// One process runs at a time under this scheduler; a yield has nothing to
|
|
413
|
+
// yield to that the caller has not already reached.
|
|
414
|
+
sched_yield: () => 0,
|
|
415
|
+
fd_pread: nosys('fd_pread'),
|
|
416
|
+
fd_pwrite: nosys('fd_pwrite'),
|
|
417
|
+
fd_allocate: nosys('fd_allocate'),
|
|
418
|
+
fd_filestat_set_size: nosys('fd_filestat_set_size'),
|
|
419
|
+
fd_filestat_set_times: nosys('fd_filestat_set_times'),
|
|
420
|
+
fd_fdstat_set_rights: nosys('fd_fdstat_set_rights'),
|
|
421
|
+
proc_raise: nosys('proc_raise'),
|
|
422
|
+
sock_send: nosys('sock_send'),
|
|
423
|
+
sock_recv: nosys('sock_recv'),
|
|
424
|
+
sock_shutdown: nosys('sock_shutdown'),
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
function makeWasiFs(
|
|
428
|
+
s: BashSession, proc: BashProc, DV: () => DataView, U8: () => Uint8Array,
|
|
429
|
+
io: BashIo, pathBase?: string, absRoots?: Set<string>,
|
|
430
|
+
): BashWasiFsImports {
|
|
431
|
+
const resolve = makeResolve(s, pathBase, absRoots);
|
|
432
|
+
return {
|
|
433
|
+
...makeUnsupported(s),
|
|
434
|
+
fd_prestat_get: (fd, buf) => { if (fd === 3) { DV().setUint8(buf, 0); DV().setUint32(buf + 4, 1, true); return 0; } return E.BADF; },
|
|
435
|
+
// The buffer length is a contract, not decoration: fd_prestat_get reports
|
|
436
|
+
// the name is 1 byte, and a guest that offers less must not be written to.
|
|
437
|
+
fd_prestat_dir_name: (fd, path, plen) => {
|
|
438
|
+
if (fd !== 3) return E.BADF;
|
|
439
|
+
if (plen < 1) return E.INVAL;
|
|
440
|
+
U8()[path] = 0x2f;
|
|
441
|
+
return 0;
|
|
442
|
+
},
|
|
443
|
+
path_open: (dirfd, dirflags, pathPtr, pathLen, oflags, rightsBase, _ri, fdflags, retPtr) => {
|
|
444
|
+
const raw = resolve(td.decode(U8().subarray(pathPtr, pathPtr + pathLen)));
|
|
445
|
+
// SYMLINK_FOLLOW (lookupflags bit 0). Without it only the leading
|
|
446
|
+
// directories resolve, so O_NOFOLLOW opens the link itself.
|
|
447
|
+
const r = (dirflags & 1) !== 0 ? followPath(s, raw) : followParents(s, raw);
|
|
448
|
+
if (r.errno) return r.errno;
|
|
449
|
+
const path = r.path;
|
|
450
|
+
const dv = DV();
|
|
451
|
+
if (path === 'dev/null' || path === 'dev/tty') {
|
|
452
|
+
const fd = lowestFd(proc); proc.fds.set(fd, { kind: path === 'dev/tty' ? 'tty' : 'null' });
|
|
453
|
+
dv.setUint32(retPtr, fd, true); return 0;
|
|
454
|
+
}
|
|
455
|
+
const trav = checkTraversal(s, path);
|
|
456
|
+
if (trav) return trav;
|
|
457
|
+
const wantDir = (oflags & 2) !== 0;
|
|
458
|
+
if (isDir(s, path)) {
|
|
459
|
+
if ((effMode(s, path) & 1) === 0) return E.ACCES;
|
|
460
|
+
const fd = lowestFd(proc); proc.fds.set(fd, { kind: 'dir', path });
|
|
461
|
+
dv.setUint32(retPtr, fd, true); return 0;
|
|
462
|
+
}
|
|
463
|
+
if (wantDir) return fileExists(s, path) ? E.NOTDIR : E.NOENT;
|
|
464
|
+
// wasi-libc encodes the open intent in the rights: fd_read is bit 1,
|
|
465
|
+
// fd_write bit 6. Enforce the S2a effective mode accordingly.
|
|
466
|
+
const rights = typeof rightsBase === 'bigint' ? rightsBase : BigInt(rightsBase >>> 0);
|
|
467
|
+
let need = 0;
|
|
468
|
+
if ((rights & 2n) !== 0n) need |= 4;
|
|
469
|
+
if ((rights & 64n) !== 0n) need |= 2;
|
|
470
|
+
if ((oflags & 8) !== 0) need |= 2; // O_TRUNC implies write
|
|
471
|
+
let entry = fileLookup(s, path);
|
|
472
|
+
const exists = entry !== null || fileExists(s, path);
|
|
473
|
+
if (exists && (oflags & 4)) return E.EXIST; // O_EXCL
|
|
474
|
+
if (!exists) {
|
|
475
|
+
if (!(oflags & 1)) return E.NOENT; // no O_CREAT
|
|
476
|
+
const denied = checkParentWritable(s, path);
|
|
477
|
+
if (denied) return denied;
|
|
478
|
+
entry = { bytes: new Uint8Array(0) };
|
|
479
|
+
s.fs.files.set(path, entry); markWritten(s, path);
|
|
480
|
+
} else {
|
|
481
|
+
if (need && (effMode(s, path) & need) !== need) return E.ACCES;
|
|
482
|
+
if (!entry) {
|
|
483
|
+
// Exists per the mode map but bytes were not snapshotted
|
|
484
|
+
// (write-only). Start from empty content.
|
|
485
|
+
entry = { bytes: new Uint8Array(0) };
|
|
486
|
+
s.fs.files.set(path, entry);
|
|
487
|
+
}
|
|
488
|
+
if (oflags & 8) { // O_TRUNC
|
|
489
|
+
entry.bytes = new Uint8Array(0); markWritten(s, path);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
const fd = lowestFd(proc);
|
|
493
|
+
proc.fds.set(fd, { kind: 'file', path, pos: 0, append: (fdflags & 1) !== 0 });
|
|
494
|
+
dv.setUint32(retPtr, fd, true);
|
|
495
|
+
return 0;
|
|
496
|
+
},
|
|
497
|
+
fd_filestat_get: (fd, buf) => {
|
|
498
|
+
const e = proc.fds.get(fd);
|
|
499
|
+
if (!e) return E.BADF;
|
|
500
|
+
if (e.kind === 'file') { const f = fileLookup(s, e.path); statBuf(DV(), U8(), buf, 4, f ? f.bytes.length : 0, mtimeNs(s, e.path)); return 0; }
|
|
501
|
+
if (e && (e.kind === 'dir' || e.kind === 'preopen')) { statBuf(DV(), U8(), buf, 3, 0, mtimeNs(s, e.kind === 'preopen' ? '' : e.path)); return 0; }
|
|
502
|
+
statBuf(DV(), U8(), buf, e && (e.kind === 'stdin' || e.kind === 'stdout' || e.kind === 'stderr' || e.kind === 'tty') ? 2 : 0, 0, realtimeNs());
|
|
503
|
+
return 0;
|
|
504
|
+
},
|
|
505
|
+
path_filestat_get: (dirfd, flags, pathPtr, pathLen, buf) => {
|
|
506
|
+
const raw = resolve(td.decode(U8().subarray(pathPtr, pathPtr + pathLen)));
|
|
507
|
+
if (raw === 'dev/null' || raw === 'dev/tty') { statBuf(DV(), U8(), buf, 2, 0, realtimeNs()); return 0; }
|
|
508
|
+
// SYMLINK_FOLLOW (bit 0) distinguishes stat from lstat: without it the
|
|
509
|
+
// link itself is the subject, which is how 'test -L' and 'ls -l' tell a
|
|
510
|
+
// symlink apart from what it points at.
|
|
511
|
+
const follow = (flags & 1) !== 0;
|
|
512
|
+
if (!follow && s.fs.symlinks.has(raw)) {
|
|
513
|
+
statBuf(DV(), U8(), buf, 7, te.encode(s.fs.symlinks.get(raw) as string).length, mtimeNs(s, raw));
|
|
514
|
+
return 0;
|
|
515
|
+
}
|
|
516
|
+
const r = followPath(s, raw);
|
|
517
|
+
if (r.errno) return r.errno;
|
|
518
|
+
const path = r.path;
|
|
519
|
+
const trav = checkTraversal(s, path);
|
|
520
|
+
if (trav) return trav;
|
|
521
|
+
const f = fileLookup(s, path);
|
|
522
|
+
if (f) { statBuf(DV(), U8(), buf, 4, f.bytes.length, mtimeNs(s, path)); return 0; }
|
|
523
|
+
if (isCoreutil(s, path)) { statBuf(DV(), U8(), buf, 4, 1024, mtimeNs(s, path)); return 0; }
|
|
524
|
+
if (isDir(s, path)) { statBuf(DV(), U8(), buf, 3, 0, mtimeNs(s, path)); return 0; }
|
|
525
|
+
if (fileExists(s, path)) { statBuf(DV(), U8(), buf, 4, 0, mtimeNs(s, path)); return 0; } // unreadable: size unknown
|
|
526
|
+
return E.NOENT;
|
|
527
|
+
},
|
|
528
|
+
// fstflags: ATIM=1, ATIM_NOW=2, MTIM=4, MTIM_NOW=8. Only the mtime half is
|
|
529
|
+
// recorded — this layer keeps one timestamp per inode, and mtime is the
|
|
530
|
+
// one every build tool reads.
|
|
531
|
+
path_filestat_set_times: (dirfd, _flags, pathPtr, pathLen, _atim, mtim, fstflags) => {
|
|
532
|
+
const path = resolve(td.decode(U8().subarray(pathPtr, pathPtr + pathLen)));
|
|
533
|
+
if (!inodeExists(s, path) && !s.fs.symlinks.has(path)) return E.NOENT;
|
|
534
|
+
if (fstflags & 8) s.fs.times.set(path, realtimeNs());
|
|
535
|
+
else if (fstflags & 4) s.fs.times.set(path, typeof mtim === 'bigint' ? mtim : BigInt(mtim >>> 0));
|
|
536
|
+
return 0;
|
|
537
|
+
},
|
|
538
|
+
path_unlink_file: (dirfd, pathPtr, pathLen) => {
|
|
539
|
+
// unlink removes the link, never what it points at.
|
|
540
|
+
const r = followParents(s, resolve(td.decode(U8().subarray(pathPtr, pathPtr + pathLen))));
|
|
541
|
+
if (r.errno) return r.errno;
|
|
542
|
+
const path = r.path;
|
|
543
|
+
const trav = checkTraversal(s, path);
|
|
544
|
+
if (trav) return trav;
|
|
545
|
+
if (isDir(s, path)) return E.ISDIR;
|
|
546
|
+
if (!fileExists(s, path) && !s.fs.symlinks.has(path)) return E.NOENT;
|
|
547
|
+
const denied = checkParentWritable(s, path);
|
|
548
|
+
if (denied) return denied;
|
|
549
|
+
recordFileRemoved(s, path);
|
|
550
|
+
return 0;
|
|
551
|
+
},
|
|
552
|
+
path_rename: (fd1, oldPtr, oldLen, fd2, newPtr, newLen) => {
|
|
553
|
+
const from = resolve(td.decode(U8().subarray(oldPtr, oldPtr + oldLen)));
|
|
554
|
+
const to = resolve(td.decode(U8().subarray(newPtr, newPtr + newLen)));
|
|
555
|
+
let trav = checkTraversal(s, from);
|
|
556
|
+
if (trav) return trav;
|
|
557
|
+
trav = checkTraversal(s, to);
|
|
558
|
+
if (trav) return trav;
|
|
559
|
+
const fromIsDir = from !== '' && isDir(s, from);
|
|
560
|
+
const f = fileLookup(s, from);
|
|
561
|
+
if (!fromIsDir && !f && !fileExists(s, from)) return E.NOENT;
|
|
562
|
+
if (from === to) return 0;
|
|
563
|
+
let denied = checkParentWritable(s, from);
|
|
564
|
+
if (!denied) denied = checkParentWritable(s, to);
|
|
565
|
+
if (denied) return denied;
|
|
566
|
+
const moveMode = (a: string, b: string) => {
|
|
567
|
+
const m = s.fs.modes.get(a); s.fs.modes.delete(a);
|
|
568
|
+
if (m !== undefined) s.fs.modes.set(b, m);
|
|
569
|
+
};
|
|
570
|
+
// Atomic-overwrite destination of the matching kind.
|
|
571
|
+
if (isDir(s, to)) {
|
|
572
|
+
if (!fromIsDir) return E.ISDIR;
|
|
573
|
+
const prefix = to + '/';
|
|
574
|
+
for (const p of s.fs.files.keys()) if (p.startsWith(prefix)) return E.NOTEMPTY;
|
|
575
|
+
for (const p of s.fs.dirs) if (p.startsWith(prefix)) return E.NOTEMPTY;
|
|
576
|
+
recordDirRemoved(s, to);
|
|
577
|
+
} else if (fileExists(s, to)) {
|
|
578
|
+
if (fromIsDir) return E.NOTDIR;
|
|
579
|
+
recordFileRemoved(s, to);
|
|
580
|
+
}
|
|
581
|
+
if (fromIsDir) {
|
|
582
|
+
const mode = s.fs.modes.get(from);
|
|
583
|
+
recordDirRemoved(s, from);
|
|
584
|
+
recordDirAdded(s, to);
|
|
585
|
+
if (mode !== undefined) s.fs.modes.set(to, mode);
|
|
586
|
+
const prefix = from + '/';
|
|
587
|
+
const rebase = (key: string) => to + '/' + key.slice(prefix.length);
|
|
588
|
+
for (const key of [...s.fs.files.keys()]) {
|
|
589
|
+
if (!key.startsWith(prefix)) continue;
|
|
590
|
+
const nk = rebase(key);
|
|
591
|
+
const entry = s.fs.files.get(key) as BashFileEntry;
|
|
592
|
+
recordFileRemoved(s, key);
|
|
593
|
+
moveMode(key, nk);
|
|
594
|
+
s.fs.files.set(nk, entry);
|
|
595
|
+
s.fs.written.add(nk); s.fs.deleted.delete(nk);
|
|
596
|
+
if (!s.fs.modes.has(nk)) s.fs.modes.set(nk, 6);
|
|
597
|
+
}
|
|
598
|
+
for (const key of [...s.fs.dirs]) {
|
|
599
|
+
if (!key.startsWith(prefix)) continue;
|
|
600
|
+
const nk = rebase(key);
|
|
601
|
+
const mode2 = s.fs.modes.get(key);
|
|
602
|
+
recordDirRemoved(s, key);
|
|
603
|
+
recordDirAdded(s, nk);
|
|
604
|
+
if (mode2 !== undefined) s.fs.modes.set(nk, mode2);
|
|
605
|
+
}
|
|
606
|
+
return 0;
|
|
607
|
+
}
|
|
608
|
+
const entry = f || { bytes: new Uint8Array(0) };
|
|
609
|
+
const mode = s.fs.modes.get(from);
|
|
610
|
+
recordFileRemoved(s, from);
|
|
611
|
+
s.fs.files.set(to, entry); markWritten(s, to);
|
|
612
|
+
if (mode !== undefined) s.fs.modes.set(to, mode);
|
|
613
|
+
return 0;
|
|
614
|
+
},
|
|
615
|
+
path_create_directory: (dirfd, pathPtr, pathLen) => {
|
|
616
|
+
const path = resolve(td.decode(U8().subarray(pathPtr, pathPtr + pathLen)));
|
|
617
|
+
const trav = checkTraversal(s, path);
|
|
618
|
+
if (trav) return trav;
|
|
619
|
+
if (isDir(s, path) || fileExists(s, path)) return E.EXIST;
|
|
620
|
+
const denied = checkParentWritable(s, path);
|
|
621
|
+
if (denied) return denied;
|
|
622
|
+
recordDirAdded(s, path);
|
|
623
|
+
return 0;
|
|
624
|
+
},
|
|
625
|
+
// path_readlink(fd, path, path_len, buf, buf_len, *bufused). Never follows
|
|
626
|
+
// the last component — reading a link is the one op that is about the link.
|
|
627
|
+
path_readlink: (dirfd, pathPtr, pathLen, bufPtr, bufLen, bufUsedPtr) => {
|
|
628
|
+
const r = followParents(s, resolve(td.decode(U8().subarray(pathPtr, pathPtr + pathLen))));
|
|
629
|
+
if (r.errno) return r.errno;
|
|
630
|
+
const target = s.fs.symlinks.get(r.path);
|
|
631
|
+
if (target === undefined) return inodeExists(s, r.path) ? E.INVAL : E.NOENT;
|
|
632
|
+
const bytes = te.encode(target);
|
|
633
|
+
const n = Math.min(bytes.length, bufLen);
|
|
634
|
+
U8().set(bytes.subarray(0, n), bufPtr);
|
|
635
|
+
DV().setUint32(bufUsedPtr, n, true);
|
|
636
|
+
return 0;
|
|
637
|
+
},
|
|
638
|
+
// path_symlink(old_path, old_path_len, fd, new_path, new_path_len) — the
|
|
639
|
+
// target comes FIRST and is not a path in this filesystem: it is stored
|
|
640
|
+
// verbatim and may dangle, exactly as ln -s allows.
|
|
641
|
+
path_symlink: (oldPtr, oldLen, newFd, newPtr, newLen) => {
|
|
642
|
+
const target = td.decode(U8().subarray(oldPtr, oldPtr + oldLen));
|
|
643
|
+
const r = followParents(s, resolve(td.decode(U8().subarray(newPtr, newPtr + newLen))));
|
|
644
|
+
if (r.errno) return r.errno;
|
|
645
|
+
const link = r.path;
|
|
646
|
+
const trav = checkTraversal(s, link);
|
|
647
|
+
if (trav) return trav;
|
|
648
|
+
if (inodeExists(s, link) || s.fs.symlinks.has(link)) return E.EXIST;
|
|
649
|
+
const denied = checkParentWritable(s, link);
|
|
650
|
+
if (denied) return denied;
|
|
651
|
+
s.fs.symlinks.set(link, target);
|
|
652
|
+
s.fs.symlinksCreated.set(link, target);
|
|
653
|
+
s.fs.modes.set(link, 7);
|
|
654
|
+
s.fs.deleted.delete(link);
|
|
655
|
+
touchPath(s, link);
|
|
656
|
+
return 0;
|
|
657
|
+
},
|
|
658
|
+
path_link: (fd1, _lookupFlags, oldPtr, oldLen, fd2, newPtr, newLen) => {
|
|
659
|
+
const from = resolve(td.decode(U8().subarray(oldPtr, oldPtr + oldLen)));
|
|
660
|
+
const to = resolve(td.decode(U8().subarray(newPtr, newPtr + newLen)));
|
|
661
|
+
let trav = checkTraversal(s, from);
|
|
662
|
+
if (trav) return trav;
|
|
663
|
+
trav = checkTraversal(s, to);
|
|
664
|
+
if (trav) return trav;
|
|
665
|
+
if (isDir(s, from)) return E.PERM;
|
|
666
|
+
const f = fileLookup(s, from);
|
|
667
|
+
if (!f) return fileExists(s, from) ? E.ACCES : E.NOENT;
|
|
668
|
+
if (fileExists(s, to) || isDir(s, to)) return E.EXIST;
|
|
669
|
+
const denied = checkParentWritable(s, to);
|
|
670
|
+
if (denied) return denied;
|
|
671
|
+
// Both names share ONE entry object — writes through either alias
|
|
672
|
+
// mutate the same bytes, which is real hard-link semantics here.
|
|
673
|
+
s.fs.files.set(to, f); markWritten(s, to);
|
|
674
|
+
s.fs.modes.set(to, effMode(s, from));
|
|
675
|
+
return 0;
|
|
676
|
+
},
|
|
677
|
+
path_remove_directory: (dirfd, pathPtr, pathLen) => {
|
|
678
|
+
const path = resolve(td.decode(U8().subarray(pathPtr, pathPtr + pathLen)));
|
|
679
|
+
const trav = checkTraversal(s, path);
|
|
680
|
+
if (trav) return trav;
|
|
681
|
+
if (path === '' || path === 'etc' || path === 'dev' || path === 'bin' || path === 'usr' || path === 'usr/bin') return E.ACCES;
|
|
682
|
+
if (!isDir(s, path)) return fileExists(s, path) ? E.NOTDIR : E.NOENT;
|
|
683
|
+
const denied = checkParentWritable(s, path);
|
|
684
|
+
if (denied) return denied;
|
|
685
|
+
const prefix = path + '/';
|
|
686
|
+
for (const p of s.fs.files.keys()) if (p.startsWith(prefix)) return E.NOTEMPTY;
|
|
687
|
+
for (const p of s.fs.dirs) if (p.startsWith(prefix)) return E.NOTEMPTY;
|
|
688
|
+
for (const p of s.fs.modes.keys()) if (p.startsWith(prefix)) return E.NOTEMPTY;
|
|
689
|
+
recordDirRemoved(s, path);
|
|
690
|
+
return 0;
|
|
691
|
+
},
|
|
692
|
+
fd_renumber: (from, to) => {
|
|
693
|
+
const e = proc.fds.get(from);
|
|
694
|
+
if (!e) return E.BADF;
|
|
695
|
+
if (from !== to) {
|
|
696
|
+
closeFd(s, proc, to);
|
|
697
|
+
proc.fds.set(to, e);
|
|
698
|
+
proc.fds.delete(from);
|
|
699
|
+
}
|
|
700
|
+
return 0;
|
|
701
|
+
},
|
|
702
|
+
fd_readdir: (fd, buf, bufLen, cookie, retPtr) => {
|
|
703
|
+
const e = proc.fds.get(fd);
|
|
704
|
+
if (!e || (e.kind !== 'dir' && e.kind !== 'preopen')) return E.BADF;
|
|
705
|
+
const dir = e.kind === 'preopen' ? '' : e.path;
|
|
706
|
+
if ((effMode(s, dir) & 4) === 0) return E.ACCES;
|
|
707
|
+
const prefix = dir ? dir + '/' : '';
|
|
708
|
+
const seen = new Set(['.', '..']);
|
|
709
|
+
const children: Array<{ name: string; type: number }> = [];
|
|
710
|
+
const push = (name: string, type: number) => { if (!seen.has(name)) { seen.add(name); children.push({ name, type }); } };
|
|
711
|
+
for (const p of s.fs.dirs) {
|
|
712
|
+
if (p !== dir && p.startsWith(prefix) && !p.slice(prefix.length).includes('/')) push(p.slice(prefix.length), 3);
|
|
713
|
+
}
|
|
714
|
+
for (const p of s.fs.files.keys()) {
|
|
715
|
+
if (p.startsWith(prefix) && !p.slice(prefix.length).includes('/')) push(p.slice(prefix.length), 4);
|
|
716
|
+
}
|
|
717
|
+
for (const p of s.fs.symlinks.keys()) {
|
|
718
|
+
if (p.startsWith(prefix) && !p.slice(prefix.length).includes('/')) push(p.slice(prefix.length), 7);
|
|
719
|
+
}
|
|
720
|
+
for (const p of s.fs.modes.keys()) { // modes-only inodes (unreadable files)
|
|
721
|
+
if (p !== dir && p.startsWith(prefix) && !p.slice(prefix.length).includes('/') && !s.fs.dirs.has(p)) push(p.slice(prefix.length), 4);
|
|
722
|
+
}
|
|
723
|
+
if (dir === 'bin' || dir === 'usr/bin') for (const name of s.coreutils.keys()) push(name, 4);
|
|
724
|
+
children.sort((a, b) => (a.name < b.name ? -1 : 1));
|
|
725
|
+
const entries = [{ name: '.', type: 3 }, { name: '..', type: 3 }, ...children];
|
|
726
|
+
const dv = DV(), u8 = U8();
|
|
727
|
+
let off = 0;
|
|
728
|
+
for (let i = Number(cookie); i < entries.length; i++) {
|
|
729
|
+
const nb = te.encode(entries[i].name);
|
|
730
|
+
const need = 24 + nb.length;
|
|
731
|
+
const record = new Uint8Array(need);
|
|
732
|
+
const rdv = new DataView(record.buffer);
|
|
733
|
+
rdv.setBigUint64(0, BigInt(i + 1), true); // d_next: cookie of the next entry
|
|
734
|
+
rdv.setBigUint64(8, 1n, true); // d_ino
|
|
735
|
+
rdv.setUint32(16, nb.length, true); // d_namlen
|
|
736
|
+
record[20] = entries[i].type; // d_type
|
|
737
|
+
record.set(nb, 24);
|
|
738
|
+
const room = bufLen - off;
|
|
739
|
+
if (need > room) { // truncated tail: "more remain"
|
|
740
|
+
u8.set(record.subarray(0, room), buf + off);
|
|
741
|
+
off = bufLen;
|
|
742
|
+
break;
|
|
743
|
+
}
|
|
744
|
+
u8.set(record, buf + off);
|
|
745
|
+
off += need;
|
|
746
|
+
}
|
|
747
|
+
dv.setUint32(retPtr, off, true);
|
|
748
|
+
return 0;
|
|
749
|
+
},
|
|
750
|
+
// An fd that was never opened is EBADF everywhere below. It used to be
|
|
751
|
+
// ESPIPE from fd_seek and plain SUCCESS from fd_tell and fd_filestat_get,
|
|
752
|
+
// while fd_close, fd_fdstat_get and fd_write in this same table already
|
|
753
|
+
// answered EBADF — one descriptor table, five different opinions about what
|
|
754
|
+
// a closed descriptor is. ESPIPE in particular is a yes: it means "open, and
|
|
755
|
+
// not seekable", so a caller using lseek to test validity read it as valid.
|
|
756
|
+
fd_seek: (fd, offset, whence, retPtr) => {
|
|
757
|
+
const e = proc.fds.get(fd);
|
|
758
|
+
if (!e) return E.BADF;
|
|
759
|
+
if (e.kind === 'file') {
|
|
760
|
+
const f = fileLookup(s, e.path); const len = f ? f.bytes.length : 0;
|
|
761
|
+
const off = Number(offset);
|
|
762
|
+
const next = whence === 0 ? off : whence === 1 ? e.pos + off : len + off;
|
|
763
|
+
if (next < 0) return E.INVAL;
|
|
764
|
+
e.pos = next;
|
|
765
|
+
DV().setBigUint64(retPtr, BigInt(next), true);
|
|
766
|
+
return 0;
|
|
767
|
+
}
|
|
768
|
+
DV().setBigUint64(retPtr, 0n, true);
|
|
769
|
+
return E.SPIPE;
|
|
770
|
+
},
|
|
771
|
+
fd_tell: (fd, retPtr) => {
|
|
772
|
+
const e = proc.fds.get(fd);
|
|
773
|
+
if (!e) return E.BADF;
|
|
774
|
+
DV().setBigUint64(retPtr, BigInt(e.kind === 'file' ? e.pos : 0), true);
|
|
775
|
+
return 0;
|
|
776
|
+
},
|
|
777
|
+
fd_close: (fd) => {
|
|
778
|
+
if (!proc.fds.has(fd)) return E.BADF;
|
|
779
|
+
closeFd(s, proc, fd);
|
|
780
|
+
return 0;
|
|
781
|
+
},
|
|
782
|
+
fd_fdstat_get: (fd, st) => {
|
|
783
|
+
const dv = DV(); const e = proc.fds.get(fd);
|
|
784
|
+
let ft = 4;
|
|
785
|
+
if (e && e.kind === 'pipe') ft = 0;
|
|
786
|
+
else if (e && (e.kind === 'stdin' || e.kind === 'stdout' || e.kind === 'stderr')) ft = s.stdinTty || fd !== 0 ? 2 : 0;
|
|
787
|
+
else if (e && e.kind === 'tty') ft = 2;
|
|
788
|
+
else if (e && (e.kind === 'dir' || e.kind === 'preopen')) ft = 3;
|
|
789
|
+
else if (!e) return E.BADF;
|
|
790
|
+
dv.setUint8(st, ft);
|
|
791
|
+
dv.setUint16(st + 2, 0, true);
|
|
792
|
+
dv.setBigUint64(st + 8, 0xffffffffffffffffn, true);
|
|
793
|
+
dv.setBigUint64(st + 16, 0xffffffffffffffffn, true);
|
|
794
|
+
return 0;
|
|
795
|
+
},
|
|
796
|
+
fd_fdstat_set_flags: () => 0,
|
|
797
|
+
fd_read: (fd, iovs, n, nread) => io.read(fd, readIovs(DV(), iovs, n), nread),
|
|
798
|
+
fd_write: (fd, iovs, n, nw) => {
|
|
799
|
+
if (!proc.fds.has(fd)) return E.BADF;
|
|
800
|
+
const dv = DV(), u8 = U8(); let w = 0;
|
|
801
|
+
for (let i = 0; i < n; i++) {
|
|
802
|
+
const p = dv.getUint32(iovs + i * 8, true), l = dv.getUint32(iovs + i * 8 + 4, true);
|
|
803
|
+
const n2 = io.write(fd, u8.subarray(p, p + l));
|
|
804
|
+
if (n2 === null) return E.BADF;
|
|
805
|
+
w += n2;
|
|
806
|
+
}
|
|
807
|
+
dv.setUint32(nw, w, true);
|
|
808
|
+
return 0;
|
|
809
|
+
},
|
|
810
|
+
poll_oneoff: (inPtr, outPtr, nsubs, retPtr) => io.poll(inPtr, outPtr, nsubs, retPtr),
|
|
811
|
+
clock_time_get: (id, _pr, t) => {
|
|
812
|
+
const ns = clockNs(id);
|
|
813
|
+
if (ns === null) return E.INVAL;
|
|
814
|
+
DV().setBigUint64(t, ns, true);
|
|
815
|
+
return 0;
|
|
816
|
+
},
|
|
817
|
+
clock_res_get: (id, r) => {
|
|
818
|
+
if (clockNs(id) === null) return E.INVAL;
|
|
819
|
+
DV().setBigUint64(r, id === CLOCK_REALTIME ? 1000000n : 1000n, true);
|
|
820
|
+
return 0;
|
|
821
|
+
},
|
|
822
|
+
// getRandomValues caps at 65536 bytes per call; a guest asking for more
|
|
823
|
+
// gets the same CSPRNG, chunked, never a weaker one.
|
|
824
|
+
random_get: (b, l) => {
|
|
825
|
+
const u = U8();
|
|
826
|
+
for (let off = 0; off < l; off += 65536) {
|
|
827
|
+
crypto.getRandomValues(u.subarray(b + off, b + off + Math.min(l - off, 65536)));
|
|
828
|
+
}
|
|
829
|
+
return 0;
|
|
830
|
+
},
|
|
831
|
+
proc_exit: (code) => { throw new Exit(code); },
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// Route a write through the process fd table. The caller has already rejected
|
|
836
|
+
// an fd the table does not hold, so every branch here answers a real entry —
|
|
837
|
+
// an unknown fd must never land in the user's terminal.
|
|
838
|
+
function writeThroughFd(s: BashSession, proc: BashProc, fd: number, bytes: Uint8Array): number | null {
|
|
839
|
+
const e = proc.fds.get(fd);
|
|
840
|
+
if (e && e.kind === 'pipe') {
|
|
841
|
+
// A pipe descriptor has a direction, and it was recorded and never read.
|
|
842
|
+
// Writing to the READ end used to push bytes into the pipe and report them
|
|
843
|
+
// written, so `exec 3< …; echo x >&3` fed the reader its own output — data
|
|
844
|
+
// appearing from nowhere, attributed to the wrong writer, with no error
|
|
845
|
+
// anywhere. POSIX makes the direction part of the descriptor: EBADF.
|
|
846
|
+
if (e.end === 'r') return null;
|
|
847
|
+
const pp = s.pipes.get(e.pipeId) as BashPipe;
|
|
848
|
+
pp.chunks.push(bytes.slice()); pp.queued += bytes.length;
|
|
849
|
+
wakePipe(s, pp);
|
|
850
|
+
return bytes.length;
|
|
851
|
+
}
|
|
852
|
+
if (e && e.kind === 'file') {
|
|
853
|
+
const f = fileLookup(s, e.path) || { bytes: new Uint8Array(0) };
|
|
854
|
+
if (!s.fs.files.has(e.path)) s.fs.files.set(e.path, f);
|
|
855
|
+
const pos = e.append ? f.bytes.length : e.pos;
|
|
856
|
+
fileWrite(s, f, e.path, pos, bytes);
|
|
857
|
+
e.pos = pos + bytes.length;
|
|
858
|
+
return bytes.length;
|
|
859
|
+
}
|
|
860
|
+
if (e && (e.kind === 'null' || e.kind === 'tty')) return bytes.length;
|
|
861
|
+
const text = td.decode(bytes);
|
|
862
|
+
if ((e as BashFdEntry).kind === 'stderr') s.err += text;
|
|
863
|
+
else s.out += text;
|
|
864
|
+
return bytes.length;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
// Synchronous read for non-parking consumers (files, buffered pipes).
|
|
868
|
+
// Returns {errno} or null when the source would block.
|
|
869
|
+
function tryReadFd(
|
|
870
|
+
s: BashSession, proc: BashProc, fd: number, dv: DataView, u8: Uint8Array,
|
|
871
|
+
iov: BashIovs, nreadPtr: number,
|
|
872
|
+
): { errno: BashErrno } | null {
|
|
873
|
+
const e = proc.fds.get(fd);
|
|
874
|
+
const deliver = (bytes: Uint8Array): { errno: BashErrno } => { dv.setUint32(nreadPtr, scatter(u8, iov, bytes), true); return { errno: 0 }; };
|
|
875
|
+
if (e && e.kind === 'file') {
|
|
876
|
+
const f = fileLookup(s, e.path);
|
|
877
|
+
const bytes = f ? f.bytes.subarray(e.pos, e.pos + iov.total) : new Uint8Array(0);
|
|
878
|
+
e.pos += bytes.length;
|
|
879
|
+
return deliver(bytes);
|
|
880
|
+
}
|
|
881
|
+
if (e && (e.kind === 'null' || e.kind === 'tty')) { dv.setUint32(nreadPtr, 0, true); return { errno: 0 }; }
|
|
882
|
+
if (e && e.kind === 'pipe') {
|
|
883
|
+
const pp = s.pipes.get(e.pipeId) as BashPipe;
|
|
884
|
+
if (pp.queued > 0) return deliver(takeUpTo(pp, iov.total));
|
|
885
|
+
if (pp.writers === 0) { dv.setUint32(nreadPtr, 0, true); return { errno: 0 }; }
|
|
886
|
+
return null;
|
|
887
|
+
}
|
|
888
|
+
if (e && e.kind === 'stdin') {
|
|
889
|
+
const st = s.stdin;
|
|
890
|
+
if (st.queued > 0) return deliver(takeUpTo(st, iov.total));
|
|
891
|
+
if (st.closed) { dv.setUint32(nreadPtr, 0, true); return { errno: 0 }; }
|
|
892
|
+
return null;
|
|
893
|
+
}
|
|
894
|
+
if (e) { dv.setUint32(nreadPtr, 0, true); return { errno: 0 }; }
|
|
895
|
+
return { errno: E.BADF };
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// Subscription record: 48B, userdata u64 at +0, tag u8 at +8. A CLOCK carries
|
|
899
|
+
// id u32 at +16, timeout u64 at +24, flags u16 at +40 (bit 0 = ABSTIME); an
|
|
900
|
+
// FD_READ/FD_WRITE carries the fd u32 at +16.
|
|
901
|
+
function readSubs(dv: DataView, inPtr: number, nsubs: number): BashPollSub[] {
|
|
902
|
+
const subs: BashPollSub[] = [];
|
|
903
|
+
for (let i = 0; i < nsubs; i++) {
|
|
904
|
+
const base = inPtr + i * 48;
|
|
905
|
+
const userdata = dv.getBigUint64(base, true);
|
|
906
|
+
const tag = dv.getUint8(base + 8);
|
|
907
|
+
if (tag === 0) {
|
|
908
|
+
const id = dv.getUint32(base + 16, true);
|
|
909
|
+
const timeout = dv.getBigUint64(base + 24, true);
|
|
910
|
+
const abs = (dv.getUint16(base + 40, true) & 1) !== 0;
|
|
911
|
+
const now = clockNs(id);
|
|
912
|
+
// An unknown clock id cannot produce a deadline; the event reports
|
|
913
|
+
// EINVAL rather than firing.
|
|
914
|
+
subs.push(now === null
|
|
915
|
+
? { tag, userdata, id, bad: true }
|
|
916
|
+
: { tag, userdata, id, deadline: abs ? timeout : now + timeout });
|
|
917
|
+
} else {
|
|
918
|
+
subs.push({ tag, userdata, fd: dv.getUint32(base + 16, true) });
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
return subs;
|
|
922
|
+
}
|
|
923
|
+
function clockExpired(sub: BashPollSub): boolean {
|
|
924
|
+
const now = clockNs(sub.id);
|
|
925
|
+
return now !== null && now >= (sub.deadline as bigint);
|
|
926
|
+
}
|
|
927
|
+
function writeEvent(dv: DataView, outPtr: number, slot: number, sub: BashPollSub, errno: BashErrno, nbytes: number): void {
|
|
928
|
+
const ev = outPtr + slot * 32;
|
|
929
|
+
dv.setBigUint64(ev, sub.userdata, true);
|
|
930
|
+
dv.setUint16(ev + 8, errno, true);
|
|
931
|
+
dv.setUint8(ev + 10, sub.tag);
|
|
932
|
+
dv.setBigUint64(ev + 16, BigInt(nbytes), true);
|
|
933
|
+
dv.setUint16(ev + 24, 0, true);
|
|
934
|
+
}
|
|
935
|
+
// Readiness of an FD_READ subscription. FD_WRITE and anything on an fd this
|
|
936
|
+
// table does not hold are handled by the caller.
|
|
937
|
+
function fdReadReady(s: BashSession, proc: BashProc, fd: number | undefined): BashFdReadiness | null {
|
|
938
|
+
const e = proc.fds.get(fd as number);
|
|
939
|
+
if (!e) return null;
|
|
940
|
+
if (e.kind === 'pipe') { const pp = s.pipes.get(e.pipeId) as BashPipe; return { ready: pp.queued > 0 || pp.writers === 0, avail: pp.queued }; }
|
|
941
|
+
if (e.kind === 'stdin') return { ready: s.stdin.queued > 0 || s.stdin.closed, avail: s.stdin.queued };
|
|
942
|
+
return { ready: true, avail: 0 };
|
|
943
|
+
}
|
|
944
|
+
// Emit every subscription that is ready right now. Returns the event count.
|
|
945
|
+
function emitReady(s: BashSession, proc: BashProc, dv: DataView, outPtr: number, subs: BashPollSub[]): number {
|
|
946
|
+
let n = 0;
|
|
947
|
+
for (const sub of subs) {
|
|
948
|
+
if (sub.tag === 0) {
|
|
949
|
+
if (sub.bad) { writeEvent(dv, outPtr, n++, sub, E.INVAL, 0); continue; }
|
|
950
|
+
if (clockExpired(sub)) writeEvent(dv, outPtr, n++, sub, 0, 0);
|
|
951
|
+
continue;
|
|
952
|
+
}
|
|
953
|
+
const st = fdReadReady(s, proc, sub.fd);
|
|
954
|
+
if (!st) { writeEvent(dv, outPtr, n++, sub, E.BADF, 0); continue; }
|
|
955
|
+
// FD_WRITE (tag 2) never blocks here: pipes and the output buffers accept
|
|
956
|
+
// whatever is handed to them.
|
|
957
|
+
if (sub.tag === 2 || st.ready) writeEvent(dv, outPtr, n++, sub, 0, st.avail);
|
|
958
|
+
}
|
|
959
|
+
return n;
|
|
960
|
+
}
|
|
961
|
+
// Spend a clock subscription's interval by running whatever else is runnable.
|
|
962
|
+
//
|
|
963
|
+
// It cannot be spent waiting. This facet's clock does not advance during
|
|
964
|
+
// synchronous execution — 50M spin iterations move both Date.now() and
|
|
965
|
+
// performance.now() by exactly 0ms, which is the platform's timing-attack
|
|
966
|
+
// mitigation rather than a quirk. A busy-wait therefore never terminates, and
|
|
967
|
+
// a poll that returns no events leaves the guest spinning on the same frozen
|
|
968
|
+
// clock. Running the scheduler is the only progress available; past that the
|
|
969
|
+
// deadline is reported as reached. A host whose clock does advance gets a real
|
|
970
|
+
// wait, and honouring one in-facet needs poll_oneoff to become async — that is
|
|
971
|
+
// the migration onto runtime/wasi-instance.ts, not something a synchronous
|
|
972
|
+
// implementation can express.
|
|
973
|
+
// FROZEN_PROBE is a measurement, not a timeout: if the clock has not moved by
|
|
974
|
+
// a single nanosecond after this many iterations, it is not going to.
|
|
975
|
+
const FROZEN_PROBE = 200000;
|
|
976
|
+
function waitForDeadline(s: BashSession, proc: BashProc, subs: BashPollSub[]): void {
|
|
977
|
+
const clocks = subs.filter((x) => x.tag === 0 && !x.bad);
|
|
978
|
+
if (!clocks.length) return;
|
|
979
|
+
const startedNs = realtimeNs();
|
|
980
|
+
let spins = 0;
|
|
981
|
+
while (!clocks.some(clockExpired)) {
|
|
982
|
+
if (s.rootExit !== null) return;
|
|
983
|
+
if (subs.some((x) => x.tag === 1 && (fdReadReady(s, proc, x.fd) || { ready: true }).ready)) return;
|
|
984
|
+
if (s.runnable.length) { pumpOne(s); continue; }
|
|
985
|
+
if (++spins > FROZEN_PROBE && realtimeNs() === startedNs) return;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
// Report every live clock subscription as fired. Reached only once the wait
|
|
989
|
+
// above can make no further progress: the alternative is an eventless success,
|
|
990
|
+
// which poll_oneoff may not return and which a guest cannot act on.
|
|
991
|
+
function emitClocks(dv: DataView, outPtr: number, subs: BashPollSub[]): number {
|
|
992
|
+
let n = 0;
|
|
993
|
+
for (const sub of subs) if (sub.tag === 0 && !sub.bad) writeEvent(dv, outPtr, n++, sub, 0, 0);
|
|
994
|
+
return n;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
function blockTarget(s: BashSession, proc: BashProc, fd: number | undefined): BashBlockTarget | null {
|
|
998
|
+
const e = proc.fds.get(fd as number);
|
|
999
|
+
if (e && e.kind === 'pipe') return { list: (s.pipes.get(e.pipeId) as BashPipe).readW, wake: () => wakePipe(s, s.pipes.get(e.pipeId) as BashPipe) };
|
|
1000
|
+
if (e && e.kind === 'stdin') return { list: s.stdin.waiters, wake: () => wakeStdin(s) };
|
|
1001
|
+
return null;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// ── per-process bash instance ─────────────────────────────────────────
|
|
1005
|
+
function makeProc(s: BashSession, pid: number, ppid: number, fds: Map<number, BashFdEntry>): BashProc {
|
|
1006
|
+
const proc = {
|
|
1007
|
+
pid, ppid, fds, inst: null, __s: s,
|
|
1008
|
+
ctx: { reason: null, rewinding: false, captureEnv: 0, ljEnv: 0, ljVal: 0, nextSlot: 0, resume: 0 },
|
|
1009
|
+
MAIN_BUF: 0, SLOT0: 0, pendingRead: null,
|
|
1010
|
+
slotByEnv: new Map(), freeSlots: [],
|
|
1011
|
+
} as unknown as BashProc;
|
|
1012
|
+
const DV = () => new DataView(proc.inst.exports.memory.buffer);
|
|
1013
|
+
const U8 = () => new Uint8Array(proc.inst.exports.memory.buffer);
|
|
1014
|
+
proc.DV = DV; proc.U8 = U8;
|
|
1015
|
+
const slotAddr = (i: number) => proc.SLOT0 + i * SLOT_SIZE;
|
|
1016
|
+
proc.slotAddr = slotAddr;
|
|
1017
|
+
const initHdr = (a: number, sz: number) => { const dv = DV(); dv.setUint32(a, a + 8, true); dv.setUint32(a + 4, a + sz, true); };
|
|
1018
|
+
proc.initHdr = initHdr;
|
|
1019
|
+
const wstr = (p: number, str: string) => { const b = te.encode(str); U8().set(b, p); return b.length; };
|
|
1020
|
+
const c = proc.ctx;
|
|
1021
|
+
|
|
1022
|
+
const io: BashIo = {
|
|
1023
|
+
read: (fd, iov, nread) => {
|
|
1024
|
+
if (proc.pendingRead) {
|
|
1025
|
+
proc.inst.exports.asyncify_stop_rewind(); c.rewinding = false;
|
|
1026
|
+
const pr = proc.pendingRead; proc.pendingRead = null;
|
|
1027
|
+
DV().setUint32(pr.nreadPtr, scatter(U8(), pr.iov, pr.bytes), true);
|
|
1028
|
+
return 0;
|
|
1029
|
+
}
|
|
1030
|
+
const dv = DV();
|
|
1031
|
+
const sync = tryReadFd(s, proc, fd, dv, U8(), iov, nread);
|
|
1032
|
+
if (sync) return sync.errno;
|
|
1033
|
+
// would block: asyncify-park until bytes/EOF arrive
|
|
1034
|
+
dv.setUint32(nread, 0, true);
|
|
1035
|
+
c.reason = 'blockread';
|
|
1036
|
+
c.pipeReq = { fd, iov, nreadPtr: nread };
|
|
1037
|
+
initHdr(proc.MAIN_BUF, MAIN_SIZE);
|
|
1038
|
+
proc.inst.exports.asyncify_start_unwind(proc.MAIN_BUF);
|
|
1039
|
+
return 0;
|
|
1040
|
+
},
|
|
1041
|
+
write: (fd, bytes) => writeThroughFd(s, proc, fd, bytes),
|
|
1042
|
+
poll: (inPtr, outPtr, nsubs, retPtr) => {
|
|
1043
|
+
if (proc.pendingRead) { // poll resume: report the fd readable
|
|
1044
|
+
proc.inst.exports.asyncify_stop_rewind(); c.rewinding = false;
|
|
1045
|
+
const pr = proc.pendingRead; proc.pendingRead = null;
|
|
1046
|
+
const dv = DV();
|
|
1047
|
+
dv.setBigUint64(outPtr, pr.pollUserdata ?? 0n, true);
|
|
1048
|
+
dv.setUint16(outPtr + 8, 0, true);
|
|
1049
|
+
dv.setUint8(outPtr + 10, 1); // eventtype fd_read
|
|
1050
|
+
dv.setBigUint64(outPtr + 16, BigInt(pr.bytes.length), true);
|
|
1051
|
+
dv.setUint16(outPtr + 24, 0, true);
|
|
1052
|
+
dv.setUint32(retPtr, 1, true);
|
|
1053
|
+
return 0;
|
|
1054
|
+
}
|
|
1055
|
+
const dv = DV();
|
|
1056
|
+
const subs = readSubs(dv, inPtr, nsubs);
|
|
1057
|
+
let emitted = emitReady(s, proc, dv, outPtr, subs);
|
|
1058
|
+
if (emitted > 0) { dv.setUint32(retPtr, emitted, true); return 0; }
|
|
1059
|
+
// Nothing ready. A blockable fd-read subscription parks the process so
|
|
1060
|
+
// the host can supply input; a clock-only wait has no such source and is
|
|
1061
|
+
// spent in-facet.
|
|
1062
|
+
const blockSub = subs.find((x) => x.tag === 1 && blockTarget(s, proc, x.fd));
|
|
1063
|
+
if (!blockSub) {
|
|
1064
|
+
waitForDeadline(s, proc, subs);
|
|
1065
|
+
emitted = emitReady(s, proc, dv, outPtr, subs) || emitClocks(dv, outPtr, subs);
|
|
1066
|
+
dv.setUint32(retPtr, emitted, true);
|
|
1067
|
+
return 0;
|
|
1068
|
+
}
|
|
1069
|
+
dv.setUint32(retPtr, 0, true);
|
|
1070
|
+
c.reason = 'blockread';
|
|
1071
|
+
c.pipeReq = { fd: blockSub.fd as number, iov: EMPTY_IOV, nreadPtr: 0, pollUserdata: blockSub.userdata };
|
|
1072
|
+
initHdr(proc.MAIN_BUF, MAIN_SIZE);
|
|
1073
|
+
proc.inst.exports.asyncify_start_unwind(proc.MAIN_BUF);
|
|
1074
|
+
return 0;
|
|
1075
|
+
},
|
|
1076
|
+
};
|
|
1077
|
+
|
|
1078
|
+
const wasiBase = makeWasiFs(s, proc, DV, U8, io);
|
|
1079
|
+
const wasi: BashWasiImports = {
|
|
1080
|
+
...wasiBase,
|
|
1081
|
+
args_sizes_get: (a, b) => { const dv = DV(); dv.setUint32(a, s.argv.length, true); dv.setUint32(b, s.argv.reduce((x, v) => x + te.encode(v).length + 1, 0), true); return 0; },
|
|
1082
|
+
args_get: (ptrs, buf) => { const dv = DV(); let p = buf; for (const a of s.argv) { dv.setUint32(ptrs, p, true); ptrs += 4; p += wstr(p, a); U8()[p++] = 0; } return 0; },
|
|
1083
|
+
environ_sizes_get: (a, b) => { const dv = DV(); dv.setUint32(a, s.environ.length, true); dv.setUint32(b, s.environ.reduce((x, v) => x + te.encode(v).length + 1, 0), true); return 0; },
|
|
1084
|
+
environ_get: (ptrs, buf) => { const dv = DV(); let p = buf; for (const v of s.environ) { dv.setUint32(ptrs, p, true); ptrs += 4; p += wstr(p, v); U8()[p++] = 0; } return 0; },
|
|
1085
|
+
};
|
|
1086
|
+
|
|
1087
|
+
// Slot allocator. Each setjmp captures into a FRESH physical slot
|
|
1088
|
+
// (overwriting a slot in place while its snapshot is still a live
|
|
1089
|
+
// longjmp target corrupts the asyncify rewind — the exit-builtin's
|
|
1090
|
+
// jump_to_top_level recursion proved this). Correct recycling: a
|
|
1091
|
+
// re-setjmp of the SAME jmp_buf makes that buf's previous slot a dead
|
|
1092
|
+
// target (POSIX: only the most recent setjmp per buf is live), so we
|
|
1093
|
+
// return it to a FIFO free-list — reused only after other allocations
|
|
1094
|
+
// cycle through, never the just-freed address. This bounds slot use
|
|
1095
|
+
// for long interactive sessions (bash re-setjmps top_level per
|
|
1096
|
+
// command) without the in-place-overwrite hazard.
|
|
1097
|
+
const allocSlot = (env: number): number => {
|
|
1098
|
+
const prev = proc.slotByEnv.get(env);
|
|
1099
|
+
if (prev !== undefined) { proc.slotByEnv.delete(env); proc.freeSlots.push(prev); }
|
|
1100
|
+
let idx: number | undefined;
|
|
1101
|
+
if (proc.freeSlots.length > 1) idx = proc.freeSlots.shift();
|
|
1102
|
+
else if (c.nextSlot < NSLOT) idx = c.nextSlot++;
|
|
1103
|
+
else idx = proc.freeSlots.shift();
|
|
1104
|
+
if (idx === undefined) throw new Error('bash-runner: setjmp slot budget exceeded (' + NSLOT + ')');
|
|
1105
|
+
proc.slotByEnv.set(env, idx);
|
|
1106
|
+
return idx;
|
|
1107
|
+
};
|
|
1108
|
+
|
|
1109
|
+
const nimbus_proc: BashProcImports = {
|
|
1110
|
+
setjmp: (env) => {
|
|
1111
|
+
if (c.rewinding) { proc.inst.exports.asyncify_stop_rewind(); c.rewinding = false; return; }
|
|
1112
|
+
c.reason = 'capture'; c.captureEnv = env;
|
|
1113
|
+
const idx = allocSlot(env);
|
|
1114
|
+
const dv = DV(); dv.setInt32(env, idx, true); dv.setInt32(env + 4, 0, true);
|
|
1115
|
+
initHdr(slotAddr(idx), SLOT_SIZE);
|
|
1116
|
+
proc.inst.exports.asyncify_start_unwind(slotAddr(idx));
|
|
1117
|
+
},
|
|
1118
|
+
longjmp: (env, val) => {
|
|
1119
|
+
if (c.rewinding) { proc.inst.exports.asyncify_stop_rewind(); c.rewinding = false; return; }
|
|
1120
|
+
c.reason = 'longjmp'; c.ljEnv = env; c.ljVal = val;
|
|
1121
|
+
initHdr(proc.MAIN_BUF, MAIN_SIZE);
|
|
1122
|
+
proc.inst.exports.asyncify_start_unwind(proc.MAIN_BUF);
|
|
1123
|
+
},
|
|
1124
|
+
fork: () => {
|
|
1125
|
+
if (c.rewinding) { proc.inst.exports.asyncify_stop_rewind(); c.rewinding = false; return c.resume; }
|
|
1126
|
+
c.reason = 'fork';
|
|
1127
|
+
initHdr(proc.MAIN_BUF, MAIN_SIZE);
|
|
1128
|
+
proc.inst.exports.asyncify_start_unwind(proc.MAIN_BUF);
|
|
1129
|
+
return 0;
|
|
1130
|
+
},
|
|
1131
|
+
vfork: () => nimbus_proc.fork(),
|
|
1132
|
+
waitpid: (pid, statusPtr, _opt) => {
|
|
1133
|
+
if (c.rewinding) {
|
|
1134
|
+
proc.inst.exports.asyncify_stop_rewind(); c.rewinding = false;
|
|
1135
|
+
if (c.waitStatusPtr != null) DV().setInt32(c.waitStatusPtr, c.resumeStatus, true);
|
|
1136
|
+
return c.resume;
|
|
1137
|
+
}
|
|
1138
|
+
c.reason = 'waitpid'; c.waitTarget = pid; c.waitStatusPtr = statusPtr;
|
|
1139
|
+
initHdr(proc.MAIN_BUF, MAIN_SIZE);
|
|
1140
|
+
proc.inst.exports.asyncify_start_unwind(proc.MAIN_BUF);
|
|
1141
|
+
return 0;
|
|
1142
|
+
},
|
|
1143
|
+
execve: (pathPtr, argvFlatPtr, argvLen, envFlatPtr, envLen) => {
|
|
1144
|
+
if (c.rewinding) { proc.inst.exports.asyncify_stop_rewind(); c.rewinding = false; return c.resume; }
|
|
1145
|
+
const u8 = U8();
|
|
1146
|
+
let e = pathPtr; while (u8[e]) e++;
|
|
1147
|
+
c.reason = 'exec';
|
|
1148
|
+
c.execPath = td.decode(u8.subarray(pathPtr, e));
|
|
1149
|
+
c.execArgv = td.decode(u8.subarray(argvFlatPtr, argvFlatPtr + argvLen)).split('\0').filter((x) => x.length);
|
|
1150
|
+
// The child's REAL environment (bash's export set at exec time —
|
|
1151
|
+
// PWD tracks the shell's cd, unlike the boot-time s.environ).
|
|
1152
|
+
c.execEnv = td.decode(u8.subarray(envFlatPtr, envFlatPtr + envLen)).split('\0').filter((x) => x.length);
|
|
1153
|
+
initHdr(proc.MAIN_BUF, MAIN_SIZE);
|
|
1154
|
+
proc.inst.exports.asyncify_start_unwind(proc.MAIN_BUF);
|
|
1155
|
+
return 0;
|
|
1156
|
+
},
|
|
1157
|
+
pipe: (fdsPtr) => {
|
|
1158
|
+
const id = newPipe(s);
|
|
1159
|
+
const rfd = lowestFd(proc); proc.fds.set(rfd, { kind: 'pipe', pipeId: id, end: 'r' });
|
|
1160
|
+
const wfd = lowestFd(proc); proc.fds.set(wfd, { kind: 'pipe', pipeId: id, end: 'w' });
|
|
1161
|
+
const dv = DV(); dv.setInt32(fdsPtr, rfd, true); dv.setInt32(fdsPtr + 4, wfd, true);
|
|
1162
|
+
return 0;
|
|
1163
|
+
},
|
|
1164
|
+
dup: (o) => {
|
|
1165
|
+
const e = proc.fds.get(o); if (!e) return -E.BADF;
|
|
1166
|
+
const nf = lowestFd(proc); proc.fds.set(nf, { ...e });
|
|
1167
|
+
if (e.kind === 'pipe') bumpPipe(s, e, 1);
|
|
1168
|
+
return nf;
|
|
1169
|
+
},
|
|
1170
|
+
dup2: (o, n) => {
|
|
1171
|
+
const e = proc.fds.get(o); if (!e) return -E.BADF;
|
|
1172
|
+
if (o === n) return n;
|
|
1173
|
+
if (proc.fds.has(n)) closeFd(s, proc, n);
|
|
1174
|
+
proc.fds.set(n, { ...e });
|
|
1175
|
+
if (e.kind === 'pipe') bumpPipe(s, e, 1);
|
|
1176
|
+
return n;
|
|
1177
|
+
},
|
|
1178
|
+
kill: () => 0, setpgid: () => 0, getpgid: () => proc.pid, getppid: () => proc.ppid,
|
|
1179
|
+
tcsetpgrp: () => 0, tcgetpgrp: () => proc.pid, tcgetattr: () => -1, tcsetattr: () => 0,
|
|
1180
|
+
};
|
|
1181
|
+
const envImports = {
|
|
1182
|
+
getpid: () => proc.pid, getuid: () => 0, geteuid: () => 0, getgid: () => 0, getegid: () => 0,
|
|
1183
|
+
setuid: () => 0, setgid: () => 0, umask: () => 0o22,
|
|
1184
|
+
gethostname: (p: number, _l: number) => { U8().set(te.encode('nimbus'), p); return 0; },
|
|
1185
|
+
dlopen: () => 0, dlsym: () => 0, dlclose: () => 0, dlerror: () => 0,
|
|
1186
|
+
};
|
|
1187
|
+
proc.inst = new WebAssembly.Instance(s.mod, { wasi_snapshot_preview1: wasi, nimbus_proc, env: envImports }) as BashInstance;
|
|
1188
|
+
s.stats.instances++;
|
|
1189
|
+
s.procs.set(pid, proc);
|
|
1190
|
+
return proc;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
function setupArena(proc: BashProc): void {
|
|
1194
|
+
const base = proc.inst.exports.memory.buffer.byteLength;
|
|
1195
|
+
const need = MAIN_SIZE + NSLOT * SLOT_SIZE;
|
|
1196
|
+
proc.inst.exports.memory.grow(Math.ceil(need / PAGE));
|
|
1197
|
+
proc.MAIN_BUF = base;
|
|
1198
|
+
proc.SLOT0 = proc.MAIN_BUF + MAIN_SIZE;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
// ── scheduler ─────────────────────────────────────────────────────────
|
|
1202
|
+
function resumeProc(proc: BashProc): void {
|
|
1203
|
+
proc.ctx.rewinding = true;
|
|
1204
|
+
proc.inst.exports.asyncify_start_rewind(proc.MAIN_BUF);
|
|
1205
|
+
proc.__s.runnable.push(proc);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
function trackArena(s: BashSession, proc: BashProc, bufAddr: number, size: number, isSlot: boolean): void {
|
|
1209
|
+
const used = proc.DV().getUint32(bufAddr, true) - (bufAddr + 8);
|
|
1210
|
+
if (isSlot) { if (used > s.stats.slotHi) s.stats.slotHi = used; }
|
|
1211
|
+
else if (used > s.stats.mainHi) s.stats.mainHi = used;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
function step(s: BashSession, proc: BashProc): void {
|
|
1215
|
+
const c = proc.ctx, ex = proc.inst.exports;
|
|
1216
|
+
try { ex._start(); }
|
|
1217
|
+
catch (e) {
|
|
1218
|
+
if (e instanceof Exit) { finishProc(s, proc, e.code); return; }
|
|
1219
|
+
throw e;
|
|
1220
|
+
}
|
|
1221
|
+
if (c.reason === null) { finishProc(s, proc, 0); return; }
|
|
1222
|
+
ex.asyncify_stop_unwind();
|
|
1223
|
+
const r = c.reason; c.reason = null;
|
|
1224
|
+
const dv = proc.DV();
|
|
1225
|
+
const mem = proc.inst.exports.memory.buffer.byteLength;
|
|
1226
|
+
if (mem > s.stats.memPeak) s.stats.memPeak = mem;
|
|
1227
|
+
if (r === 'capture') {
|
|
1228
|
+
const idx = dv.getInt32(c.captureEnv, true);
|
|
1229
|
+
trackArena(s, proc, proc.slotAddr(idx), SLOT_SIZE, true);
|
|
1230
|
+
dv.setUint32(c.captureEnv + 8, dv.getUint32(proc.slotAddr(idx), true), true);
|
|
1231
|
+
c.rewinding = true;
|
|
1232
|
+
ex.asyncify_start_rewind(proc.slotAddr(idx));
|
|
1233
|
+
s.runnable.push(proc);
|
|
1234
|
+
} else if (r === 'longjmp') {
|
|
1235
|
+
trackArena(s, proc, proc.MAIN_BUF, MAIN_SIZE, false);
|
|
1236
|
+
const idx = dv.getInt32(c.ljEnv, true), hw = dv.getUint32(c.ljEnv + 8, true);
|
|
1237
|
+
dv.setInt32(c.ljEnv + 4, c.ljVal, true);
|
|
1238
|
+
dv.setUint32(proc.slotAddr(idx), hw, true);
|
|
1239
|
+
c.rewinding = true;
|
|
1240
|
+
ex.asyncify_start_rewind(proc.slotAddr(idx));
|
|
1241
|
+
s.runnable.push(proc);
|
|
1242
|
+
} else if (r === 'fork') {
|
|
1243
|
+
trackArena(s, proc, proc.MAIN_BUF, MAIN_SIZE, false);
|
|
1244
|
+
doFork(s, proc);
|
|
1245
|
+
} else if (r === 'waitpid') {
|
|
1246
|
+
trackArena(s, proc, proc.MAIN_BUF, MAIN_SIZE, false);
|
|
1247
|
+
doWait(s, proc);
|
|
1248
|
+
} else if (r === 'blockread') {
|
|
1249
|
+
trackArena(s, proc, proc.MAIN_BUF, MAIN_SIZE, false);
|
|
1250
|
+
const target = blockTarget(s, proc, c.pipeReq.fd);
|
|
1251
|
+
if (!target) { // fd closed under us: deliver EOF
|
|
1252
|
+
proc.pendingRead = { iov: c.pipeReq.iov, bytes: new Uint8Array(0), nreadPtr: c.pipeReq.nreadPtr, pollUserdata: c.pipeReq.pollUserdata };
|
|
1253
|
+
resumeProc(proc);
|
|
1254
|
+
} else {
|
|
1255
|
+
target.list.push({ proc });
|
|
1256
|
+
target.wake();
|
|
1257
|
+
}
|
|
1258
|
+
} else if (r === 'exec') {
|
|
1259
|
+
trackArena(s, proc, proc.MAIN_BUF, MAIN_SIZE, false);
|
|
1260
|
+
doExec(s, proc);
|
|
1261
|
+
} else {
|
|
1262
|
+
throw new Error('bash-runner: unknown unwind reason ' + r);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
function pumpOne(s: BashSession): boolean {
|
|
1267
|
+
if (!s.runnable.length) return false;
|
|
1268
|
+
step(s, s.runnable.shift() as BashProc);
|
|
1269
|
+
return true;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
// exec re-homes the forked child onto a staged plain-WASI coreutil
|
|
1273
|
+
// bound to the process fd table (M2 exec-into-runner, in-facet). The
|
|
1274
|
+
// tool's blocking pipe reads synchronously pump the writer procs.
|
|
1275
|
+
function doExec(s: BashSession, proc: BashProc): void {
|
|
1276
|
+
const name = proc.ctx.execPath.split('/').pop() as string;
|
|
1277
|
+
// The child inherits bash's LIVE cwd: bash keeps the exported PWD
|
|
1278
|
+
// current across cd, and the execve import threads the child env.
|
|
1279
|
+
const env = proc.ctx.execEnv && proc.ctx.execEnv.length ? proc.ctx.execEnv : s.environ;
|
|
1280
|
+
const pwdVar = env.find((x) => x.startsWith('PWD='));
|
|
1281
|
+
const childCwd = pwdVar ? norm(pwdVar.slice(4)) : s.cwd;
|
|
1282
|
+
const path = makeResolve(s, childCwd)(proc.ctx.execPath);
|
|
1283
|
+
// Applets dispatch only from the PATH dirs they are staged in — a user
|
|
1284
|
+
// file that happens to share an applet's name (./ls) is NOT the applet.
|
|
1285
|
+
const viaPath = path.startsWith('bin/') || path.startsWith('usr/bin/');
|
|
1286
|
+
const m = viaPath ? s.coreutils.get(name) : null;
|
|
1287
|
+
if (!m) {
|
|
1288
|
+
// wasi-libc errno numbering: EACCES=2, ENOENT=44, ENOEXEC=45. An
|
|
1289
|
+
// existing non-wasm file gets ENOEXEC so bash falls back to running
|
|
1290
|
+
// it as a shell script (execute_disk_command's ENOEXEC path); an
|
|
1291
|
+
// unreadable one gets EACCES.
|
|
1292
|
+
proc.ctx.resume = fileLookup(s, path) ? -45 : (fileExists(s, path) ? -2 : -44);
|
|
1293
|
+
proc.ctx.rewinding = true;
|
|
1294
|
+
proc.inst.exports.asyncify_start_rewind(proc.MAIN_BUF);
|
|
1295
|
+
s.runnable.push(proc);
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
let inst2: BashInstance;
|
|
1299
|
+
const DV = () => new DataView(inst2.exports.memory.buffer);
|
|
1300
|
+
const U8 = () => new Uint8Array(inst2.exports.memory.buffer);
|
|
1301
|
+
const tv = proc.ctx.execArgv;
|
|
1302
|
+
// Absolute argv paths (leading '/'), normalized to root-relative keys.
|
|
1303
|
+
// These recover the absolute-vs-relative bit the tool's wasi-libc drops
|
|
1304
|
+
// (see makeResolve): a path under one of these is never re-anchored.
|
|
1305
|
+
const absRoots = new Set<string>();
|
|
1306
|
+
for (let i = 1; i < tv.length; i++) if (tv[i].startsWith('/')) absRoots.add(norm(tv[i]));
|
|
1307
|
+
const wstr = (p: number, str: string) => { const b = te.encode(str); U8().set(b, p); return b.length; };
|
|
1308
|
+
let code = 0;
|
|
1309
|
+
const io: BashIo = {
|
|
1310
|
+
read: (fd, iov, nread) => {
|
|
1311
|
+
const dv = DV();
|
|
1312
|
+
for (;;) {
|
|
1313
|
+
const sync = tryReadFd(s, proc, fd, dv, U8(), iov, nread);
|
|
1314
|
+
if (sync) return sync.errno;
|
|
1315
|
+
// Would block: pump the scheduler so writer procs make progress.
|
|
1316
|
+
// When nothing is runnable the source can't produce more
|
|
1317
|
+
// synchronously (interactive stdin mid-exec) — deliver EOF.
|
|
1318
|
+
if (!pumpOne(s)) { dv.setUint32(nread, 0, true); return 0; }
|
|
1319
|
+
}
|
|
1320
|
+
},
|
|
1321
|
+
write: (fd, bytes) => writeThroughFd(s, proc, fd, bytes),
|
|
1322
|
+
// An exec'd tool cannot park, so a wait it cannot satisfy locally is spent
|
|
1323
|
+
// pumping the scheduler — the same discipline its blocking reads use.
|
|
1324
|
+
poll: (inPtr, outPtr, nsubs, retPtr) => {
|
|
1325
|
+
const dv = DV();
|
|
1326
|
+
const subs = readSubs(dv, inPtr, nsubs);
|
|
1327
|
+
let emitted = emitReady(s, proc, dv, outPtr, subs);
|
|
1328
|
+
if (emitted === 0) {
|
|
1329
|
+
waitForDeadline(s, proc, subs);
|
|
1330
|
+
emitted = emitReady(s, proc, dv, outPtr, subs) || emitClocks(dv, outPtr, subs);
|
|
1331
|
+
}
|
|
1332
|
+
dv.setUint32(retPtr, emitted, true);
|
|
1333
|
+
return 0;
|
|
1334
|
+
},
|
|
1335
|
+
};
|
|
1336
|
+
const base = makeWasiFs(s, proc, DV, U8, io, childCwd, absRoots);
|
|
1337
|
+
const w: BashWasiImports = {
|
|
1338
|
+
...base,
|
|
1339
|
+
args_sizes_get: (a, b) => { const dv = DV(); dv.setUint32(a, tv.length, true); dv.setUint32(b, tv.reduce((x, v) => x + te.encode(v).length + 1, 0), true); return 0; },
|
|
1340
|
+
args_get: (ptrs, buf) => { const dv = DV(); let p = buf; for (const a of tv) { dv.setUint32(ptrs, p, true); ptrs += 4; p += wstr(p, a); U8()[p++] = 0; } return 0; },
|
|
1341
|
+
// POSIX environment inheritance: the child env bash passed to execve.
|
|
1342
|
+
environ_sizes_get: (a, b) => { const dv = DV(); dv.setUint32(a, env.length, true); dv.setUint32(b, env.reduce((x, v) => x + te.encode(v).length + 1, 0), true); return 0; },
|
|
1343
|
+
environ_get: (ptrs, buf) => { const dv = DV(); let p = buf; for (const v of env) { dv.setUint32(ptrs, p, true); ptrs += 4; p += wstr(p, v); U8()[p++] = 0; } return 0; },
|
|
1344
|
+
proc_exit: (ec) => { code = ec; throw new Exit(ec); },
|
|
1345
|
+
};
|
|
1346
|
+
// nimbus_proc.chmod: WASI preview1 has no mode syscall, so busybox's
|
|
1347
|
+
// chmod threads through this import. In-facet the effective-mode table
|
|
1348
|
+
// updates immediately (chmod +x → ./script runs); the durable, S2a
|
|
1349
|
+
// ownership-checked chmod happens at fsDiff flush.
|
|
1350
|
+
const chmodResolve = makeResolve(s, childCwd, absRoots);
|
|
1351
|
+
const nimbus_proc = {
|
|
1352
|
+
chmod: (pathPtr: number, pathLen: number, mode: number) => {
|
|
1353
|
+
const p = chmodResolve(td.decode(U8().subarray(pathPtr, pathPtr + pathLen)));
|
|
1354
|
+
const trav = checkTraversal(s, p);
|
|
1355
|
+
if (trav) return trav;
|
|
1356
|
+
if (!inodeExists(s, p) && !(p !== '' && isDir(s, p))) return E.NOENT;
|
|
1357
|
+
const bits = mode & 0o777;
|
|
1358
|
+
s.fs.modes.set(p, (bits >> 6) & 7);
|
|
1359
|
+
s.fs.modesChanged.set(p, bits);
|
|
1360
|
+
return 0;
|
|
1361
|
+
},
|
|
1362
|
+
};
|
|
1363
|
+
inst2 = new WebAssembly.Instance(m, { wasi_snapshot_preview1: w, nimbus_proc }) as BashInstance;
|
|
1364
|
+
s.stats.instances++;
|
|
1365
|
+
const mem2 = inst2.exports.memory.buffer.byteLength;
|
|
1366
|
+
if (mem2 > s.stats.memPeak) s.stats.memPeak = mem2;
|
|
1367
|
+
try { inst2.exports._start(); }
|
|
1368
|
+
catch (e) { if (e instanceof Exit) code = e.code; else throw e; }
|
|
1369
|
+
finishProc(s, proc, code);
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
function doFork(s: BashSession, parent: BashProc): void {
|
|
1373
|
+
const childPid = s.pidNext++;
|
|
1374
|
+
const childFds = new Map();
|
|
1375
|
+
for (const [fd, e] of parent.fds) { childFds.set(fd, { ...e }); if (e.kind === 'pipe') bumpPipe(s, e, 1); }
|
|
1376
|
+
const child = makeProc(s, childPid, parent.pid, childFds);
|
|
1377
|
+
const pmem = parent.inst.exports.memory, cmem = child.inst.exports.memory;
|
|
1378
|
+
if (cmem.buffer.byteLength < pmem.buffer.byteLength) cmem.grow((pmem.buffer.byteLength - cmem.buffer.byteLength) / PAGE);
|
|
1379
|
+
new Uint8Array(cmem.buffer).set(new Uint8Array(pmem.buffer));
|
|
1380
|
+
for (const [k, v] of Object.entries(parent.inst.exports)) if (v instanceof WebAssembly.Global) (child.inst.exports[k] as WebAssembly.Global).value = v.value;
|
|
1381
|
+
child.MAIN_BUF = parent.MAIN_BUF; child.SLOT0 = parent.SLOT0;
|
|
1382
|
+
child.ctx.nextSlot = parent.ctx.nextSlot;
|
|
1383
|
+
child.slotByEnv = new Map(parent.slotByEnv);
|
|
1384
|
+
child.freeSlots = parent.freeSlots.slice();
|
|
1385
|
+
const total = s.procs.size;
|
|
1386
|
+
if (total * cmem.buffer.byteLength > s.stats.memPeak) s.stats.memPeak = total * cmem.buffer.byteLength;
|
|
1387
|
+
child.ctx.resume = 0; child.ctx.rewinding = true;
|
|
1388
|
+
child.inst.exports.asyncify_start_rewind(child.MAIN_BUF);
|
|
1389
|
+
s.runnable.push(child);
|
|
1390
|
+
parent.ctx.resume = childPid; parent.ctx.rewinding = true;
|
|
1391
|
+
parent.inst.exports.asyncify_start_rewind(parent.MAIN_BUF);
|
|
1392
|
+
s.runnable.push(parent);
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
function doWait(s: BashSession, proc: BashProc): void {
|
|
1396
|
+
const t = proc.ctx.waitTarget;
|
|
1397
|
+
let pid: number | null = null;
|
|
1398
|
+
if (t > 0) {
|
|
1399
|
+
// A named target still has to be this process's child; reaping another
|
|
1400
|
+
// process's child by pid is the same error, just harder to reach.
|
|
1401
|
+
const e = s.exitStatus.get(t);
|
|
1402
|
+
if (e && e.ppid === proc.pid) pid = t;
|
|
1403
|
+
} else {
|
|
1404
|
+
// `wait` with no target reaps one of MY children. This used to take the
|
|
1405
|
+
// first entry in the map regardless of parentage, so with two subshells
|
|
1406
|
+
// each having had a child exit, one could consume the other's status —
|
|
1407
|
+
// and then block forever waiting for a child already reaped elsewhere.
|
|
1408
|
+
for (const [p, e] of s.exitStatus) { if (e.ppid === proc.pid) { pid = p; break; } }
|
|
1409
|
+
}
|
|
1410
|
+
if (pid != null) {
|
|
1411
|
+
const st = (s.exitStatus.get(pid) as BashExitStatus).status; s.exitStatus.delete(pid);
|
|
1412
|
+
proc.ctx.resume = pid; proc.ctx.resumeStatus = st;
|
|
1413
|
+
resumeProc(proc);
|
|
1414
|
+
} else {
|
|
1415
|
+
s.waiters.push({ proc, targetPid: t });
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
function finishProc(s: BashSession, proc: BashProc, code: number): void {
|
|
1420
|
+
const st = (code & 0xff) << 8;
|
|
1421
|
+
s.procs.delete(proc.pid);
|
|
1422
|
+
for (const fd of [...proc.fds.keys()]) closeFd(s, proc, fd);
|
|
1423
|
+
if (proc.ppid === 0) s.rootExit = code;
|
|
1424
|
+
s.exitStatus.set(proc.pid, { status: st, ppid: proc.ppid });
|
|
1425
|
+
for (let i = 0; i < s.waiters.length; i++) {
|
|
1426
|
+
const w = s.waiters[i];
|
|
1427
|
+
// Only the parent may be woken by this exit — a waiter in another subshell
|
|
1428
|
+
// is not waiting for this child, and waking it hands over a status that was
|
|
1429
|
+
// never its to claim.
|
|
1430
|
+
if (w.proc.pid === proc.ppid && (w.targetPid === proc.pid || w.targetPid <= 0)) {
|
|
1431
|
+
s.waiters.splice(i, 1);
|
|
1432
|
+
w.proc.ctx.resume = proc.pid; w.proc.ctx.resumeStatus = st;
|
|
1433
|
+
s.exitStatus.delete(proc.pid);
|
|
1434
|
+
resumeProc(w.proc);
|
|
1435
|
+
break;
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
function composeFsDiff(s: BashSession): WasiFsDiff {
|
|
1441
|
+
const filesWritten: Record<string, string> = {};
|
|
1442
|
+
for (const path of s.fs.written) {
|
|
1443
|
+
if (path.startsWith('etc/nimbus.bashrc')) continue;
|
|
1444
|
+
const f = s.fs.files.get(path);
|
|
1445
|
+
if (f) filesWritten[path] = bytesToB64(f.bytes);
|
|
1446
|
+
}
|
|
1447
|
+
return {
|
|
1448
|
+
filesWritten,
|
|
1449
|
+
filesDeleted: [...s.fs.deleted],
|
|
1450
|
+
dirsCreated: [...s.fs.dirsCreated],
|
|
1451
|
+
// Deepest-first so the flush rmdirs children before their parents.
|
|
1452
|
+
dirsDeleted: [...s.fs.dirsDeleted].sort((a, b) => b.length - a.length),
|
|
1453
|
+
modesChanged: Object.fromEntries(s.fs.modesChanged),
|
|
1454
|
+
symlinksCreated: Object.fromEntries(s.fs.symlinksCreated),
|
|
1455
|
+
};
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
function pump(s: BashSession): BashSlice {
|
|
1459
|
+
try {
|
|
1460
|
+
while (s.runnable.length) {
|
|
1461
|
+
if (++s.steps > 5_000_000) throw new Error('bash-runner: runaway scheduler (>5M steps)');
|
|
1462
|
+
step(s, s.runnable.shift() as BashProc);
|
|
1463
|
+
if (s.rootExit !== null) break;
|
|
1464
|
+
}
|
|
1465
|
+
} catch (e) {
|
|
1466
|
+
s.error = String(e && (e as Error).stack || e && (e as Error).message || e);
|
|
1467
|
+
}
|
|
1468
|
+
const out = s.out, err = s.err;
|
|
1469
|
+
s.out = ''; s.err = '';
|
|
1470
|
+
const stats = { ...s.stats, steps: s.steps, missingWasi: [...s.missingWasi] };
|
|
1471
|
+
if (s.error) {
|
|
1472
|
+
S = null;
|
|
1473
|
+
return { state: 'error', exitCode: 1, stdout: out, stderr: err, error: s.error, stats };
|
|
1474
|
+
}
|
|
1475
|
+
if (s.rootExit !== null || s.procs.size === 0) {
|
|
1476
|
+
const code = s.rootExit === null ? 0 : s.rootExit;
|
|
1477
|
+
const fsDiff = composeFsDiff(s);
|
|
1478
|
+
S = null;
|
|
1479
|
+
return { state: 'exited', exitCode: code, stdout: out, stderr: err, fsDiff, stats };
|
|
1480
|
+
}
|
|
1481
|
+
if (s.stdin.waiters.length > 0) {
|
|
1482
|
+
return { state: 'need-input', exitCode: 0, stdout: out, stderr: err, stats };
|
|
1483
|
+
}
|
|
1484
|
+
S = null;
|
|
1485
|
+
return { state: 'error', exitCode: 1, stdout: out, stderr: err, error: 'bash-runner: deadlock — live procs with empty run queue', stats };
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
globalThis.__bashBoot = function __bashBoot(args: BashBootArgs): BashSlice {
|
|
1489
|
+
try {
|
|
1490
|
+
S = newSession(args);
|
|
1491
|
+
initStdinQueued(S);
|
|
1492
|
+
const root = makeProc(S, S.pidNext++, 0, new Map([
|
|
1493
|
+
[0, { kind: 'stdin' }], [1, { kind: 'stdout' }], [2, { kind: 'stderr' }],
|
|
1494
|
+
[3, { kind: 'preopen' }],
|
|
1495
|
+
]));
|
|
1496
|
+
S.rootPid = root.pid;
|
|
1497
|
+
setupArena(root);
|
|
1498
|
+
S.runnable.push(root);
|
|
1499
|
+
return pump(S);
|
|
1500
|
+
} catch (e) {
|
|
1501
|
+
S = null;
|
|
1502
|
+
return { state: 'error', exitCode: 1, stdout: '', stderr: '', error: 'boot failed: ' + String(e && (e as Error).message || e) };
|
|
1503
|
+
}
|
|
1504
|
+
};
|
|
1505
|
+
|
|
1506
|
+
globalThis.__bashFeed = function __bashFeed(args: BashFeedArgs): BashSlice {
|
|
1507
|
+
if (!S) {
|
|
1508
|
+
return { state: 'error', exitCode: 1, stdout: '', stderr: '', error: 'bash facet has no active session (warm isolate recycled?)' };
|
|
1509
|
+
}
|
|
1510
|
+
try {
|
|
1511
|
+
if (args.data) { const b = te.encode(args.data); S.stdin.chunks.push(b); S.stdin.queued += b.length; }
|
|
1512
|
+
if (args.eof) S.stdin.closed = true;
|
|
1513
|
+
wakeStdin(S);
|
|
1514
|
+
return pump(S);
|
|
1515
|
+
} catch (e) {
|
|
1516
|
+
const s = S; S = null;
|
|
1517
|
+
return { state: 'error', exitCode: 1, stdout: s ? s.out : '', stderr: s ? s.err : '', error: 'feed failed: ' + String(e && (e as Error).message || e) };
|
|
1518
|
+
}
|
|
1519
|
+
};
|