@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,166 @@
1
+ /**
2
+ * 模型目录(设计 §3.4):`catalog/*.json` 是数据源(人工校对、PR 更新),随包携带。
3
+ *
4
+ * 运行时零依赖、bundle 为单文件,不能在运行时读 JSON 文件;`catalog-data.ts` 是由 JSON 生成
5
+ * 的字符串表(`UPDATE_CATALOG=1 pnpm vitest run src/ai/providers/catalog.test.ts` 重新生成,
6
+ * 测试守住两者一致)。本模块解析并校验它,另提供覆盖合并。
7
+ *
8
+ * 必填字段:id、name、maxTokens、reasoning;contextWindow 与 cost「宁缺不猜」——数据源里
9
+ * 不确定的就不写(缺 contextWindow → 关自动压缩并警告;缺 cost → 显示 `$?`)。`promptCache`
10
+ * (TTL 秒数与最小可缓存长度)同样只写有公开依据的值(第三波 §1.4),留空 = 不承诺。
11
+ */
12
+ import { AmaError } from "../../errors.js";
13
+ import { CATALOG_SOURCES } from "./catalog-data.js";
14
+ import { THINKING_LEVELS } from "../thinking.js";
15
+ function isRecord(value) {
16
+ return typeof value === "object" && value !== null && !Array.isArray(value);
17
+ }
18
+ function isNonNegative(value) {
19
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
20
+ }
21
+ /** 校验一个模型条目,返回问题列表(空 = 合法)。 */
22
+ export function checkCatalogModel(value, path) {
23
+ const problems = [];
24
+ if (!isRecord(value))
25
+ return [`${path}: must be an object`];
26
+ if (typeof value["id"] !== "string" || value["id"].length === 0)
27
+ problems.push(`${path}.id`);
28
+ if (typeof value["name"] !== "string")
29
+ problems.push(`${path}.name`);
30
+ if (typeof value["reasoning"] !== "boolean")
31
+ problems.push(`${path}.reasoning`);
32
+ if (!isNonNegative(value["maxTokens"]) || value["maxTokens"] === 0) {
33
+ problems.push(`${path}.maxTokens`);
34
+ }
35
+ if (value["contextWindow"] !== undefined && !isNonNegative(value["contextWindow"])) {
36
+ problems.push(`${path}.contextWindow`);
37
+ }
38
+ const input = value["input"];
39
+ if (input !== undefined &&
40
+ (!Array.isArray(input) || !input.every((i) => i === "text" || i === "image"))) {
41
+ problems.push(`${path}.input`);
42
+ }
43
+ const cost = value["cost"];
44
+ if (cost !== undefined) {
45
+ if (!isRecord(cost))
46
+ problems.push(`${path}.cost`);
47
+ else {
48
+ for (const key of ["input", "output", "cacheRead", "cacheWrite"]) {
49
+ if (!isNonNegative(cost[key]))
50
+ problems.push(`${path}.cost.${key}`);
51
+ }
52
+ if (cost["tiers"] !== undefined && !Array.isArray(cost["tiers"])) {
53
+ problems.push(`${path}.cost.tiers`);
54
+ }
55
+ }
56
+ }
57
+ const promptCache = value["promptCache"];
58
+ if (promptCache !== undefined) {
59
+ if (!isRecord(promptCache))
60
+ problems.push(`${path}.promptCache`);
61
+ else {
62
+ for (const [key, seconds] of Object.entries(promptCache)) {
63
+ const known = key === "short" || key === "long" || key === "minTokens";
64
+ if (!known || !Number.isInteger(seconds) || seconds <= 0) {
65
+ problems.push(`${path}.promptCache.${key}`);
66
+ }
67
+ }
68
+ const { short, long } = promptCache;
69
+ if (typeof short === "number" && typeof long === "number" && long < short) {
70
+ problems.push(`${path}.promptCache.long < short`);
71
+ }
72
+ }
73
+ }
74
+ const map = value["thinkingLevelMap"];
75
+ if (map !== undefined) {
76
+ if (!isRecord(map))
77
+ problems.push(`${path}.thinkingLevelMap`);
78
+ else {
79
+ for (const [level, mapped] of Object.entries(map)) {
80
+ const okLevel = THINKING_LEVELS.includes(level);
81
+ const okValue = mapped === null || typeof mapped === "string" || typeof mapped === "number";
82
+ if (!okLevel || !okValue)
83
+ problems.push(`${path}.thinkingLevelMap.${level}`);
84
+ }
85
+ }
86
+ }
87
+ return problems;
88
+ }
89
+ export function parseCatalogFile(value, source) {
90
+ const problems = [];
91
+ if (!isRecord(value))
92
+ problems.push("$: must be an object");
93
+ else {
94
+ if (value["version"] !== 1)
95
+ problems.push("$.version must be 1");
96
+ if (typeof value["provider"] !== "string")
97
+ problems.push("$.provider");
98
+ if (!Array.isArray(value["models"]))
99
+ problems.push("$.models must be an array");
100
+ else {
101
+ const seen = new Set();
102
+ value["models"].forEach((model, i) => {
103
+ problems.push(...checkCatalogModel(model, `$.models[${i}]`));
104
+ const id = isRecord(model) ? model["id"] : undefined;
105
+ if (typeof id === "string") {
106
+ if (seen.has(id))
107
+ problems.push(`$.models[${i}].id duplicated: ${id}`);
108
+ seen.add(id);
109
+ }
110
+ });
111
+ }
112
+ }
113
+ if (problems.length > 0) {
114
+ throw new AmaError("config_invalid", `model catalog ${source} is invalid: ${problems.join(", ")}`);
115
+ }
116
+ return value;
117
+ }
118
+ let cache;
119
+ /** 内置目录:供应商 id → 模型条目(深拷贝,调用方可随意修改)。 */
120
+ export function loadBuiltinCatalog() {
121
+ if (!cache) {
122
+ cache = new Map();
123
+ for (const [provider, raw] of Object.entries(CATALOG_SOURCES)) {
124
+ const file = parseCatalogFile(JSON.parse(raw), `catalog/${provider}.json`);
125
+ cache.set(file.provider, file.models);
126
+ }
127
+ }
128
+ const copy = new Map();
129
+ for (const [provider, models] of cache)
130
+ copy.set(provider, structuredClone(models));
131
+ return copy;
132
+ }
133
+ /** 目录条目 → Model(补 provider / api 与缺省 input)。 */
134
+ export function toModel(entry, provider, api) {
135
+ return { ...structuredClone(entry), input: entry.input ?? ["text"], provider, api };
136
+ }
137
+ /** 自定义模型缺省:maxTokens 8192、reasoning false、input ["text"]、name = id;不猜 contextWindow。 */
138
+ export function withCustomDefaults(entry, provider, api) {
139
+ return {
140
+ ...structuredClone(entry),
141
+ name: entry.name ?? entry.id,
142
+ input: entry.input ?? ["text"],
143
+ reasoning: entry.reasoning ?? false,
144
+ maxTokens: entry.maxTokens ?? 8192,
145
+ provider,
146
+ api,
147
+ };
148
+ }
149
+ /** 只改元数据:浅合并,`compat` / `cost` / `thinkingLevelMap` / `headers` 再合并一层。 */
150
+ export function applyModelOverride(model, override) {
151
+ const { id: _id, ...rest } = override;
152
+ const next = { ...model, ...structuredClone(rest) };
153
+ if (override.compat && model.compat)
154
+ next.compat = { ...model.compat, ...override.compat };
155
+ if (override.cost && model.cost)
156
+ next.cost = { ...model.cost, ...override.cost };
157
+ if (override.headers && model.headers)
158
+ next.headers = { ...model.headers, ...override.headers };
159
+ if (override.thinkingLevelMap && model.thinkingLevelMap) {
160
+ next.thinkingLevelMap = {
161
+ ...model.thinkingLevelMap,
162
+ ...override.thinkingLevelMap,
163
+ };
164
+ }
165
+ return next;
166
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * 渠道(docs/providers.md「渠道」):一个供应商下的多种接口(协议 + 地址 + 可选 key / headers / compat)。
3
+ *
4
+ * - 没有 `channels` 的供应商按单渠道处理:供应商级 `api` + `baseUrl` 就是隐式的 `default` 渠道,
5
+ * 模型上不出现 `channel` / `channels` 字段,行为与引入渠道之前完全相同。
6
+ * - 有 `channels` 时:模型 `channels[0]` 是首选,缺省 `defaultChannel`;模型级 `api` / `baseUrl`
7
+ * 覆盖在所选渠道之上(等价于匿名渠道)。
8
+ * - 模型引用 `provider/model@channel`:`@` 之后是该供应商的渠道名才当渠道,否则整串仍是 model id。
9
+ * - 渠道自己的 key 以 `<provider>@<channel>` 为键进 key 解析(config 的 `channels.<c>.apiKey` 或
10
+ * auth.json 的同名条目),没有时用供应商的 key。
11
+ */
12
+ import type { ProviderConfig } from "../../config/types.js";
13
+ import type { Api, ProviderChannel } from "../types.js";
14
+ /** 隐式渠道名(单渠道供应商)。 */
15
+ export declare const DEFAULT_CHANNEL = "default";
16
+ /** 渠道 key 在 key 解析里的键。 */
17
+ export declare function channelKeyId(providerId: string, channel: string): string;
18
+ /** config 的 `channels` → 物化渠道表(跳过名字非法或缺 api / baseUrl 的,返回警告)。 */
19
+ export declare function parseChannels(providerId: string, config: ProviderConfig): {
20
+ channels: ProviderChannel[];
21
+ defaultChannel: string | undefined;
22
+ warnings: string[];
23
+ };
24
+ /** 模型的渠道表:去掉不存在的;空 → [defaultChannel]。 */
25
+ export declare function modelChannels(wanted: readonly string[] | undefined, known: readonly ProviderChannel[], defaultChannel: string, onUnknown: (name: string) => void): string[];
26
+ /**
27
+ * 拆 `provider/model@channel` 末尾的渠道:只看最后一个 `/` 之后的部分;渠道名不合法时返回 undefined。
28
+ * 是否真的是渠道由调用方结合供应商的渠道表判断。
29
+ */
30
+ export declare function splitChannelRef(ref: string): {
31
+ base: string;
32
+ channel: string;
33
+ } | undefined;
34
+ /** `provider/model` 或 `provider/model@channel`(channel 为空时不带后缀)。 */
35
+ export declare function formatModelRef(ref: {
36
+ provider: string;
37
+ id: string;
38
+ channel?: string;
39
+ }): string;
40
+ /** Model / ModelRef → ModelRef(带上渠道,没有渠道时不出现该键)。 */
41
+ export declare function modelRefOf(model: {
42
+ provider: string;
43
+ id: string;
44
+ channel?: string | undefined;
45
+ }): {
46
+ provider: string;
47
+ id: string;
48
+ channel?: string;
49
+ };
50
+ /** 协议的短名(表格用)。 */
51
+ export declare function apiShortName(api: Api): string;
@@ -0,0 +1,96 @@
1
+ /**
2
+ * 渠道(docs/providers.md「渠道」):一个供应商下的多种接口(协议 + 地址 + 可选 key / headers / compat)。
3
+ *
4
+ * - 没有 `channels` 的供应商按单渠道处理:供应商级 `api` + `baseUrl` 就是隐式的 `default` 渠道,
5
+ * 模型上不出现 `channel` / `channels` 字段,行为与引入渠道之前完全相同。
6
+ * - 有 `channels` 时:模型 `channels[0]` 是首选,缺省 `defaultChannel`;模型级 `api` / `baseUrl`
7
+ * 覆盖在所选渠道之上(等价于匿名渠道)。
8
+ * - 模型引用 `provider/model@channel`:`@` 之后是该供应商的渠道名才当渠道,否则整串仍是 model id。
9
+ * - 渠道自己的 key 以 `<provider>@<channel>` 为键进 key 解析(config 的 `channels.<c>.apiKey` 或
10
+ * auth.json 的同名条目),没有时用供应商的 key。
11
+ */
12
+ import { CHANNEL_NAME_PATTERN } from "../../config/types.js";
13
+ /** 隐式渠道名(单渠道供应商)。 */
14
+ export const DEFAULT_CHANNEL = "default";
15
+ /** 渠道 key 在 key 解析里的键。 */
16
+ export function channelKeyId(providerId, channel) {
17
+ return `${providerId}@${channel}`;
18
+ }
19
+ /** config 的 `channels` → 物化渠道表(跳过名字非法或缺 api / baseUrl 的,返回警告)。 */
20
+ export function parseChannels(providerId, config) {
21
+ const warnings = [];
22
+ const channels = [];
23
+ for (const [name, raw] of Object.entries(config.channels ?? {})) {
24
+ const c = raw;
25
+ if (!CHANNEL_NAME_PATTERN.test(name) || !c || !c.api || !c.baseUrl) {
26
+ warnings.push(`provider "${providerId}" channel "${name}" is invalid; ignored`);
27
+ continue;
28
+ }
29
+ const channel = { name, api: c.api, baseUrl: c.baseUrl };
30
+ if (c.authHeader !== undefined)
31
+ channel.authHeader = c.authHeader;
32
+ if (c.headers !== undefined)
33
+ channel.headers = { ...c.headers };
34
+ if (c.compat !== undefined)
35
+ channel.compat = { ...c.compat };
36
+ channels.push(channel);
37
+ }
38
+ let defaultChannel = config.defaultChannel;
39
+ if (defaultChannel !== undefined && !channels.some((c) => c.name === defaultChannel)) {
40
+ warnings.push(`provider "${providerId}" defaultChannel "${defaultChannel}" not found`);
41
+ defaultChannel = undefined;
42
+ }
43
+ return { channels, defaultChannel: defaultChannel ?? channels[0]?.name, warnings };
44
+ }
45
+ /** 模型的渠道表:去掉不存在的;空 → [defaultChannel]。 */
46
+ export function modelChannels(wanted, known, defaultChannel, onUnknown) {
47
+ const names = new Set(known.map((c) => c.name));
48
+ const out = [];
49
+ for (const name of wanted ?? []) {
50
+ if (!names.has(name))
51
+ onUnknown(name);
52
+ else if (!out.includes(name))
53
+ out.push(name);
54
+ }
55
+ return out.length > 0 ? out : [defaultChannel];
56
+ }
57
+ /**
58
+ * 拆 `provider/model@channel` 末尾的渠道:只看最后一个 `/` 之后的部分;渠道名不合法时返回 undefined。
59
+ * 是否真的是渠道由调用方结合供应商的渠道表判断。
60
+ */
61
+ export function splitChannelRef(ref) {
62
+ const at = ref.lastIndexOf("@");
63
+ if (at <= 0 || at < ref.lastIndexOf("/"))
64
+ return undefined;
65
+ const channel = ref.slice(at + 1);
66
+ if (!CHANNEL_NAME_PATTERN.test(channel))
67
+ return undefined;
68
+ return { base: ref.slice(0, at), channel };
69
+ }
70
+ /** `provider/model` 或 `provider/model@channel`(channel 为空时不带后缀)。 */
71
+ export function formatModelRef(ref) {
72
+ return `${ref.provider}/${ref.id}${ref.channel !== undefined ? `@${ref.channel}` : ""}`;
73
+ }
74
+ /** Model / ModelRef → ModelRef(带上渠道,没有渠道时不出现该键)。 */
75
+ export function modelRefOf(model) {
76
+ return {
77
+ provider: model.provider,
78
+ id: model.id,
79
+ ...(model.channel !== undefined ? { channel: model.channel } : {}),
80
+ };
81
+ }
82
+ /** 协议的短名(表格用)。 */
83
+ export function apiShortName(api) {
84
+ switch (api) {
85
+ case "openai-completions":
86
+ return "chat";
87
+ case "openai-responses":
88
+ return "responses";
89
+ case "anthropic-messages":
90
+ return "messages";
91
+ case "google-generative-ai":
92
+ return "gemini";
93
+ default:
94
+ return api;
95
+ }
96
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * 用 models.dev 缓存给自定义模型补元数据(docs/providers.md「模型元数据:models.dev」)。
3
+ *
4
+ * 优先级:用户配置写了的字段 > models.dev > 自定义缺省(maxTokens 8192、input ["text"]、
5
+ * reasoning false、不猜 contextWindow)。内置目录的模型不经过这里(目录本身就是人工校对的值)。
6
+ * 只读传入的索引,不联网。
7
+ */
8
+ import type { ModelConfig } from "../../config/types.js";
9
+ import type { Model } from "../types.js";
10
+ import { type EnrichableField, type ModelsDevIndex, type ModelsDevMatch } from "./models-dev.js";
11
+ export type FieldSource = "config" | "catalog" | "models.dev" | "default";
12
+ export interface ModelMetadata {
13
+ sources: Record<EnrichableField, FieldSource>;
14
+ /** models.dev 匹配结果;未查(没有缓存或 `modelsDev: false`)为 undefined。 */
15
+ match?: ModelsDevMatch | undefined;
16
+ /** 是否查过 models.dev(有缓存且没关闭)。 */
17
+ looked: boolean;
18
+ /** models.dev 的 tool_call。 */
19
+ toolCall?: boolean;
20
+ }
21
+ export type ModelsDevSource = ModelsDevIndex | (() => ModelsDevIndex | undefined) | undefined;
22
+ /** 惰性取索引:只有真的要补字段时才读缓存(启动时没有自定义模型就不读约 2 MB 的缓存)。 */
23
+ export declare function lazyIndex(source: ModelsDevSource): () => ModelsDevIndex | undefined;
24
+ /**
25
+ * 补全一个自定义模型条目(config 的 `models[]` 或合成的模型)。返回补过字段的条目(不含
26
+ * `modelsDev` / `channels` 这些配置专用键)与每个字段的来源。
27
+ */
28
+ export declare function enrichEntry(entry: ModelConfig, index: () => ModelsDevIndex | undefined): {
29
+ entry: Omit<ModelConfig, "modelsDev" | "channels">;
30
+ metadata: ModelMetadata;
31
+ };
32
+ /** 内置目录模型的来源(全部 catalog;目录没写 contextWindow / cost 的为缺省)。 */
33
+ export declare function catalogMetadata(model: Model): ModelMetadata;
34
+ export declare function sourceText(source: FieldSource): string;
@@ -0,0 +1,86 @@
1
+ /**
2
+ * 用 models.dev 缓存给自定义模型补元数据(docs/providers.md「模型元数据:models.dev」)。
3
+ *
4
+ * 优先级:用户配置写了的字段 > models.dev > 自定义缺省(maxTokens 8192、input ["text"]、
5
+ * reasoning false、不猜 contextWindow)。内置目录的模型不经过这里(目录本身就是人工校对的值)。
6
+ * 只读传入的索引,不联网。
7
+ */
8
+ import { ENRICHABLE_FIELDS, modelsDevFields, } from "./models-dev.js";
9
+ /** 惰性取索引:只有真的要补字段时才读缓存(启动时没有自定义模型就不读约 2 MB 的缓存)。 */
10
+ export function lazyIndex(source) {
11
+ if (typeof source !== "function")
12
+ return () => source;
13
+ let loaded = false;
14
+ let index;
15
+ return () => {
16
+ if (!loaded) {
17
+ loaded = true;
18
+ try {
19
+ index = source();
20
+ }
21
+ catch {
22
+ index = undefined;
23
+ }
24
+ }
25
+ return index;
26
+ };
27
+ }
28
+ function needsLookup(entry) {
29
+ return ENRICHABLE_FIELDS.some((field) => entry[field] === undefined);
30
+ }
31
+ /**
32
+ * 补全一个自定义模型条目(config 的 `models[]` 或合成的模型)。返回补过字段的条目(不含
33
+ * `modelsDev` / `channels` 这些配置专用键)与每个字段的来源。
34
+ */
35
+ export function enrichEntry(entry, index) {
36
+ const { modelsDev, channels: _channels, ...rest } = entry;
37
+ const sources = {};
38
+ for (const field of ENRICHABLE_FIELDS) {
39
+ sources[field] = rest[field] !== undefined ? "config" : "default";
40
+ }
41
+ const metadata = { sources, looked: false };
42
+ if (modelsDev === false || (!needsLookup(rest) && modelsDev === undefined)) {
43
+ return { entry: rest, metadata };
44
+ }
45
+ const idx = index();
46
+ if (idx === undefined)
47
+ return { entry: rest, metadata };
48
+ metadata.looked = true;
49
+ const match = idx.match(entry.id, typeof modelsDev === "string" ? modelsDev : undefined);
50
+ metadata.match = match;
51
+ if (match === undefined)
52
+ return { entry: rest, metadata };
53
+ const fields = modelsDevFields(match.model);
54
+ if (fields.toolCall !== undefined)
55
+ metadata.toolCall = fields.toolCall;
56
+ const out = { ...rest };
57
+ for (const field of ENRICHABLE_FIELDS) {
58
+ if (out[field] !== undefined)
59
+ continue;
60
+ const value = fields[field];
61
+ if (value === undefined)
62
+ continue;
63
+ out[field] = structuredClone(value);
64
+ sources[field] = "models.dev";
65
+ }
66
+ if (out.name === undefined && fields.name !== undefined)
67
+ out.name = fields.name;
68
+ return { entry: out, metadata };
69
+ }
70
+ /** 内置目录模型的来源(全部 catalog;目录没写 contextWindow / cost 的为缺省)。 */
71
+ export function catalogMetadata(model) {
72
+ const sources = {};
73
+ for (const field of ENRICHABLE_FIELDS) {
74
+ sources[field] = model[field] !== undefined ? "catalog" : "default";
75
+ }
76
+ return { sources, looked: false };
77
+ }
78
+ const SOURCE_TEXT = {
79
+ config: "config",
80
+ catalog: "目录",
81
+ "models.dev": "models.dev",
82
+ default: "缺省",
83
+ };
84
+ export function sourceText(source) {
85
+ return SOURCE_TEXT[source];
86
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * models.dev 缓存(docs/providers.md「模型元数据:models.dev」)。
3
+ *
4
+ * - 缓存文件 `<dataDir>/models-dev.json`:`{ version, url, fetchedAt, etag?, providers }`,providers 是
5
+ * `trimModelsDev()` 裁剪后的数据(约 2.3 MB,原始约 5 MB)。写入走同目录临时文件 + rename。
6
+ * - **启动不联网**:`loadModelsDevIndex()` 只读缓存(按路径 + mtime 记忆,同一进程只解析一次)。
7
+ * - 联网只在 `ama providers add|refresh`、`ama models discover`、`ama models refresh-catalog` 调
8
+ * `refreshModelsDev()` 时发生:缓存 24 小时内不重拉(`force` 例外),带 `If-None-Match`;304 只刷新时间;
9
+ * 失败时返回旧缓存与 warning。`AMA_MODELS_DEV_URL` 换数据源。
10
+ */
11
+ import { ModelsDevIndex, type ModelsDevData } from "./models-dev.js";
12
+ export declare const MODELS_DEV_URL = "https://models.dev/api.json";
13
+ export declare const MODELS_DEV_URL_ENV = "AMA_MODELS_DEV_URL";
14
+ export declare const MODELS_DEV_FILE = "models-dev.json";
15
+ export declare const MODELS_DEV_TTL_MS: number;
16
+ export declare const MODELS_DEV_TIMEOUT_MS = 30000;
17
+ export interface ModelsDevCacheFile {
18
+ version: 1;
19
+ url: string;
20
+ /** ISO 8601。 */
21
+ fetchedAt: string;
22
+ etag?: string;
23
+ providers: ModelsDevData;
24
+ }
25
+ export declare function modelsDevCachePath(dataDir: string): string;
26
+ export declare function modelsDevUrl(env?: Readonly<Record<string, string | undefined>>): string;
27
+ /** 读缓存;不存在或损坏返回 undefined(损坏不报错:下次拉取会覆盖)。 */
28
+ export declare function readModelsDevCache(dataDir: string): ModelsDevCacheFile | undefined;
29
+ export declare function writeModelsDevCache(dataDir: string, file: ModelsDevCacheFile): void;
30
+ /** 只读缓存构造索引(不联网);没有缓存返回 undefined。 */
31
+ export declare function loadModelsDevIndex(dataDir: string): ModelsDevIndex | undefined;
32
+ export type RefreshStatus = "fresh" | "updated" | "not-modified" | "stale" | "unavailable";
33
+ export interface RefreshResult {
34
+ status: RefreshStatus;
35
+ index?: ModelsDevIndex;
36
+ fetchedAt?: string;
37
+ url: string;
38
+ warning?: string;
39
+ }
40
+ export interface RefreshOptions {
41
+ dataDir: string;
42
+ env?: Readonly<Record<string, string | undefined>>;
43
+ /** 忽略 TTL(`ama models refresh-catalog`)。 */
44
+ force?: boolean;
45
+ ttlMs?: number;
46
+ timeoutMs?: number;
47
+ now?: () => number;
48
+ fetch?: typeof fetch;
49
+ }
50
+ /** 按需拉取 models.dev 并更新缓存;失败时回落旧缓存(status `stale`)或无数据(`unavailable`)。 */
51
+ export declare function refreshModelsDev(options: RefreshOptions): Promise<RefreshResult>;
52
+ /** 一行状态说明(命令输出用)。 */
53
+ export declare function describeRefresh(result: RefreshResult): string;
@@ -0,0 +1,147 @@
1
+ /**
2
+ * models.dev 缓存(docs/providers.md「模型元数据:models.dev」)。
3
+ *
4
+ * - 缓存文件 `<dataDir>/models-dev.json`:`{ version, url, fetchedAt, etag?, providers }`,providers 是
5
+ * `trimModelsDev()` 裁剪后的数据(约 2.3 MB,原始约 5 MB)。写入走同目录临时文件 + rename。
6
+ * - **启动不联网**:`loadModelsDevIndex()` 只读缓存(按路径 + mtime 记忆,同一进程只解析一次)。
7
+ * - 联网只在 `ama providers add|refresh`、`ama models discover`、`ama models refresh-catalog` 调
8
+ * `refreshModelsDev()` 时发生:缓存 24 小时内不重拉(`force` 例外),带 `If-None-Match`;304 只刷新时间;
9
+ * 失败时返回旧缓存与 warning。`AMA_MODELS_DEV_URL` 换数据源。
10
+ */
11
+ import { mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
12
+ import { dirname, join } from "node:path";
13
+ import { ModelsDevIndex, trimModelsDev } from "./models-dev.js";
14
+ export const MODELS_DEV_URL = "https://models.dev/api.json";
15
+ export const MODELS_DEV_URL_ENV = "AMA_MODELS_DEV_URL";
16
+ export const MODELS_DEV_FILE = "models-dev.json";
17
+ export const MODELS_DEV_TTL_MS = 24 * 60 * 60 * 1000;
18
+ export const MODELS_DEV_TIMEOUT_MS = 30_000;
19
+ export function modelsDevCachePath(dataDir) {
20
+ return join(dataDir, MODELS_DEV_FILE);
21
+ }
22
+ export function modelsDevUrl(env = process.env) {
23
+ const custom = env[MODELS_DEV_URL_ENV]?.trim();
24
+ return custom ? custom : MODELS_DEV_URL;
25
+ }
26
+ /** 读缓存;不存在或损坏返回 undefined(损坏不报错:下次拉取会覆盖)。 */
27
+ export function readModelsDevCache(dataDir) {
28
+ try {
29
+ const value = JSON.parse(readFileSync(modelsDevCachePath(dataDir), "utf8"));
30
+ if (typeof value !== "object" || value === null)
31
+ return undefined;
32
+ const file = value;
33
+ if (file.version !== 1 || typeof file.fetchedAt !== "string")
34
+ return undefined;
35
+ if (typeof file.providers !== "object" || file.providers === null)
36
+ return undefined;
37
+ return file;
38
+ }
39
+ catch {
40
+ return undefined;
41
+ }
42
+ }
43
+ export function writeModelsDevCache(dataDir, file) {
44
+ const path = modelsDevCachePath(dataDir);
45
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
46
+ const tmp = `${path}.${process.pid}.tmp`;
47
+ writeFileSync(tmp, JSON.stringify(file));
48
+ renameSync(tmp, path);
49
+ memo = undefined;
50
+ }
51
+ let memo;
52
+ /** 只读缓存构造索引(不联网);没有缓存返回 undefined。 */
53
+ export function loadModelsDevIndex(dataDir) {
54
+ const path = modelsDevCachePath(dataDir);
55
+ let key;
56
+ try {
57
+ const stat = statSync(path);
58
+ key = `${path}\0${stat.mtimeMs}\0${stat.size}`;
59
+ }
60
+ catch {
61
+ return undefined;
62
+ }
63
+ if (memo?.key === key)
64
+ return memo.index;
65
+ const file = readModelsDevCache(dataDir);
66
+ const index = file !== undefined ? new ModelsDevIndex(file.providers) : undefined;
67
+ memo = { key, index };
68
+ return index;
69
+ }
70
+ /** 按需拉取 models.dev 并更新缓存;失败时回落旧缓存(status `stale`)或无数据(`unavailable`)。 */
71
+ export async function refreshModelsDev(options) {
72
+ const url = modelsDevUrl(options.env);
73
+ const now = options.now ?? Date.now;
74
+ const cached = readModelsDevCache(options.dataDir);
75
+ const cachedIndex = () => cached !== undefined ? new ModelsDevIndex(cached.providers) : undefined;
76
+ const age = cached !== undefined ? now() - Date.parse(cached.fetchedAt) : Infinity;
77
+ if (cached !== undefined &&
78
+ cached.url === url &&
79
+ !options.force &&
80
+ age >= 0 &&
81
+ age < (options.ttlMs ?? MODELS_DEV_TTL_MS)) {
82
+ return {
83
+ status: "fresh",
84
+ index: cachedIndex(),
85
+ fetchedAt: cached.fetchedAt,
86
+ url,
87
+ };
88
+ }
89
+ const doFetch = options.fetch ?? fetch;
90
+ try {
91
+ const headers = { accept: "application/json" };
92
+ if (cached?.etag !== undefined && cached.url === url)
93
+ headers["if-none-match"] = cached.etag;
94
+ const response = await doFetch(url, {
95
+ headers,
96
+ signal: AbortSignal.timeout(options.timeoutMs ?? MODELS_DEV_TIMEOUT_MS),
97
+ });
98
+ const fetchedAt = new Date(now()).toISOString();
99
+ if (response.status === 304 && cached !== undefined) {
100
+ writeModelsDevCache(options.dataDir, { ...cached, fetchedAt });
101
+ return { status: "not-modified", index: cachedIndex(), fetchedAt, url };
102
+ }
103
+ if (!response.ok)
104
+ throw new Error(`HTTP ${response.status}`);
105
+ const providers = trimModelsDev(await response.json());
106
+ const etag = response.headers.get("etag") ?? undefined;
107
+ writeModelsDevCache(options.dataDir, {
108
+ version: 1,
109
+ url,
110
+ fetchedAt,
111
+ ...(etag !== undefined ? { etag } : {}),
112
+ providers,
113
+ });
114
+ return { status: "updated", index: new ModelsDevIndex(providers), fetchedAt, url };
115
+ }
116
+ catch (error) {
117
+ const reason = error instanceof Error ? error.message : String(error);
118
+ if (cached !== undefined) {
119
+ return {
120
+ status: "stale",
121
+ index: cachedIndex(),
122
+ fetchedAt: cached.fetchedAt,
123
+ url,
124
+ warning: `models.dev 拉取失败(${reason}),沿用 ${cached.fetchedAt} 的缓存`,
125
+ };
126
+ }
127
+ return {
128
+ status: "unavailable",
129
+ url,
130
+ warning: `models.dev 拉取失败(${reason}),没有缓存可用`,
131
+ };
132
+ }
133
+ }
134
+ /** 一行状态说明(命令输出用)。 */
135
+ export function describeRefresh(result) {
136
+ const size = result.index
137
+ ? `${result.index.providerCount} 家供应商、${result.index.modelCount} 个模型`
138
+ : "无数据";
139
+ const text = {
140
+ fresh: "缓存未过期",
141
+ updated: "已更新",
142
+ "not-modified": "未变化(304)",
143
+ stale: "拉取失败,用旧缓存",
144
+ unavailable: "不可用",
145
+ };
146
+ return `models.dev:${text[result.status]}(${size}${result.fetchedAt ? `,${result.fetchedAt}` : ""})`;
147
+ }