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