@smthrs/std 0.0.0-stage → 1.0.0-rc.12

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.
Files changed (582) hide show
  1. package/CHANGELOG.md +148 -0
  2. package/LICENSE +21 -0
  3. package/README.md +156 -2
  4. package/dist/cjs/ApplyPatch.d.ts +174 -0
  5. package/dist/cjs/ApplyPatch.d.ts.map +1 -0
  6. package/dist/cjs/ApplyPatch.js +309 -0
  7. package/dist/cjs/ApplyPatch.js.map +7 -0
  8. package/dist/cjs/Bash.d.ts +390 -0
  9. package/dist/cjs/Bash.d.ts.map +1 -0
  10. package/dist/cjs/Bash.js +333 -0
  11. package/dist/cjs/Bash.js.map +7 -0
  12. package/dist/cjs/Checkpoints.d.ts +322 -0
  13. package/dist/cjs/Checkpoints.d.ts.map +1 -0
  14. package/dist/cjs/Checkpoints.js +233 -0
  15. package/dist/cjs/Checkpoints.js.map +7 -0
  16. package/dist/cjs/Container.d.ts +132 -0
  17. package/dist/cjs/Container.d.ts.map +1 -0
  18. package/dist/cjs/Container.js +113 -0
  19. package/dist/cjs/Container.js.map +7 -0
  20. package/dist/cjs/Edit.d.ts +203 -0
  21. package/dist/cjs/Edit.d.ts.map +1 -0
  22. package/dist/cjs/Edit.js +258 -0
  23. package/dist/cjs/Edit.js.map +7 -0
  24. package/dist/cjs/ExaWebSearch.d.ts +22 -0
  25. package/dist/cjs/ExaWebSearch.d.ts.map +1 -0
  26. package/dist/cjs/ExaWebSearch.js +138 -0
  27. package/dist/cjs/ExaWebSearch.js.map +7 -0
  28. package/dist/cjs/Explore.d.ts +328 -0
  29. package/dist/cjs/Explore.d.ts.map +1 -0
  30. package/dist/cjs/Explore.js +90 -0
  31. package/dist/cjs/Explore.js.map +7 -0
  32. package/dist/cjs/Fetch.d.ts +130 -0
  33. package/dist/cjs/Fetch.d.ts.map +1 -0
  34. package/dist/cjs/Fetch.js +64 -0
  35. package/dist/cjs/Fetch.js.map +7 -0
  36. package/dist/cjs/Glob.d.ts +204 -0
  37. package/dist/cjs/Glob.d.ts.map +1 -0
  38. package/dist/cjs/Glob.js +94 -0
  39. package/dist/cjs/Glob.js.map +7 -0
  40. package/dist/cjs/Grep.d.ts +347 -0
  41. package/dist/cjs/Grep.d.ts.map +1 -0
  42. package/dist/cjs/Grep.js +181 -0
  43. package/dist/cjs/Grep.js.map +7 -0
  44. package/dist/cjs/HttpPost.d.ts +130 -0
  45. package/dist/cjs/HttpPost.d.ts.map +1 -0
  46. package/dist/cjs/HttpPost.js +72 -0
  47. package/dist/cjs/HttpPost.js.map +7 -0
  48. package/dist/cjs/LanguageServer.d.ts +90 -0
  49. package/dist/cjs/LanguageServer.d.ts.map +1 -0
  50. package/dist/cjs/LanguageServer.js +60 -0
  51. package/dist/cjs/LanguageServer.js.map +7 -0
  52. package/dist/cjs/Ls.d.ts +164 -0
  53. package/dist/cjs/Ls.d.ts.map +1 -0
  54. package/dist/cjs/Ls.js +136 -0
  55. package/dist/cjs/Ls.js.map +7 -0
  56. package/dist/cjs/Lsp.d.ts +125 -0
  57. package/dist/cjs/Lsp.d.ts.map +1 -0
  58. package/dist/cjs/Lsp.js +137 -0
  59. package/dist/cjs/Lsp.js.map +7 -0
  60. package/dist/cjs/Manifest.d.ts +918 -0
  61. package/dist/cjs/Manifest.d.ts.map +1 -0
  62. package/dist/cjs/Manifest.js +144 -0
  63. package/dist/cjs/Manifest.js.map +7 -0
  64. package/dist/cjs/NativeSearch.d.ts +38 -0
  65. package/dist/cjs/NativeSearch.d.ts.map +1 -0
  66. package/dist/cjs/NativeSearch.js +326 -0
  67. package/dist/cjs/NativeSearch.js.map +7 -0
  68. package/dist/cjs/NodeLanguageServer.d.ts +128 -0
  69. package/dist/cjs/NodeLanguageServer.d.ts.map +1 -0
  70. package/dist/cjs/NodeLanguageServer.js +796 -0
  71. package/dist/cjs/NodeLanguageServer.js.map +7 -0
  72. package/dist/cjs/PortableSearch.d.ts +36 -0
  73. package/dist/cjs/PortableSearch.d.ts.map +1 -0
  74. package/dist/cjs/PortableSearch.js +240 -0
  75. package/dist/cjs/PortableSearch.js.map +7 -0
  76. package/dist/cjs/Probe.d.ts +193 -0
  77. package/dist/cjs/Probe.d.ts.map +1 -0
  78. package/dist/cjs/Probe.js +134 -0
  79. package/dist/cjs/Probe.js.map +7 -0
  80. package/dist/cjs/Read.d.ts +189 -0
  81. package/dist/cjs/Read.d.ts.map +1 -0
  82. package/dist/cjs/Read.js +223 -0
  83. package/dist/cjs/Read.js.map +7 -0
  84. package/dist/cjs/Relocate.d.ts +73 -0
  85. package/dist/cjs/Relocate.d.ts.map +1 -0
  86. package/dist/cjs/Relocate.js +72 -0
  87. package/dist/cjs/Relocate.js.map +7 -0
  88. package/dist/cjs/RemoteSearch.d.ts +54 -0
  89. package/dist/cjs/RemoteSearch.d.ts.map +1 -0
  90. package/dist/cjs/RemoteSearch.js +134 -0
  91. package/dist/cjs/RemoteSearch.js.map +7 -0
  92. package/dist/cjs/Search.d.ts +163 -0
  93. package/dist/cjs/Search.d.ts.map +1 -0
  94. package/dist/cjs/Search.js +51 -0
  95. package/dist/cjs/Search.js.map +7 -0
  96. package/dist/cjs/SearchConformance.d.ts +115 -0
  97. package/dist/cjs/SearchConformance.d.ts.map +1 -0
  98. package/dist/cjs/SearchConformance.js +172 -0
  99. package/dist/cjs/SearchConformance.js.map +7 -0
  100. package/dist/cjs/SearchContract.d.ts +114 -0
  101. package/dist/cjs/SearchContract.d.ts.map +1 -0
  102. package/dist/cjs/SearchContract.js +339 -0
  103. package/dist/cjs/SearchContract.js.map +7 -0
  104. package/dist/cjs/ShellCommand.d.ts +164 -0
  105. package/dist/cjs/ShellCommand.d.ts.map +1 -0
  106. package/dist/cjs/ShellCommand.js +102 -0
  107. package/dist/cjs/ShellCommand.js.map +7 -0
  108. package/dist/cjs/StdError.d.ts +55 -0
  109. package/dist/cjs/StdError.d.ts.map +1 -0
  110. package/dist/cjs/StdError.js +74 -0
  111. package/dist/cjs/StdError.js.map +7 -0
  112. package/dist/cjs/TestRun.d.ts +311 -0
  113. package/dist/cjs/TestRun.d.ts.map +1 -0
  114. package/dist/cjs/TestRun.js +228 -0
  115. package/dist/cjs/TestRun.js.map +7 -0
  116. package/dist/cjs/TestRunner.d.ts +102 -0
  117. package/dist/cjs/TestRunner.d.ts.map +1 -0
  118. package/dist/cjs/TestRunner.js +54 -0
  119. package/dist/cjs/TestRunner.js.map +7 -0
  120. package/dist/cjs/TreeFingerprint.d.ts +140 -0
  121. package/dist/cjs/TreeFingerprint.d.ts.map +1 -0
  122. package/dist/cjs/TreeFingerprint.js +87 -0
  123. package/dist/cjs/TreeFingerprint.js.map +7 -0
  124. package/dist/cjs/UpdatePlan.d.ts +152 -0
  125. package/dist/cjs/UpdatePlan.d.ts.map +1 -0
  126. package/dist/cjs/UpdatePlan.js +87 -0
  127. package/dist/cjs/UpdatePlan.js.map +7 -0
  128. package/dist/cjs/WebFetch.d.ts +134 -0
  129. package/dist/cjs/WebFetch.d.ts.map +1 -0
  130. package/dist/cjs/WebFetch.js +140 -0
  131. package/dist/cjs/WebFetch.js.map +7 -0
  132. package/dist/cjs/WebSearch.d.ts +181 -0
  133. package/dist/cjs/WebSearch.d.ts.map +1 -0
  134. package/dist/cjs/WebSearch.js +86 -0
  135. package/dist/cjs/WebSearch.js.map +7 -0
  136. package/dist/cjs/Write.d.ts +147 -0
  137. package/dist/cjs/Write.d.ts.map +1 -0
  138. package/dist/cjs/Write.js +122 -0
  139. package/dist/cjs/Write.js.map +7 -0
  140. package/dist/cjs/index.d.ts +129 -0
  141. package/dist/cjs/index.d.ts.map +1 -0
  142. package/dist/cjs/index.js +101 -0
  143. package/dist/cjs/index.js.map +7 -0
  144. package/dist/cjs/internal/ApplyPatch.d.ts +145 -0
  145. package/dist/cjs/internal/ApplyPatch.d.ts.map +1 -0
  146. package/dist/cjs/internal/ApplyPatch.js +462 -0
  147. package/dist/cjs/internal/ApplyPatch.js.map +7 -0
  148. package/dist/cjs/internal/CodexText.d.ts +73 -0
  149. package/dist/cjs/internal/CodexText.d.ts.map +1 -0
  150. package/dist/cjs/internal/CodexText.js +108 -0
  151. package/dist/cjs/internal/CodexText.js.map +7 -0
  152. package/dist/cjs/internal/Declaration.d.ts +49 -0
  153. package/dist/cjs/internal/Declaration.d.ts.map +1 -0
  154. package/dist/cjs/internal/Declaration.js +46 -0
  155. package/dist/cjs/internal/Declaration.js.map +7 -0
  156. package/dist/cjs/internal/Diagnostics.d.ts +77 -0
  157. package/dist/cjs/internal/Diagnostics.d.ts.map +1 -0
  158. package/dist/cjs/internal/Diagnostics.js +89 -0
  159. package/dist/cjs/internal/Diagnostics.js.map +7 -0
  160. package/dist/cjs/internal/EnvelopePrecheck.d.ts +69 -0
  161. package/dist/cjs/internal/EnvelopePrecheck.d.ts.map +1 -0
  162. package/dist/cjs/internal/EnvelopePrecheck.js +323 -0
  163. package/dist/cjs/internal/EnvelopePrecheck.js.map +7 -0
  164. package/dist/cjs/internal/Exec.d.ts +151 -0
  165. package/dist/cjs/internal/Exec.d.ts.map +1 -0
  166. package/dist/cjs/internal/Exec.js +207 -0
  167. package/dist/cjs/internal/Exec.js.map +7 -0
  168. package/dist/cjs/internal/FileMutation.d.ts +109 -0
  169. package/dist/cjs/internal/FileMutation.d.ts.map +1 -0
  170. package/dist/cjs/internal/FileMutation.js +116 -0
  171. package/dist/cjs/internal/FileMutation.js.map +7 -0
  172. package/dist/cjs/internal/FsFailure.d.ts +38 -0
  173. package/dist/cjs/internal/FsFailure.d.ts.map +1 -0
  174. package/dist/cjs/internal/FsFailure.js +40 -0
  175. package/dist/cjs/internal/FsFailure.js.map +7 -0
  176. package/dist/cjs/internal/GitWorktree.d.ts +108 -0
  177. package/dist/cjs/internal/GitWorktree.d.ts.map +1 -0
  178. package/dist/cjs/internal/GitWorktree.js +276 -0
  179. package/dist/cjs/internal/GitWorktree.js.map +7 -0
  180. package/dist/cjs/internal/Grouping.d.ts +51 -0
  181. package/dist/cjs/internal/Grouping.d.ts.map +1 -0
  182. package/dist/cjs/internal/Grouping.js +83 -0
  183. package/dist/cjs/internal/Grouping.js.map +7 -0
  184. package/dist/cjs/internal/Html.d.ts +20 -0
  185. package/dist/cjs/internal/Html.d.ts.map +1 -0
  186. package/dist/cjs/internal/Html.js +81 -0
  187. package/dist/cjs/internal/Html.js.map +7 -0
  188. package/dist/cjs/internal/Http.d.ts +95 -0
  189. package/dist/cjs/internal/Http.d.ts.map +1 -0
  190. package/dist/cjs/internal/Http.js +153 -0
  191. package/dist/cjs/internal/Http.js.map +7 -0
  192. package/dist/cjs/internal/HttpNetwork.d.ts +27 -0
  193. package/dist/cjs/internal/HttpNetwork.d.ts.map +1 -0
  194. package/dist/cjs/internal/HttpNetwork.js +146 -0
  195. package/dist/cjs/internal/HttpNetwork.js.map +7 -0
  196. package/dist/cjs/internal/Ignore.d.ts +37 -0
  197. package/dist/cjs/internal/Ignore.d.ts.map +1 -0
  198. package/dist/cjs/internal/Ignore.js +100 -0
  199. package/dist/cjs/internal/Ignore.js.map +7 -0
  200. package/dist/cjs/internal/LinearRegex.d.ts +19 -0
  201. package/dist/cjs/internal/LinearRegex.d.ts.map +1 -0
  202. package/dist/cjs/internal/LinearRegex.js +166 -0
  203. package/dist/cjs/internal/LinearRegex.js.map +7 -0
  204. package/dist/cjs/internal/Match.d.ts +85 -0
  205. package/dist/cjs/internal/Match.d.ts.map +1 -0
  206. package/dist/cjs/internal/Match.js +99 -0
  207. package/dist/cjs/internal/Match.js.map +7 -0
  208. package/dist/cjs/internal/Paths.d.ts +23 -0
  209. package/dist/cjs/internal/Paths.d.ts.map +1 -0
  210. package/dist/cjs/internal/Paths.js +52 -0
  211. package/dist/cjs/internal/Paths.js.map +7 -0
  212. package/dist/cjs/internal/Preserve.d.ts +25 -0
  213. package/dist/cjs/internal/Preserve.d.ts.map +1 -0
  214. package/dist/cjs/internal/Preserve.js +88 -0
  215. package/dist/cjs/internal/Preserve.js.map +7 -0
  216. package/dist/cjs/internal/Prune.d.ts +35 -0
  217. package/dist/cjs/internal/Prune.d.ts.map +1 -0
  218. package/dist/cjs/internal/Prune.js +54 -0
  219. package/dist/cjs/internal/Prune.js.map +7 -0
  220. package/dist/cjs/internal/RemoteTree.d.ts +60 -0
  221. package/dist/cjs/internal/RemoteTree.d.ts.map +1 -0
  222. package/dist/cjs/internal/RemoteTree.js +123 -0
  223. package/dist/cjs/internal/RemoteTree.js.map +7 -0
  224. package/dist/cjs/internal/SearchContract.d.ts +63 -0
  225. package/dist/cjs/internal/SearchContract.d.ts.map +1 -0
  226. package/dist/cjs/internal/SearchContract.js +46 -0
  227. package/dist/cjs/internal/SearchContract.js.map +7 -0
  228. package/dist/cjs/internal/Symbols.d.ts +39 -0
  229. package/dist/cjs/internal/Symbols.d.ts.map +1 -0
  230. package/dist/cjs/internal/Symbols.js +59 -0
  231. package/dist/cjs/internal/Symbols.js.map +7 -0
  232. package/dist/cjs/internal/Tar.d.ts +23 -0
  233. package/dist/cjs/internal/Tar.d.ts.map +1 -0
  234. package/dist/cjs/internal/Tar.js +86 -0
  235. package/dist/cjs/internal/Tar.js.map +7 -0
  236. package/dist/cjs/internal/TestReport.d.ts +52 -0
  237. package/dist/cjs/internal/TestReport.d.ts.map +1 -0
  238. package/dist/cjs/internal/TestReport.js +131 -0
  239. package/dist/cjs/internal/TestReport.js.map +7 -0
  240. package/dist/cjs/internal/Text.d.ts +125 -0
  241. package/dist/cjs/internal/Text.d.ts.map +1 -0
  242. package/dist/cjs/internal/Text.js +85 -0
  243. package/dist/cjs/internal/Text.js.map +7 -0
  244. package/dist/cjs/internal/Url.d.ts +13 -0
  245. package/dist/cjs/internal/Url.d.ts.map +1 -0
  246. package/dist/cjs/internal/Url.js +35 -0
  247. package/dist/cjs/internal/Url.js.map +7 -0
  248. package/dist/cjs/internal/Walk.d.ts +72 -0
  249. package/dist/cjs/internal/Walk.d.ts.map +1 -0
  250. package/dist/cjs/internal/Walk.js +108 -0
  251. package/dist/cjs/internal/Walk.js.map +7 -0
  252. package/dist/cjs/package.json +1 -0
  253. package/dist/esm/ApplyPatch.d.ts +174 -0
  254. package/dist/esm/ApplyPatch.d.ts.map +1 -0
  255. package/dist/esm/ApplyPatch.js +346 -0
  256. package/dist/esm/ApplyPatch.js.map +1 -0
  257. package/dist/esm/Bash.d.ts +390 -0
  258. package/dist/esm/Bash.d.ts.map +1 -0
  259. package/dist/esm/Bash.js +514 -0
  260. package/dist/esm/Bash.js.map +1 -0
  261. package/dist/esm/Checkpoints.d.ts +322 -0
  262. package/dist/esm/Checkpoints.d.ts.map +1 -0
  263. package/dist/esm/Checkpoints.js +396 -0
  264. package/dist/esm/Checkpoints.js.map +1 -0
  265. package/dist/esm/Container.d.ts +132 -0
  266. package/dist/esm/Container.d.ts.map +1 -0
  267. package/dist/esm/Container.js +151 -0
  268. package/dist/esm/Container.js.map +1 -0
  269. package/dist/esm/Edit.d.ts +203 -0
  270. package/dist/esm/Edit.d.ts.map +1 -0
  271. package/dist/esm/Edit.js +296 -0
  272. package/dist/esm/Edit.js.map +1 -0
  273. package/dist/esm/ExaWebSearch.d.ts +22 -0
  274. package/dist/esm/ExaWebSearch.d.ts.map +1 -0
  275. package/dist/esm/ExaWebSearch.js +107 -0
  276. package/dist/esm/ExaWebSearch.js.map +1 -0
  277. package/dist/esm/Explore.d.ts +328 -0
  278. package/dist/esm/Explore.d.ts.map +1 -0
  279. package/dist/esm/Explore.js +106 -0
  280. package/dist/esm/Explore.js.map +1 -0
  281. package/dist/esm/Fetch.d.ts +130 -0
  282. package/dist/esm/Fetch.d.ts.map +1 -0
  283. package/dist/esm/Fetch.js +91 -0
  284. package/dist/esm/Fetch.js.map +1 -0
  285. package/dist/esm/Glob.d.ts +204 -0
  286. package/dist/esm/Glob.d.ts.map +1 -0
  287. package/dist/esm/Glob.js +177 -0
  288. package/dist/esm/Glob.js.map +1 -0
  289. package/dist/esm/Grep.d.ts +347 -0
  290. package/dist/esm/Grep.d.ts.map +1 -0
  291. package/dist/esm/Grep.js +289 -0
  292. package/dist/esm/Grep.js.map +1 -0
  293. package/dist/esm/HttpPost.d.ts +130 -0
  294. package/dist/esm/HttpPost.d.ts.map +1 -0
  295. package/dist/esm/HttpPost.js +89 -0
  296. package/dist/esm/HttpPost.js.map +1 -0
  297. package/dist/esm/LanguageServer.d.ts +90 -0
  298. package/dist/esm/LanguageServer.d.ts.map +1 -0
  299. package/dist/esm/LanguageServer.js +57 -0
  300. package/dist/esm/LanguageServer.js.map +1 -0
  301. package/dist/esm/Ls.d.ts +164 -0
  302. package/dist/esm/Ls.d.ts.map +1 -0
  303. package/dist/esm/Ls.js +178 -0
  304. package/dist/esm/Ls.js.map +1 -0
  305. package/dist/esm/Lsp.d.ts +125 -0
  306. package/dist/esm/Lsp.d.ts.map +1 -0
  307. package/dist/esm/Lsp.js +165 -0
  308. package/dist/esm/Lsp.js.map +1 -0
  309. package/dist/esm/Manifest.d.ts +918 -0
  310. package/dist/esm/Manifest.d.ts.map +1 -0
  311. package/dist/esm/Manifest.js +153 -0
  312. package/dist/esm/Manifest.js.map +1 -0
  313. package/dist/esm/NativeSearch.d.ts +38 -0
  314. package/dist/esm/NativeSearch.d.ts.map +1 -0
  315. package/dist/esm/NativeSearch.js +378 -0
  316. package/dist/esm/NativeSearch.js.map +1 -0
  317. package/dist/esm/NodeLanguageServer.d.ts +128 -0
  318. package/dist/esm/NodeLanguageServer.d.ts.map +1 -0
  319. package/dist/esm/NodeLanguageServer.js +843 -0
  320. package/dist/esm/NodeLanguageServer.js.map +1 -0
  321. package/dist/esm/PortableSearch.d.ts +36 -0
  322. package/dist/esm/PortableSearch.d.ts.map +1 -0
  323. package/dist/esm/PortableSearch.js +256 -0
  324. package/dist/esm/PortableSearch.js.map +1 -0
  325. package/dist/esm/Probe.d.ts +193 -0
  326. package/dist/esm/Probe.d.ts.map +1 -0
  327. package/dist/esm/Probe.js +225 -0
  328. package/dist/esm/Probe.js.map +1 -0
  329. package/dist/esm/Read.d.ts +189 -0
  330. package/dist/esm/Read.d.ts.map +1 -0
  331. package/dist/esm/Read.js +300 -0
  332. package/dist/esm/Read.js.map +1 -0
  333. package/dist/esm/Relocate.d.ts +73 -0
  334. package/dist/esm/Relocate.d.ts.map +1 -0
  335. package/dist/esm/Relocate.js +153 -0
  336. package/dist/esm/Relocate.js.map +1 -0
  337. package/dist/esm/RemoteSearch.d.ts +54 -0
  338. package/dist/esm/RemoteSearch.d.ts.map +1 -0
  339. package/dist/esm/RemoteSearch.js +162 -0
  340. package/dist/esm/RemoteSearch.js.map +1 -0
  341. package/dist/esm/Search.d.ts +163 -0
  342. package/dist/esm/Search.d.ts.map +1 -0
  343. package/dist/esm/Search.js +47 -0
  344. package/dist/esm/Search.js.map +1 -0
  345. package/dist/esm/SearchConformance.d.ts +115 -0
  346. package/dist/esm/SearchConformance.d.ts.map +1 -0
  347. package/dist/esm/SearchConformance.js +193 -0
  348. package/dist/esm/SearchConformance.js.map +1 -0
  349. package/dist/esm/SearchContract.d.ts +114 -0
  350. package/dist/esm/SearchContract.d.ts.map +1 -0
  351. package/dist/esm/SearchContract.js +461 -0
  352. package/dist/esm/SearchContract.js.map +1 -0
  353. package/dist/esm/ShellCommand.d.ts +164 -0
  354. package/dist/esm/ShellCommand.d.ts.map +1 -0
  355. package/dist/esm/ShellCommand.js +159 -0
  356. package/dist/esm/ShellCommand.js.map +1 -0
  357. package/dist/esm/StdError.d.ts +55 -0
  358. package/dist/esm/StdError.d.ts.map +1 -0
  359. package/dist/esm/StdError.js +71 -0
  360. package/dist/esm/StdError.js.map +1 -0
  361. package/dist/esm/TestRun.d.ts +311 -0
  362. package/dist/esm/TestRun.d.ts.map +1 -0
  363. package/dist/esm/TestRun.js +316 -0
  364. package/dist/esm/TestRun.js.map +1 -0
  365. package/dist/esm/TestRunner.d.ts +102 -0
  366. package/dist/esm/TestRunner.d.ts.map +1 -0
  367. package/dist/esm/TestRunner.js +68 -0
  368. package/dist/esm/TestRunner.js.map +1 -0
  369. package/dist/esm/TreeFingerprint.d.ts +140 -0
  370. package/dist/esm/TreeFingerprint.d.ts.map +1 -0
  371. package/dist/esm/TreeFingerprint.js +170 -0
  372. package/dist/esm/TreeFingerprint.js.map +1 -0
  373. package/dist/esm/UpdatePlan.d.ts +152 -0
  374. package/dist/esm/UpdatePlan.d.ts.map +1 -0
  375. package/dist/esm/UpdatePlan.js +129 -0
  376. package/dist/esm/UpdatePlan.js.map +1 -0
  377. package/dist/esm/WebFetch.d.ts +134 -0
  378. package/dist/esm/WebFetch.d.ts.map +1 -0
  379. package/dist/esm/WebFetch.js +151 -0
  380. package/dist/esm/WebFetch.js.map +1 -0
  381. package/dist/esm/WebSearch.d.ts +181 -0
  382. package/dist/esm/WebSearch.d.ts.map +1 -0
  383. package/dist/esm/WebSearch.js +134 -0
  384. package/dist/esm/WebSearch.js.map +1 -0
  385. package/dist/esm/Write.d.ts +147 -0
  386. package/dist/esm/Write.d.ts.map +1 -0
  387. package/dist/esm/Write.js +144 -0
  388. package/dist/esm/Write.js.map +1 -0
  389. package/dist/esm/index.d.ts +129 -0
  390. package/dist/esm/index.d.ts.map +1 -0
  391. package/dist/esm/index.js +129 -0
  392. package/dist/esm/index.js.map +1 -0
  393. package/dist/esm/internal/ApplyPatch.d.ts +145 -0
  394. package/dist/esm/internal/ApplyPatch.d.ts.map +1 -0
  395. package/dist/esm/internal/ApplyPatch.js +489 -0
  396. package/dist/esm/internal/ApplyPatch.js.map +1 -0
  397. package/dist/esm/internal/CodexText.d.ts +73 -0
  398. package/dist/esm/internal/CodexText.d.ts.map +1 -0
  399. package/dist/esm/internal/CodexText.js +156 -0
  400. package/dist/esm/internal/CodexText.js.map +1 -0
  401. package/dist/esm/internal/Declaration.d.ts +49 -0
  402. package/dist/esm/internal/Declaration.d.ts.map +1 -0
  403. package/dist/esm/internal/Declaration.js +42 -0
  404. package/dist/esm/internal/Declaration.js.map +1 -0
  405. package/dist/esm/internal/Diagnostics.d.ts +77 -0
  406. package/dist/esm/internal/Diagnostics.d.ts.map +1 -0
  407. package/dist/esm/internal/Diagnostics.js +99 -0
  408. package/dist/esm/internal/Diagnostics.js.map +1 -0
  409. package/dist/esm/internal/EnvelopePrecheck.d.ts +69 -0
  410. package/dist/esm/internal/EnvelopePrecheck.d.ts.map +1 -0
  411. package/dist/esm/internal/EnvelopePrecheck.js +426 -0
  412. package/dist/esm/internal/EnvelopePrecheck.js.map +1 -0
  413. package/dist/esm/internal/Exec.d.ts +151 -0
  414. package/dist/esm/internal/Exec.d.ts.map +1 -0
  415. package/dist/esm/internal/Exec.js +285 -0
  416. package/dist/esm/internal/Exec.js.map +1 -0
  417. package/dist/esm/internal/FileMutation.d.ts +109 -0
  418. package/dist/esm/internal/FileMutation.d.ts.map +1 -0
  419. package/dist/esm/internal/FileMutation.js +123 -0
  420. package/dist/esm/internal/FileMutation.js.map +1 -0
  421. package/dist/esm/internal/FsFailure.d.ts +38 -0
  422. package/dist/esm/internal/FsFailure.d.ts.map +1 -0
  423. package/dist/esm/internal/FsFailure.js +39 -0
  424. package/dist/esm/internal/FsFailure.js.map +1 -0
  425. package/dist/esm/internal/GitWorktree.d.ts +108 -0
  426. package/dist/esm/internal/GitWorktree.d.ts.map +1 -0
  427. package/dist/esm/internal/GitWorktree.js +352 -0
  428. package/dist/esm/internal/GitWorktree.js.map +1 -0
  429. package/dist/esm/internal/Grouping.d.ts +51 -0
  430. package/dist/esm/internal/Grouping.d.ts.map +1 -0
  431. package/dist/esm/internal/Grouping.js +99 -0
  432. package/dist/esm/internal/Grouping.js.map +1 -0
  433. package/dist/esm/internal/Html.d.ts +20 -0
  434. package/dist/esm/internal/Html.d.ts.map +1 -0
  435. package/dist/esm/internal/Html.js +70 -0
  436. package/dist/esm/internal/Html.js.map +1 -0
  437. package/dist/esm/internal/Http.d.ts +95 -0
  438. package/dist/esm/internal/Http.d.ts.map +1 -0
  439. package/dist/esm/internal/Http.js +154 -0
  440. package/dist/esm/internal/Http.js.map +1 -0
  441. package/dist/esm/internal/HttpNetwork.d.ts +27 -0
  442. package/dist/esm/internal/HttpNetwork.d.ts.map +1 -0
  443. package/dist/esm/internal/HttpNetwork.js +114 -0
  444. package/dist/esm/internal/HttpNetwork.js.map +1 -0
  445. package/dist/esm/internal/Ignore.d.ts +37 -0
  446. package/dist/esm/internal/Ignore.d.ts.map +1 -0
  447. package/dist/esm/internal/Ignore.js +128 -0
  448. package/dist/esm/internal/Ignore.js.map +1 -0
  449. package/dist/esm/internal/LinearRegex.d.ts +19 -0
  450. package/dist/esm/internal/LinearRegex.d.ts.map +1 -0
  451. package/dist/esm/internal/LinearRegex.js +176 -0
  452. package/dist/esm/internal/LinearRegex.js.map +1 -0
  453. package/dist/esm/internal/Match.d.ts +85 -0
  454. package/dist/esm/internal/Match.d.ts.map +1 -0
  455. package/dist/esm/internal/Match.js +142 -0
  456. package/dist/esm/internal/Match.js.map +1 -0
  457. package/dist/esm/internal/Paths.d.ts +23 -0
  458. package/dist/esm/internal/Paths.d.ts.map +1 -0
  459. package/dist/esm/internal/Paths.js +40 -0
  460. package/dist/esm/internal/Paths.js.map +1 -0
  461. package/dist/esm/internal/Preserve.d.ts +25 -0
  462. package/dist/esm/internal/Preserve.d.ts.map +1 -0
  463. package/dist/esm/internal/Preserve.js +70 -0
  464. package/dist/esm/internal/Preserve.js.map +1 -0
  465. package/dist/esm/internal/Prune.d.ts +35 -0
  466. package/dist/esm/internal/Prune.d.ts.map +1 -0
  467. package/dist/esm/internal/Prune.js +60 -0
  468. package/dist/esm/internal/Prune.js.map +1 -0
  469. package/dist/esm/internal/RemoteTree.d.ts +60 -0
  470. package/dist/esm/internal/RemoteTree.d.ts.map +1 -0
  471. package/dist/esm/internal/RemoteTree.js +132 -0
  472. package/dist/esm/internal/RemoteTree.js.map +1 -0
  473. package/dist/esm/internal/SearchContract.d.ts +63 -0
  474. package/dist/esm/internal/SearchContract.d.ts.map +1 -0
  475. package/dist/esm/internal/SearchContract.js +66 -0
  476. package/dist/esm/internal/SearchContract.js.map +1 -0
  477. package/dist/esm/internal/Symbols.d.ts +39 -0
  478. package/dist/esm/internal/Symbols.d.ts.map +1 -0
  479. package/dist/esm/internal/Symbols.js +79 -0
  480. package/dist/esm/internal/Symbols.js.map +1 -0
  481. package/dist/esm/internal/Tar.d.ts +23 -0
  482. package/dist/esm/internal/Tar.d.ts.map +1 -0
  483. package/dist/esm/internal/Tar.js +97 -0
  484. package/dist/esm/internal/Tar.js.map +1 -0
  485. package/dist/esm/internal/TestReport.d.ts +52 -0
  486. package/dist/esm/internal/TestReport.d.ts.map +1 -0
  487. package/dist/esm/internal/TestReport.js +172 -0
  488. package/dist/esm/internal/TestReport.js.map +1 -0
  489. package/dist/esm/internal/Text.d.ts +125 -0
  490. package/dist/esm/internal/Text.d.ts.map +1 -0
  491. package/dist/esm/internal/Text.js +141 -0
  492. package/dist/esm/internal/Text.js.map +1 -0
  493. package/dist/esm/internal/Url.d.ts +13 -0
  494. package/dist/esm/internal/Url.d.ts.map +1 -0
  495. package/dist/esm/internal/Url.js +27 -0
  496. package/dist/esm/internal/Url.js.map +1 -0
  497. package/dist/esm/internal/Walk.d.ts +72 -0
  498. package/dist/esm/internal/Walk.d.ts.map +1 -0
  499. package/dist/esm/internal/Walk.js +126 -0
  500. package/dist/esm/internal/Walk.js.map +1 -0
  501. package/docs/README.md +119 -0
  502. package/docs/api.md +499 -0
  503. package/docs/concepts/effects-and-capabilities.md +138 -0
  504. package/docs/concepts/flows-and-handlers.md +115 -0
  505. package/docs/concepts/limits-and-disclosure.md +105 -0
  506. package/docs/concepts/search-contract.md +182 -0
  507. package/docs/guides/bind-the-standard-flows.md +116 -0
  508. package/docs/guides/implement-a-search-peer.md +133 -0
  509. package/docs/guides/pin-a-checkpoint.md +161 -0
  510. package/docs/guides/query-a-language-server.md +257 -0
  511. package/docs/guides/reach-the-network.md +141 -0
  512. package/docs/guides/read-and-edit-a-file.md +240 -0
  513. package/docs/guides/run-a-shell-command.md +236 -0
  514. package/docs/guides/run-the-test-suite.md +154 -0
  515. package/docs/guides/search-a-workspace.md +148 -0
  516. package/docs/installation.md +115 -0
  517. package/docs/quickstart.md +125 -0
  518. package/docs/reference/flows.md +556 -0
  519. package/docs/troubleshooting.md +347 -0
  520. package/package.json +449 -3
  521. package/src/ApplyPatch.ts +458 -0
  522. package/src/Bash.ts +619 -0
  523. package/src/Checkpoints.ts +589 -0
  524. package/src/Container.ts +212 -0
  525. package/src/Edit.ts +366 -0
  526. package/src/ExaWebSearch.ts +145 -0
  527. package/src/Explore.ts +139 -0
  528. package/src/Fetch.ts +117 -0
  529. package/src/Glob.ts +200 -0
  530. package/src/Grep.ts +320 -0
  531. package/src/HttpPost.ts +120 -0
  532. package/src/LanguageServer.ts +110 -0
  533. package/src/Ls.ts +228 -0
  534. package/src/Lsp.ts +184 -0
  535. package/src/Manifest.ts +162 -0
  536. package/src/NativeSearch.ts +457 -0
  537. package/src/NodeLanguageServer.ts +1148 -0
  538. package/src/PortableSearch.ts +284 -0
  539. package/src/Probe.ts +284 -0
  540. package/src/Read.ts +359 -0
  541. package/src/Relocate.ts +166 -0
  542. package/src/RemoteSearch.ts +199 -0
  543. package/src/Search.ts +185 -0
  544. package/src/SearchConformance.ts +288 -0
  545. package/src/SearchContract.ts +489 -0
  546. package/src/ShellCommand.ts +201 -0
  547. package/src/StdError.ts +80 -0
  548. package/src/TestRun.ts +401 -0
  549. package/src/TestRunner.ts +119 -0
  550. package/src/TreeFingerprint.ts +200 -0
  551. package/src/UpdatePlan.ts +171 -0
  552. package/src/WebFetch.ts +194 -0
  553. package/src/WebSearch.ts +165 -0
  554. package/src/Write.ts +195 -0
  555. package/src/index.ts +157 -0
  556. package/src/internal/ApplyPatch.ts +601 -0
  557. package/src/internal/CodexText.ts +182 -0
  558. package/src/internal/Declaration.ts +60 -0
  559. package/src/internal/Diagnostics.ts +134 -0
  560. package/src/internal/EnvelopePrecheck.ts +489 -0
  561. package/src/internal/Exec.ts +427 -0
  562. package/src/internal/FileMutation.ts +231 -0
  563. package/src/internal/FsFailure.ts +51 -0
  564. package/src/internal/GitWorktree.ts +491 -0
  565. package/src/internal/Grouping.ts +106 -0
  566. package/src/internal/Html.ts +87 -0
  567. package/src/internal/Http.ts +209 -0
  568. package/src/internal/HttpNetwork.ts +153 -0
  569. package/src/internal/Ignore.ts +135 -0
  570. package/src/internal/LinearRegex.ts +173 -0
  571. package/src/internal/Match.ts +172 -0
  572. package/src/internal/Paths.ts +42 -0
  573. package/src/internal/Preserve.ts +108 -0
  574. package/src/internal/Prune.ts +62 -0
  575. package/src/internal/RemoteTree.ts +159 -0
  576. package/src/internal/SearchContract.ts +77 -0
  577. package/src/internal/Symbols.ts +88 -0
  578. package/src/internal/Tar.ts +95 -0
  579. package/src/internal/TestReport.ts +194 -0
  580. package/src/internal/Text.ts +185 -0
  581. package/src/internal/Url.ts +23 -0
  582. package/src/internal/Walk.ts +176 -0
@@ -0,0 +1,285 @@
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 ChildProcess from "effect/process/ChildProcess";
20
+ import * as Schema from "effect/Schema";
21
+ import * as Stream from "effect/Stream";
22
+ import * as StdError from "../StdError.js";
23
+ import { elideMiddle } from "./Text.js";
24
+ /**
25
+ * Failure codes a buffered execution can report, matching the codes the
26
+ * removed kernel `Shell` used so handler-level mapping is unchanged.
27
+ *
28
+ * @category models
29
+ * @since 1.0.0
30
+ */
31
+ export const ExecErrorCode = Schema.Literals(["timeout", "spawn_error", "arguments_too_long", "capture_overflow"]);
32
+ /**
33
+ * A command that never produced an exit code.
34
+ *
35
+ * A non-zero exit is a successful {@link ExecResult}, not a failure; this
36
+ * error is reserved for a command that could not start (`arguments_too_long`
37
+ * when the operating system refused it for the size of its arguments) or was
38
+ * cut off by its timeout, or whose captured output exceeded a refusing capture
39
+ * bound.
40
+ *
41
+ * @category errors
42
+ * @since 1.0.0
43
+ */
44
+ export class ExecError extends Schema.TaggedError()("@smthrs/std/ExecError", {
45
+ code: ExecErrorCode,
46
+ message: Schema.String,
47
+ /** The limit a `timeout` ran past, in milliseconds. */
48
+ limitMillis: Schema.optional(Schema.Number)
49
+ }) {
50
+ }
51
+ const decoder = new TextDecoder("utf-8");
52
+ const emptyTail = () => ({ chunks: [], bytes: 0, dropped: 0 });
53
+ /** A UTF-8 continuation byte is `10xxxxxx`; every other byte starts a character. */
54
+ const isContinuation = (byte) => (byte & 0b1100_0000) === 0b1000_0000;
55
+ const join = (chunks, size) => {
56
+ const bytes = new Uint8Array(size);
57
+ let offset = 0;
58
+ for (const chunk of chunks) {
59
+ bytes.set(chunk, offset);
60
+ offset += chunk.byteLength;
61
+ }
62
+ return bytes;
63
+ };
64
+ /** Appends one chunk and drops from the front whatever no longer fits. */
65
+ const keepTail = (state, chunk, maxBytes) => {
66
+ if (chunk.byteLength === 0)
67
+ return;
68
+ state.chunks.push(chunk);
69
+ state.bytes += chunk.byteLength;
70
+ while (state.bytes > maxBytes && state.chunks.length > 0) {
71
+ const excess = state.bytes - maxBytes;
72
+ const first = state.chunks[0];
73
+ if (first.byteLength <= excess) {
74
+ state.chunks.shift();
75
+ state.bytes -= first.byteLength;
76
+ state.dropped += first.byteLength;
77
+ }
78
+ else {
79
+ state.chunks[0] = first.subarray(excess);
80
+ state.bytes -= excess;
81
+ state.dropped += excess;
82
+ }
83
+ }
84
+ };
85
+ /**
86
+ * Joins the retained chunks and decodes them, skipping the partial code point a
87
+ * dropped head can leave behind.
88
+ *
89
+ * A code point is at most four bytes, so at most three leading continuation
90
+ * bytes can precede the first whole character. Skipping them is counted as
91
+ * dropped rather than decoded to a replacement character the process never
92
+ * printed.
93
+ */
94
+ const decodeTail = (state) => {
95
+ const bytes = join(state.chunks, state.bytes);
96
+ if (state.dropped === 0)
97
+ return { text: decoder.decode(bytes), droppedBytes: 0 };
98
+ let start = 0;
99
+ while (start < bytes.byteLength && start < 3 && isContinuation(bytes[start]))
100
+ start++;
101
+ return { text: decoder.decode(bytes.subarray(start)), droppedBytes: state.dropped + start };
102
+ };
103
+ /**
104
+ * The length of a cut head without the character the cut left unfinished.
105
+ *
106
+ * Its last character starts at the last byte that is not a continuation byte,
107
+ * and that byte says how many bytes the character needs. One that needs more
108
+ * than the head holds is dropped, for the same reason {@link decodeTail} skips
109
+ * a partial code point.
110
+ */
111
+ const wholeLength = (bytes) => {
112
+ for (let back = 1; back <= Math.min(3, bytes.byteLength); back++) {
113
+ const byte = bytes[bytes.byteLength - back];
114
+ if (isContinuation(byte))
115
+ continue;
116
+ const width = byte >= 0b1111_0000 ? 4 : byte >= 0b1110_0000 ? 3 : byte >= 0b1100_0000 ? 2 : 1;
117
+ return width > back ? bytes.byteLength - back : bytes.byteLength;
118
+ }
119
+ return bytes.byteLength;
120
+ };
121
+ /**
122
+ * Decodes a stream kept by its ends. A stream that fit is decoded whole;
123
+ * otherwise each end is cut on a character boundary and one line naming every
124
+ * byte between them stands where the middle was.
125
+ */
126
+ const decodeEnds = (state) => {
127
+ if (state.tail.dropped === 0) {
128
+ const whole = join([...state.head, ...state.tail.chunks], state.headBytes + state.tail.bytes);
129
+ return { text: decoder.decode(whole), droppedBytes: 0 };
130
+ }
131
+ const head = join(state.head, state.headBytes);
132
+ const kept = wholeLength(head);
133
+ const tail = decodeTail(state.tail);
134
+ const droppedBytes = head.byteLength - kept + tail.droppedBytes;
135
+ return { text: elideMiddle(decoder.decode(head.subarray(0, kept)), droppedBytes, tail.text), droppedBytes };
136
+ };
137
+ const boundedTail = (stream, maxBytes, overflow) => Stream.runFold(stream, emptyTail, (state, chunk) => {
138
+ keepTail(state, chunk, maxBytes);
139
+ return state;
140
+ }).pipe(Effect.map((state) => overflow === "refuse" && state.dropped > 0
141
+ ? { text: "", droppedBytes: state.dropped }
142
+ : decodeTail(state)));
143
+ /**
144
+ * Keeps the first half of the bound and the last half. The head fills first,
145
+ * and every byte after it passes through the tail, which drops from its front:
146
+ * what falls out of the tail is the middle.
147
+ */
148
+ const boundedEnds = (stream, maxBytes) => {
149
+ const headBytes = Math.floor(maxBytes / 2);
150
+ return Stream.runFold(stream, () => ({ head: [], headBytes: 0, tail: emptyTail() }), (state, chunk) => {
151
+ const taken = Math.min(headBytes - state.headBytes, chunk.byteLength);
152
+ if (taken > 0) {
153
+ state.head.push(chunk.subarray(0, taken));
154
+ state.headBytes += taken;
155
+ }
156
+ keepTail(state.tail, chunk.subarray(taken), maxBytes - headBytes);
157
+ return state;
158
+ }).pipe(Effect.map(decodeEnds));
159
+ };
160
+ const capture = (stream, maxBytes, overflow) => maxBytes === undefined
161
+ ? Effect.map(Stream.mkString(Stream.decodeText(stream)), (text) => ({ text, droppedBytes: 0 }))
162
+ : overflow === "ends"
163
+ ? boundedEnds(stream, maxBytes)
164
+ : boundedTail(stream, maxBytes, overflow);
165
+ /**
166
+ * A command whose arguments exceed the operating system's limit fails `execve`
167
+ * with `E2BIG` before it starts: one argument over 128 KiB on Linux, all of
168
+ * them over 1 MiB on macOS. A heredoc in a command line, or a file's text
169
+ * passed as an argument, is one such argument. The platform error's own text
170
+ * repeats the whole command line, so this text is written here instead, and a
171
+ * caller may show it as it stands.
172
+ */
173
+ const argumentsTooLong = (error) => error.cause?.code === "E2BIG"
174
+ ? new ExecError({
175
+ code: "arguments_too_long",
176
+ message: "Command not started: its arguments exceed the operating system's size limit (E2BIG). Send long text on standard input or in a file, not in the command line or its arguments."
177
+ })
178
+ : undefined;
179
+ const unbounded = (command, options) => Effect.gen(function* () {
180
+ const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
181
+ const settings = {
182
+ ...(options.args === undefined ? { shell: true } : {}),
183
+ ...(options.cwd === undefined ? {} : { cwd: options.cwd }),
184
+ env: ChildProcessEnvironment.make(process.env, options.env),
185
+ extendEnv: false,
186
+ ...(options.stdin === undefined
187
+ ? {}
188
+ : { stdin: Stream.make(new TextEncoder().encode(options.stdin)) })
189
+ };
190
+ const handle = yield* spawner.spawn(options.args === undefined
191
+ ? ChildProcess.make(command, settings)
192
+ : ChildProcess.make(command, [...options.args], settings)).pipe(
193
+ // Node's `spawn` throws E2BIG instead of emitting `error`, and Effect's
194
+ // raw Node spawner calls it unguarded, so there it arrives as a defect.
195
+ Effect.catchDefect((defect) => {
196
+ const refused = argumentsTooLong({ cause: defect });
197
+ return refused === undefined ? Effect.die(defect) : Effect.fail(refused);
198
+ }));
199
+ // stdout, stderr, and the exit code have to be consumed concurrently: a
200
+ // command that fills a pipe blocks until the pipe is drained, so waiting
201
+ // for exit first would deadlock on any output larger than the buffer.
202
+ const [stdout, stderr, exitCode] = yield* Effect.all([
203
+ capture(handle.stdout, options.maxCaptureBytes, options.overflow),
204
+ capture(handle.stderr, options.maxCaptureBytes, options.overflow),
205
+ handle.exitCode
206
+ ], { concurrency: "unbounded" });
207
+ if (options.overflow === "refuse") {
208
+ const overflowed = [
209
+ stdout.droppedBytes > 0 ? "stdout" : undefined,
210
+ stderr.droppedBytes > 0 ? "stderr" : undefined
211
+ ]
212
+ .filter((stream) => stream !== undefined);
213
+ if (overflowed.length > 0) {
214
+ return yield* Effect.fail(new ExecError({
215
+ code: "capture_overflow",
216
+ message: `${command} ${overflowed.join(" and ")} exceeded the ${options.maxCaptureBytes}-byte capture cap; refusing partial output`
217
+ }));
218
+ }
219
+ }
220
+ return {
221
+ stdout: stdout.text,
222
+ stderr: stderr.text,
223
+ exitCode,
224
+ stdoutDroppedBytes: stdout.droppedBytes,
225
+ stderrDroppedBytes: stderr.droppedBytes
226
+ };
227
+ }).pipe(Effect.scoped, Effect.mapError((error) => error instanceof ExecError ? error : argumentsTooLong(error) ?? new ExecError({
228
+ code: "spawn_error",
229
+ message: `exec: ${command}: ${error.message}`
230
+ })));
231
+ /**
232
+ * Runs a command line through the platform shell and buffers its output.
233
+ *
234
+ * Cancellation is scope closure: the spawn is scoped, so an interrupted fiber
235
+ * — including the one a `timeoutMs` interrupts — terminates the process rather
236
+ * than leaking it.
237
+ *
238
+ * @category execution
239
+ * @since 1.0.0
240
+ */
241
+ export const exec = (command, options = {}) => options.timeoutMs === undefined
242
+ ? unbounded(command, options)
243
+ : Effect.timeoutOrElse(unbounded(command, options), {
244
+ duration: options.timeoutMs,
245
+ orElse: () => Effect.fail(new ExecError({
246
+ code: "timeout",
247
+ message: `exec: \`${command}\` exceeded ${options.timeoutMs}ms`,
248
+ limitMillis: options.timeoutMs
249
+ }))
250
+ });
251
+ /**
252
+ * The model-facing failure code a command that never produced an exit code
253
+ * carries.
254
+ *
255
+ * A timeout keeps its own code, because "the command never finished" and "the
256
+ * command never started" are different things to a caller deciding whether to
257
+ * retry. Arguments the operating system refused for their size are the
258
+ * caller's own input, which a shell flow publishes, so they read as
259
+ * `invalid_input`. Every other way of producing no exit code is a host failure.
260
+ *
261
+ * @category errors
262
+ * @since 1.0.0
263
+ */
264
+ export const toStdErrorCode = (error) => error.code === "timeout" ? "timeout" : error.code === "arguments_too_long" ? "invalid_input" : "command_failed";
265
+ /**
266
+ * The model-facing failure one execution error becomes.
267
+ *
268
+ * A timeout names the command, which is what the caller asked for and all a
269
+ * timeout has to say. Arguments too long to start carry the reason and the
270
+ * fix, and nothing of the command. Any other failure carries the message the
271
+ * execution produced, which already names the command.
272
+ *
273
+ * @category errors
274
+ * @since 1.0.0
275
+ */
276
+ export const toStdError = (command, error) => new StdError.StdError({
277
+ code: toStdErrorCode(error),
278
+ message: error.code === "timeout"
279
+ ? `Command timed out: ${command}`
280
+ : error.code === "arguments_too_long"
281
+ ? error.message
282
+ : `Command failed to start: ${error.message}`,
283
+ ...(error.limitMillis === undefined ? {} : { limitMillis: error.limitMillis })
284
+ });
285
+ //# 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 ChildProcess from \"effect/process/ChildProcess\"\nimport * as Schema from \"effect/Schema\"\nimport * as Stream from \"effect/Stream\"\nimport * as StdError from \"../StdError.ts\"\nimport { elideMiddle } from \"./Text.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\", \"arguments_too_long\", \"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 (`arguments_too_long`\n * when the operating system refused it for the size of its arguments) or was\n * cut off by its timeout, or whose captured output exceeded a refusing capture\n * 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 of stdout dropped to stay inside the capture bound; see `overflow`. */\n readonly stdoutDroppedBytes: number\n /** UTF-8 bytes of stderr dropped to stay inside the capture bound; see `overflow`. */\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 what\n * `overflow` names and reports what it dropped, so a command printing\n * gigabytes costs a bounded amount of memory rather than the whole of what it\n * 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 /**\n * What a stream past the bound keeps. `tail`, the default, keeps its last\n * bytes. `ends` keeps its first and last halves and puts one line naming the\n * dropped bytes where the middle was. `refuse` fails if either stream exceeds\n * the bound.\n */\n readonly overflow?: Overflow | undefined\n}\n\ntype Overflow = \"tail\" | \"ends\" | \"refuse\"\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\n/** A stream's first bytes and its last ones: what `overflow: \"ends\"` keeps. */\ninterface Ends {\n readonly head: Array<Uint8Array>\n headBytes: number\n readonly tail: Tail\n}\n\nconst decoder = new TextDecoder(\"utf-8\")\n\nconst emptyTail = (): Tail => ({ chunks: [], bytes: 0, dropped: 0 })\n\n/** A UTF-8 continuation byte is `10xxxxxx`; every other byte starts a character. */\nconst isContinuation = (byte: number): boolean => (byte & 0b1100_0000) === 0b1000_0000\n\nconst join = (chunks: ReadonlyArray<Uint8Array>, size: number): Uint8Array => {\n const bytes = new Uint8Array(size)\n let offset = 0\n for (const chunk of chunks) {\n bytes.set(chunk, offset)\n offset += chunk.byteLength\n }\n return bytes\n}\n\n/** Appends one chunk and drops from the front whatever no longer fits. */\nconst keepTail = (state: Tail, chunk: Uint8Array, maxBytes: number): void => {\n if (chunk.byteLength === 0) return\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}\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 code point is at most four bytes, so at most three leading continuation\n * bytes can precede the first whole character. Skipping them is counted as\n * dropped rather than decoded to a replacement character the process never\n * printed.\n */\nconst decodeTail = (state: Tail): Capture => {\n const bytes = join(state.chunks, state.bytes)\n if (state.dropped === 0) return { text: decoder.decode(bytes), droppedBytes: 0 }\n let start = 0\n while (start < bytes.byteLength && start < 3 && isContinuation(bytes[start]!)) start++\n return { text: decoder.decode(bytes.subarray(start)), droppedBytes: state.dropped + start }\n}\n\n/**\n * The length of a cut head without the character the cut left unfinished.\n *\n * Its last character starts at the last byte that is not a continuation byte,\n * and that byte says how many bytes the character needs. One that needs more\n * than the head holds is dropped, for the same reason {@link decodeTail} skips\n * a partial code point.\n */\nconst wholeLength = (bytes: Uint8Array): number => {\n for (let back = 1; back <= Math.min(3, bytes.byteLength); back++) {\n const byte = bytes[bytes.byteLength - back]!\n if (isContinuation(byte)) continue\n const width = byte >= 0b1111_0000 ? 4 : byte >= 0b1110_0000 ? 3 : byte >= 0b1100_0000 ? 2 : 1\n return width > back ? bytes.byteLength - back : bytes.byteLength\n }\n return bytes.byteLength\n}\n\n/**\n * Decodes a stream kept by its ends. A stream that fit is decoded whole;\n * otherwise each end is cut on a character boundary and one line naming every\n * byte between them stands where the middle was.\n */\nconst decodeEnds = (state: Ends): Capture => {\n if (state.tail.dropped === 0) {\n const whole = join([...state.head, ...state.tail.chunks], state.headBytes + state.tail.bytes)\n return { text: decoder.decode(whole), droppedBytes: 0 }\n }\n const head = join(state.head, state.headBytes)\n const kept = wholeLength(head)\n const tail = decodeTail(state.tail)\n const droppedBytes = head.byteLength - kept + tail.droppedBytes\n return { text: elideMiddle(decoder.decode(head.subarray(0, kept)), droppedBytes, tail.text), droppedBytes }\n}\n\nconst boundedTail = <E>(\n stream: Stream.Stream<Uint8Array, E>,\n maxBytes: number,\n overflow: Overflow | undefined\n): Effect.Effect<Capture, E> =>\n Stream.runFold(stream, emptyTail, (state, chunk) => {\n keepTail(state, chunk, maxBytes)\n return state\n }).pipe(Effect.map((state) =>\n overflow === \"refuse\" && state.dropped > 0\n ? { text: \"\", droppedBytes: state.dropped }\n : decodeTail(state)\n ))\n\n/**\n * Keeps the first half of the bound and the last half. The head fills first,\n * and every byte after it passes through the tail, which drops from its front:\n * what falls out of the tail is the middle.\n */\nconst boundedEnds = <E>(stream: Stream.Stream<Uint8Array, E>, maxBytes: number): Effect.Effect<Capture, E> => {\n const headBytes = Math.floor(maxBytes / 2)\n return Stream.runFold(stream, (): Ends => ({ head: [], headBytes: 0, tail: emptyTail() }), (state, chunk) => {\n const taken = Math.min(headBytes - state.headBytes, chunk.byteLength)\n if (taken > 0) {\n state.head.push(chunk.subarray(0, taken))\n state.headBytes += taken\n }\n keepTail(state.tail, chunk.subarray(taken), maxBytes - headBytes)\n return state\n }).pipe(Effect.map(decodeEnds))\n}\n\nconst capture = <E>(\n stream: Stream.Stream<Uint8Array, E>,\n maxBytes: number | undefined,\n overflow: Overflow | undefined\n): Effect.Effect<Capture, E> =>\n maxBytes === undefined\n ? Effect.map(Stream.mkString(Stream.decodeText(stream)), (text) => ({ text, droppedBytes: 0 }))\n : overflow === \"ends\"\n ? boundedEnds(stream, maxBytes)\n : boundedTail(stream, maxBytes, overflow)\n\n/**\n * A command whose arguments exceed the operating system's limit fails `execve`\n * with `E2BIG` before it starts: one argument over 128 KiB on Linux, all of\n * them over 1 MiB on macOS. A heredoc in a command line, or a file's text\n * passed as an argument, is one such argument. The platform error's own text\n * repeats the whole command line, so this text is written here instead, and a\n * caller may show it as it stands.\n */\nconst argumentsTooLong = (error: { readonly cause?: unknown }): ExecError | undefined =>\n (error.cause as { readonly code?: unknown } | undefined)?.code === \"E2BIG\"\n ? new ExecError({\n code: \"arguments_too_long\",\n message:\n \"Command not started: its arguments exceed the operating system's size limit (E2BIG). Send long text on standard input or in a file, not in the command line or its arguments.\"\n })\n : undefined\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 ).pipe(\n // Node's `spawn` throws E2BIG instead of emitting `error`, and Effect's\n // raw Node spawner calls it unguarded, so there it arrives as a defect.\n Effect.catchDefect((defect) => {\n const refused = argumentsTooLong({ cause: defect })\n return refused === undefined ? Effect.die(defect) : Effect.fail(refused)\n })\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 : argumentsTooLong(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. Arguments the operating system refused for their size are the\n * caller's own input, which a shell flow publishes, so they read as\n * `invalid_input`. 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\" : error.code === \"arguments_too_long\" ? \"invalid_input\" : \"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. Arguments too long to start carry the reason and the\n * fix, and nothing of the command. Any other failure carries the message the\n * execution 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 : error.code === \"arguments_too_long\"\n ? error.message\n : `Command failed to start: ${error.message}`,\n ...(error.limitMillis === undefined ? {} : { limitMillis: error.limitMillis })\n })\n"]}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Mutation preparation for versioned hosts and cooperative locks for ordinary hosts.
3
+ * Ordinary locks fail closed after process death; they are never stolen on a timer.
4
+ *
5
+ * @since 1.0.0
6
+ */
7
+ import * as Context from "effect/Context";
8
+ import * as Effect from "effect/Effect";
9
+ import type * as FileSystem from "effect/FileSystem";
10
+ import type * as Path from "effect/Path";
11
+ import type * as Scope from "effect/Scope";
12
+ import * as StdError from "../StdError.ts";
13
+ /**
14
+ * Authenticated standard-flow call session; absent callers cannot borrow another run's base.
15
+ *
16
+ * @category context
17
+ * @since 1.0.0
18
+ */
19
+ export declare const ReadSession: Context.Reference<string | undefined>;
20
+ /**
21
+ * The coding host attaches one run-scoped precondition policy to its guarded filesystem.
22
+ *
23
+ * @category identifiers
24
+ * @since 1.0.0
25
+ */
26
+ export declare const Preconditions: unique symbol;
27
+ /**
28
+ * One prepared file change; moves include a write and a guarded source removal.
29
+ *
30
+ * @category models
31
+ * @since 1.0.0
32
+ */
33
+ export interface Change {
34
+ readonly path: string;
35
+ /** Null removes a file. Writes preserve existing metadata; new files use host defaults. */
36
+ readonly content: Uint8Array | null;
37
+ }
38
+ /**
39
+ * A per-invocation provider receipt boundary, with immutable captured read bases.
40
+ *
41
+ * @category models
42
+ * @since 1.0.0
43
+ */
44
+ export interface Prepared {
45
+ /** Return bytes matching the captured base, never an unchecked current read. */
46
+ readonly read: (path: string) => Effect.Effect<Uint8Array, StdError.StdError>;
47
+ readonly commit: (changes: ReadonlyArray<Change>) => Effect.Effect<void, StdError.StdError>;
48
+ }
49
+ /**
50
+ * Read records only successful model-facing reads; mutation-internal reads never refresh a base.
51
+ *
52
+ * @category models
53
+ * @since 1.0.0
54
+ */
55
+ export interface VersionedFileSystem extends FileSystem.FileSystem {
56
+ readonly [Preconditions]?: {
57
+ readonly record: (path: string, bytes: Uint8Array, session: string | undefined) => Effect.Effect<void, StdError.StdError>;
58
+ readonly validate: (paths: ReadonlyArray<string>, session: string | undefined) => Effect.Effect<void, StdError.StdError>;
59
+ /** Capture this invocation's bases from the existing ledger before tool-internal
60
+ * reads. The returned commit must compare them again inside the provider, then
61
+ * settle the entire batch before advancing the ledger. Concurrent reads or
62
+ * commits must never replace these captured bases. Preparation does not freeze
63
+ * callers; only the complete commit may acquire guest-wide exclusion.
64
+ */
65
+ readonly prepare?: (paths: ReadonlyArray<string>, session: string | undefined) => Effect.Effect<Prepared, StdError.StdError>;
66
+ };
67
+ }
68
+ /**
69
+ * Versioned hosts own parent creation and locking inside their atomic commit.
70
+ *
71
+ * @category guards
72
+ * @since 1.0.0
73
+ */
74
+ export declare const isVersioned: (fs: FileSystem.FileSystem) => boolean;
75
+ /**
76
+ * Record the entire original file, before pagination, through the host's existing policy.
77
+ *
78
+ * @category filesystem
79
+ * @since 1.0.0
80
+ */
81
+ export declare const recordRead: (fs: FileSystem.FileSystem, path: string, bytes: Uint8Array) => Effect.Effect<void, StdError.StdError, never>;
82
+ /**
83
+ * Refuse an unqualified provider before creating any parent or lock directories.
84
+ *
85
+ * @category filesystem
86
+ * @since 1.0.0
87
+ */
88
+ export declare const validate: (fs: FileSystem.FileSystem, paths: ReadonlyArray<string>) => Effect.Effect<undefined, StdError.StdError, never>;
89
+ /**
90
+ * The portable body is used only for an ordinary, unversioned filesystem.
91
+ *
92
+ * @category models
93
+ * @since 1.0.0
94
+ */
95
+ export interface Mutation {
96
+ readonly read: (path: string, ordinary: Effect.Effect<Uint8Array, StdError.StdError>) => Effect.Effect<Uint8Array, StdError.StdError>;
97
+ readonly commit: (changes: ReadonlyArray<Change>, ordinary: Effect.Effect<void, StdError.StdError>) => Effect.Effect<void, StdError.StdError>;
98
+ }
99
+ /**
100
+ * Versioned hosts capture bases under a local semaphore until scope exit.
101
+ * Ordinary hosts hold all named files using exclusive mkdir, so independently
102
+ * composed hosts and processes agree.
103
+ * A hash collision only refuses an unrelated edit; it cannot lose exclusion.
104
+ *
105
+ * @private
106
+ * @since 1.0.0
107
+ */
108
+ export declare const acquire: (fileSystem: FileSystem.FileSystem, paths: ReadonlyArray<string>, creationPaths?: Path.Path) => Effect.Effect<Mutation, StdError.StdError, Scope.Scope>;
109
+ //# sourceMappingURL=FileMutation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileMutation.d.ts","sourceRoot":"","sources":["../../../src/internal/FileMutation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Mutation preparation for versioned hosts and cooperative locks for ordinary hosts.
3
+ * Ordinary locks fail closed after process death; they are never stolen on a timer.
4
+ *
5
+ * @since 1.0.0
6
+ */
7
+ import * as Context from "effect/Context";
8
+ import * as Effect from "effect/Effect";
9
+ import * as Semaphore from "effect/Semaphore";
10
+ import * as StdError from "../StdError.js";
11
+ import * as FsFailure from "./FsFailure.js";
12
+ /**
13
+ * Authenticated standard-flow call session; absent callers cannot borrow another run's base.
14
+ *
15
+ * @category context
16
+ * @since 1.0.0
17
+ */
18
+ export const ReadSession = Context.Reference("@smthrs/std/ReadSession", {
19
+ defaultValue: () => undefined
20
+ });
21
+ /**
22
+ * The coding host attaches one run-scoped precondition policy to its guarded filesystem.
23
+ *
24
+ * @category identifiers
25
+ * @since 1.0.0
26
+ */
27
+ export const Preconditions = Symbol.for("@smthrs/std/file-preconditions");
28
+ const unavailable = () => Effect.fail(new StdError.StdError({
29
+ code: "provider_unavailable",
30
+ message: "Authenticated atomic file mutation provider unavailable"
31
+ }));
32
+ /**
33
+ * Versioned hosts own parent creation and locking inside their atomic commit.
34
+ *
35
+ * @category guards
36
+ * @since 1.0.0
37
+ */
38
+ export const isVersioned = (fs) => fs[Preconditions] !== undefined;
39
+ /**
40
+ * Record the entire original file, before pagination, through the host's existing policy.
41
+ *
42
+ * @category filesystem
43
+ * @since 1.0.0
44
+ */
45
+ export const recordRead = (fs, path, bytes) => Effect.flatMap(ReadSession, (session) => fs[Preconditions]?.record(path, bytes, session) ?? Effect.void);
46
+ /**
47
+ * Refuse an unqualified provider before creating any parent or lock directories.
48
+ *
49
+ * @category filesystem
50
+ * @since 1.0.0
51
+ */
52
+ export const validate = (fs, paths) => Effect.gen(function* () {
53
+ const policy = fs[Preconditions];
54
+ if (policy === undefined)
55
+ return;
56
+ const session = yield* ReadSession;
57
+ yield* policy.validate(paths, session);
58
+ if (policy.prepare === undefined)
59
+ return yield* unavailable();
60
+ });
61
+ // This serializes one host's preparation, ledger and diagnostic ordering only.
62
+ // It is NOT the guest's outside-writer exclusion or its atomic commit boundary.
63
+ const hostLocks = new WeakMap();
64
+ // The on-disk lock protocol owns this hash; dependency upgrades must not
65
+ // change the name and let two versions acquire different locks for one file.
66
+ // Case/normalization aliases deliberately share a lock, including on hosts
67
+ // whose realPath preserves caller casing. Case-sensitive hosts may refuse a
68
+ // simultaneous change to a distinct case variant; that is safe contention.
69
+ const lockHash = (value) => {
70
+ value = value.normalize("NFC").toUpperCase();
71
+ let hash = 5381;
72
+ for (let index = 0; index < value.length; index++)
73
+ hash = (hash * 33) ^ value.charCodeAt(index);
74
+ return (hash >>> 0).toString(16);
75
+ };
76
+ const parent = (path) => path.slice(0, path.lastIndexOf(path.startsWith("/") ? "/" : "\\") + 1);
77
+ /**
78
+ * Versioned hosts capture bases under a local semaphore until scope exit.
79
+ * Ordinary hosts hold all named files using exclusive mkdir, so independently
80
+ * composed hosts and processes agree.
81
+ * A hash collision only refuses an unrelated edit; it cannot lose exclusion.
82
+ *
83
+ * @private
84
+ * @since 1.0.0
85
+ */
86
+ export const acquire = (fileSystem, paths, creationPaths) => Effect.gen(function* () {
87
+ // Unavailable coding providers refuse before acquiring even protocol state.
88
+ // The versioned provider owns exclusion and revalidation at its commit;
89
+ // cooperative caller-side locks cannot exclude terminal or SSH writers.
90
+ yield* validate(fileSystem, paths);
91
+ const policy = fileSystem[Preconditions];
92
+ if (policy !== undefined) {
93
+ let lock = hostLocks.get(policy);
94
+ if (lock === undefined) {
95
+ lock = Semaphore.makeUnsafe(1);
96
+ hostLocks.set(policy, lock);
97
+ }
98
+ const held = lock;
99
+ yield* Effect.acquireRelease(held.take(1), () => held.release(1), { interruptible: true });
100
+ const session = yield* ReadSession;
101
+ const prepared = yield* policy.prepare(paths, session);
102
+ return { read: (path) => prepared.read(path), commit: (changes) => prepared.commit(changes) };
103
+ }
104
+ const locks = new Map();
105
+ for (const path of paths) {
106
+ const destination = yield* fileSystem.realPath(path).pipe(Effect.catch((error) => error.reason._tag === "NotFound" && creationPaths !== undefined
107
+ ? fileSystem.realPath(creationPaths.dirname(path)).pipe(Effect.map((directory) => creationPaths.join(directory, creationPaths.basename(path))))
108
+ : Effect.fail(error)), Effect.mapError(FsFailure.reading(path, `File not found: ${path}`)));
109
+ const lock = `${parent(destination)}.smithers-${lockHash(destination)}.lock`;
110
+ locks.set(lock, path);
111
+ }
112
+ for (const [lock, path] of [...locks].sort(([left], [right]) => left.localeCompare(right))) {
113
+ yield* Effect.acquireRelease(fileSystem.makeDirectory(lock, { mode: 0o700 }).pipe(Effect.mapError(FsFailure.denied(path, (error) => new StdError.StdError({
114
+ code: error.reason._tag === "AlreadyExists" ? "no_match" : "command_failed",
115
+ message: error.reason._tag === "AlreadyExists"
116
+ ? `Concurrent mutation of ${path}: lock ${lock} is held. Re-read and retry after the other writer finishes; a stopped writer's lock requires explicit recovery.`
117
+ : `Could not lock ${path} for mutation`,
118
+ path
119
+ })))), () => fileSystem.remove(lock, { recursive: true }).pipe(Effect.orDie));
120
+ }
121
+ return { read: (_path, ordinary) => ordinary, commit: (_changes, ordinary) => ordinary };
122
+ });
123
+ //# sourceMappingURL=FileMutation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileMutation.js","sourceRoot":"","sources":["../../../src/internal/FileMutation.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Mutation preparation for versioned hosts and cooperative locks for ordinary hosts.\n * Ordinary locks fail closed after process death; they are never stolen on a timer.\n *\n * @since 1.0.0\n */\n\nimport * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport type * as FileSystem from \"effect/FileSystem\"\nimport type * as Path from \"effect/Path\"\nimport type * as Scope from \"effect/Scope\"\nimport * as Semaphore from \"effect/Semaphore\"\nimport * as StdError from \"../StdError.ts\"\nimport * as FsFailure from \"./FsFailure.ts\"\n\n/**\n * Authenticated standard-flow call session; absent callers cannot borrow another run's base.\n *\n * @category context\n * @since 1.0.0\n */\nexport const ReadSession = Context.Reference<string | undefined>(\"@smthrs/std/ReadSession\", {\n defaultValue: () => undefined\n})\n\n/**\n * The coding host attaches one run-scoped precondition policy to its guarded filesystem.\n *\n * @category identifiers\n * @since 1.0.0\n */\nexport const Preconditions = Symbol.for(\"@smthrs/std/file-preconditions\")\n\n/**\n * One prepared file change; moves include a write and a guarded source removal.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface Change {\n readonly path: string\n /** Null removes a file. Writes preserve existing metadata; new files use host defaults. */\n readonly content: Uint8Array | null\n}\n\n/**\n * A per-invocation provider receipt boundary, with immutable captured read bases.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface Prepared {\n /** Return bytes matching the captured base, never an unchecked current read. */\n readonly read: (path: string) => Effect.Effect<Uint8Array, StdError.StdError>\n readonly commit: (changes: ReadonlyArray<Change>) => Effect.Effect<void, StdError.StdError>\n}\n\n/**\n * Read records only successful model-facing reads; mutation-internal reads never refresh a base.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface VersionedFileSystem extends FileSystem.FileSystem {\n readonly [Preconditions]?: {\n readonly record: (\n path: string,\n bytes: Uint8Array,\n session: string | undefined\n ) => Effect.Effect<void, StdError.StdError>\n readonly validate: (\n paths: ReadonlyArray<string>,\n session: string | undefined\n ) => Effect.Effect<void, StdError.StdError>\n /** Capture this invocation's bases from the existing ledger before tool-internal\n * reads. The returned commit must compare them again inside the provider, then\n * settle the entire batch before advancing the ledger. Concurrent reads or\n * commits must never replace these captured bases. Preparation does not freeze\n * callers; only the complete commit may acquire guest-wide exclusion.\n */\n readonly prepare?: (\n paths: ReadonlyArray<string>,\n session: string | undefined\n ) => Effect.Effect<Prepared, StdError.StdError>\n }\n}\n\nconst unavailable = () =>\n Effect.fail(\n new StdError.StdError({\n code: \"provider_unavailable\",\n message: \"Authenticated atomic file mutation provider unavailable\"\n })\n )\n\n/**\n * Versioned hosts own parent creation and locking inside their atomic commit.\n *\n * @category guards\n * @since 1.0.0\n */\nexport const isVersioned = (fs: FileSystem.FileSystem): boolean =>\n (fs as VersionedFileSystem)[Preconditions] !== undefined\n\n/**\n * Record the entire original file, before pagination, through the host's existing policy.\n *\n * @category filesystem\n * @since 1.0.0\n */\nexport const recordRead = (fs: FileSystem.FileSystem, path: string, bytes: Uint8Array) =>\n Effect.flatMap(\n ReadSession,\n (session) => (fs as VersionedFileSystem)[Preconditions]?.record(path, bytes, session) ?? Effect.void\n )\n\n/**\n * Refuse an unqualified provider before creating any parent or lock directories.\n *\n * @category filesystem\n * @since 1.0.0\n */\nexport const validate = (fs: FileSystem.FileSystem, paths: ReadonlyArray<string>) =>\n Effect.gen(function*() {\n const policy = (fs as VersionedFileSystem)[Preconditions]\n if (policy === undefined) return\n const session = yield* ReadSession\n yield* policy.validate(paths, session)\n if (policy.prepare === undefined) return yield* unavailable()\n })\n\n/**\n * The portable body is used only for an ordinary, unversioned filesystem.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface Mutation {\n readonly read: (\n path: string,\n ordinary: Effect.Effect<Uint8Array, StdError.StdError>\n ) => Effect.Effect<Uint8Array, StdError.StdError>\n readonly commit: (\n changes: ReadonlyArray<Change>,\n ordinary: Effect.Effect<void, StdError.StdError>\n ) => Effect.Effect<void, StdError.StdError>\n}\n\n// This serializes one host's preparation, ledger and diagnostic ordering only.\n// It is NOT the guest's outside-writer exclusion or its atomic commit boundary.\nconst hostLocks = new WeakMap<object, Semaphore.Semaphore>()\n\n// The on-disk lock protocol owns this hash; dependency upgrades must not\n// change the name and let two versions acquire different locks for one file.\n// Case/normalization aliases deliberately share a lock, including on hosts\n// whose realPath preserves caller casing. Case-sensitive hosts may refuse a\n// simultaneous change to a distinct case variant; that is safe contention.\nconst lockHash = (value: string): string => {\n value = value.normalize(\"NFC\").toUpperCase()\n let hash = 5381\n for (let index = 0; index < value.length; index++) hash = (hash * 33) ^ value.charCodeAt(index)\n return (hash >>> 0).toString(16)\n}\n\nconst parent = (path: string) => path.slice(0, path.lastIndexOf(path.startsWith(\"/\") ? \"/\" : \"\\\\\") + 1)\n\n/**\n * Versioned hosts capture bases under a local semaphore until scope exit.\n * Ordinary hosts hold all named files using exclusive mkdir, so independently\n * composed hosts and processes agree.\n * A hash collision only refuses an unrelated edit; it cannot lose exclusion.\n *\n * @private\n * @since 1.0.0\n */\nexport const acquire = (\n fileSystem: FileSystem.FileSystem,\n paths: ReadonlyArray<string>,\n creationPaths?: Path.Path\n): Effect.Effect<Mutation, StdError.StdError, Scope.Scope> =>\n Effect.gen(function*() {\n // Unavailable coding providers refuse before acquiring even protocol state.\n // The versioned provider owns exclusion and revalidation at its commit;\n // cooperative caller-side locks cannot exclude terminal or SSH writers.\n yield* validate(fileSystem, paths)\n const policy = (fileSystem as VersionedFileSystem)[Preconditions]\n if (policy !== undefined) {\n let lock = hostLocks.get(policy)\n if (lock === undefined) {\n lock = Semaphore.makeUnsafe(1)\n hostLocks.set(policy, lock)\n }\n const held = lock\n yield* Effect.acquireRelease(held.take(1), () => held.release(1), { interruptible: true })\n const session = yield* ReadSession\n const prepared = yield* policy.prepare!(paths, session)\n return { read: (path) => prepared.read(path), commit: (changes) => prepared.commit(changes) }\n }\n const locks = new Map<string, string>()\n for (const path of paths) {\n const destination = yield* fileSystem.realPath(path).pipe(\n Effect.catch((error) =>\n error.reason._tag === \"NotFound\" && creationPaths !== undefined\n ? fileSystem.realPath(creationPaths.dirname(path)).pipe(\n Effect.map((directory) => creationPaths.join(directory, creationPaths.basename(path)))\n )\n : Effect.fail(error)\n ),\n Effect.mapError(FsFailure.reading(path, `File not found: ${path}`))\n )\n const lock = `${parent(destination)}.smithers-${lockHash(destination)}.lock`\n locks.set(lock, path)\n }\n for (const [lock, path] of [...locks].sort(([left], [right]) => left.localeCompare(right))) {\n yield* Effect.acquireRelease(\n fileSystem.makeDirectory(lock, { mode: 0o700 }).pipe(\n Effect.mapError(FsFailure.denied(path, (error) =>\n new StdError.StdError({\n code: error.reason._tag === \"AlreadyExists\" ? \"no_match\" : \"command_failed\",\n message: error.reason._tag === \"AlreadyExists\"\n ? `Concurrent mutation of ${path}: lock ${lock} is held. Re-read and retry after the other writer finishes; a stopped writer's lock requires explicit recovery.`\n : `Could not lock ${path} for mutation`,\n path\n })))\n ),\n () => fileSystem.remove(lock, { recursive: true }).pipe(Effect.orDie)\n )\n }\n return { read: (_path, ordinary) => ordinary, commit: (_changes, ordinary) => ordinary }\n })\n"]}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Maps a filesystem failure to the standard-flow code that names its reason.
3
+ *
4
+ * A permission denial is not a missing file. Telling the model a denied path
5
+ * does not exist sends it searching for a file that is right there, and hides
6
+ * the denial from whoever reads the run. Every standard flow that touches the
7
+ * filesystem routes its failures through here so `PermissionDenied` always
8
+ * reaches the model as `permission_denied`.
9
+ *
10
+ * @since 1.0.0
11
+ */
12
+ import type * as PlatformError from "effect/PlatformError";
13
+ import * as StdError from "../StdError.ts";
14
+ /**
15
+ * The failure for a path the host or the kernel guard refused.
16
+ *
17
+ * @private
18
+ * @since 1.0.0
19
+ */
20
+ export declare const permissionDenied: (path: string) => StdError.StdError;
21
+ /**
22
+ * Maps `PermissionDenied` to `permission_denied` and any other reason through
23
+ * `otherwise`.
24
+ *
25
+ * @private
26
+ * @since 1.0.0
27
+ */
28
+ export declare const denied: (path: string, otherwise: (error: PlatformError.PlatformError) => StdError.StdError) => (error: PlatformError.PlatformError) => StdError.StdError;
29
+ /**
30
+ * Maps a read-side failure: `NotFound` to `not_found` with `notFoundMessage`,
31
+ * `PermissionDenied` to `permission_denied`, and anything else to
32
+ * `command_failed` carrying the host's reason.
33
+ *
34
+ * @private
35
+ * @since 1.0.0
36
+ */
37
+ export declare const reading: (path: string, notFoundMessage: string) => (error: PlatformError.PlatformError) => StdError.StdError;
38
+ //# sourceMappingURL=FsFailure.d.ts.map