@armadra/agent 0.0.0-stage → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (433) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/LICENSE +21 -0
  3. package/README.md +470 -2
  4. package/dist/agent/agent.d.ts +56 -0
  5. package/dist/agent/agent.js +129 -0
  6. package/dist/agent/loop.d.ts +51 -0
  7. package/dist/agent/loop.js +158 -0
  8. package/dist/agent/queue.d.ts +25 -0
  9. package/dist/agent/queue.js +55 -0
  10. package/dist/agent/retry.d.ts +22 -0
  11. package/dist/agent/retry.js +92 -0
  12. package/dist/agent/schema.d.ts +27 -0
  13. package/dist/agent/schema.js +186 -0
  14. package/dist/agent/session-cache.d.ts +100 -0
  15. package/dist/agent/session-cache.js +514 -0
  16. package/dist/agent/session-compaction.d.ts +49 -0
  17. package/dist/agent/session-compaction.js +251 -0
  18. package/dist/agent/session-core.d.ts +103 -0
  19. package/dist/agent/session-core.js +7 -0
  20. package/dist/agent/session-run.d.ts +55 -0
  21. package/dist/agent/session-run.js +246 -0
  22. package/dist/agent/session-state.d.ts +39 -0
  23. package/dist/agent/session-state.js +119 -0
  24. package/dist/agent/session-subagent.d.ts +54 -0
  25. package/dist/agent/session-subagent.js +157 -0
  26. package/dist/agent/session-sync.d.ts +20 -0
  27. package/dist/agent/session-sync.js +111 -0
  28. package/dist/agent/session-tools.d.ts +24 -0
  29. package/dist/agent/session-tools.js +277 -0
  30. package/dist/agent/session.d.ts +117 -0
  31. package/dist/agent/session.js +485 -0
  32. package/dist/agent/system-prompt.d.ts +49 -0
  33. package/dist/agent/system-prompt.js +121 -0
  34. package/dist/agent/tool-runner.d.ts +64 -0
  35. package/dist/agent/tool-runner.js +309 -0
  36. package/dist/agent/transform.d.ts +26 -0
  37. package/dist/agent/transform.js +171 -0
  38. package/dist/agent/types.d.ts +378 -0
  39. package/dist/agent/types.js +21 -0
  40. package/dist/ai/apis/anthropic-messages.d.ts +26 -0
  41. package/dist/ai/apis/anthropic-messages.js +214 -0
  42. package/dist/ai/apis/anthropic-request.d.ts +42 -0
  43. package/dist/ai/apis/anthropic-request.js +282 -0
  44. package/dist/ai/apis/api.d.ts +28 -0
  45. package/dist/ai/apis/api.js +102 -0
  46. package/dist/ai/apis/cache-params.d.ts +48 -0
  47. package/dist/ai/apis/cache-params.js +163 -0
  48. package/dist/ai/apis/google-generative-ai.d.ts +32 -0
  49. package/dist/ai/apis/google-generative-ai.js +249 -0
  50. package/dist/ai/apis/google-request.d.ts +40 -0
  51. package/dist/ai/apis/google-request.js +255 -0
  52. package/dist/ai/apis/openai-compat.d.ts +35 -0
  53. package/dist/ai/apis/openai-compat.js +142 -0
  54. package/dist/ai/apis/openai-completions.d.ts +28 -0
  55. package/dist/ai/apis/openai-completions.js +246 -0
  56. package/dist/ai/apis/openai-request.d.ts +35 -0
  57. package/dist/ai/apis/openai-request.js +303 -0
  58. package/dist/ai/apis/openai-responses-request.d.ts +52 -0
  59. package/dist/ai/apis/openai-responses-request.js +266 -0
  60. package/dist/ai/apis/openai-responses.d.ts +28 -0
  61. package/dist/ai/apis/openai-responses.js +304 -0
  62. package/dist/ai/apis/shared.d.ts +56 -0
  63. package/dist/ai/apis/shared.js +179 -0
  64. package/dist/ai/cache/economics.d.ts +24 -0
  65. package/dist/ai/cache/economics.js +69 -0
  66. package/dist/ai/cache/fingerprint.d.ts +17 -0
  67. package/dist/ai/cache/fingerprint.js +39 -0
  68. package/dist/ai/cache/miss.d.ts +49 -0
  69. package/dist/ai/cache/miss.js +96 -0
  70. package/dist/ai/cache/reporting.d.ts +39 -0
  71. package/dist/ai/cache/reporting.js +103 -0
  72. package/dist/ai/cache/types.d.ts +94 -0
  73. package/dist/ai/cache/types.js +19 -0
  74. package/dist/ai/cache/warmer.d.ts +94 -0
  75. package/dist/ai/cache/warmer.js +224 -0
  76. package/dist/ai/context.d.ts +35 -0
  77. package/dist/ai/context.js +114 -0
  78. package/dist/ai/cost.d.ts +21 -0
  79. package/dist/ai/cost.js +60 -0
  80. package/dist/ai/event-stream.d.ts +33 -0
  81. package/dist/ai/event-stream.js +101 -0
  82. package/dist/ai/fake/fake-provider.d.ts +68 -0
  83. package/dist/ai/fake/fake-provider.js +278 -0
  84. package/dist/ai/fake/fake-script.d.ts +69 -0
  85. package/dist/ai/fake/fake-script.js +142 -0
  86. package/dist/ai/http.d.ts +54 -0
  87. package/dist/ai/http.js +212 -0
  88. package/dist/ai/json-partial.d.ts +17 -0
  89. package/dist/ai/json-partial.js +243 -0
  90. package/dist/ai/overflow.d.ts +22 -0
  91. package/dist/ai/overflow.js +61 -0
  92. package/dist/ai/providers/auth.d.ts +71 -0
  93. package/dist/ai/providers/auth.js +202 -0
  94. package/dist/ai/providers/builtin.d.ts +23 -0
  95. package/dist/ai/providers/builtin.js +148 -0
  96. package/dist/ai/providers/catalog-data.d.ts +5 -0
  97. package/dist/ai/providers/catalog-data.js +19 -0
  98. package/dist/ai/providers/catalog.d.ts +28 -0
  99. package/dist/ai/providers/catalog.js +166 -0
  100. package/dist/ai/providers/channels.d.ts +51 -0
  101. package/dist/ai/providers/channels.js +96 -0
  102. package/dist/ai/providers/enrich.d.ts +34 -0
  103. package/dist/ai/providers/enrich.js +86 -0
  104. package/dist/ai/providers/models-dev-cache.d.ts +53 -0
  105. package/dist/ai/providers/models-dev-cache.js +147 -0
  106. package/dist/ai/providers/models-dev.d.ts +99 -0
  107. package/dist/ai/providers/models-dev.js +315 -0
  108. package/dist/ai/providers/registry.d.ts +114 -0
  109. package/dist/ai/providers/registry.js +458 -0
  110. package/dist/ai/sse.d.ts +32 -0
  111. package/dist/ai/sse.js +140 -0
  112. package/dist/ai/thinking.d.ts +42 -0
  113. package/dist/ai/thinking.js +101 -0
  114. package/dist/ai/types.d.ts +448 -0
  115. package/dist/ai/types.js +19 -0
  116. package/dist/bundle/ama-sandbox.cjs +324 -0
  117. package/dist/bundle/ama.cjs +27837 -0
  118. package/dist/bundle.d.ts +9 -0
  119. package/dist/bundle.js +15 -0
  120. package/dist/cli/args.d.ts +85 -0
  121. package/dist/cli/args.js +408 -0
  122. package/dist/cli/bootstrap.d.ts +18 -0
  123. package/dist/cli/bootstrap.js +399 -0
  124. package/dist/cli/compose-providers.d.ts +37 -0
  125. package/dist/cli/compose-providers.js +107 -0
  126. package/dist/cli/compose-session.d.ts +72 -0
  127. package/dist/cli/compose-session.js +370 -0
  128. package/dist/cli/compose-store.d.ts +24 -0
  129. package/dist/cli/compose-store.js +125 -0
  130. package/dist/cli/compose.d.ts +108 -0
  131. package/dist/cli/compose.js +221 -0
  132. package/dist/cli/default-model.d.ts +16 -0
  133. package/dist/cli/default-model.js +24 -0
  134. package/dist/cli/deps.d.ts +207 -0
  135. package/dist/cli/deps.js +12 -0
  136. package/dist/cli/exit-codes.d.ts +29 -0
  137. package/dist/cli/exit-codes.js +40 -0
  138. package/dist/cli/main.d.ts +27 -0
  139. package/dist/cli/main.js +177 -0
  140. package/dist/cli/runtime.d.ts +90 -0
  141. package/dist/cli/runtime.js +8 -0
  142. package/dist/cli/startup-screen.d.ts +15 -0
  143. package/dist/cli/startup-screen.js +56 -0
  144. package/dist/cli/startup-steps.d.ts +35 -0
  145. package/dist/cli/startup-steps.js +202 -0
  146. package/dist/cli/subcommands/auth.d.ts +12 -0
  147. package/dist/cli/subcommands/auth.js +91 -0
  148. package/dist/cli/subcommands/config.d.ts +53 -0
  149. package/dist/cli/subcommands/config.js +277 -0
  150. package/dist/cli/subcommands/context.d.ts +21 -0
  151. package/dist/cli/subcommands/context.js +42 -0
  152. package/dist/cli/subcommands/doctor.d.ts +11 -0
  153. package/dist/cli/subcommands/doctor.js +246 -0
  154. package/dist/cli/subcommands/init.d.ts +6 -0
  155. package/dist/cli/subcommands/init.js +22 -0
  156. package/dist/cli/subcommands/model-meta.d.ts +16 -0
  157. package/dist/cli/subcommands/model-meta.js +54 -0
  158. package/dist/cli/subcommands/models-cache-probe.d.ts +34 -0
  159. package/dist/cli/subcommands/models-cache-probe.js +190 -0
  160. package/dist/cli/subcommands/models-discover.d.ts +46 -0
  161. package/dist/cli/subcommands/models-discover.js +232 -0
  162. package/dist/cli/subcommands/models.d.ts +40 -0
  163. package/dist/cli/subcommands/models.js +152 -0
  164. package/dist/cli/subcommands/providers-list.d.ts +8 -0
  165. package/dist/cli/subcommands/providers-list.js +97 -0
  166. package/dist/cli/subcommands/providers-plan.d.ts +79 -0
  167. package/dist/cli/subcommands/providers-plan.js +215 -0
  168. package/dist/cli/subcommands/providers.d.ts +27 -0
  169. package/dist/cli/subcommands/providers.js +433 -0
  170. package/dist/cli/subcommands/sessions.d.ts +11 -0
  171. package/dist/cli/subcommands/sessions.js +100 -0
  172. package/dist/codemode/capability.d.ts +37 -0
  173. package/dist/codemode/capability.js +55 -0
  174. package/dist/codemode/declarations.d.ts +44 -0
  175. package/dist/codemode/declarations.js +111 -0
  176. package/dist/codemode/host-side.d.ts +59 -0
  177. package/dist/codemode/host-side.js +253 -0
  178. package/dist/codemode/modes.d.ts +23 -0
  179. package/dist/codemode/modes.js +40 -0
  180. package/dist/codemode/protocol.d.ts +91 -0
  181. package/dist/codemode/protocol.js +114 -0
  182. package/dist/codemode/sandbox-entry.d.ts +25 -0
  183. package/dist/codemode/sandbox-entry.js +323 -0
  184. package/dist/codemode/store.d.ts +26 -0
  185. package/dist/codemode/store.js +53 -0
  186. package/dist/codemode/tool.d.ts +105 -0
  187. package/dist/codemode/tool.js +283 -0
  188. package/dist/compaction/branch-summary.d.ts +30 -0
  189. package/dist/compaction/branch-summary.js +79 -0
  190. package/dist/compaction/breaker.d.ts +32 -0
  191. package/dist/compaction/breaker.js +65 -0
  192. package/dist/compaction/cut-point.d.ts +23 -0
  193. package/dist/compaction/cut-point.js +70 -0
  194. package/dist/compaction/estimate.d.ts +30 -0
  195. package/dist/compaction/estimate.js +114 -0
  196. package/dist/compaction/prune-tier.d.ts +31 -0
  197. package/dist/compaction/prune-tier.js +83 -0
  198. package/dist/compaction/serialize.d.ts +20 -0
  199. package/dist/compaction/serialize.js +115 -0
  200. package/dist/compaction/summarize-tier.d.ts +111 -0
  201. package/dist/compaction/summarize-tier.js +305 -0
  202. package/dist/config/auth-file.d.ts +39 -0
  203. package/dist/config/auth-file.js +92 -0
  204. package/dist/config/context-files.d.ts +30 -0
  205. package/dist/config/context-files.js +77 -0
  206. package/dist/config/init.d.ts +30 -0
  207. package/dist/config/init.js +92 -0
  208. package/dist/config/json-schema.d.ts +14 -0
  209. package/dist/config/json-schema.js +182 -0
  210. package/dist/config/load.d.ts +45 -0
  211. package/dist/config/load.js +97 -0
  212. package/dist/config/merge.d.ts +73 -0
  213. package/dist/config/merge.js +248 -0
  214. package/dist/config/paths.d.ts +38 -0
  215. package/dist/config/paths.js +108 -0
  216. package/dist/config/profile.d.ts +27 -0
  217. package/dist/config/profile.js +65 -0
  218. package/dist/config/schema.d.ts +34 -0
  219. package/dist/config/schema.js +476 -0
  220. package/dist/config/trust.d.ts +42 -0
  221. package/dist/config/trust.js +122 -0
  222. package/dist/config/types.d.ts +190 -0
  223. package/dist/config/types.js +29 -0
  224. package/dist/config/write.d.ts +10 -0
  225. package/dist/config/write.js +25 -0
  226. package/dist/errors.d.ts +28 -0
  227. package/dist/errors.js +30 -0
  228. package/dist/hooks/config.d.ts +49 -0
  229. package/dist/hooks/config.js +102 -0
  230. package/dist/hooks/dispatcher.d.ts +41 -0
  231. package/dist/hooks/dispatcher.js +104 -0
  232. package/dist/hooks/matcher.d.ts +22 -0
  233. package/dist/hooks/matcher.js +108 -0
  234. package/dist/hooks/protocol.d.ts +33 -0
  235. package/dist/hooks/protocol.js +240 -0
  236. package/dist/hooks/runner.d.ts +30 -0
  237. package/dist/hooks/runner.js +143 -0
  238. package/dist/hooks/types.d.ts +147 -0
  239. package/dist/hooks/types.js +20 -0
  240. package/dist/host/api-impl.d.ts +75 -0
  241. package/dist/host/api-impl.js +173 -0
  242. package/dist/host/loader.d.ts +31 -0
  243. package/dist/host/loader.js +147 -0
  244. package/dist/host/types.d.ts +169 -0
  245. package/dist/host/types.js +14 -0
  246. package/dist/host.d.ts +5 -0
  247. package/dist/host.js +4 -0
  248. package/dist/index.d.ts +41 -0
  249. package/dist/index.js +28 -0
  250. package/dist/modes/commands-core.d.ts +44 -0
  251. package/dist/modes/commands-core.js +171 -0
  252. package/dist/modes/image-input.d.ts +27 -0
  253. package/dist/modes/image-input.js +78 -0
  254. package/dist/modes/interactive/approval-dialog.d.ts +48 -0
  255. package/dist/modes/interactive/approval-dialog.js +202 -0
  256. package/dist/modes/interactive/commands.d.ts +41 -0
  257. package/dist/modes/interactive/commands.js +201 -0
  258. package/dist/modes/interactive/completion.d.ts +53 -0
  259. package/dist/modes/interactive/completion.js +157 -0
  260. package/dist/modes/interactive/interactive-mode.d.ts +46 -0
  261. package/dist/modes/interactive/interactive-mode.js +491 -0
  262. package/dist/modes/interactive/key-dispatch.d.ts +29 -0
  263. package/dist/modes/interactive/key-dispatch.js +107 -0
  264. package/dist/modes/interactive/line/line-editor.d.ts +50 -0
  265. package/dist/modes/interactive/line/line-editor.js +232 -0
  266. package/dist/modes/interactive/line/line-mode.d.ts +20 -0
  267. package/dist/modes/interactive/line/line-mode.js +218 -0
  268. package/dist/modes/interactive/line/line-render.d.ts +35 -0
  269. package/dist/modes/interactive/line/line-render.js +171 -0
  270. package/dist/modes/interactive/line/paste-state.d.ts +26 -0
  271. package/dist/modes/interactive/line/paste-state.js +72 -0
  272. package/dist/modes/interactive/message-view.d.ts +77 -0
  273. package/dist/modes/interactive/message-view.js +269 -0
  274. package/dist/modes/interactive/pickers.d.ts +42 -0
  275. package/dist/modes/interactive/pickers.js +115 -0
  276. package/dist/modes/interactive/startup-ui.d.ts +33 -0
  277. package/dist/modes/interactive/startup-ui.js +268 -0
  278. package/dist/modes/interactive/status-bar.d.ts +48 -0
  279. package/dist/modes/interactive/status-bar.js +139 -0
  280. package/dist/modes/interactive/tool-view.d.ts +95 -0
  281. package/dist/modes/interactive/tool-view.js +344 -0
  282. package/dist/modes/print/json-event.d.ts +15 -0
  283. package/dist/modes/print/json-event.js +39 -0
  284. package/dist/modes/print/print-mode.d.ts +17 -0
  285. package/dist/modes/print/print-mode.js +107 -0
  286. package/dist/modes/rpc/commands.d.ts +40 -0
  287. package/dist/modes/rpc/commands.js +263 -0
  288. package/dist/modes/rpc/jsonl.d.ts +16 -0
  289. package/dist/modes/rpc/jsonl.js +55 -0
  290. package/dist/modes/rpc/rpc-mode.d.ts +22 -0
  291. package/dist/modes/rpc/rpc-mode.js +153 -0
  292. package/dist/modes/session-report.d.ts +55 -0
  293. package/dist/modes/session-report.js +278 -0
  294. package/dist/modes/shared.d.ts +13 -0
  295. package/dist/modes/shared.js +52 -0
  296. package/dist/modes/startup-ui-text.d.ts +16 -0
  297. package/dist/modes/startup-ui-text.js +103 -0
  298. package/dist/permissions/broker.d.ts +47 -0
  299. package/dist/permissions/broker.js +107 -0
  300. package/dist/permissions/dangerous.d.ts +47 -0
  301. package/dist/permissions/dangerous.js +370 -0
  302. package/dist/permissions/pipeline.d.ts +51 -0
  303. package/dist/permissions/pipeline.js +181 -0
  304. package/dist/permissions/preview.d.ts +43 -0
  305. package/dist/permissions/preview.js +463 -0
  306. package/dist/permissions/rules.d.ts +58 -0
  307. package/dist/permissions/rules.js +249 -0
  308. package/dist/permissions/types.d.ts +100 -0
  309. package/dist/permissions/types.js +18 -0
  310. package/dist/rpc.d.ts +173 -0
  311. package/dist/rpc.js +13 -0
  312. package/dist/sdk.d.ts +98 -0
  313. package/dist/sdk.js +195 -0
  314. package/dist/session/manager.d.ts +70 -0
  315. package/dist/session/manager.js +278 -0
  316. package/dist/session/migrate.d.ts +19 -0
  317. package/dist/session/migrate.js +53 -0
  318. package/dist/session/projection.d.ts +50 -0
  319. package/dist/session/projection.js +184 -0
  320. package/dist/session/store.d.ts +47 -0
  321. package/dist/session/store.js +210 -0
  322. package/dist/session/tree.d.ts +21 -0
  323. package/dist/session/tree.js +88 -0
  324. package/dist/session/types.d.ts +201 -0
  325. package/dist/session/types.js +18 -0
  326. package/dist/skills/discover.d.ts +60 -0
  327. package/dist/skills/discover.js +160 -0
  328. package/dist/skills/expand.d.ts +35 -0
  329. package/dist/skills/expand.js +46 -0
  330. package/dist/skills/frontmatter.d.ts +27 -0
  331. package/dist/skills/frontmatter.js +157 -0
  332. package/dist/skills/index-prompt.d.ts +13 -0
  333. package/dist/skills/index-prompt.js +42 -0
  334. package/dist/skills/templates.d.ts +52 -0
  335. package/dist/skills/templates.js +177 -0
  336. package/dist/tools/bash.d.ts +49 -0
  337. package/dist/tools/bash.js +222 -0
  338. package/dist/tools/edit-fuzzy.d.ts +36 -0
  339. package/dist/tools/edit-fuzzy.js +129 -0
  340. package/dist/tools/edit.d.ts +38 -0
  341. package/dist/tools/edit.js +241 -0
  342. package/dist/tools/file-mutex.d.ts +8 -0
  343. package/dist/tools/file-mutex.js +29 -0
  344. package/dist/tools/glob.d.ts +44 -0
  345. package/dist/tools/glob.js +217 -0
  346. package/dist/tools/grep.d.ts +39 -0
  347. package/dist/tools/grep.js +179 -0
  348. package/dist/tools/ignore.d.ts +40 -0
  349. package/dist/tools/ignore.js +174 -0
  350. package/dist/tools/image-file.d.ts +31 -0
  351. package/dist/tools/image-file.js +114 -0
  352. package/dist/tools/ls.d.ts +21 -0
  353. package/dist/tools/ls.js +96 -0
  354. package/dist/tools/output-accumulator.d.ts +48 -0
  355. package/dist/tools/output-accumulator.js +106 -0
  356. package/dist/tools/paths.d.ts +20 -0
  357. package/dist/tools/paths.js +58 -0
  358. package/dist/tools/presets.d.ts +71 -0
  359. package/dist/tools/presets.js +128 -0
  360. package/dist/tools/process-tree.d.ts +35 -0
  361. package/dist/tools/process-tree.js +125 -0
  362. package/dist/tools/read.d.ts +24 -0
  363. package/dist/tools/read.js +132 -0
  364. package/dist/tools/registry.d.ts +43 -0
  365. package/dist/tools/registry.js +106 -0
  366. package/dist/tools/shell.d.ts +38 -0
  367. package/dist/tools/shell.js +91 -0
  368. package/dist/tools/task.d.ts +29 -0
  369. package/dist/tools/task.js +107 -0
  370. package/dist/tools/todo.d.ts +28 -0
  371. package/dist/tools/todo.js +107 -0
  372. package/dist/tools/truncate.d.ts +44 -0
  373. package/dist/tools/truncate.js +175 -0
  374. package/dist/tools/types.d.ts +135 -0
  375. package/dist/tools/types.js +19 -0
  376. package/dist/tools/write.d.ts +20 -0
  377. package/dist/tools/write.js +84 -0
  378. package/dist/tui/ansi.d.ts +29 -0
  379. package/dist/tui/ansi.js +420 -0
  380. package/dist/tui/component.d.ts +38 -0
  381. package/dist/tui/component.js +13 -0
  382. package/dist/tui/components/box.d.ts +26 -0
  383. package/dist/tui/components/box.js +65 -0
  384. package/dist/tui/components/container.d.ts +14 -0
  385. package/dist/tui/components/container.js +39 -0
  386. package/dist/tui/components/editor-buffer.d.ts +67 -0
  387. package/dist/tui/components/editor-buffer.js +300 -0
  388. package/dist/tui/components/editor-paste.d.ts +29 -0
  389. package/dist/tui/components/editor-paste.js +64 -0
  390. package/dist/tui/components/editor.d.ts +114 -0
  391. package/dist/tui/components/editor.js +530 -0
  392. package/dist/tui/components/key-value.d.ts +29 -0
  393. package/dist/tui/components/key-value.js +48 -0
  394. package/dist/tui/components/loader.d.ts +35 -0
  395. package/dist/tui/components/loader.js +71 -0
  396. package/dist/tui/components/markdown.d.ts +90 -0
  397. package/dist/tui/components/markdown.js +462 -0
  398. package/dist/tui/components/meter.d.ts +29 -0
  399. package/dist/tui/components/meter.js +47 -0
  400. package/dist/tui/components/overlay.d.ts +24 -0
  401. package/dist/tui/components/overlay.js +52 -0
  402. package/dist/tui/components/select-list.d.ts +57 -0
  403. package/dist/tui/components/select-list.js +179 -0
  404. package/dist/tui/components/spacer.d.ts +11 -0
  405. package/dist/tui/components/spacer.js +16 -0
  406. package/dist/tui/components/text.d.ts +30 -0
  407. package/dist/tui/components/text.js +65 -0
  408. package/dist/tui/keybindings.d.ts +66 -0
  409. package/dist/tui/keybindings.js +120 -0
  410. package/dist/tui/keys.d.ts +38 -0
  411. package/dist/tui/keys.js +275 -0
  412. package/dist/tui/stdin-buffer.d.ts +41 -0
  413. package/dist/tui/stdin-buffer.js +206 -0
  414. package/dist/tui/terminal.d.ts +79 -0
  415. package/dist/tui/terminal.js +153 -0
  416. package/dist/tui/theme.d.ts +31 -0
  417. package/dist/tui/theme.js +199 -0
  418. package/dist/tui/tui.d.ts +103 -0
  419. package/dist/tui/tui.js +363 -0
  420. package/dist/tui/vt-screen.d.ts +46 -0
  421. package/dist/tui/vt-screen.js +251 -0
  422. package/dist/tui.d.ts +30 -0
  423. package/dist/tui.js +31 -0
  424. package/dist/version.d.ts +8 -0
  425. package/dist/version.js +24 -0
  426. package/docs/codemode.md +102 -0
  427. package/docs/hooks.md +196 -0
  428. package/docs/host-api.md +161 -0
  429. package/docs/providers.md +531 -0
  430. package/docs/rpc.md +253 -0
  431. package/docs/session-format.md +119 -0
  432. package/docs/tui.md +170 -0
  433. package/package.json +101 -4
@@ -0,0 +1,174 @@
1
+ /**
2
+ * `.gitignore` / `.ignore` 解析与匹配,以及尊重它们的目录遍历(设计 §5.2 grep / glob)。[B3]
3
+ *
4
+ * 规则语义(gitignore 子集):空行与 `#` 注释;`\#`、`\!`、`\ ` 转义;`!` 取反;末尾 `/` 只匹配目录;
5
+ * 中间或开头含 `/` 的模式锚定在 ignore 文件所在目录,否则匹配任意深度的名字;`**` 的三种位置;
6
+ * 同一路径**最后**命中的规则生效;被忽略的目录不再下钻(其下的取反规则无效,与 git 一致)。
7
+ * 嵌套:每个目录的 `.gitignore` 先、`.ignore` 后(后者优先);从搜索根向上到仓库根(含 `.git` 的
8
+ * 目录)的祖先 ignore 文件也生效。`.git` 目录永远跳过;目录符号链接不跟随。
9
+ */
10
+ import { readdir, readFile, stat } from "node:fs/promises";
11
+ import { dirname, join } from "node:path";
12
+ import { toPosix } from "./paths.js";
13
+ import { globBody } from "./glob.js";
14
+ export const IGNORE_FILES = [".gitignore", ".ignore"];
15
+ function trimTrailingSpaces(line) {
16
+ let end = line.length;
17
+ while (end > 0 && line[end - 1] === " " && line[end - 2] !== "\\")
18
+ end--;
19
+ return line.slice(0, end);
20
+ }
21
+ /** 解析一行;空 / 注释返回 undefined。 */
22
+ export function parseIgnoreLine(rawLine, base, nocase = false) {
23
+ let line = trimTrailingSpaces(rawLine.replace(/\r$/, ""));
24
+ if (line === "" || line.startsWith("#"))
25
+ return undefined;
26
+ let negate = false;
27
+ if (line.startsWith("!")) {
28
+ negate = true;
29
+ line = line.slice(1);
30
+ }
31
+ else if (line.startsWith("\\!") || line.startsWith("\\#")) {
32
+ line = line.slice(1);
33
+ }
34
+ let dirOnly = false;
35
+ if (line.endsWith("/") && !line.endsWith("\\/")) {
36
+ dirOnly = true;
37
+ line = line.replace(/\/+$/, "");
38
+ }
39
+ if (line === "")
40
+ return undefined;
41
+ const anchored = line.includes("/");
42
+ if (line.startsWith("/"))
43
+ line = line.slice(1);
44
+ const body = globBody(line, { braces: false });
45
+ const prefix = anchored || line.startsWith("**/") ? "" : "(?:[^/]*/)*";
46
+ return {
47
+ negate,
48
+ dirOnly,
49
+ regex: new RegExp(`^${prefix}${body}$`, nocase ? "i" : ""),
50
+ base: base.replace(/\/+$/, ""),
51
+ source: rawLine,
52
+ };
53
+ }
54
+ export function parseIgnoreFile(content, base, nocase = false) {
55
+ const rules = [];
56
+ for (const line of content.split("\n")) {
57
+ const rule = parseIgnoreLine(line, base, nocase);
58
+ if (rule)
59
+ rules.push(rule);
60
+ }
61
+ return rules;
62
+ }
63
+ /** `absPosix` 相对 `base` 的路径;不在其下返回 undefined。 */
64
+ function relativeTo(base, absPosix) {
65
+ if (base === "")
66
+ return absPosix.replace(/^\/+/, "");
67
+ if (absPosix === base)
68
+ return "";
69
+ const prefix = base.endsWith("/") ? base : `${base}/`;
70
+ return absPosix.startsWith(prefix) ? absPosix.slice(prefix.length) : undefined;
71
+ }
72
+ /** 按规则顺序求值,最后命中者生效。 */
73
+ export function isIgnoredBy(rules, absPath, isDir) {
74
+ const absPosix = toPosix(absPath);
75
+ let ignored = false;
76
+ for (const rule of rules) {
77
+ if (rule.dirOnly && !isDir)
78
+ continue;
79
+ const rel = relativeTo(rule.base, absPosix);
80
+ if (rel === undefined || rel === "")
81
+ continue;
82
+ if (rule.regex.test(rel))
83
+ ignored = !rule.negate;
84
+ }
85
+ return ignored;
86
+ }
87
+ async function readRules(dir, nocase) {
88
+ const rules = [];
89
+ for (const name of IGNORE_FILES) {
90
+ try {
91
+ const text = await readFile(join(dir, name), "utf8");
92
+ rules.push(...parseIgnoreFile(text, toPosix(dir), nocase));
93
+ }
94
+ catch {
95
+ // 不存在
96
+ }
97
+ }
98
+ return rules;
99
+ }
100
+ /** 搜索根之上、直到仓库根(含 `.git` 的目录)的祖先 ignore 规则,外层在前。 */
101
+ export async function ancestorRules(root, nocase = false) {
102
+ const chain = [];
103
+ let dir = root;
104
+ let foundRepo = await exists(join(root, ".git"));
105
+ while (!foundRepo) {
106
+ const parent = dirname(dir);
107
+ if (parent === dir)
108
+ return [];
109
+ dir = parent;
110
+ chain.unshift(dir);
111
+ foundRepo = await exists(join(dir, ".git"));
112
+ }
113
+ const rules = [];
114
+ for (const d of chain)
115
+ rules.push(...(await readRules(d, nocase)));
116
+ return rules;
117
+ }
118
+ async function exists(path) {
119
+ try {
120
+ await stat(path);
121
+ return true;
122
+ }
123
+ catch {
124
+ return false;
125
+ }
126
+ }
127
+ /** 深度优先、目录内按名字排序的遍历;产出文件与目录(目录先于其内容)。 */
128
+ export async function* walk(root, options = {}) {
129
+ const respect = options.respectIgnore ?? true;
130
+ const nocase = options.nocase ?? process.platform === "win32";
131
+ const base = respect ? await ancestorRules(root, nocase) : [];
132
+ yield* walkDir(root, "", base, respect, nocase, options.signal);
133
+ }
134
+ async function* walkDir(dir, rel, inherited, respect, nocase, signal) {
135
+ if (signal?.aborted)
136
+ return;
137
+ const rules = respect ? [...inherited, ...(await readRules(dir, nocase))] : inherited;
138
+ let entries;
139
+ try {
140
+ entries = await readdir(dir, { withFileTypes: true });
141
+ }
142
+ catch {
143
+ return;
144
+ }
145
+ entries.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
146
+ for (const entry of entries) {
147
+ if (signal?.aborted)
148
+ return;
149
+ if (entry.name === ".git")
150
+ continue;
151
+ const abs = join(dir, entry.name);
152
+ const childRel = rel === "" ? entry.name : `${rel}/${entry.name}`;
153
+ let isDir = entry.isDirectory();
154
+ if (entry.isSymbolicLink()) {
155
+ // 链接到文件的照常产出;链接到目录的不跟随(避免环),作为普通条目跳过。
156
+ try {
157
+ if ((await stat(abs)).isDirectory())
158
+ continue;
159
+ }
160
+ catch {
161
+ continue;
162
+ }
163
+ isDir = false;
164
+ }
165
+ else if (!isDir && !entry.isFile()) {
166
+ continue;
167
+ }
168
+ if (respect && isIgnoredBy(rules, abs, isDir))
169
+ continue;
170
+ yield { abs, rel: childRel, isDir };
171
+ if (isDir)
172
+ yield* walkDir(abs, childRel, rules, respect, nocase, signal);
173
+ }
174
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * 图片文件(`read` 工具、`ama -p --image`、界面里的 `@图片路径` 共用):MIME 检测、尺寸、大小上限。
3
+ *
4
+ * - MIME 以文件头为准(PNG / JPEG / GIF / WebP),文件头认不出时退回扩展名;
5
+ * - 单张上限 5 MB(各家图片上限里最小的 Anthropic);
6
+ * - 不解码像素,只读文件头取尺寸。
7
+ */
8
+ import type { ImageBlock } from "../ai/types.js";
9
+ export declare const MAX_IMAGE_BYTES: number;
10
+ export declare const IMAGE_EXTENSIONS: Readonly<Record<string, string>>;
11
+ /** 按扩展名(不分大小写);不是图片返回 undefined。 */
12
+ export declare function imageMimeFromPath(path: string): string | undefined;
13
+ /** 按文件头识别;认不出返回 undefined。 */
14
+ export declare function sniffImageMime(buf: Buffer): string | undefined;
15
+ export interface ImageSize {
16
+ width: number;
17
+ height: number;
18
+ }
19
+ /** 只读文件头取尺寸;不认识返回 undefined。 */
20
+ export declare function imageSize(buf: Buffer, mimeType: string): ImageSize | undefined;
21
+ export interface LoadedImage {
22
+ block: ImageBlock;
23
+ path: string;
24
+ mimeType: string;
25
+ bytes: number;
26
+ size?: ImageSize;
27
+ }
28
+ /**
29
+ * 读一张图片作为附件。文件不存在、不是图片、超过上限 → AmaError(`invalid_arguments`),文案给用户看。
30
+ */
31
+ export declare function loadImageFile(path: string): Promise<LoadedImage>;
@@ -0,0 +1,114 @@
1
+ /**
2
+ * 图片文件(`read` 工具、`ama -p --image`、界面里的 `@图片路径` 共用):MIME 检测、尺寸、大小上限。
3
+ *
4
+ * - MIME 以文件头为准(PNG / JPEG / GIF / WebP),文件头认不出时退回扩展名;
5
+ * - 单张上限 5 MB(各家图片上限里最小的 Anthropic);
6
+ * - 不解码像素,只读文件头取尺寸。
7
+ */
8
+ import { readFile, stat } from "node:fs/promises";
9
+ import { extname } from "node:path";
10
+ import { AmaError } from "../errors.js";
11
+ export const MAX_IMAGE_BYTES = 5 * 1024 * 1024;
12
+ export const IMAGE_EXTENSIONS = {
13
+ ".png": "image/png",
14
+ ".jpg": "image/jpeg",
15
+ ".jpeg": "image/jpeg",
16
+ ".gif": "image/gif",
17
+ ".webp": "image/webp",
18
+ };
19
+ /** 按扩展名(不分大小写);不是图片返回 undefined。 */
20
+ export function imageMimeFromPath(path) {
21
+ return IMAGE_EXTENSIONS[extname(path).toLowerCase()];
22
+ }
23
+ /** 按文件头识别;认不出返回 undefined。 */
24
+ export function sniffImageMime(buf) {
25
+ if (buf.length >= 8 && buf.readUInt32BE(0) === 0x89504e47 && buf.readUInt32BE(4) === 0x0d0a1a0a)
26
+ return "image/png";
27
+ if (buf.length >= 3 && buf[0] === 0xff && buf[1] === 0xd8 && buf[2] === 0xff)
28
+ return "image/jpeg";
29
+ if (buf.length >= 6 && /^GIF8[79]a$/.test(buf.toString("ascii", 0, 6)))
30
+ return "image/gif";
31
+ if (buf.length >= 12 &&
32
+ buf.toString("ascii", 0, 4) === "RIFF" &&
33
+ buf.toString("ascii", 8, 12) === "WEBP")
34
+ return "image/webp";
35
+ return undefined;
36
+ }
37
+ /** 只读文件头取尺寸;不认识返回 undefined。 */
38
+ export function imageSize(buf, mimeType) {
39
+ try {
40
+ if (mimeType === "image/png" && buf.length >= 24) {
41
+ return { width: buf.readUInt32BE(16), height: buf.readUInt32BE(20) };
42
+ }
43
+ if (mimeType === "image/gif" && buf.length >= 10) {
44
+ return { width: buf.readUInt16LE(6), height: buf.readUInt16LE(8) };
45
+ }
46
+ if (mimeType === "image/jpeg")
47
+ return jpegSize(buf);
48
+ if (mimeType === "image/webp")
49
+ return webpSize(buf);
50
+ }
51
+ catch {
52
+ return undefined;
53
+ }
54
+ return undefined;
55
+ }
56
+ function jpegSize(buf) {
57
+ let i = 2;
58
+ while (i + 9 < buf.length) {
59
+ if (buf[i] !== 0xff) {
60
+ i++;
61
+ continue;
62
+ }
63
+ const marker = buf[i + 1] ?? 0;
64
+ const isSof = marker >= 0xc0 && marker <= 0xcf && ![0xc4, 0xc8, 0xcc].includes(marker);
65
+ if (isSof)
66
+ return { height: buf.readUInt16BE(i + 5), width: buf.readUInt16BE(i + 7) };
67
+ i += 2 + buf.readUInt16BE(i + 2);
68
+ }
69
+ return undefined;
70
+ }
71
+ function webpSize(buf) {
72
+ if (buf.length < 30 || buf.toString("ascii", 0, 4) !== "RIFF")
73
+ return undefined;
74
+ const chunk = buf.toString("ascii", 12, 16);
75
+ if (chunk === "VP8 ") {
76
+ return { width: buf.readUInt16LE(26) & 0x3fff, height: buf.readUInt16LE(28) & 0x3fff };
77
+ }
78
+ if (chunk === "VP8L") {
79
+ const b = buf.readUInt32LE(21);
80
+ return { width: (b & 0x3fff) + 1, height: ((b >> 14) & 0x3fff) + 1 };
81
+ }
82
+ if (chunk === "VP8X") {
83
+ return { width: buf.readUIntLE(24, 3) + 1, height: buf.readUIntLE(27, 3) + 1 };
84
+ }
85
+ return undefined;
86
+ }
87
+ /**
88
+ * 读一张图片作为附件。文件不存在、不是图片、超过上限 → AmaError(`invalid_arguments`),文案给用户看。
89
+ */
90
+ export async function loadImageFile(path) {
91
+ let info;
92
+ try {
93
+ info = await stat(path);
94
+ }
95
+ catch {
96
+ throw new AmaError("invalid_arguments", `图片不存在:${path}`);
97
+ }
98
+ if (!info.isFile())
99
+ throw new AmaError("invalid_arguments", `不是文件:${path}`);
100
+ if (info.size > MAX_IMAGE_BYTES)
101
+ throw new AmaError("invalid_arguments", `图片超过 ${MAX_IMAGE_BYTES / 1024 / 1024} MB 上限:${path}(${(info.size / 1024 / 1024).toFixed(1)} MB)`);
102
+ const buf = await readFile(path);
103
+ const mimeType = sniffImageMime(buf) ?? imageMimeFromPath(path);
104
+ if (mimeType === undefined)
105
+ throw new AmaError("invalid_arguments", `不是支持的图片(PNG / JPEG / GIF / WebP):${path}`);
106
+ const size = imageSize(buf, mimeType);
107
+ return {
108
+ block: { type: "image", data: buf.toString("base64"), mimeType },
109
+ path,
110
+ mimeType,
111
+ bytes: buf.length,
112
+ ...(size ? { size } : {}),
113
+ };
114
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * `ls` 工具(设计 §5.2)。[B3]
3
+ *
4
+ * 目录项按名字排序(目录与文件混排,与 `ls` 一致);目录写 `name/`,文件附大小,符号链接写
5
+ * `name -> target`;含点文件,不过滤 ignore;缺省上限 500。
6
+ */
7
+ import type { ToolContext, ToolDefinition, ToolResult } from "./types.js";
8
+ export interface LsInput {
9
+ path?: string;
10
+ limit?: number;
11
+ }
12
+ export declare const DEFAULT_LS_LIMIT = 500;
13
+ export interface LsEntry {
14
+ name: string;
15
+ type: "file" | "dir" | "symlink" | "other";
16
+ size?: number;
17
+ target?: string;
18
+ }
19
+ export declare function formatEntry(entry: LsEntry): string;
20
+ export declare function executeLs(input: LsInput, ctx: ToolContext): Promise<ToolResult>;
21
+ export declare function createLsTool(): ToolDefinition<LsInput>;
@@ -0,0 +1,96 @@
1
+ /**
2
+ * `ls` 工具(设计 §5.2)。[B3]
3
+ *
4
+ * 目录项按名字排序(目录与文件混排,与 `ls` 一致);目录写 `name/`,文件附大小,符号链接写
5
+ * `name -> target`;含点文件,不过滤 ignore;缺省上限 500。
6
+ */
7
+ import { lstat, readdir, readlink, stat } from "node:fs/promises";
8
+ import { join } from "node:path";
9
+ import { displayPath, resolvePath } from "./paths.js";
10
+ import { formatSize } from "./truncate.js";
11
+ export const DEFAULT_LS_LIMIT = 500;
12
+ async function describe(dir, name) {
13
+ const abs = join(dir, name);
14
+ try {
15
+ const info = await lstat(abs);
16
+ if (info.isSymbolicLink()) {
17
+ let target = "?";
18
+ try {
19
+ target = await readlink(abs);
20
+ }
21
+ catch {
22
+ // 保留 ?
23
+ }
24
+ let pointsToDir = false;
25
+ try {
26
+ pointsToDir = (await stat(abs)).isDirectory();
27
+ }
28
+ catch {
29
+ // 悬空链接
30
+ }
31
+ return { name: pointsToDir ? `${name}/` : name, type: "symlink", target };
32
+ }
33
+ if (info.isDirectory())
34
+ return { name: `${name}/`, type: "dir" };
35
+ if (info.isFile())
36
+ return { name, type: "file", size: info.size };
37
+ return { name, type: "other" };
38
+ }
39
+ catch {
40
+ return { name, type: "other" };
41
+ }
42
+ }
43
+ export function formatEntry(entry) {
44
+ if (entry.type === "symlink")
45
+ return `${entry.name} -> ${entry.target ?? "?"}`;
46
+ if (entry.type === "file")
47
+ return `${entry.name} (${formatSize(entry.size ?? 0)})`;
48
+ return entry.name;
49
+ }
50
+ export async function executeLs(input, ctx) {
51
+ const limit = input.limit ?? DEFAULT_LS_LIMIT;
52
+ if (!Number.isInteger(limit) || limit < 1) {
53
+ return { content: "limit must be an integer ≥ 1", isError: true };
54
+ }
55
+ const dir = resolvePath(input.path ?? ".", ctx.cwd);
56
+ const shown = displayPath(dir, ctx.cwd);
57
+ let names;
58
+ try {
59
+ if (!(await stat(dir)).isDirectory())
60
+ return { content: `${shown} is not a directory`, isError: true };
61
+ names = await readdir(dir);
62
+ }
63
+ catch {
64
+ return { content: `Directory not found: ${shown}`, isError: true };
65
+ }
66
+ names.sort((a, b) => a.localeCompare(b, "en"));
67
+ if (names.length === 0)
68
+ return { content: `(${shown} is empty)`, details: { count: 0 } };
69
+ const entries = await Promise.all(names.slice(0, limit).map((n) => describe(dir, n)));
70
+ let content = entries.map(formatEntry).join("\n");
71
+ if (names.length > limit) {
72
+ content += `\n\n[${names.length} entries; showing the first ${limit}. Raise limit to see more.]`;
73
+ }
74
+ return { content, details: { count: names.length, entries } };
75
+ }
76
+ export function createLsTool() {
77
+ return {
78
+ name: "ls",
79
+ label: "List",
80
+ description: "List a directory: subdirectories end with `/`, files show their size, symlinks show their " +
81
+ `target. Includes dotfiles. Default limit ${DEFAULT_LS_LIMIT}.`,
82
+ parameters: {
83
+ type: "object",
84
+ properties: {
85
+ path: { type: "string", description: "Directory (default: cwd)" },
86
+ limit: { type: "integer", description: `Maximum entries (default ${DEFAULT_LS_LIMIT})` },
87
+ },
88
+ additionalProperties: false,
89
+ },
90
+ permission: "read",
91
+ executionMode: "parallel",
92
+ annotations: { readOnly: true },
93
+ promptSnippet: "ls: list directory contents",
94
+ execute: executeLs,
95
+ };
96
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * 进程输出累积(设计 §5.2 bash)。[B3]
3
+ *
4
+ * - 保留滚动尾部(约 2 × maxBytes)用于流式 `onUpdate` 与最终尾截断;
5
+ * - 内存里最多攒 `memoryLimitBytes`,超出即把已有内容落文件,之后增量追加,内存只留尾部;
6
+ * - `finish()` 做尾截断(行数 + 字节);发生截断而尚未落盘时补写全文,给出 `fullOutputPath`;
7
+ * - 输出清洗:去 ANSI 转义与除 `\t\n` 外的控制字符,`\r\n` → `\n`。
8
+ */
9
+ export interface OutputAccumulatorOptions {
10
+ maxLines?: number;
11
+ maxBytes?: number;
12
+ /** 内存上限,缺省 1 MB。 */
13
+ memoryLimitBytes?: number;
14
+ /** 全文落盘路径(懒求值,只在需要时调用一次)。 */
15
+ spillPath(): string;
16
+ }
17
+ export interface AccumulatedOutput {
18
+ /** 尾截断后的输出。 */
19
+ output: string;
20
+ truncated: boolean;
21
+ fullOutputPath?: string;
22
+ totalBytes: number;
23
+ totalLines: number;
24
+ outputLines: number;
25
+ }
26
+ export declare function sanitizeOutput(text: string): string;
27
+ export declare class OutputAccumulator {
28
+ private readonly options;
29
+ private readonly maxLines;
30
+ private readonly maxBytes;
31
+ private readonly memoryLimit;
32
+ private readonly decoder;
33
+ private chunks;
34
+ private memoryBytes;
35
+ private tailText;
36
+ private spilledTo;
37
+ private bytes;
38
+ private newlines;
39
+ private lastChar;
40
+ constructor(options: OutputAccumulatorOptions);
41
+ get totalBytes(): number;
42
+ append(chunk: Buffer | string): void;
43
+ private pushText;
44
+ private spill;
45
+ /** 当前滚动尾部(已清洗、尾截断),给流式 onUpdate。 */
46
+ tail(maxLines?: number): string;
47
+ finish(): AccumulatedOutput;
48
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * 进程输出累积(设计 §5.2 bash)。[B3]
3
+ *
4
+ * - 保留滚动尾部(约 2 × maxBytes)用于流式 `onUpdate` 与最终尾截断;
5
+ * - 内存里最多攒 `memoryLimitBytes`,超出即把已有内容落文件,之后增量追加,内存只留尾部;
6
+ * - `finish()` 做尾截断(行数 + 字节);发生截断而尚未落盘时补写全文,给出 `fullOutputPath`;
7
+ * - 输出清洗:去 ANSI 转义与除 `\t\n` 外的控制字符,`\r\n` → `\n`。
8
+ */
9
+ import { appendFileSync, mkdirSync, writeFileSync } from "node:fs";
10
+ import { dirname } from "node:path";
11
+ import { StringDecoder } from "node:string_decoder";
12
+ import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, sliceTailBytes, truncateTail } from "./truncate.js";
13
+ const ANSI = /\x1b\[[0-?]*[ -/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-Z\\-_]/g;
14
+ const CONTROL = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
15
+ export function sanitizeOutput(text) {
16
+ return text.replace(/\r\n/g, "\n").replace(ANSI, "").replace(/\r/g, "\n").replace(CONTROL, "");
17
+ }
18
+ export class OutputAccumulator {
19
+ options;
20
+ maxLines;
21
+ maxBytes;
22
+ memoryLimit;
23
+ decoder = new StringDecoder("utf8");
24
+ chunks = [];
25
+ memoryBytes = 0;
26
+ tailText = "";
27
+ spilledTo;
28
+ bytes = 0;
29
+ newlines = 0;
30
+ lastChar = "";
31
+ constructor(options) {
32
+ this.options = options;
33
+ this.maxLines = options.maxLines ?? DEFAULT_MAX_LINES;
34
+ this.maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
35
+ this.memoryLimit = options.memoryLimitBytes ?? 1024 * 1024;
36
+ }
37
+ get totalBytes() {
38
+ return this.bytes;
39
+ }
40
+ append(chunk) {
41
+ const raw = typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
42
+ this.bytes += raw.length;
43
+ this.pushText(this.decoder.write(raw));
44
+ }
45
+ pushText(text) {
46
+ if (text === "")
47
+ return;
48
+ for (let i = 0; i < text.length; i++)
49
+ if (text.charCodeAt(i) === 10)
50
+ this.newlines++;
51
+ this.lastChar = text[text.length - 1] ?? this.lastChar;
52
+ this.tailText += text;
53
+ if (this.tailText.length > this.maxBytes * 4) {
54
+ this.tailText = sliceTailBytes(this.tailText, this.maxBytes * 2);
55
+ }
56
+ if (this.spilledTo !== undefined) {
57
+ appendFileSync(this.spilledTo, text);
58
+ return;
59
+ }
60
+ this.chunks.push(text);
61
+ this.memoryBytes += Buffer.byteLength(text, "utf8");
62
+ if (this.memoryBytes > this.memoryLimit)
63
+ this.spill();
64
+ }
65
+ spill() {
66
+ const target = this.options.spillPath();
67
+ mkdirSync(dirname(target), { recursive: true });
68
+ writeFileSync(target, this.chunks.join(""));
69
+ this.spilledTo = target;
70
+ this.chunks = [];
71
+ this.memoryBytes = 0;
72
+ }
73
+ /** 当前滚动尾部(已清洗、尾截断),给流式 onUpdate。 */
74
+ tail(maxLines = 50) {
75
+ return truncateTail(sanitizeOutput(this.tailText), { maxLines, maxBytes: this.maxBytes })
76
+ .content;
77
+ }
78
+ finish() {
79
+ this.pushText(this.decoder.end());
80
+ const totalLines = this.newlines + (this.bytes > 0 && this.lastChar !== "\n" ? 1 : 0);
81
+ let source;
82
+ if (this.spilledTo !== undefined) {
83
+ // 尾部可能从半行开始:去掉第一行残片。
84
+ const cut = this.tailText.indexOf("\n");
85
+ source = cut >= 0 ? this.tailText.slice(cut + 1) : this.tailText;
86
+ }
87
+ else {
88
+ source = this.chunks.join("");
89
+ }
90
+ const result = truncateTail(sanitizeOutput(source), {
91
+ maxLines: this.maxLines,
92
+ maxBytes: this.maxBytes,
93
+ });
94
+ const truncated = result.truncated || this.spilledTo !== undefined;
95
+ if (truncated && this.spilledTo === undefined)
96
+ this.spill();
97
+ return {
98
+ output: result.content,
99
+ truncated,
100
+ ...(truncated && this.spilledTo !== undefined ? { fullOutputPath: this.spilledTo } : {}),
101
+ totalBytes: this.bytes,
102
+ totalLines,
103
+ outputLines: result.outputLines,
104
+ };
105
+ }
106
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * 路径解析(设计 §5.2「通用安全」)。[B3]
3
+ *
4
+ * - 展开 `~` / `~/…`(`~user` 不展开);相对路径按 cwd 解析;结果是规范化绝对路径。
5
+ * - 含 NUL 的路径一律拒绝(AmaError invalid_arguments)。
6
+ * - 符号链接策略:**跟随**,不做沙箱(信任边界是容器 / VM)。规则匹配用词法路径,
7
+ * `realPathOrSelf()` 给需要真实路径的调用方(如 ls 标注链接目标)。
8
+ * - `toPosix()` 把路径统一成 `/` 分隔,供 glob / ignore / 权限规则匹配。
9
+ */
10
+ export declare function assertNoNul(path: string): void;
11
+ /** 展开 `~` 与 `~/…`、`~\…`。 */
12
+ export declare function expandHome(path: string, home?: string): string;
13
+ /** 解析为绝对路径;空串视为 cwd。 */
14
+ export declare function resolvePath(path: string, cwd: string, home?: string): string;
15
+ export declare function toPosix(path: string): string;
16
+ /** cwd 内给相对路径,cwd 外给绝对路径;统一 `/`。 */
17
+ export declare function displayPath(absolutePath: string, cwd: string): string;
18
+ /** 是否在 dir 之内(含 dir 本身)。 */
19
+ export declare function isWithin(dir: string, target: string): boolean;
20
+ export declare function realPathOrSelf(path: string): string;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * 路径解析(设计 §5.2「通用安全」)。[B3]
3
+ *
4
+ * - 展开 `~` / `~/…`(`~user` 不展开);相对路径按 cwd 解析;结果是规范化绝对路径。
5
+ * - 含 NUL 的路径一律拒绝(AmaError invalid_arguments)。
6
+ * - 符号链接策略:**跟随**,不做沙箱(信任边界是容器 / VM)。规则匹配用词法路径,
7
+ * `realPathOrSelf()` 给需要真实路径的调用方(如 ls 标注链接目标)。
8
+ * - `toPosix()` 把路径统一成 `/` 分隔,供 glob / ignore / 权限规则匹配。
9
+ */
10
+ import { realpathSync } from "node:fs";
11
+ import { homedir } from "node:os";
12
+ import { isAbsolute, join, relative, resolve, sep } from "node:path";
13
+ import { AmaError } from "../errors.js";
14
+ export function assertNoNul(path) {
15
+ if (path.includes("\0")) {
16
+ throw new AmaError("invalid_arguments", "Path must not contain NUL characters");
17
+ }
18
+ }
19
+ /** 展开 `~` 与 `~/…`、`~\…`。 */
20
+ export function expandHome(path, home = homedir()) {
21
+ if (path === "~")
22
+ return home;
23
+ if (path.startsWith("~/") || path.startsWith("~\\"))
24
+ return join(home, path.slice(2));
25
+ return path;
26
+ }
27
+ /** 解析为绝对路径;空串视为 cwd。 */
28
+ export function resolvePath(path, cwd, home) {
29
+ assertNoNul(path);
30
+ const trimmed = path.trim() === "" ? "." : path;
31
+ const expanded = expandHome(trimmed, home);
32
+ return isAbsolute(expanded) ? resolve(expanded) : resolve(cwd, expanded);
33
+ }
34
+ export function toPosix(path) {
35
+ return sep === "\\" ? path.replace(/\\/g, "/") : path;
36
+ }
37
+ /** cwd 内给相对路径,cwd 外给绝对路径;统一 `/`。 */
38
+ export function displayPath(absolutePath, cwd) {
39
+ const rel = relative(cwd, absolutePath);
40
+ if (rel === "")
41
+ return ".";
42
+ if (rel.startsWith("..") || isAbsolute(rel))
43
+ return toPosix(absolutePath);
44
+ return toPosix(rel);
45
+ }
46
+ /** 是否在 dir 之内(含 dir 本身)。 */
47
+ export function isWithin(dir, target) {
48
+ const rel = relative(dir, target);
49
+ return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
50
+ }
51
+ export function realPathOrSelf(path) {
52
+ try {
53
+ return realpathSync(path);
54
+ }
55
+ catch {
56
+ return path;
57
+ }
58
+ }