@smthrs/std 0.0.0-stage → 1.0.0-rc.4
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/CHANGELOG.md +130 -0
- package/LICENSE +21 -0
- package/README.md +152 -2
- package/dist/cjs/ApplyPatch.d.ts +174 -0
- package/dist/cjs/ApplyPatch.d.ts.map +1 -0
- package/dist/cjs/ApplyPatch.js +309 -0
- package/dist/cjs/ApplyPatch.js.map +7 -0
- package/dist/cjs/Bash.d.ts +380 -0
- package/dist/cjs/Bash.d.ts.map +1 -0
- package/dist/cjs/Bash.js +332 -0
- package/dist/cjs/Bash.js.map +7 -0
- package/dist/cjs/Checkpoints.d.ts +279 -0
- package/dist/cjs/Checkpoints.d.ts.map +1 -0
- package/dist/cjs/Checkpoints.js +145 -0
- package/dist/cjs/Checkpoints.js.map +7 -0
- package/dist/cjs/Container.d.ts +132 -0
- package/dist/cjs/Container.d.ts.map +1 -0
- package/dist/cjs/Container.js +113 -0
- package/dist/cjs/Container.js.map +7 -0
- package/dist/cjs/Edit.d.ts +203 -0
- package/dist/cjs/Edit.d.ts.map +1 -0
- package/dist/cjs/Edit.js +258 -0
- package/dist/cjs/Edit.js.map +7 -0
- package/dist/cjs/ExaWebSearch.d.ts +22 -0
- package/dist/cjs/ExaWebSearch.d.ts.map +1 -0
- package/dist/cjs/ExaWebSearch.js +138 -0
- package/dist/cjs/ExaWebSearch.js.map +7 -0
- package/dist/cjs/Explore.d.ts +328 -0
- package/dist/cjs/Explore.d.ts.map +1 -0
- package/dist/cjs/Explore.js +90 -0
- package/dist/cjs/Explore.js.map +7 -0
- package/dist/cjs/Fetch.d.ts +130 -0
- package/dist/cjs/Fetch.d.ts.map +1 -0
- package/dist/cjs/Fetch.js +64 -0
- package/dist/cjs/Fetch.js.map +7 -0
- package/dist/cjs/Glob.d.ts +204 -0
- package/dist/cjs/Glob.d.ts.map +1 -0
- package/dist/cjs/Glob.js +94 -0
- package/dist/cjs/Glob.js.map +7 -0
- package/dist/cjs/Grep.d.ts +347 -0
- package/dist/cjs/Grep.d.ts.map +1 -0
- package/dist/cjs/Grep.js +181 -0
- package/dist/cjs/Grep.js.map +7 -0
- package/dist/cjs/HttpPost.d.ts +130 -0
- package/dist/cjs/HttpPost.d.ts.map +1 -0
- package/dist/cjs/HttpPost.js +72 -0
- package/dist/cjs/HttpPost.js.map +7 -0
- package/dist/cjs/LanguageServer.d.ts +90 -0
- package/dist/cjs/LanguageServer.d.ts.map +1 -0
- package/dist/cjs/LanguageServer.js +60 -0
- package/dist/cjs/LanguageServer.js.map +7 -0
- package/dist/cjs/Ls.d.ts +164 -0
- package/dist/cjs/Ls.d.ts.map +1 -0
- package/dist/cjs/Ls.js +136 -0
- package/dist/cjs/Ls.js.map +7 -0
- package/dist/cjs/Lsp.d.ts +125 -0
- package/dist/cjs/Lsp.d.ts.map +1 -0
- package/dist/cjs/Lsp.js +137 -0
- package/dist/cjs/Lsp.js.map +7 -0
- package/dist/cjs/Manifest.d.ts +918 -0
- package/dist/cjs/Manifest.d.ts.map +1 -0
- package/dist/cjs/Manifest.js +144 -0
- package/dist/cjs/Manifest.js.map +7 -0
- package/dist/cjs/NativeSearch.d.ts +38 -0
- package/dist/cjs/NativeSearch.d.ts.map +1 -0
- package/dist/cjs/NativeSearch.js +326 -0
- package/dist/cjs/NativeSearch.js.map +7 -0
- package/dist/cjs/NodeLanguageServer.d.ts +128 -0
- package/dist/cjs/NodeLanguageServer.d.ts.map +1 -0
- package/dist/cjs/NodeLanguageServer.js +796 -0
- package/dist/cjs/NodeLanguageServer.js.map +7 -0
- package/dist/cjs/PortableSearch.d.ts +24 -0
- package/dist/cjs/PortableSearch.d.ts.map +1 -0
- package/dist/cjs/PortableSearch.js +233 -0
- package/dist/cjs/PortableSearch.js.map +7 -0
- package/dist/cjs/Probe.d.ts +193 -0
- package/dist/cjs/Probe.d.ts.map +1 -0
- package/dist/cjs/Probe.js +134 -0
- package/dist/cjs/Probe.js.map +7 -0
- package/dist/cjs/Read.d.ts +189 -0
- package/dist/cjs/Read.d.ts.map +1 -0
- package/dist/cjs/Read.js +223 -0
- package/dist/cjs/Read.js.map +7 -0
- package/dist/cjs/Relocate.d.ts +73 -0
- package/dist/cjs/Relocate.d.ts.map +1 -0
- package/dist/cjs/Relocate.js +72 -0
- package/dist/cjs/Relocate.js.map +7 -0
- package/dist/cjs/Search.d.ts +163 -0
- package/dist/cjs/Search.d.ts.map +1 -0
- package/dist/cjs/Search.js +51 -0
- package/dist/cjs/Search.js.map +7 -0
- package/dist/cjs/SearchConformance.d.ts +115 -0
- package/dist/cjs/SearchConformance.d.ts.map +1 -0
- package/dist/cjs/SearchConformance.js +172 -0
- package/dist/cjs/SearchConformance.js.map +7 -0
- package/dist/cjs/SearchContract.d.ts +114 -0
- package/dist/cjs/SearchContract.d.ts.map +1 -0
- package/dist/cjs/SearchContract.js +339 -0
- package/dist/cjs/SearchContract.js.map +7 -0
- package/dist/cjs/ShellCommand.d.ts +164 -0
- package/dist/cjs/ShellCommand.d.ts.map +1 -0
- package/dist/cjs/ShellCommand.js +102 -0
- package/dist/cjs/ShellCommand.js.map +7 -0
- package/dist/cjs/StdError.d.ts +55 -0
- package/dist/cjs/StdError.d.ts.map +1 -0
- package/dist/cjs/StdError.js +74 -0
- package/dist/cjs/StdError.js.map +7 -0
- package/dist/cjs/TestRun.d.ts +311 -0
- package/dist/cjs/TestRun.d.ts.map +1 -0
- package/dist/cjs/TestRun.js +228 -0
- package/dist/cjs/TestRun.js.map +7 -0
- package/dist/cjs/TestRunner.d.ts +102 -0
- package/dist/cjs/TestRunner.d.ts.map +1 -0
- package/dist/cjs/TestRunner.js +54 -0
- package/dist/cjs/TestRunner.js.map +7 -0
- package/dist/cjs/TreeFingerprint.d.ts +140 -0
- package/dist/cjs/TreeFingerprint.d.ts.map +1 -0
- package/dist/cjs/TreeFingerprint.js +87 -0
- package/dist/cjs/TreeFingerprint.js.map +7 -0
- package/dist/cjs/UpdatePlan.d.ts +152 -0
- package/dist/cjs/UpdatePlan.d.ts.map +1 -0
- package/dist/cjs/UpdatePlan.js +87 -0
- package/dist/cjs/UpdatePlan.js.map +7 -0
- package/dist/cjs/WebFetch.d.ts +134 -0
- package/dist/cjs/WebFetch.d.ts.map +1 -0
- package/dist/cjs/WebFetch.js +140 -0
- package/dist/cjs/WebFetch.js.map +7 -0
- package/dist/cjs/WebSearch.d.ts +181 -0
- package/dist/cjs/WebSearch.d.ts.map +1 -0
- package/dist/cjs/WebSearch.js +86 -0
- package/dist/cjs/WebSearch.js.map +7 -0
- package/dist/cjs/Write.d.ts +147 -0
- package/dist/cjs/Write.d.ts.map +1 -0
- package/dist/cjs/Write.js +122 -0
- package/dist/cjs/Write.js.map +7 -0
- package/dist/cjs/index.d.ts +127 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +99 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/internal/ApplyPatch.d.ts +145 -0
- package/dist/cjs/internal/ApplyPatch.d.ts.map +1 -0
- package/dist/cjs/internal/ApplyPatch.js +462 -0
- package/dist/cjs/internal/ApplyPatch.js.map +7 -0
- package/dist/cjs/internal/CodexText.d.ts +73 -0
- package/dist/cjs/internal/CodexText.d.ts.map +1 -0
- package/dist/cjs/internal/CodexText.js +108 -0
- package/dist/cjs/internal/CodexText.js.map +7 -0
- package/dist/cjs/internal/Declaration.d.ts +49 -0
- package/dist/cjs/internal/Declaration.d.ts.map +1 -0
- package/dist/cjs/internal/Declaration.js +46 -0
- package/dist/cjs/internal/Declaration.js.map +7 -0
- package/dist/cjs/internal/Diagnostics.d.ts +77 -0
- package/dist/cjs/internal/Diagnostics.d.ts.map +1 -0
- package/dist/cjs/internal/Diagnostics.js +89 -0
- package/dist/cjs/internal/Diagnostics.js.map +7 -0
- package/dist/cjs/internal/EnvelopePrecheck.d.ts +67 -0
- package/dist/cjs/internal/EnvelopePrecheck.d.ts.map +1 -0
- package/dist/cjs/internal/EnvelopePrecheck.js +124 -0
- package/dist/cjs/internal/EnvelopePrecheck.js.map +7 -0
- package/dist/cjs/internal/Exec.d.ts +139 -0
- package/dist/cjs/internal/Exec.d.ts.map +1 -0
- package/dist/cjs/internal/Exec.js +158 -0
- package/dist/cjs/internal/Exec.js.map +7 -0
- package/dist/cjs/internal/FileMutation.d.ts +109 -0
- package/dist/cjs/internal/FileMutation.d.ts.map +1 -0
- package/dist/cjs/internal/FileMutation.js +116 -0
- package/dist/cjs/internal/FileMutation.js.map +7 -0
- package/dist/cjs/internal/FsFailure.d.ts +38 -0
- package/dist/cjs/internal/FsFailure.d.ts.map +1 -0
- package/dist/cjs/internal/FsFailure.js +40 -0
- package/dist/cjs/internal/FsFailure.js.map +7 -0
- package/dist/cjs/internal/GitWorktree.d.ts +108 -0
- package/dist/cjs/internal/GitWorktree.d.ts.map +1 -0
- package/dist/cjs/internal/GitWorktree.js +276 -0
- package/dist/cjs/internal/GitWorktree.js.map +7 -0
- package/dist/cjs/internal/Grouping.d.ts +51 -0
- package/dist/cjs/internal/Grouping.d.ts.map +1 -0
- package/dist/cjs/internal/Grouping.js +83 -0
- package/dist/cjs/internal/Grouping.js.map +7 -0
- package/dist/cjs/internal/Html.d.ts +20 -0
- package/dist/cjs/internal/Html.d.ts.map +1 -0
- package/dist/cjs/internal/Html.js +81 -0
- package/dist/cjs/internal/Html.js.map +7 -0
- package/dist/cjs/internal/Http.d.ts +95 -0
- package/dist/cjs/internal/Http.d.ts.map +1 -0
- package/dist/cjs/internal/Http.js +153 -0
- package/dist/cjs/internal/Http.js.map +7 -0
- package/dist/cjs/internal/HttpNetwork.d.ts +27 -0
- package/dist/cjs/internal/HttpNetwork.d.ts.map +1 -0
- package/dist/cjs/internal/HttpNetwork.js +146 -0
- package/dist/cjs/internal/HttpNetwork.js.map +7 -0
- package/dist/cjs/internal/Ignore.d.ts +37 -0
- package/dist/cjs/internal/Ignore.d.ts.map +1 -0
- package/dist/cjs/internal/Ignore.js +100 -0
- package/dist/cjs/internal/Ignore.js.map +7 -0
- package/dist/cjs/internal/LinearRegex.d.ts +19 -0
- package/dist/cjs/internal/LinearRegex.d.ts.map +1 -0
- package/dist/cjs/internal/LinearRegex.js +166 -0
- package/dist/cjs/internal/LinearRegex.js.map +7 -0
- package/dist/cjs/internal/Match.d.ts +85 -0
- package/dist/cjs/internal/Match.d.ts.map +1 -0
- package/dist/cjs/internal/Match.js +99 -0
- package/dist/cjs/internal/Match.js.map +7 -0
- package/dist/cjs/internal/Paths.d.ts +23 -0
- package/dist/cjs/internal/Paths.d.ts.map +1 -0
- package/dist/cjs/internal/Paths.js +52 -0
- package/dist/cjs/internal/Paths.js.map +7 -0
- package/dist/cjs/internal/Preserve.d.ts +25 -0
- package/dist/cjs/internal/Preserve.d.ts.map +1 -0
- package/dist/cjs/internal/Preserve.js +88 -0
- package/dist/cjs/internal/Preserve.js.map +7 -0
- package/dist/cjs/internal/Prune.d.ts +35 -0
- package/dist/cjs/internal/Prune.d.ts.map +1 -0
- package/dist/cjs/internal/Prune.js +54 -0
- package/dist/cjs/internal/Prune.js.map +7 -0
- package/dist/cjs/internal/SearchContract.d.ts +63 -0
- package/dist/cjs/internal/SearchContract.d.ts.map +1 -0
- package/dist/cjs/internal/SearchContract.js +46 -0
- package/dist/cjs/internal/SearchContract.js.map +7 -0
- package/dist/cjs/internal/Symbols.d.ts +39 -0
- package/dist/cjs/internal/Symbols.d.ts.map +1 -0
- package/dist/cjs/internal/Symbols.js +59 -0
- package/dist/cjs/internal/Symbols.js.map +7 -0
- package/dist/cjs/internal/TestReport.d.ts +52 -0
- package/dist/cjs/internal/TestReport.d.ts.map +1 -0
- package/dist/cjs/internal/TestReport.js +131 -0
- package/dist/cjs/internal/TestReport.js.map +7 -0
- package/dist/cjs/internal/Text.d.ts +114 -0
- package/dist/cjs/internal/Text.d.ts.map +1 -0
- package/dist/cjs/internal/Text.js +82 -0
- package/dist/cjs/internal/Text.js.map +7 -0
- package/dist/cjs/internal/Url.d.ts +13 -0
- package/dist/cjs/internal/Url.d.ts.map +1 -0
- package/dist/cjs/internal/Url.js +35 -0
- package/dist/cjs/internal/Url.js.map +7 -0
- package/dist/cjs/internal/Walk.d.ts +72 -0
- package/dist/cjs/internal/Walk.d.ts.map +1 -0
- package/dist/cjs/internal/Walk.js +108 -0
- package/dist/cjs/internal/Walk.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/esm/ApplyPatch.d.ts +174 -0
- package/dist/esm/ApplyPatch.d.ts.map +1 -0
- package/dist/esm/ApplyPatch.js +346 -0
- package/dist/esm/ApplyPatch.js.map +1 -0
- package/dist/esm/Bash.d.ts +380 -0
- package/dist/esm/Bash.d.ts.map +1 -0
- package/dist/esm/Bash.js +505 -0
- package/dist/esm/Bash.js.map +1 -0
- package/dist/esm/Checkpoints.d.ts +279 -0
- package/dist/esm/Checkpoints.d.ts.map +1 -0
- package/dist/esm/Checkpoints.js +298 -0
- package/dist/esm/Checkpoints.js.map +1 -0
- package/dist/esm/Container.d.ts +132 -0
- package/dist/esm/Container.d.ts.map +1 -0
- package/dist/esm/Container.js +151 -0
- package/dist/esm/Container.js.map +1 -0
- package/dist/esm/Edit.d.ts +203 -0
- package/dist/esm/Edit.d.ts.map +1 -0
- package/dist/esm/Edit.js +296 -0
- package/dist/esm/Edit.js.map +1 -0
- package/dist/esm/ExaWebSearch.d.ts +22 -0
- package/dist/esm/ExaWebSearch.d.ts.map +1 -0
- package/dist/esm/ExaWebSearch.js +107 -0
- package/dist/esm/ExaWebSearch.js.map +1 -0
- package/dist/esm/Explore.d.ts +328 -0
- package/dist/esm/Explore.d.ts.map +1 -0
- package/dist/esm/Explore.js +106 -0
- package/dist/esm/Explore.js.map +1 -0
- package/dist/esm/Fetch.d.ts +130 -0
- package/dist/esm/Fetch.d.ts.map +1 -0
- package/dist/esm/Fetch.js +91 -0
- package/dist/esm/Fetch.js.map +1 -0
- package/dist/esm/Glob.d.ts +204 -0
- package/dist/esm/Glob.d.ts.map +1 -0
- package/dist/esm/Glob.js +177 -0
- package/dist/esm/Glob.js.map +1 -0
- package/dist/esm/Grep.d.ts +347 -0
- package/dist/esm/Grep.d.ts.map +1 -0
- package/dist/esm/Grep.js +289 -0
- package/dist/esm/Grep.js.map +1 -0
- package/dist/esm/HttpPost.d.ts +130 -0
- package/dist/esm/HttpPost.d.ts.map +1 -0
- package/dist/esm/HttpPost.js +89 -0
- package/dist/esm/HttpPost.js.map +1 -0
- package/dist/esm/LanguageServer.d.ts +90 -0
- package/dist/esm/LanguageServer.d.ts.map +1 -0
- package/dist/esm/LanguageServer.js +57 -0
- package/dist/esm/LanguageServer.js.map +1 -0
- package/dist/esm/Ls.d.ts +164 -0
- package/dist/esm/Ls.d.ts.map +1 -0
- package/dist/esm/Ls.js +178 -0
- package/dist/esm/Ls.js.map +1 -0
- package/dist/esm/Lsp.d.ts +125 -0
- package/dist/esm/Lsp.d.ts.map +1 -0
- package/dist/esm/Lsp.js +165 -0
- package/dist/esm/Lsp.js.map +1 -0
- package/dist/esm/Manifest.d.ts +918 -0
- package/dist/esm/Manifest.d.ts.map +1 -0
- package/dist/esm/Manifest.js +153 -0
- package/dist/esm/Manifest.js.map +1 -0
- package/dist/esm/NativeSearch.d.ts +38 -0
- package/dist/esm/NativeSearch.d.ts.map +1 -0
- package/dist/esm/NativeSearch.js +378 -0
- package/dist/esm/NativeSearch.js.map +1 -0
- package/dist/esm/NodeLanguageServer.d.ts +128 -0
- package/dist/esm/NodeLanguageServer.d.ts.map +1 -0
- package/dist/esm/NodeLanguageServer.js +843 -0
- package/dist/esm/NodeLanguageServer.js.map +1 -0
- package/dist/esm/PortableSearch.d.ts +24 -0
- package/dist/esm/PortableSearch.d.ts.map +1 -0
- package/dist/esm/PortableSearch.js +240 -0
- package/dist/esm/PortableSearch.js.map +1 -0
- package/dist/esm/Probe.d.ts +193 -0
- package/dist/esm/Probe.d.ts.map +1 -0
- package/dist/esm/Probe.js +225 -0
- package/dist/esm/Probe.js.map +1 -0
- package/dist/esm/Read.d.ts +189 -0
- package/dist/esm/Read.d.ts.map +1 -0
- package/dist/esm/Read.js +300 -0
- package/dist/esm/Read.js.map +1 -0
- package/dist/esm/Relocate.d.ts +73 -0
- package/dist/esm/Relocate.d.ts.map +1 -0
- package/dist/esm/Relocate.js +153 -0
- package/dist/esm/Relocate.js.map +1 -0
- package/dist/esm/Search.d.ts +163 -0
- package/dist/esm/Search.d.ts.map +1 -0
- package/dist/esm/Search.js +47 -0
- package/dist/esm/Search.js.map +1 -0
- package/dist/esm/SearchConformance.d.ts +115 -0
- package/dist/esm/SearchConformance.d.ts.map +1 -0
- package/dist/esm/SearchConformance.js +193 -0
- package/dist/esm/SearchConformance.js.map +1 -0
- package/dist/esm/SearchContract.d.ts +114 -0
- package/dist/esm/SearchContract.d.ts.map +1 -0
- package/dist/esm/SearchContract.js +461 -0
- package/dist/esm/SearchContract.js.map +1 -0
- package/dist/esm/ShellCommand.d.ts +164 -0
- package/dist/esm/ShellCommand.d.ts.map +1 -0
- package/dist/esm/ShellCommand.js +159 -0
- package/dist/esm/ShellCommand.js.map +1 -0
- package/dist/esm/StdError.d.ts +55 -0
- package/dist/esm/StdError.d.ts.map +1 -0
- package/dist/esm/StdError.js +71 -0
- package/dist/esm/StdError.js.map +1 -0
- package/dist/esm/TestRun.d.ts +311 -0
- package/dist/esm/TestRun.d.ts.map +1 -0
- package/dist/esm/TestRun.js +316 -0
- package/dist/esm/TestRun.js.map +1 -0
- package/dist/esm/TestRunner.d.ts +102 -0
- package/dist/esm/TestRunner.d.ts.map +1 -0
- package/dist/esm/TestRunner.js +68 -0
- package/dist/esm/TestRunner.js.map +1 -0
- package/dist/esm/TreeFingerprint.d.ts +140 -0
- package/dist/esm/TreeFingerprint.d.ts.map +1 -0
- package/dist/esm/TreeFingerprint.js +170 -0
- package/dist/esm/TreeFingerprint.js.map +1 -0
- package/dist/esm/UpdatePlan.d.ts +152 -0
- package/dist/esm/UpdatePlan.d.ts.map +1 -0
- package/dist/esm/UpdatePlan.js +129 -0
- package/dist/esm/UpdatePlan.js.map +1 -0
- package/dist/esm/WebFetch.d.ts +134 -0
- package/dist/esm/WebFetch.d.ts.map +1 -0
- package/dist/esm/WebFetch.js +151 -0
- package/dist/esm/WebFetch.js.map +1 -0
- package/dist/esm/WebSearch.d.ts +181 -0
- package/dist/esm/WebSearch.d.ts.map +1 -0
- package/dist/esm/WebSearch.js +134 -0
- package/dist/esm/WebSearch.js.map +1 -0
- package/dist/esm/Write.d.ts +147 -0
- package/dist/esm/Write.d.ts.map +1 -0
- package/dist/esm/Write.js +144 -0
- package/dist/esm/Write.js.map +1 -0
- package/dist/esm/index.d.ts +127 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +127 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/ApplyPatch.d.ts +145 -0
- package/dist/esm/internal/ApplyPatch.d.ts.map +1 -0
- package/dist/esm/internal/ApplyPatch.js +489 -0
- package/dist/esm/internal/ApplyPatch.js.map +1 -0
- package/dist/esm/internal/CodexText.d.ts +73 -0
- package/dist/esm/internal/CodexText.d.ts.map +1 -0
- package/dist/esm/internal/CodexText.js +156 -0
- package/dist/esm/internal/CodexText.js.map +1 -0
- package/dist/esm/internal/Declaration.d.ts +49 -0
- package/dist/esm/internal/Declaration.d.ts.map +1 -0
- package/dist/esm/internal/Declaration.js +42 -0
- package/dist/esm/internal/Declaration.js.map +1 -0
- package/dist/esm/internal/Diagnostics.d.ts +77 -0
- package/dist/esm/internal/Diagnostics.d.ts.map +1 -0
- package/dist/esm/internal/Diagnostics.js +99 -0
- package/dist/esm/internal/Diagnostics.js.map +1 -0
- package/dist/esm/internal/EnvelopePrecheck.d.ts +67 -0
- package/dist/esm/internal/EnvelopePrecheck.d.ts.map +1 -0
- package/dist/esm/internal/EnvelopePrecheck.js +167 -0
- package/dist/esm/internal/EnvelopePrecheck.js.map +1 -0
- package/dist/esm/internal/Exec.d.ts +139 -0
- package/dist/esm/internal/Exec.d.ts.map +1 -0
- package/dist/esm/internal/Exec.js +193 -0
- package/dist/esm/internal/Exec.js.map +1 -0
- package/dist/esm/internal/FileMutation.d.ts +109 -0
- package/dist/esm/internal/FileMutation.d.ts.map +1 -0
- package/dist/esm/internal/FileMutation.js +123 -0
- package/dist/esm/internal/FileMutation.js.map +1 -0
- package/dist/esm/internal/FsFailure.d.ts +38 -0
- package/dist/esm/internal/FsFailure.d.ts.map +1 -0
- package/dist/esm/internal/FsFailure.js +39 -0
- package/dist/esm/internal/FsFailure.js.map +1 -0
- package/dist/esm/internal/GitWorktree.d.ts +108 -0
- package/dist/esm/internal/GitWorktree.d.ts.map +1 -0
- package/dist/esm/internal/GitWorktree.js +352 -0
- package/dist/esm/internal/GitWorktree.js.map +1 -0
- package/dist/esm/internal/Grouping.d.ts +51 -0
- package/dist/esm/internal/Grouping.d.ts.map +1 -0
- package/dist/esm/internal/Grouping.js +99 -0
- package/dist/esm/internal/Grouping.js.map +1 -0
- package/dist/esm/internal/Html.d.ts +20 -0
- package/dist/esm/internal/Html.d.ts.map +1 -0
- package/dist/esm/internal/Html.js +70 -0
- package/dist/esm/internal/Html.js.map +1 -0
- package/dist/esm/internal/Http.d.ts +95 -0
- package/dist/esm/internal/Http.d.ts.map +1 -0
- package/dist/esm/internal/Http.js +154 -0
- package/dist/esm/internal/Http.js.map +1 -0
- package/dist/esm/internal/HttpNetwork.d.ts +27 -0
- package/dist/esm/internal/HttpNetwork.d.ts.map +1 -0
- package/dist/esm/internal/HttpNetwork.js +114 -0
- package/dist/esm/internal/HttpNetwork.js.map +1 -0
- package/dist/esm/internal/Ignore.d.ts +37 -0
- package/dist/esm/internal/Ignore.d.ts.map +1 -0
- package/dist/esm/internal/Ignore.js +128 -0
- package/dist/esm/internal/Ignore.js.map +1 -0
- package/dist/esm/internal/LinearRegex.d.ts +19 -0
- package/dist/esm/internal/LinearRegex.d.ts.map +1 -0
- package/dist/esm/internal/LinearRegex.js +176 -0
- package/dist/esm/internal/LinearRegex.js.map +1 -0
- package/dist/esm/internal/Match.d.ts +85 -0
- package/dist/esm/internal/Match.d.ts.map +1 -0
- package/dist/esm/internal/Match.js +142 -0
- package/dist/esm/internal/Match.js.map +1 -0
- package/dist/esm/internal/Paths.d.ts +23 -0
- package/dist/esm/internal/Paths.d.ts.map +1 -0
- package/dist/esm/internal/Paths.js +40 -0
- package/dist/esm/internal/Paths.js.map +1 -0
- package/dist/esm/internal/Preserve.d.ts +25 -0
- package/dist/esm/internal/Preserve.d.ts.map +1 -0
- package/dist/esm/internal/Preserve.js +70 -0
- package/dist/esm/internal/Preserve.js.map +1 -0
- package/dist/esm/internal/Prune.d.ts +35 -0
- package/dist/esm/internal/Prune.d.ts.map +1 -0
- package/dist/esm/internal/Prune.js +60 -0
- package/dist/esm/internal/Prune.js.map +1 -0
- package/dist/esm/internal/SearchContract.d.ts +63 -0
- package/dist/esm/internal/SearchContract.d.ts.map +1 -0
- package/dist/esm/internal/SearchContract.js +66 -0
- package/dist/esm/internal/SearchContract.js.map +1 -0
- package/dist/esm/internal/Symbols.d.ts +39 -0
- package/dist/esm/internal/Symbols.d.ts.map +1 -0
- package/dist/esm/internal/Symbols.js +79 -0
- package/dist/esm/internal/Symbols.js.map +1 -0
- package/dist/esm/internal/TestReport.d.ts +52 -0
- package/dist/esm/internal/TestReport.d.ts.map +1 -0
- package/dist/esm/internal/TestReport.js +172 -0
- package/dist/esm/internal/TestReport.js.map +1 -0
- package/dist/esm/internal/Text.d.ts +114 -0
- package/dist/esm/internal/Text.d.ts.map +1 -0
- package/dist/esm/internal/Text.js +130 -0
- package/dist/esm/internal/Text.js.map +1 -0
- package/dist/esm/internal/Url.d.ts +13 -0
- package/dist/esm/internal/Url.d.ts.map +1 -0
- package/dist/esm/internal/Url.js +27 -0
- package/dist/esm/internal/Url.js.map +1 -0
- package/dist/esm/internal/Walk.d.ts +72 -0
- package/dist/esm/internal/Walk.d.ts.map +1 -0
- package/dist/esm/internal/Walk.js +126 -0
- package/dist/esm/internal/Walk.js.map +1 -0
- package/docs/README.md +117 -0
- package/docs/api.md +472 -0
- package/docs/concepts/effects-and-capabilities.md +136 -0
- package/docs/concepts/flows-and-handlers.md +115 -0
- package/docs/concepts/limits-and-disclosure.md +102 -0
- package/docs/concepts/search-contract.md +182 -0
- package/docs/guides/bind-the-standard-flows.md +114 -0
- package/docs/guides/implement-a-search-peer.md +133 -0
- package/docs/guides/pin-a-checkpoint.md +141 -0
- package/docs/guides/query-a-language-server.md +257 -0
- package/docs/guides/reach-the-network.md +141 -0
- package/docs/guides/read-and-edit-a-file.md +240 -0
- package/docs/guides/run-a-shell-command.md +215 -0
- package/docs/guides/run-the-test-suite.md +154 -0
- package/docs/guides/search-a-workspace.md +148 -0
- package/docs/installation.md +111 -0
- package/docs/quickstart.md +125 -0
- package/docs/reference/flows.md +556 -0
- package/docs/troubleshooting.md +322 -0
- package/package.json +439 -3
- package/src/ApplyPatch.ts +458 -0
- package/src/Bash.ts +607 -0
- package/src/Checkpoints.ts +422 -0
- package/src/Container.ts +212 -0
- package/src/Edit.ts +366 -0
- package/src/ExaWebSearch.ts +145 -0
- package/src/Explore.ts +139 -0
- package/src/Fetch.ts +117 -0
- package/src/Glob.ts +200 -0
- package/src/Grep.ts +320 -0
- package/src/HttpPost.ts +120 -0
- package/src/LanguageServer.ts +110 -0
- package/src/Ls.ts +228 -0
- package/src/Lsp.ts +184 -0
- package/src/Manifest.ts +162 -0
- package/src/NativeSearch.ts +457 -0
- package/src/NodeLanguageServer.ts +1148 -0
- package/src/PortableSearch.ts +264 -0
- package/src/Probe.ts +284 -0
- package/src/Read.ts +359 -0
- package/src/Relocate.ts +166 -0
- package/src/Search.ts +185 -0
- package/src/SearchConformance.ts +288 -0
- package/src/SearchContract.ts +489 -0
- package/src/ShellCommand.ts +201 -0
- package/src/StdError.ts +80 -0
- package/src/TestRun.ts +401 -0
- package/src/TestRunner.ts +119 -0
- package/src/TreeFingerprint.ts +200 -0
- package/src/UpdatePlan.ts +171 -0
- package/src/WebFetch.ts +194 -0
- package/src/WebSearch.ts +165 -0
- package/src/Write.ts +195 -0
- package/src/index.ts +154 -0
- package/src/internal/ApplyPatch.ts +601 -0
- package/src/internal/CodexText.ts +182 -0
- package/src/internal/Declaration.ts +60 -0
- package/src/internal/Diagnostics.ts +134 -0
- package/src/internal/EnvelopePrecheck.ts +233 -0
- package/src/internal/Exec.ts +314 -0
- package/src/internal/FileMutation.ts +231 -0
- package/src/internal/FsFailure.ts +51 -0
- package/src/internal/GitWorktree.ts +491 -0
- package/src/internal/Grouping.ts +106 -0
- package/src/internal/Html.ts +87 -0
- package/src/internal/Http.ts +209 -0
- package/src/internal/HttpNetwork.ts +153 -0
- package/src/internal/Ignore.ts +135 -0
- package/src/internal/LinearRegex.ts +173 -0
- package/src/internal/Match.ts +172 -0
- package/src/internal/Paths.ts +42 -0
- package/src/internal/Preserve.ts +108 -0
- package/src/internal/Prune.ts +62 -0
- package/src/internal/SearchContract.ts +77 -0
- package/src/internal/Symbols.ts +88 -0
- package/src/internal/TestReport.ts +194 -0
- package/src/internal/Text.ts +170 -0
- package/src/internal/Url.ts +23 -0
- package/src/internal/Walk.ts +176 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The hermetic pre-check: a lexical scan of shell text for the paths it names,
|
|
3
|
+
* and the refusal when one of them falls outside the declared envelope.
|
|
4
|
+
*
|
|
5
|
+
* The scan is fail-closed over explicit path tokens and nothing more. It
|
|
6
|
+
* cannot prove confinement — a shell can name a path no lexer sees, through a
|
|
7
|
+
* variable, a substitution, or a program that opens a file of its own — so a
|
|
8
|
+
* kernel or host sandbox must eventually enforce and report the complete read
|
|
9
|
+
* and write sets.
|
|
10
|
+
*
|
|
11
|
+
* It lives beside the flow rather than inside it because it is its own
|
|
12
|
+
* concept, with its own vocabulary (Access, PathReference) and its own
|
|
13
|
+
* security history: three independent bypasses, each sufficient on its own to
|
|
14
|
+
* defeat it, are closed here.
|
|
15
|
+
*
|
|
16
|
+
* @since 1.0.0
|
|
17
|
+
*/
|
|
18
|
+
import * as StdError from "../StdError.js";
|
|
19
|
+
import { withinEnvelope } from "./Paths.js";
|
|
20
|
+
const commandSeparators = new Set(["&&", "||", ";", "|", "&", "\n"]);
|
|
21
|
+
const redirections = new Set([">", ">>", "<", "<<"]);
|
|
22
|
+
const writeCommands = new Set(["chmod", "chown", "mkdir", "rm", "rmdir", "tee", "touch", "truncate", "unlink"]);
|
|
23
|
+
const destinationCommands = new Set(["cp", "install", "mv"]);
|
|
24
|
+
const prefixWrappers = new Set(["env", "nice", "time", "sudo", "nohup", "xargs", "command", "exec"]);
|
|
25
|
+
// Shell spaces may disappear, but a physical line ends one command. Retaining
|
|
26
|
+
// LF prevents a command on line one from classifying every later line's paths.
|
|
27
|
+
//
|
|
28
|
+
// A word is one or more runs of bare characters and quoted sections, not a
|
|
29
|
+
// bare run alone: `FOO="a b" rm /work/target` used to tokenize as `FOO="a`,
|
|
30
|
+
// `b"`, `rm`, `/work/target`, and since only the first token carried an `=`
|
|
31
|
+
// the command was read as `b"` rather than `rm` — so the delete was
|
|
32
|
+
// classified as a read and a hermetic call with `writes: []` spawned. The
|
|
33
|
+
// quoted alternatives come first inside the group so a quoted span is taken
|
|
34
|
+
// whole; the bare class excludes quotes so the branches cannot overlap.
|
|
35
|
+
const tokenize = (command) => command.match(/\n|>>|<<|&&|\|\||[<>;|&]|(?:"(?:\\.|[^"\\])*"|'[^']*'|[^\s<>;|&"']+)+/g) ?? [];
|
|
36
|
+
const unquote = (token) => {
|
|
37
|
+
const first = token[0];
|
|
38
|
+
const last = token[token.length - 1];
|
|
39
|
+
return token.length >= 2 && (first === "'" || first === "\"") && last === first
|
|
40
|
+
? token.slice(1, -1)
|
|
41
|
+
: token;
|
|
42
|
+
};
|
|
43
|
+
const pathValue = (token) => unquote(token)
|
|
44
|
+
.replace(/^\$\(+/, "")
|
|
45
|
+
.replace(/[),]+$/, "");
|
|
46
|
+
const isPathToken = (path, token) => token === "." ||
|
|
47
|
+
token === ".." ||
|
|
48
|
+
token.startsWith("./") ||
|
|
49
|
+
token.startsWith("../") ||
|
|
50
|
+
path.isAbsolute(token) ||
|
|
51
|
+
/^[A-Za-z]:[\\/]/.test(token) ||
|
|
52
|
+
token.includes("/");
|
|
53
|
+
const segmentReferences = (path, tokens) => {
|
|
54
|
+
const commentIndex = tokens.findIndex((token) => token.startsWith("#"));
|
|
55
|
+
const active = commentIndex === -1 ? tokens : tokens.slice(0, commentIndex);
|
|
56
|
+
const hasWrapper = active.some((token) => prefixWrappers.has(path.basename(pathValue(token))));
|
|
57
|
+
let commandIndex = active.findIndex((token) => !redirections.has(token) &&
|
|
58
|
+
!token.includes("=") &&
|
|
59
|
+
!prefixWrappers.has(path.basename(pathValue(token))) &&
|
|
60
|
+
!token.startsWith("-") &&
|
|
61
|
+
token !== "");
|
|
62
|
+
if (hasWrapper) {
|
|
63
|
+
// Wrapper options can carry their own values. Prefer a later known
|
|
64
|
+
// mutating command so an option value cannot lend it a read classification.
|
|
65
|
+
const mutatingIndex = active.findIndex((token) => {
|
|
66
|
+
const command = path.basename(pathValue(token));
|
|
67
|
+
return writeCommands.has(command) || destinationCommands.has(command);
|
|
68
|
+
});
|
|
69
|
+
if (mutatingIndex !== -1)
|
|
70
|
+
commandIndex = mutatingIndex;
|
|
71
|
+
}
|
|
72
|
+
if (commandIndex === -1)
|
|
73
|
+
return [];
|
|
74
|
+
const command = path.basename(pathValue(active[commandIndex]));
|
|
75
|
+
const candidates = [];
|
|
76
|
+
for (let index = 0; index < active.length; index++) {
|
|
77
|
+
const token = active[index];
|
|
78
|
+
if (index === commandIndex || redirections.has(token) || token.startsWith("-"))
|
|
79
|
+
continue;
|
|
80
|
+
const value = pathValue(token);
|
|
81
|
+
if (value !== "" && isPathToken(path, value))
|
|
82
|
+
candidates.push({ index, value });
|
|
83
|
+
}
|
|
84
|
+
const destinationIndex = destinationCommands.has(command)
|
|
85
|
+
? candidates[candidates.length - 1]?.index
|
|
86
|
+
: undefined;
|
|
87
|
+
return candidates.map(({ index, value }) => {
|
|
88
|
+
const previous = active[index - 1];
|
|
89
|
+
const access = previous === ">" || previous === ">>" ||
|
|
90
|
+
writeCommands.has(command) || destinationIndex === index
|
|
91
|
+
? "write"
|
|
92
|
+
: "read";
|
|
93
|
+
return { access, value };
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Every path token the shell text names, one segment at a time.
|
|
98
|
+
*
|
|
99
|
+
* @category pre-check
|
|
100
|
+
* @since 1.0.0
|
|
101
|
+
*/
|
|
102
|
+
export const commandReferences = (path, command) => {
|
|
103
|
+
const references = [];
|
|
104
|
+
let segment = [];
|
|
105
|
+
for (const token of tokenize(command)) {
|
|
106
|
+
if (commandSeparators.has(token)) {
|
|
107
|
+
references.push(...segmentReferences(path, segment));
|
|
108
|
+
segment = [];
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
segment.push(token);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
references.push(...segmentReferences(path, segment));
|
|
115
|
+
return references;
|
|
116
|
+
};
|
|
117
|
+
const resolvePath = (path, cwd, value) => path.isAbsolute(value) ? path.normalize(value) : path.resolve(cwd, value);
|
|
118
|
+
const isDeclared = (path, cwd, declared, source) => withinEnvelope(declared.map((entry) => resolvePath(path, cwd, entry)), resolvePath(path, cwd, source));
|
|
119
|
+
/**
|
|
120
|
+
* The refusal shell text earns by naming a path outside its declared
|
|
121
|
+
* envelope, or `undefined` when every path it names is declared.
|
|
122
|
+
*
|
|
123
|
+
* @category pre-check
|
|
124
|
+
* @since 1.0.0
|
|
125
|
+
*/
|
|
126
|
+
export const outsideEnvelope = (input, text, path) => {
|
|
127
|
+
const base = path.resolve(".");
|
|
128
|
+
const cwd = path.resolve(input.cwd ?? ".");
|
|
129
|
+
// Passing the default explicitly is not a declaration the caller owes. A
|
|
130
|
+
// working directory is where the command runs, not a file it reads, and
|
|
131
|
+
// `cwd: "."` names exactly what omitting `cwd` names — so refusing it asked
|
|
132
|
+
// the caller to list the workspace root among its reads to say nothing at
|
|
133
|
+
// all. Agents hit this constantly: one SWE-bench run spent ten of its
|
|
134
|
+
// forty-eight tool calls re-issuing the same command after
|
|
135
|
+
// "Working directory is outside declared reads: <the workspace root>". A
|
|
136
|
+
// cwd that points somewhere else is still declared or refused.
|
|
137
|
+
if (input.cwd !== undefined && cwd !== base && !isDeclared(path, cwd, input.reads, cwd)) {
|
|
138
|
+
return new StdError.StdError({
|
|
139
|
+
code: "outside_declared_reads",
|
|
140
|
+
message: `Working directory is outside declared reads: ${cwd}`,
|
|
141
|
+
path: cwd
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
// The Shell contract currently has no sandbox or access-reporting surface.
|
|
145
|
+
// This lexical scan is intentionally only a fail-closed pre-check for
|
|
146
|
+
// explicit path tokens. It cannot prove confinement; a kernel/host sandbox
|
|
147
|
+
// must eventually enforce and report the complete read and write sets.
|
|
148
|
+
for (const reference of commandReferences(path, text)) {
|
|
149
|
+
const resolved = resolvePath(path, cwd, reference.value);
|
|
150
|
+
// /dev/* is the process plumbing every shell one-liner leans on:
|
|
151
|
+
// `2>/dev/null`, `cat /dev/stdin`. Normalize first so a dot-dot segment
|
|
152
|
+
// cannot disguise an ordinary filesystem path as process plumbing.
|
|
153
|
+
if (resolved.startsWith("/dev/"))
|
|
154
|
+
continue;
|
|
155
|
+
const declared = reference.access === "write" ? input.writes : input.reads;
|
|
156
|
+
if (!isDeclared(path, cwd, declared, resolved)) {
|
|
157
|
+
const code = reference.access === "write" ? "outside_declared_writes" : "outside_declared_reads";
|
|
158
|
+
return new StdError.StdError({
|
|
159
|
+
code,
|
|
160
|
+
message: `Command path is outside declared ${reference.access}s: ${resolved}`,
|
|
161
|
+
path: resolved
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return undefined;
|
|
166
|
+
};
|
|
167
|
+
//# sourceMappingURL=EnvelopePrecheck.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EnvelopePrecheck.js","sourceRoot":"","sources":["../../../src/internal/EnvelopePrecheck.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The hermetic pre-check: a lexical scan of shell text for the paths it names,\n * and the refusal when one of them falls outside the declared envelope.\n *\n * The scan is fail-closed over explicit path tokens and nothing more. It\n * cannot prove confinement — a shell can name a path no lexer sees, through a\n * variable, a substitution, or a program that opens a file of its own — so a\n * kernel or host sandbox must eventually enforce and report the complete read\n * and write sets.\n *\n * It lives beside the flow rather than inside it because it is its own\n * concept, with its own vocabulary (Access, PathReference) and its own\n * security history: three independent bypasses, each sufficient on its own to\n * defeat it, are closed here.\n *\n * @since 1.0.0\n */\n\nimport type * as Path from \"@smthrs/kernel/Path\"\nimport * as StdError from \"../StdError.ts\"\nimport { withinEnvelope } from \"./Paths.ts\"\n\n/**\n * The filesystem envelope one invocation declared.\n *\n * Structural rather than the hermetic `Bash.Input` itself: the scan needs the\n * three fields and none of the rest, and depending on the flow module would\n * make the flow and its pre-check import each other.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface Envelope {\n readonly cwd?: string | undefined\n readonly reads: ReadonlyArray<string>\n readonly writes: ReadonlyArray<string>\n}\n\n/**\n * How a command touches a path it names.\n *\n * @category models\n * @since 1.0.0\n */\nexport type Access = \"read\" | \"write\"\n\n/**\n * One path a command names, and how it touches it.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface PathReference {\n readonly access: Access\n readonly value: string\n}\n\nconst commandSeparators = new Set([\"&&\", \"||\", \";\", \"|\", \"&\", \"\\n\"])\nconst redirections = new Set([\">\", \">>\", \"<\", \"<<\"])\nconst writeCommands = new Set([\"chmod\", \"chown\", \"mkdir\", \"rm\", \"rmdir\", \"tee\", \"touch\", \"truncate\", \"unlink\"])\nconst destinationCommands = new Set([\"cp\", \"install\", \"mv\"])\nconst prefixWrappers = new Set([\"env\", \"nice\", \"time\", \"sudo\", \"nohup\", \"xargs\", \"command\", \"exec\"])\n\n// Shell spaces may disappear, but a physical line ends one command. Retaining\n// LF prevents a command on line one from classifying every later line's paths.\n//\n// A word is one or more runs of bare characters and quoted sections, not a\n// bare run alone: `FOO=\"a b\" rm /work/target` used to tokenize as `FOO=\"a`,\n// `b\"`, `rm`, `/work/target`, and since only the first token carried an `=`\n// the command was read as `b\"` rather than `rm` — so the delete was\n// classified as a read and a hermetic call with `writes: []` spawned. The\n// quoted alternatives come first inside the group so a quoted span is taken\n// whole; the bare class excludes quotes so the branches cannot overlap.\nconst tokenize = (command: string): ReadonlyArray<string> =>\n command.match(/\\n|>>|<<|&&|\\|\\||[<>;|&]|(?:\"(?:\\\\.|[^\"\\\\])*\"|'[^']*'|[^\\s<>;|&\"']+)+/g) ?? []\n\nconst unquote = (token: string): string => {\n const first = token[0]\n const last = token[token.length - 1]\n return token.length >= 2 && (first === \"'\" || first === \"\\\"\") && last === first\n ? token.slice(1, -1)\n : token\n}\n\nconst pathValue = (token: string): string =>\n unquote(token)\n .replace(/^\\$\\(+/, \"\")\n .replace(/[),]+$/, \"\")\n\nconst isPathToken = (path: Path.Path, token: string): boolean =>\n token === \".\" ||\n token === \"..\" ||\n token.startsWith(\"./\") ||\n token.startsWith(\"../\") ||\n path.isAbsolute(token) ||\n /^[A-Za-z]:[\\\\/]/.test(token) ||\n token.includes(\"/\")\n\nconst segmentReferences = (path: Path.Path, tokens: ReadonlyArray<string>): ReadonlyArray<PathReference> => {\n const commentIndex = tokens.findIndex((token) => token.startsWith(\"#\"))\n const active = commentIndex === -1 ? tokens : tokens.slice(0, commentIndex)\n const hasWrapper = active.some((token) => prefixWrappers.has(path.basename(pathValue(token))))\n let commandIndex = active.findIndex((token) =>\n !redirections.has(token) &&\n !token.includes(\"=\") &&\n !prefixWrappers.has(path.basename(pathValue(token))) &&\n !token.startsWith(\"-\") &&\n token !== \"\"\n )\n if (hasWrapper) {\n // Wrapper options can carry their own values. Prefer a later known\n // mutating command so an option value cannot lend it a read classification.\n const mutatingIndex = active.findIndex((token) => {\n const command = path.basename(pathValue(token))\n return writeCommands.has(command) || destinationCommands.has(command)\n })\n if (mutatingIndex !== -1) commandIndex = mutatingIndex\n }\n if (commandIndex === -1) return []\n\n const command = path.basename(pathValue(active[commandIndex]!))\n const candidates: Array<{ readonly index: number; readonly value: string }> = []\n for (let index = 0; index < active.length; index++) {\n const token = active[index]!\n if (index === commandIndex || redirections.has(token) || token.startsWith(\"-\")) continue\n const value = pathValue(token)\n if (value !== \"\" && isPathToken(path, value)) candidates.push({ index, value })\n }\n\n const destinationIndex = destinationCommands.has(command)\n ? candidates[candidates.length - 1]?.index\n : undefined\n\n return candidates.map(({ index, value }) => {\n const previous = active[index - 1]\n const access: Access = previous === \">\" || previous === \">>\" ||\n writeCommands.has(command) || destinationIndex === index\n ? \"write\"\n : \"read\"\n return { access, value }\n })\n}\n\n/**\n * Every path token the shell text names, one segment at a time.\n *\n * @category pre-check\n * @since 1.0.0\n */\nexport const commandReferences = (path: Path.Path, command: string): ReadonlyArray<PathReference> => {\n const references: Array<PathReference> = []\n let segment: Array<string> = []\n for (const token of tokenize(command)) {\n if (commandSeparators.has(token)) {\n references.push(...segmentReferences(path, segment))\n segment = []\n } else {\n segment.push(token)\n }\n }\n references.push(...segmentReferences(path, segment))\n return references\n}\n\nconst resolvePath = (\n path: Path.Path,\n cwd: string,\n value: string\n): string => path.isAbsolute(value) ? path.normalize(value) : path.resolve(cwd, value)\n\nconst isDeclared = (\n path: Path.Path,\n cwd: string,\n declared: ReadonlyArray<string>,\n source: string\n): boolean =>\n withinEnvelope(\n declared.map((entry) => resolvePath(path, cwd, entry)),\n resolvePath(path, cwd, source)\n )\n\n/**\n * The refusal shell text earns by naming a path outside its declared\n * envelope, or `undefined` when every path it names is declared.\n *\n * @category pre-check\n * @since 1.0.0\n */\nexport const outsideEnvelope = (\n input: Envelope,\n text: string,\n path: Path.Path\n): StdError.StdError | undefined => {\n const base = path.resolve(\".\")\n const cwd = path.resolve(input.cwd ?? \".\")\n // Passing the default explicitly is not a declaration the caller owes. A\n // working directory is where the command runs, not a file it reads, and\n // `cwd: \".\"` names exactly what omitting `cwd` names — so refusing it asked\n // the caller to list the workspace root among its reads to say nothing at\n // all. Agents hit this constantly: one SWE-bench run spent ten of its\n // forty-eight tool calls re-issuing the same command after\n // \"Working directory is outside declared reads: <the workspace root>\". A\n // cwd that points somewhere else is still declared or refused.\n if (input.cwd !== undefined && cwd !== base && !isDeclared(path, cwd, input.reads, cwd)) {\n return new StdError.StdError({\n code: \"outside_declared_reads\",\n message: `Working directory is outside declared reads: ${cwd}`,\n path: cwd\n })\n }\n\n // The Shell contract currently has no sandbox or access-reporting surface.\n // This lexical scan is intentionally only a fail-closed pre-check for\n // explicit path tokens. It cannot prove confinement; a kernel/host sandbox\n // must eventually enforce and report the complete read and write sets.\n for (const reference of commandReferences(path, text)) {\n const resolved = resolvePath(path, cwd, reference.value)\n // /dev/* is the process plumbing every shell one-liner leans on:\n // `2>/dev/null`, `cat /dev/stdin`. Normalize first so a dot-dot segment\n // cannot disguise an ordinary filesystem path as process plumbing.\n if (resolved.startsWith(\"/dev/\")) continue\n const declared = reference.access === \"write\" ? input.writes : input.reads\n if (!isDeclared(path, cwd, declared, resolved)) {\n const code = reference.access === \"write\" ? \"outside_declared_writes\" : \"outside_declared_reads\"\n return new StdError.StdError({\n code,\n message: `Command path is outside declared ${reference.access}s: ${resolved}`,\n path: resolved\n })\n }\n }\n return undefined\n}\n"]}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Buffered shell execution over the permission-aware process spawner.
|
|
3
|
+
*
|
|
4
|
+
* `@smthrs/kernel` used to expose a `Shell` service whose `exec` returned a
|
|
5
|
+
* buffered `{ stdout, stderr, exitCode }`. The kernel replaced it with
|
|
6
|
+
* `ChildProcessSpawner`, Effect's own process boundary, which streams and has
|
|
7
|
+
* no buffered form. This module is the buffered form the shell flows still
|
|
8
|
+
* want, expressed in terms of the spawner so the `proc:spawn` capability check
|
|
9
|
+
* and the command-line resource stay exactly where the kernel put them.
|
|
10
|
+
*
|
|
11
|
+
* Commands run through the platform shell (`shell: true`), matching the old
|
|
12
|
+
* service: the flows take a command *line*, not an argv.
|
|
13
|
+
*
|
|
14
|
+
* @since 1.0.0
|
|
15
|
+
*/
|
|
16
|
+
import * as ChildProcessSpawner from "@smthrs/kernel/ChildProcessSpawner";
|
|
17
|
+
import * as Effect from "effect/Effect";
|
|
18
|
+
import * as Schema from "effect/Schema";
|
|
19
|
+
import * as StdError from "../StdError.ts";
|
|
20
|
+
/**
|
|
21
|
+
* Failure codes a buffered execution can report, matching the codes the
|
|
22
|
+
* removed kernel `Shell` used so handler-level mapping is unchanged.
|
|
23
|
+
*
|
|
24
|
+
* @category models
|
|
25
|
+
* @since 1.0.0
|
|
26
|
+
*/
|
|
27
|
+
export declare const ExecErrorCode: Schema.Literals<readonly ["timeout", "spawn_error", "capture_overflow"]>;
|
|
28
|
+
/**
|
|
29
|
+
* Failure codes a buffered execution can report.
|
|
30
|
+
*
|
|
31
|
+
* @category models
|
|
32
|
+
* @since 1.0.0
|
|
33
|
+
*/
|
|
34
|
+
export type ExecErrorCode = typeof ExecErrorCode.Type;
|
|
35
|
+
declare const ExecError_base: Schema.Class<ExecError, Schema.TaggedStruct<"@smthrs/std/ExecError", {
|
|
36
|
+
readonly code: Schema.Literals<readonly ["timeout", "spawn_error", "capture_overflow"]>;
|
|
37
|
+
readonly message: Schema.String;
|
|
38
|
+
/** The limit a `timeout` ran past, in milliseconds. */
|
|
39
|
+
readonly limitMillis: Schema.optional<Schema.Number>;
|
|
40
|
+
}>, import("effect/Cause").YieldableError>;
|
|
41
|
+
/**
|
|
42
|
+
* A command that never produced an exit code.
|
|
43
|
+
*
|
|
44
|
+
* A non-zero exit is a successful {@link ExecResult}, not a failure; this
|
|
45
|
+
* error is reserved for a command that could not start or was cut off by its
|
|
46
|
+
* timeout, or whose captured output exceeded a refusing capture bound.
|
|
47
|
+
*
|
|
48
|
+
* @category errors
|
|
49
|
+
* @since 1.0.0
|
|
50
|
+
*/
|
|
51
|
+
export declare class ExecError extends ExecError_base {
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The buffered result of one command.
|
|
55
|
+
*
|
|
56
|
+
* @category models
|
|
57
|
+
* @since 1.0.0
|
|
58
|
+
*/
|
|
59
|
+
export interface ExecResult {
|
|
60
|
+
readonly stdout: string;
|
|
61
|
+
readonly stderr: string;
|
|
62
|
+
readonly exitCode: number;
|
|
63
|
+
/** UTF-8 bytes dropped from the head of stdout to stay inside the capture bound. */
|
|
64
|
+
readonly stdoutDroppedBytes: number;
|
|
65
|
+
/** UTF-8 bytes dropped from the head of stderr to stay inside the capture bound. */
|
|
66
|
+
readonly stderrDroppedBytes: number;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Options accepted by {@link exec}.
|
|
70
|
+
*
|
|
71
|
+
* @category models
|
|
72
|
+
* @since 1.0.0
|
|
73
|
+
*/
|
|
74
|
+
export interface ExecOptions {
|
|
75
|
+
readonly cwd?: string | undefined;
|
|
76
|
+
readonly env?: Readonly<Record<string, string>> | undefined;
|
|
77
|
+
readonly timeoutMs?: number | undefined;
|
|
78
|
+
/**
|
|
79
|
+
* The program's arguments. Present means an argv, absent means a command
|
|
80
|
+
* line for the platform shell. An argv is how a payload reaches a program
|
|
81
|
+
* without being quoted into a string: nothing between here and `execve`
|
|
82
|
+
* re-parses it.
|
|
83
|
+
*/
|
|
84
|
+
readonly args?: ReadonlyArray<string> | undefined;
|
|
85
|
+
/**
|
|
86
|
+
* Text written to the program's standard input, as data. A script delivered
|
|
87
|
+
* this way is never quoted, escaped, or heredoc-terminated, which is the
|
|
88
|
+
* whole point: quoting corruption cost the measured SWE-bench program twelve
|
|
89
|
+
* probe failures and one instance's most expensive frame.
|
|
90
|
+
*/
|
|
91
|
+
readonly stdin?: string | undefined;
|
|
92
|
+
/**
|
|
93
|
+
* Bytes retained per captured stream. A stream that prints more keeps its
|
|
94
|
+
* tail and reports what it dropped, so a command printing gigabytes costs a
|
|
95
|
+
* bounded amount of memory rather than the whole of what it printed.
|
|
96
|
+
*
|
|
97
|
+
* Absent means unbounded, which is what a caller wants when it needs every
|
|
98
|
+
* byte a tool produced (`rg --files`, `git worktree list`).
|
|
99
|
+
*/
|
|
100
|
+
readonly maxCaptureBytes?: number | undefined;
|
|
101
|
+
/** Defaults to `tail`; `refuse` fails if either stream exceeds the bound. */
|
|
102
|
+
readonly overflow?: "tail" | "refuse" | undefined;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Runs a command line through the platform shell and buffers its output.
|
|
106
|
+
*
|
|
107
|
+
* Cancellation is scope closure: the spawn is scoped, so an interrupted fiber
|
|
108
|
+
* — including the one a `timeoutMs` interrupts — terminates the process rather
|
|
109
|
+
* than leaking it.
|
|
110
|
+
*
|
|
111
|
+
* @category execution
|
|
112
|
+
* @since 1.0.0
|
|
113
|
+
*/
|
|
114
|
+
export declare const exec: (command: string, options?: ExecOptions) => Effect.Effect<ExecResult, ExecError, ChildProcessSpawner.ChildProcessSpawner>;
|
|
115
|
+
/**
|
|
116
|
+
* The model-facing failure code a command that never produced an exit code
|
|
117
|
+
* carries.
|
|
118
|
+
*
|
|
119
|
+
* A timeout keeps its own code, because "the command never finished" and "the
|
|
120
|
+
* command never started" are different things to a caller deciding whether to
|
|
121
|
+
* retry. Every other way of producing no exit code is a host failure.
|
|
122
|
+
*
|
|
123
|
+
* @category errors
|
|
124
|
+
* @since 1.0.0
|
|
125
|
+
*/
|
|
126
|
+
export declare const toStdErrorCode: (error: ExecError) => StdError.Code;
|
|
127
|
+
/**
|
|
128
|
+
* The model-facing failure one execution error becomes.
|
|
129
|
+
*
|
|
130
|
+
* A timeout names the command, which is what the caller asked for and all a
|
|
131
|
+
* timeout has to say. Any other failure carries the message the execution
|
|
132
|
+
* produced, which already names the command.
|
|
133
|
+
*
|
|
134
|
+
* @category errors
|
|
135
|
+
* @since 1.0.0
|
|
136
|
+
*/
|
|
137
|
+
export declare const toStdError: (command: string, error: ExecError) => StdError.StdError;
|
|
138
|
+
export {};
|
|
139
|
+
//# sourceMappingURL=Exec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Exec.d.ts","sourceRoot":"","sources":["../../../src/internal/Exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,mBAAmB,MAAM,oCAAoC,CAAA;AACzE,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAGvC,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAA;AAE1C;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,0EAAkE,CAAA;AAE5F;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAC,IAAI,CAAA;;;;IAenD,uDAAuD;;;AAbzD;;;;;;;;;GASG;AACH,qBAAa,SAAU,SAAQ,cAK7B;CAAG;AAEL;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,oFAAoF;IACpF,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAA;IACnC,oFAAoF;IACpF,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAA;CACpC;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,CAAA;IAC3D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,SAAS,CAAA;IACjD;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC;;;;;;;OAOG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7C,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;CAClD;AAiJD;;;;;;;;;GASG;AACH,eAAO,MAAM,IAAI,GACf,SAAS,MAAM,EACf,UAAS,WAAgB,KACxB,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,EAAE,mBAAmB,CAAC,mBAAmB,CAazE,CAAA;AAEN;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc,GAAI,OAAO,SAAS,KAAG,QAAQ,CAAC,IACF,CAAA;AAEzD;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,EAAE,OAAO,SAAS,KAAG,QAAQ,CAAC,QAOpE,CAAA"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Buffered shell execution over the permission-aware process spawner.
|
|
3
|
+
*
|
|
4
|
+
* `@smthrs/kernel` used to expose a `Shell` service whose `exec` returned a
|
|
5
|
+
* buffered `{ stdout, stderr, exitCode }`. The kernel replaced it with
|
|
6
|
+
* `ChildProcessSpawner`, Effect's own process boundary, which streams and has
|
|
7
|
+
* no buffered form. This module is the buffered form the shell flows still
|
|
8
|
+
* want, expressed in terms of the spawner so the `proc:spawn` capability check
|
|
9
|
+
* and the command-line resource stay exactly where the kernel put them.
|
|
10
|
+
*
|
|
11
|
+
* Commands run through the platform shell (`shell: true`), matching the old
|
|
12
|
+
* service: the flows take a command *line*, not an argv.
|
|
13
|
+
*
|
|
14
|
+
* @since 1.0.0
|
|
15
|
+
*/
|
|
16
|
+
import * as ChildProcessEnvironment from "@smthrs/kernel/ChildProcessEnvironment";
|
|
17
|
+
import * as ChildProcessSpawner from "@smthrs/kernel/ChildProcessSpawner";
|
|
18
|
+
import * as Effect from "effect/Effect";
|
|
19
|
+
import * as Schema from "effect/Schema";
|
|
20
|
+
import * as Stream from "effect/Stream";
|
|
21
|
+
import * as ChildProcess from "effect/unstable/process/ChildProcess";
|
|
22
|
+
import * as StdError from "../StdError.js";
|
|
23
|
+
/**
|
|
24
|
+
* Failure codes a buffered execution can report, matching the codes the
|
|
25
|
+
* removed kernel `Shell` used so handler-level mapping is unchanged.
|
|
26
|
+
*
|
|
27
|
+
* @category models
|
|
28
|
+
* @since 1.0.0
|
|
29
|
+
*/
|
|
30
|
+
export const ExecErrorCode = Schema.Literals(["timeout", "spawn_error", "capture_overflow"]);
|
|
31
|
+
/**
|
|
32
|
+
* A command that never produced an exit code.
|
|
33
|
+
*
|
|
34
|
+
* A non-zero exit is a successful {@link ExecResult}, not a failure; this
|
|
35
|
+
* error is reserved for a command that could not start or was cut off by its
|
|
36
|
+
* timeout, or whose captured output exceeded a refusing capture bound.
|
|
37
|
+
*
|
|
38
|
+
* @category errors
|
|
39
|
+
* @since 1.0.0
|
|
40
|
+
*/
|
|
41
|
+
export class ExecError extends Schema.TaggedError()("@smthrs/std/ExecError", {
|
|
42
|
+
code: ExecErrorCode,
|
|
43
|
+
message: Schema.String,
|
|
44
|
+
/** The limit a `timeout` ran past, in milliseconds. */
|
|
45
|
+
limitMillis: Schema.optional(Schema.Number)
|
|
46
|
+
}) {
|
|
47
|
+
}
|
|
48
|
+
const decoder = new TextDecoder("utf-8");
|
|
49
|
+
/**
|
|
50
|
+
* Joins the retained chunks and decodes them, skipping the partial code point a
|
|
51
|
+
* dropped head can leave behind.
|
|
52
|
+
*
|
|
53
|
+
* A UTF-8 continuation byte is `10xxxxxx`, and a code point is at most four
|
|
54
|
+
* bytes, so at most three leading continuation bytes can precede the first
|
|
55
|
+
* whole character. Skipping them is counted as dropped rather than decoded to a
|
|
56
|
+
* replacement character the process never printed.
|
|
57
|
+
*/
|
|
58
|
+
const decodeTail = (state) => {
|
|
59
|
+
const bytes = new Uint8Array(state.bytes);
|
|
60
|
+
let offset = 0;
|
|
61
|
+
for (const chunk of state.chunks) {
|
|
62
|
+
bytes.set(chunk, offset);
|
|
63
|
+
offset += chunk.byteLength;
|
|
64
|
+
}
|
|
65
|
+
if (state.dropped === 0)
|
|
66
|
+
return { text: decoder.decode(bytes), droppedBytes: 0 };
|
|
67
|
+
let start = 0;
|
|
68
|
+
while (start < bytes.byteLength && start < 3 && (bytes[start] & 0b1100_0000) === 0b1000_0000)
|
|
69
|
+
start++;
|
|
70
|
+
return { text: decoder.decode(bytes.subarray(start)), droppedBytes: state.dropped + start };
|
|
71
|
+
};
|
|
72
|
+
const boundedTail = (stream, maxBytes, overflow) => Stream.runFold(stream, () => ({ chunks: [], bytes: 0, dropped: 0 }), (state, chunk) => {
|
|
73
|
+
if (chunk.byteLength === 0)
|
|
74
|
+
return state;
|
|
75
|
+
state.chunks.push(chunk);
|
|
76
|
+
state.bytes += chunk.byteLength;
|
|
77
|
+
while (state.bytes > maxBytes && state.chunks.length > 0) {
|
|
78
|
+
const excess = state.bytes - maxBytes;
|
|
79
|
+
const first = state.chunks[0];
|
|
80
|
+
if (first.byteLength <= excess) {
|
|
81
|
+
state.chunks.shift();
|
|
82
|
+
state.bytes -= first.byteLength;
|
|
83
|
+
state.dropped += first.byteLength;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
state.chunks[0] = first.subarray(excess);
|
|
87
|
+
state.bytes -= excess;
|
|
88
|
+
state.dropped += excess;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return state;
|
|
92
|
+
}).pipe(Effect.map((state) => overflow === "refuse" && state.dropped > 0
|
|
93
|
+
? { text: "", droppedBytes: state.dropped }
|
|
94
|
+
: decodeTail(state)));
|
|
95
|
+
const capture = (stream, maxBytes, overflow) => maxBytes === undefined
|
|
96
|
+
? Effect.map(Stream.mkString(Stream.decodeText(stream)), (text) => ({ text, droppedBytes: 0 }))
|
|
97
|
+
: boundedTail(stream, maxBytes, overflow);
|
|
98
|
+
const unbounded = (command, options) => Effect.gen(function* () {
|
|
99
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
100
|
+
const settings = {
|
|
101
|
+
...(options.args === undefined ? { shell: true } : {}),
|
|
102
|
+
...(options.cwd === undefined ? {} : { cwd: options.cwd }),
|
|
103
|
+
env: ChildProcessEnvironment.make(process.env, options.env),
|
|
104
|
+
extendEnv: false,
|
|
105
|
+
...(options.stdin === undefined
|
|
106
|
+
? {}
|
|
107
|
+
: { stdin: Stream.make(new TextEncoder().encode(options.stdin)) })
|
|
108
|
+
};
|
|
109
|
+
const handle = yield* spawner.spawn(options.args === undefined
|
|
110
|
+
? ChildProcess.make(command, settings)
|
|
111
|
+
: ChildProcess.make(command, [...options.args], settings));
|
|
112
|
+
// stdout, stderr, and the exit code have to be consumed concurrently: a
|
|
113
|
+
// command that fills a pipe blocks until the pipe is drained, so waiting
|
|
114
|
+
// for exit first would deadlock on any output larger than the buffer.
|
|
115
|
+
const [stdout, stderr, exitCode] = yield* Effect.all([
|
|
116
|
+
capture(handle.stdout, options.maxCaptureBytes, options.overflow),
|
|
117
|
+
capture(handle.stderr, options.maxCaptureBytes, options.overflow),
|
|
118
|
+
handle.exitCode
|
|
119
|
+
], { concurrency: "unbounded" });
|
|
120
|
+
if (options.overflow === "refuse") {
|
|
121
|
+
const overflowed = [
|
|
122
|
+
stdout.droppedBytes > 0 ? "stdout" : undefined,
|
|
123
|
+
stderr.droppedBytes > 0 ? "stderr" : undefined
|
|
124
|
+
]
|
|
125
|
+
.filter((stream) => stream !== undefined);
|
|
126
|
+
if (overflowed.length > 0) {
|
|
127
|
+
return yield* Effect.fail(new ExecError({
|
|
128
|
+
code: "capture_overflow",
|
|
129
|
+
message: `${command} ${overflowed.join(" and ")} exceeded the ${options.maxCaptureBytes}-byte capture cap; refusing partial output`
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
stdout: stdout.text,
|
|
135
|
+
stderr: stderr.text,
|
|
136
|
+
exitCode,
|
|
137
|
+
stdoutDroppedBytes: stdout.droppedBytes,
|
|
138
|
+
stderrDroppedBytes: stderr.droppedBytes
|
|
139
|
+
};
|
|
140
|
+
}).pipe(Effect.scoped, Effect.mapError((error) => error instanceof ExecError ? error : new ExecError({
|
|
141
|
+
code: "spawn_error",
|
|
142
|
+
message: `exec: ${command}: ${error.message}`
|
|
143
|
+
})));
|
|
144
|
+
/**
|
|
145
|
+
* Runs a command line through the platform shell and buffers its output.
|
|
146
|
+
*
|
|
147
|
+
* Cancellation is scope closure: the spawn is scoped, so an interrupted fiber
|
|
148
|
+
* — including the one a `timeoutMs` interrupts — terminates the process rather
|
|
149
|
+
* than leaking it.
|
|
150
|
+
*
|
|
151
|
+
* @category execution
|
|
152
|
+
* @since 1.0.0
|
|
153
|
+
*/
|
|
154
|
+
export const exec = (command, options = {}) => options.timeoutMs === undefined
|
|
155
|
+
? unbounded(command, options)
|
|
156
|
+
: Effect.timeoutOrElse(unbounded(command, options), {
|
|
157
|
+
duration: options.timeoutMs,
|
|
158
|
+
orElse: () => Effect.fail(new ExecError({
|
|
159
|
+
code: "timeout",
|
|
160
|
+
message: `exec: \`${command}\` exceeded ${options.timeoutMs}ms`,
|
|
161
|
+
limitMillis: options.timeoutMs
|
|
162
|
+
}))
|
|
163
|
+
});
|
|
164
|
+
/**
|
|
165
|
+
* The model-facing failure code a command that never produced an exit code
|
|
166
|
+
* carries.
|
|
167
|
+
*
|
|
168
|
+
* A timeout keeps its own code, because "the command never finished" and "the
|
|
169
|
+
* command never started" are different things to a caller deciding whether to
|
|
170
|
+
* retry. Every other way of producing no exit code is a host failure.
|
|
171
|
+
*
|
|
172
|
+
* @category errors
|
|
173
|
+
* @since 1.0.0
|
|
174
|
+
*/
|
|
175
|
+
export const toStdErrorCode = (error) => error.code === "timeout" ? "timeout" : "command_failed";
|
|
176
|
+
/**
|
|
177
|
+
* The model-facing failure one execution error becomes.
|
|
178
|
+
*
|
|
179
|
+
* A timeout names the command, which is what the caller asked for and all a
|
|
180
|
+
* timeout has to say. Any other failure carries the message the execution
|
|
181
|
+
* produced, which already names the command.
|
|
182
|
+
*
|
|
183
|
+
* @category errors
|
|
184
|
+
* @since 1.0.0
|
|
185
|
+
*/
|
|
186
|
+
export const toStdError = (command, error) => new StdError.StdError({
|
|
187
|
+
code: toStdErrorCode(error),
|
|
188
|
+
message: error.code === "timeout"
|
|
189
|
+
? `Command timed out: ${command}`
|
|
190
|
+
: `Command failed to start: ${error.message}`,
|
|
191
|
+
...(error.limitMillis === undefined ? {} : { limitMillis: error.limitMillis })
|
|
192
|
+
});
|
|
193
|
+
//# sourceMappingURL=Exec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Exec.js","sourceRoot":"","sources":["../../../src/internal/Exec.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Buffered shell execution over the permission-aware process spawner.\n *\n * `@smthrs/kernel` used to expose a `Shell` service whose `exec` returned a\n * buffered `{ stdout, stderr, exitCode }`. The kernel replaced it with\n * `ChildProcessSpawner`, Effect's own process boundary, which streams and has\n * no buffered form. This module is the buffered form the shell flows still\n * want, expressed in terms of the spawner so the `proc:spawn` capability check\n * and the command-line resource stay exactly where the kernel put them.\n *\n * Commands run through the platform shell (`shell: true`), matching the old\n * service: the flows take a command *line*, not an argv.\n *\n * @since 1.0.0\n */\n\nimport * as ChildProcessEnvironment from \"@smthrs/kernel/ChildProcessEnvironment\"\nimport * as ChildProcessSpawner from \"@smthrs/kernel/ChildProcessSpawner\"\nimport * as Effect from \"effect/Effect\"\nimport * as Schema from \"effect/Schema\"\nimport * as Stream from \"effect/Stream\"\nimport * as ChildProcess from \"effect/unstable/process/ChildProcess\"\nimport * as StdError from \"../StdError.ts\"\n\n/**\n * Failure codes a buffered execution can report, matching the codes the\n * removed kernel `Shell` used so handler-level mapping is unchanged.\n *\n * @category models\n * @since 1.0.0\n */\nexport const ExecErrorCode = Schema.Literals([\"timeout\", \"spawn_error\", \"capture_overflow\"])\n\n/**\n * Failure codes a buffered execution can report.\n *\n * @category models\n * @since 1.0.0\n */\nexport type ExecErrorCode = typeof ExecErrorCode.Type\n\n/**\n * A command that never produced an exit code.\n *\n * A non-zero exit is a successful {@link ExecResult}, not a failure; this\n * error is reserved for a command that could not start or was cut off by its\n * timeout, or whose captured output exceeded a refusing capture bound.\n *\n * @category errors\n * @since 1.0.0\n */\nexport class ExecError extends Schema.TaggedError<ExecError>()(\"@smthrs/std/ExecError\", {\n code: ExecErrorCode,\n message: Schema.String,\n /** The limit a `timeout` ran past, in milliseconds. */\n limitMillis: Schema.optional(Schema.Number)\n}) {}\n\n/**\n * The buffered result of one command.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface ExecResult {\n readonly stdout: string\n readonly stderr: string\n readonly exitCode: number\n /** UTF-8 bytes dropped from the head of stdout to stay inside the capture bound. */\n readonly stdoutDroppedBytes: number\n /** UTF-8 bytes dropped from the head of stderr to stay inside the capture bound. */\n readonly stderrDroppedBytes: number\n}\n\n/**\n * Options accepted by {@link exec}.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface ExecOptions {\n readonly cwd?: string | undefined\n readonly env?: Readonly<Record<string, string>> | undefined\n readonly timeoutMs?: number | undefined\n /**\n * The program's arguments. Present means an argv, absent means a command\n * line for the platform shell. An argv is how a payload reaches a program\n * without being quoted into a string: nothing between here and `execve`\n * re-parses it.\n */\n readonly args?: ReadonlyArray<string> | undefined\n /**\n * Text written to the program's standard input, as data. A script delivered\n * this way is never quoted, escaped, or heredoc-terminated, which is the\n * whole point: quoting corruption cost the measured SWE-bench program twelve\n * probe failures and one instance's most expensive frame.\n */\n readonly stdin?: string | undefined\n /**\n * Bytes retained per captured stream. A stream that prints more keeps its\n * tail and reports what it dropped, so a command printing gigabytes costs a\n * bounded amount of memory rather than the whole of what it printed.\n *\n * Absent means unbounded, which is what a caller wants when it needs every\n * byte a tool produced (`rg --files`, `git worktree list`).\n */\n readonly maxCaptureBytes?: number | undefined\n /** Defaults to `tail`; `refuse` fails if either stream exceeds the bound. */\n readonly overflow?: \"tail\" | \"refuse\" | undefined\n}\n\n/** One captured stream: the text kept, and the bytes dropped to keep it bounded. */\ninterface Capture {\n readonly text: string\n readonly droppedBytes: number\n}\n\ninterface Tail {\n chunks: Array<Uint8Array>\n bytes: number\n dropped: number\n}\n\nconst decoder = new TextDecoder(\"utf-8\")\n\n/**\n * Joins the retained chunks and decodes them, skipping the partial code point a\n * dropped head can leave behind.\n *\n * A UTF-8 continuation byte is `10xxxxxx`, and a code point is at most four\n * bytes, so at most three leading continuation bytes can precede the first\n * whole character. Skipping them is counted as dropped rather than decoded to a\n * replacement character the process never printed.\n */\nconst decodeTail = (state: Tail): Capture => {\n const bytes = new Uint8Array(state.bytes)\n let offset = 0\n for (const chunk of state.chunks) {\n bytes.set(chunk, offset)\n offset += chunk.byteLength\n }\n if (state.dropped === 0) return { text: decoder.decode(bytes), droppedBytes: 0 }\n let start = 0\n while (start < bytes.byteLength && start < 3 && (bytes[start]! & 0b1100_0000) === 0b1000_0000) start++\n return { text: decoder.decode(bytes.subarray(start)), droppedBytes: state.dropped + start }\n}\n\nconst boundedTail = <E>(\n stream: Stream.Stream<Uint8Array, E>,\n maxBytes: number,\n overflow: \"tail\" | \"refuse\" | undefined\n): Effect.Effect<Capture, E> =>\n Stream.runFold(\n stream,\n (): Tail => ({ chunks: [], bytes: 0, dropped: 0 }),\n (state, chunk) => {\n if (chunk.byteLength === 0) return state\n state.chunks.push(chunk)\n state.bytes += chunk.byteLength\n while (state.bytes > maxBytes && state.chunks.length > 0) {\n const excess = state.bytes - maxBytes\n const first = state.chunks[0]!\n if (first.byteLength <= excess) {\n state.chunks.shift()\n state.bytes -= first.byteLength\n state.dropped += first.byteLength\n } else {\n state.chunks[0] = first.subarray(excess)\n state.bytes -= excess\n state.dropped += excess\n }\n }\n return state\n }\n ).pipe(Effect.map((state) =>\n overflow === \"refuse\" && state.dropped > 0\n ? { text: \"\", droppedBytes: state.dropped }\n : decodeTail(state)\n ))\n\nconst capture = <E>(\n stream: Stream.Stream<Uint8Array, E>,\n maxBytes: number | undefined,\n overflow: \"tail\" | \"refuse\" | undefined\n): Effect.Effect<Capture, E> =>\n maxBytes === undefined\n ? Effect.map(Stream.mkString(Stream.decodeText(stream)), (text) => ({ text, droppedBytes: 0 }))\n : boundedTail(stream, maxBytes, overflow)\n\nconst unbounded = (\n command: string,\n options: ExecOptions\n): Effect.Effect<ExecResult, ExecError, ChildProcessSpawner.ChildProcessSpawner> =>\n Effect.gen(function*() {\n const spawner = yield* ChildProcessSpawner.ChildProcessSpawner\n const settings: ChildProcess.CommandOptions = {\n ...(options.args === undefined ? { shell: true } : {}),\n ...(options.cwd === undefined ? {} : { cwd: options.cwd }),\n env: ChildProcessEnvironment.make(process.env, options.env),\n extendEnv: false,\n ...(options.stdin === undefined\n ? {}\n : { stdin: Stream.make(new TextEncoder().encode(options.stdin)) })\n }\n const handle = yield* spawner.spawn(\n options.args === undefined\n ? ChildProcess.make(command, settings)\n : ChildProcess.make(command, [...options.args], settings)\n )\n // stdout, stderr, and the exit code have to be consumed concurrently: a\n // command that fills a pipe blocks until the pipe is drained, so waiting\n // for exit first would deadlock on any output larger than the buffer.\n const [stdout, stderr, exitCode] = yield* Effect.all(\n [\n capture(handle.stdout, options.maxCaptureBytes, options.overflow),\n capture(handle.stderr, options.maxCaptureBytes, options.overflow),\n handle.exitCode\n ],\n { concurrency: \"unbounded\" }\n )\n if (options.overflow === \"refuse\") {\n const overflowed = [\n stdout.droppedBytes > 0 ? \"stdout\" : undefined,\n stderr.droppedBytes > 0 ? \"stderr\" : undefined\n ]\n .filter((stream): stream is string => stream !== undefined)\n if (overflowed.length > 0) {\n return yield* Effect.fail(\n new ExecError({\n code: \"capture_overflow\",\n message: `${command} ${\n overflowed.join(\" and \")\n } exceeded the ${options.maxCaptureBytes}-byte capture cap; refusing partial output`\n })\n )\n }\n }\n return {\n stdout: stdout.text,\n stderr: stderr.text,\n exitCode,\n stdoutDroppedBytes: stdout.droppedBytes,\n stderrDroppedBytes: stderr.droppedBytes\n }\n }).pipe(\n Effect.scoped,\n Effect.mapError((error) =>\n error instanceof ExecError ? error : new ExecError({\n code: \"spawn_error\",\n message: `exec: ${command}: ${error.message}`\n })\n )\n )\n\n/**\n * Runs a command line through the platform shell and buffers its output.\n *\n * Cancellation is scope closure: the spawn is scoped, so an interrupted fiber\n * — including the one a `timeoutMs` interrupts — terminates the process rather\n * than leaking it.\n *\n * @category execution\n * @since 1.0.0\n */\nexport const exec = (\n command: string,\n options: ExecOptions = {}\n): Effect.Effect<ExecResult, ExecError, ChildProcessSpawner.ChildProcessSpawner> =>\n options.timeoutMs === undefined\n ? unbounded(command, options)\n : Effect.timeoutOrElse(unbounded(command, options), {\n duration: options.timeoutMs,\n orElse: () =>\n Effect.fail(\n new ExecError({\n code: \"timeout\",\n message: `exec: \\`${command}\\` exceeded ${options.timeoutMs}ms`,\n limitMillis: options.timeoutMs\n })\n )\n })\n\n/**\n * The model-facing failure code a command that never produced an exit code\n * carries.\n *\n * A timeout keeps its own code, because \"the command never finished\" and \"the\n * command never started\" are different things to a caller deciding whether to\n * retry. Every other way of producing no exit code is a host failure.\n *\n * @category errors\n * @since 1.0.0\n */\nexport const toStdErrorCode = (error: ExecError): StdError.Code =>\n error.code === \"timeout\" ? \"timeout\" : \"command_failed\"\n\n/**\n * The model-facing failure one execution error becomes.\n *\n * A timeout names the command, which is what the caller asked for and all a\n * timeout has to say. Any other failure carries the message the execution\n * produced, which already names the command.\n *\n * @category errors\n * @since 1.0.0\n */\nexport const toStdError = (command: string, error: ExecError): StdError.StdError =>\n new StdError.StdError({\n code: toStdErrorCode(error),\n message: error.code === \"timeout\"\n ? `Command timed out: ${command}`\n : `Command failed to start: ${error.message}`,\n ...(error.limitMillis === undefined ? {} : { limitMillis: error.limitMillis })\n })\n"]}
|