alloy-runtime-cli 0.1.0__py3-none-any.whl

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 (451) hide show
  1. alloy_runtime_cli-0.1.0.dist-info/METADATA +61 -0
  2. alloy_runtime_cli-0.1.0.dist-info/RECORD +451 -0
  3. alloy_runtime_cli-0.1.0.dist-info/WHEEL +5 -0
  4. alloy_runtime_cli-0.1.0.dist-info/entry_points.txt +2 -0
  5. alloy_runtime_cli-0.1.0.dist-info/top_level.txt +1 -0
  6. cli/__init__.py +0 -0
  7. cli/commands/__init__.py +0 -0
  8. cli/commands/admin/__init__.py +0 -0
  9. cli/commands/admin/bootstrap_command.py +118 -0
  10. cli/commands/admin/credentials/__init__.py +0 -0
  11. cli/commands/admin/credentials/create/__init__.py +0 -0
  12. cli/commands/admin/credentials/create/command.py +148 -0
  13. cli/commands/admin/credentials/create/presenter.py +16 -0
  14. cli/commands/admin/credentials/grant/__init__.py +0 -0
  15. cli/commands/admin/credentials/grant/command.py +119 -0
  16. cli/commands/admin/credentials/grant/fields.py +33 -0
  17. cli/commands/admin/credentials/grant/presenter.py +23 -0
  18. cli/commands/agents/__init__.py +0 -0
  19. cli/commands/agents/create/__init__.py +0 -0
  20. cli/commands/agents/create/command.py +475 -0
  21. cli/commands/agents/create/fields.py +64 -0
  22. cli/commands/agents/create/presenter.py +68 -0
  23. cli/commands/agents/delete/__init__.py +0 -0
  24. cli/commands/agents/delete/command.py +47 -0
  25. cli/commands/agents/delete/presenter.py +16 -0
  26. cli/commands/agents/get/command.py +37 -0
  27. cli/commands/agents/get/presenter.py +32 -0
  28. cli/commands/agents/list/__init__.py +1 -0
  29. cli/commands/agents/list/command.py +54 -0
  30. cli/commands/agents/list/presenter.py +82 -0
  31. cli/commands/agents/update/__init__.py +0 -0
  32. cli/commands/agents/update/command.py +435 -0
  33. cli/commands/agents/update/fields.py +40 -0
  34. cli/commands/agents/update/presenter.py +68 -0
  35. cli/commands/audio/__init__.py +0 -0
  36. cli/commands/audio/transcribe/__init__.py +0 -0
  37. cli/commands/audio/transcribe/command.py +144 -0
  38. cli/commands/audio/transcribe/presenter.py +15 -0
  39. cli/commands/auth/__init__.py +0 -0
  40. cli/commands/auth/login/__init__.py +0 -0
  41. cli/commands/auth/login/command.py +80 -0
  42. cli/commands/auth/signup/__init__.py +0 -0
  43. cli/commands/auth/signup/command.py +115 -0
  44. cli/commands/billing/__init__.py +1 -0
  45. cli/commands/billing/costs/__init__.py +1 -0
  46. cli/commands/billing/costs/by_agent/__init__.py +1 -0
  47. cli/commands/billing/costs/by_agent/command.py +57 -0
  48. cli/commands/billing/costs/by_agent/presenter.py +81 -0
  49. cli/commands/billing/costs/by_model/__init__.py +1 -0
  50. cli/commands/billing/costs/by_model/command.py +57 -0
  51. cli/commands/billing/costs/by_model/presenter.py +80 -0
  52. cli/commands/billing/costs/daily/__init__.py +1 -0
  53. cli/commands/billing/costs/daily/command.py +55 -0
  54. cli/commands/billing/costs/daily/presenter.py +75 -0
  55. cli/commands/billing/costs/summary/__init__.py +1 -0
  56. cli/commands/billing/costs/summary/command.py +57 -0
  57. cli/commands/billing/costs/summary/presenter.py +42 -0
  58. cli/commands/billing/projects/__init__.py +1 -0
  59. cli/commands/billing/projects/create/__init__.py +1 -0
  60. cli/commands/billing/projects/create/command.py +60 -0
  61. cli/commands/billing/projects/create/presenter.py +26 -0
  62. cli/commands/billing/projects/get/__init__.py +1 -0
  63. cli/commands/billing/projects/get/command.py +33 -0
  64. cli/commands/billing/projects/get/presenter.py +32 -0
  65. cli/commands/billing/projects/list/__init__.py +1 -0
  66. cli/commands/billing/projects/list/command.py +40 -0
  67. cli/commands/billing/projects/list/presenter.py +57 -0
  68. cli/commands/content/__init__.py +1 -0
  69. cli/commands/content/delete/__init__.py +0 -0
  70. cli/commands/content/delete/command.py +49 -0
  71. cli/commands/content/delete/presenter.py +18 -0
  72. cli/commands/content/edit/__init__.py +1 -0
  73. cli/commands/content/edit/command.py +155 -0
  74. cli/commands/content/edit/editor.py +150 -0
  75. cli/commands/content/edit/presenter.py +146 -0
  76. cli/commands/content/get/__init__.py +1 -0
  77. cli/commands/content/get/command.py +39 -0
  78. cli/commands/content/get/presenter.py +176 -0
  79. cli/commands/content/list/__init__.py +1 -0
  80. cli/commands/content/list/command.py +347 -0
  81. cli/commands/content/list/export_formatters.py +409 -0
  82. cli/commands/content/list/export_handler.py +165 -0
  83. cli/commands/content/list/presenter.py +190 -0
  84. cli/commands/credentials/__init__.py +0 -0
  85. cli/commands/credentials/create/__init__.py +0 -0
  86. cli/commands/credentials/create/command.py +165 -0
  87. cli/commands/credentials/create/fields.py +38 -0
  88. cli/commands/credentials/create/presenter.py +20 -0
  89. cli/commands/credentials/update/__init__.py +0 -0
  90. cli/commands/credentials/update/command.py +53 -0
  91. cli/commands/credentials/update/fields.py +71 -0
  92. cli/commands/credentials/update/presenter.py +16 -0
  93. cli/commands/flag_utils.py +366 -0
  94. cli/commands/generate/__init__.py +0 -0
  95. cli/commands/generate/cancel/__init__.py +1 -0
  96. cli/commands/generate/cancel/command.py +44 -0
  97. cli/commands/generate/cancel/presenter.py +26 -0
  98. cli/commands/generate/status/__init__.py +1 -0
  99. cli/commands/generate/status/command.py +58 -0
  100. cli/commands/generate/status/presenter.py +78 -0
  101. cli/commands/generate/text/__init__.py +0 -0
  102. cli/commands/generate/text/command.py +1325 -0
  103. cli/commands/generate/text/concurrent_renderer.py +355 -0
  104. cli/commands/generate/text/presenter.py +287 -0
  105. cli/commands/generate/text/stream_renderer.py +129 -0
  106. cli/commands/knowledge/__init__.py +0 -0
  107. cli/commands/knowledge/collections/__init__.py +0 -0
  108. cli/commands/knowledge/collections/cluster/__init__.py +0 -0
  109. cli/commands/knowledge/collections/cluster/command.py +64 -0
  110. cli/commands/knowledge/collections/cluster/presenter.py +74 -0
  111. cli/commands/knowledge/collections/cluster_status/__init__.py +0 -0
  112. cli/commands/knowledge/collections/cluster_status/command.py +46 -0
  113. cli/commands/knowledge/collections/cluster_status/presenter.py +10 -0
  114. cli/commands/knowledge/collections/create/__init__.py +0 -0
  115. cli/commands/knowledge/collections/create/command.py +137 -0
  116. cli/commands/knowledge/collections/create/presenter.py +38 -0
  117. cli/commands/knowledge/collections/delete/__init__.py +1 -0
  118. cli/commands/knowledge/collections/delete/command.py +47 -0
  119. cli/commands/knowledge/collections/delete/presenter.py +20 -0
  120. cli/commands/knowledge/collections/get/__init__.py +1 -0
  121. cli/commands/knowledge/collections/get/command.py +30 -0
  122. cli/commands/knowledge/collections/get/presenter.py +44 -0
  123. cli/commands/knowledge/collections/list/__init__.py +1 -0
  124. cli/commands/knowledge/collections/list/command.py +41 -0
  125. cli/commands/knowledge/collections/list/presenter.py +68 -0
  126. cli/commands/knowledge/collections/update/__init__.py +0 -0
  127. cli/commands/knowledge/collections/update/command.py +97 -0
  128. cli/commands/knowledge/collections/update/presenter.py +42 -0
  129. cli/commands/knowledge/documents/__init__.py +0 -0
  130. cli/commands/knowledge/documents/bulk_metadata/__init__.py +0 -0
  131. cli/commands/knowledge/documents/bulk_metadata/command.py +119 -0
  132. cli/commands/knowledge/documents/bulk_metadata/presenter.py +36 -0
  133. cli/commands/knowledge/documents/delete/__init__.py +0 -0
  134. cli/commands/knowledge/documents/delete/command.py +47 -0
  135. cli/commands/knowledge/documents/delete/presenter.py +20 -0
  136. cli/commands/knowledge/documents/get/__init__.py +0 -0
  137. cli/commands/knowledge/documents/get/command.py +39 -0
  138. cli/commands/knowledge/documents/get/presenter.py +78 -0
  139. cli/commands/knowledge/documents/ingest/__init__.py +0 -0
  140. cli/commands/knowledge/documents/ingest/command.py +222 -0
  141. cli/commands/knowledge/documents/ingest/presenter.py +41 -0
  142. cli/commands/knowledge/documents/list/__init__.py +0 -0
  143. cli/commands/knowledge/documents/list/command.py +69 -0
  144. cli/commands/knowledge/documents/list/presenter.py +86 -0
  145. cli/commands/knowledge/documents/reingest/__init__.py +0 -0
  146. cli/commands/knowledge/documents/reingest/command.py +102 -0
  147. cli/commands/knowledge/documents/reingest/presenter.py +70 -0
  148. cli/commands/knowledge/documents/update/__init__.py +0 -0
  149. cli/commands/knowledge/documents/update/command.py +85 -0
  150. cli/commands/knowledge/documents/update/presenter.py +37 -0
  151. cli/commands/knowledge/recover/__init__.py +0 -0
  152. cli/commands/knowledge/recover/command.py +46 -0
  153. cli/commands/knowledge/recover/presenter.py +79 -0
  154. cli/commands/knowledge/search/__init__.py +0 -0
  155. cli/commands/knowledge/search/command.py +218 -0
  156. cli/commands/knowledge/search/presenter.py +111 -0
  157. cli/commands/knowledge/synthesis/__init__.py +0 -0
  158. cli/commands/knowledge/synthesis/create/__init__.py +0 -0
  159. cli/commands/knowledge/synthesis/create/command.py +127 -0
  160. cli/commands/knowledge/synthesis/create/presenter.py +33 -0
  161. cli/commands/knowledge/synthesis/delete/__init__.py +0 -0
  162. cli/commands/knowledge/synthesis/delete/command.py +53 -0
  163. cli/commands/knowledge/synthesis/delete/presenter.py +31 -0
  164. cli/commands/knowledge/synthesis/get/__init__.py +0 -0
  165. cli/commands/knowledge/synthesis/get/command.py +55 -0
  166. cli/commands/knowledge/synthesis/get/presenter.py +114 -0
  167. cli/commands/knowledge/synthesis/list/__init__.py +0 -0
  168. cli/commands/knowledge/synthesis/list/command.py +132 -0
  169. cli/commands/knowledge/synthesis/list/presenter.py +84 -0
  170. cli/commands/knowledge/synthesis/refresh/__init__.py +0 -0
  171. cli/commands/knowledge/synthesis/refresh/command.py +42 -0
  172. cli/commands/knowledge/synthesis/refresh/presenter.py +33 -0
  173. cli/commands/knowledge/synthesis/update/__init__.py +0 -0
  174. cli/commands/knowledge/synthesis/update/command.py +76 -0
  175. cli/commands/knowledge/synthesis/update/presenter.py +41 -0
  176. cli/commands/models/__init__.py +0 -0
  177. cli/commands/models/list/__init__.py +0 -0
  178. cli/commands/models/list/command.py +84 -0
  179. cli/commands/models/list/presenter.py +114 -0
  180. cli/commands/organizations/__init__.py +0 -0
  181. cli/commands/organizations/create/command.py +32 -0
  182. cli/commands/organizations/create/presenter.py +9 -0
  183. cli/commands/pipelines/__init__.py +1 -0
  184. cli/commands/pipelines/approvals/__init__.py +1 -0
  185. cli/commands/pipelines/approvals/decide_command.py +77 -0
  186. cli/commands/pipelines/approvals/get_command.py +44 -0
  187. cli/commands/pipelines/approvals/presenter.py +56 -0
  188. cli/commands/pipelines/costs/__init__.py +1 -0
  189. cli/commands/pipelines/costs/command.py +57 -0
  190. cli/commands/pipelines/costs/daily_command.py +54 -0
  191. cli/commands/pipelines/costs/daily_presenter.py +59 -0
  192. cli/commands/pipelines/costs/presenter.py +37 -0
  193. cli/commands/pipelines/create/__init__.py +1 -0
  194. cli/commands/pipelines/create/command.py +103 -0
  195. cli/commands/pipelines/create/presenter.py +22 -0
  196. cli/commands/pipelines/env_vars/__init__.py +1 -0
  197. cli/commands/pipelines/env_vars/command.py +51 -0
  198. cli/commands/pipelines/env_vars/presenter.py +16 -0
  199. cli/commands/pipelines/execute/__init__.py +1 -0
  200. cli/commands/pipelines/execute/command.py +142 -0
  201. cli/commands/pipelines/execute/presenter.py +47 -0
  202. cli/commands/pipelines/executions/__init__.py +1 -0
  203. cli/commands/pipelines/executions/costs/__init__.py +1 -0
  204. cli/commands/pipelines/executions/costs/command.py +48 -0
  205. cli/commands/pipelines/executions/costs/presenter.py +29 -0
  206. cli/commands/pipelines/executions/costs_by_model/__init__.py +1 -0
  207. cli/commands/pipelines/executions/costs_by_model/command.py +50 -0
  208. cli/commands/pipelines/executions/costs_by_model/presenter.py +78 -0
  209. cli/commands/pipelines/executions/costs_by_step/__init__.py +1 -0
  210. cli/commands/pipelines/executions/costs_by_step/command.py +50 -0
  211. cli/commands/pipelines/executions/costs_by_step/presenter.py +72 -0
  212. cli/commands/pipelines/executions/get_command.py +38 -0
  213. cli/commands/pipelines/executions/list_command.py +123 -0
  214. cli/commands/pipelines/executions/presenter.py +131 -0
  215. cli/commands/pipelines/executions/rerun_command.py +41 -0
  216. cli/commands/pipelines/executions/update/__init__.py +1 -0
  217. cli/commands/pipelines/executions/update/command.py +110 -0
  218. cli/commands/pipelines/executions/update/presenter.py +28 -0
  219. cli/commands/pipelines/get/__init__.py +1 -0
  220. cli/commands/pipelines/get/command.py +33 -0
  221. cli/commands/pipelines/get/presenter.py +48 -0
  222. cli/commands/pipelines/list/__init__.py +1 -0
  223. cli/commands/pipelines/list/command.py +53 -0
  224. cli/commands/pipelines/list/presenter.py +66 -0
  225. cli/commands/pipelines/schedules/__init__.py +1 -0
  226. cli/commands/pipelines/schedules/create_command.py +119 -0
  227. cli/commands/pipelines/schedules/create_presenter.py +35 -0
  228. cli/commands/pipelines/schedules/delete_command.py +52 -0
  229. cli/commands/pipelines/schedules/env_vars_command.py +59 -0
  230. cli/commands/pipelines/schedules/env_vars_presenter.py +16 -0
  231. cli/commands/pipelines/schedules/get_command.py +38 -0
  232. cli/commands/pipelines/schedules/list_command.py +33 -0
  233. cli/commands/pipelines/schedules/once_command.py +90 -0
  234. cli/commands/pipelines/schedules/once_presenter.py +30 -0
  235. cli/commands/pipelines/schedules/presenter.py +104 -0
  236. cli/commands/pipelines/schedules/update_command.py +139 -0
  237. cli/commands/pipelines/schedules/update_presenter.py +29 -0
  238. cli/commands/render/__init__.py +0 -0
  239. cli/commands/render/html_to_image/__init__.py +0 -0
  240. cli/commands/render/html_to_image/command.py +170 -0
  241. cli/commands/schemas/__init__.py +0 -0
  242. cli/commands/schemas/create/__init__.py +0 -0
  243. cli/commands/schemas/create/command.py +122 -0
  244. cli/commands/schemas/create/presenter.py +53 -0
  245. cli/commands/schemas/delete/command.py +45 -0
  246. cli/commands/schemas/delete/presenter.py +9 -0
  247. cli/commands/schemas/get/__init__.py +0 -0
  248. cli/commands/schemas/get/command.py +56 -0
  249. cli/commands/schemas/get/presenter.py +129 -0
  250. cli/commands/schemas/list/__init__.py +0 -0
  251. cli/commands/schemas/list/command.py +64 -0
  252. cli/commands/schemas/list/presenter.py +133 -0
  253. cli/commands/schemas/update/__init__.py +0 -0
  254. cli/commands/schemas/update/command.py +369 -0
  255. cli/commands/schemas/update/presenter.py +53 -0
  256. cli/commands/sessions/__init__.py +1 -0
  257. cli/commands/sessions/delete/__init__.py +1 -0
  258. cli/commands/sessions/delete/command.py +47 -0
  259. cli/commands/sessions/delete/presenter.py +10 -0
  260. cli/commands/sessions/get/__init__.py +1 -0
  261. cli/commands/sessions/get/command.py +42 -0
  262. cli/commands/sessions/get/presenter.py +59 -0
  263. cli/commands/sessions/list/__init__.py +1 -0
  264. cli/commands/sessions/list/command.py +61 -0
  265. cli/commands/sessions/list/presenter.py +68 -0
  266. cli/commands/sessions/messages/__init__.py +1 -0
  267. cli/commands/sessions/messages/command.py +78 -0
  268. cli/commands/sessions/messages/presenter.py +79 -0
  269. cli/commands/shared_flags.py +500 -0
  270. cli/commands/sync/__init__.py +0 -0
  271. cli/commands/sync/command.py +45 -0
  272. cli/commands/sync/presenter.py +49 -0
  273. cli/commands/tags/__init__.py +1 -0
  274. cli/commands/tags/create/__init__.py +1 -0
  275. cli/commands/tags/create/command.py +60 -0
  276. cli/commands/tags/delete/__init__.py +1 -0
  277. cli/commands/tags/delete/command.py +47 -0
  278. cli/commands/tags/delete/presenter.py +10 -0
  279. cli/commands/tags/get/command.py +31 -0
  280. cli/commands/tags/get/presenter.py +23 -0
  281. cli/commands/tags/list/__init__.py +1 -0
  282. cli/commands/tags/list/command.py +52 -0
  283. cli/commands/tags/list/presenter.py +49 -0
  284. cli/commands/tags/update/command.py +64 -0
  285. cli/commands/tags/update/presenter.py +9 -0
  286. cli/commands/templates/__init__.py +0 -0
  287. cli/commands/templates/create/__init__.py +0 -0
  288. cli/commands/templates/create/command.py +152 -0
  289. cli/commands/templates/create/presenter.py +86 -0
  290. cli/commands/templates/delete/__init__.py +0 -0
  291. cli/commands/templates/delete/command.py +47 -0
  292. cli/commands/templates/delete/presenter.py +16 -0
  293. cli/commands/templates/get/__init__.py +0 -0
  294. cli/commands/templates/get/command.py +52 -0
  295. cli/commands/templates/get/presenter.py +233 -0
  296. cli/commands/templates/get_by_version/command.py +32 -0
  297. cli/commands/templates/get_by_version/presenter.py +30 -0
  298. cli/commands/templates/list/__init__.py +1 -0
  299. cli/commands/templates/list/command.py +102 -0
  300. cli/commands/templates/list/presenter.py +93 -0
  301. cli/commands/templates/render/__init__.py +0 -0
  302. cli/commands/templates/render/command.py +115 -0
  303. cli/commands/templates/render/presenter.py +276 -0
  304. cli/commands/templates/update/__init__.py +0 -0
  305. cli/commands/templates/update/command.py +199 -0
  306. cli/commands/templates/update/presenter.py +94 -0
  307. cli/commands/templates/version/__init__.py +1 -0
  308. cli/commands/templates/version/command.py +116 -0
  309. cli/commands/templates/version/presenter.py +100 -0
  310. cli/commands/tool_configs/__init__.py +0 -0
  311. cli/commands/tool_configs/create/__init__.py +0 -0
  312. cli/commands/tool_configs/create/command.py +118 -0
  313. cli/commands/tool_configs/create/presenter.py +53 -0
  314. cli/commands/tool_configs/delete/__init__.py +0 -0
  315. cli/commands/tool_configs/delete/command.py +47 -0
  316. cli/commands/tool_configs/delete/presenter.py +18 -0
  317. cli/commands/tool_configs/get/__init__.py +0 -0
  318. cli/commands/tool_configs/get/command.py +31 -0
  319. cli/commands/tool_configs/get/presenter.py +62 -0
  320. cli/commands/tool_configs/list/__init__.py +0 -0
  321. cli/commands/tool_configs/list/command.py +59 -0
  322. cli/commands/tool_configs/list/presenter.py +60 -0
  323. cli/commands/tool_configs/update/__init__.py +0 -0
  324. cli/commands/tool_configs/update/command.py +128 -0
  325. cli/commands/tool_configs/update/presenter.py +53 -0
  326. cli/commands/tools/__init__.py +1 -0
  327. cli/commands/tools/get/__init__.py +1 -0
  328. cli/commands/tools/get/command.py +42 -0
  329. cli/commands/tools/get/presenter.py +45 -0
  330. cli/commands/tools/list/__init__.py +1 -0
  331. cli/commands/tools/list/command.py +56 -0
  332. cli/commands/tools/list/presenter.py +44 -0
  333. cli/commands/users/__init__.py +0 -0
  334. cli/commands/users/create/command.py +53 -0
  335. cli/commands/users/create/presenter.py +9 -0
  336. cli/commands/whoami/__init__.py +0 -0
  337. cli/commands/whoami/command.py +42 -0
  338. cli/infrastructure/__init__.py +0 -0
  339. cli/infrastructure/auth_storage.py +71 -0
  340. cli/infrastructure/client_factory.py +36 -0
  341. cli/infrastructure/command.py +75 -0
  342. cli/infrastructure/config.py +188 -0
  343. cli/infrastructure/console.py +27 -0
  344. cli/infrastructure/editor.py +138 -0
  345. cli/infrastructure/error_display.py +178 -0
  346. cli/infrastructure/field_extractor.py +360 -0
  347. cli/infrastructure/file_content.py +210 -0
  348. cli/infrastructure/filter_parser.py +256 -0
  349. cli/infrastructure/formatters/__init__.py +0 -0
  350. cli/infrastructure/formatters/base.py +99 -0
  351. cli/infrastructure/formatters/compact_formatter.py +245 -0
  352. cli/infrastructure/formatters/json_formatter.py +84 -0
  353. cli/infrastructure/formatters/lines_formatter.py +102 -0
  354. cli/infrastructure/formatting/__init__.py +0 -0
  355. cli/infrastructure/formatting/fields.py +193 -0
  356. cli/infrastructure/forms/__init__.py +0 -0
  357. cli/infrastructure/forms/agent_picker.py +123 -0
  358. cli/infrastructure/forms/agent_tool_editor.py +384 -0
  359. cli/infrastructure/forms/agent_tools_manager.py +212 -0
  360. cli/infrastructure/forms/base_picker.py +469 -0
  361. cli/infrastructure/forms/components.py +126 -0
  362. cli/infrastructure/forms/json_schema_builder.py +149 -0
  363. cli/infrastructure/forms/model_picker.py +134 -0
  364. cli/infrastructure/forms/parsers.py +173 -0
  365. cli/infrastructure/forms/resolution_modal.py +302 -0
  366. cli/infrastructure/forms/schema_picker.py +137 -0
  367. cli/infrastructure/forms/tag_management_modal.py +103 -0
  368. cli/infrastructure/forms/tag_picker.py +207 -0
  369. cli/infrastructure/forms/template_picker.py +131 -0
  370. cli/infrastructure/forms/tool_config_picker.py +130 -0
  371. cli/infrastructure/forms/tool_picker.py +103 -0
  372. cli/infrastructure/injection/__init__.py +0 -0
  373. cli/infrastructure/injection/parser.py +302 -0
  374. cli/infrastructure/injection/resolver.py +399 -0
  375. cli/infrastructure/kv_parser.py +130 -0
  376. cli/infrastructure/local_storage.py +227 -0
  377. cli/infrastructure/macro_parser.py +215 -0
  378. cli/infrastructure/output.py +192 -0
  379. cli/infrastructure/provider_setup.py +81 -0
  380. cli/infrastructure/renderers/__init__.py +0 -0
  381. cli/infrastructure/renderers/entity_renderer.py +77 -0
  382. cli/infrastructure/renderers/list_renderer.py +114 -0
  383. cli/infrastructure/scope_utils.py +47 -0
  384. cli/infrastructure/spinner.py +101 -0
  385. cli/infrastructure/tui/__init__.py +0 -0
  386. cli/infrastructure/tui/clipboard.py +41 -0
  387. cli/infrastructure/tui/formatters.py +105 -0
  388. cli/infrastructure/tui/preview.py +14 -0
  389. cli/infrastructure/tui/selectable.py +198 -0
  390. cli/infrastructure/validation/__init__.py +0 -0
  391. cli/infrastructure/validation/tag_validation.py +74 -0
  392. cli/main.py +759 -0
  393. cli/tui/__init__.py +0 -0
  394. cli/tui/app.py +199 -0
  395. cli/tui/app_store.py +73 -0
  396. cli/tui/chat/__init__.py +0 -0
  397. cli/tui/chat/commands/__init__.py +0 -0
  398. cli/tui/chat/commands/base.py +65 -0
  399. cli/tui/chat/commands/create_session.py +135 -0
  400. cli/tui/chat/commands/load_session.py +119 -0
  401. cli/tui/chat/commands/regenerate.py +120 -0
  402. cli/tui/chat/commands/reload_session.py +63 -0
  403. cli/tui/chat/commands/send_message.py +190 -0
  404. cli/tui/chat/commands/undo.py +66 -0
  405. cli/tui/chat/editor.py +71 -0
  406. cli/tui/chat/messages.py +223 -0
  407. cli/tui/chat/pane.py +141 -0
  408. cli/tui/chat/renderers/__init__.py +0 -0
  409. cli/tui/chat/renderers/base.py +72 -0
  410. cli/tui/chat/renderers/markdown.py +250 -0
  411. cli/tui/chat/renderers/plain.py +83 -0
  412. cli/tui/chat/screen.py +1155 -0
  413. cli/tui/chat/services/__init__.py +0 -0
  414. cli/tui/chat/services/injection.py +386 -0
  415. cli/tui/chat/services/name_generator.py +256 -0
  416. cli/tui/chat/slash_commands.py +424 -0
  417. cli/tui/chat/store.py +280 -0
  418. cli/tui/chat/types.py +220 -0
  419. cli/tui/chat/widgets/__init__.py +0 -0
  420. cli/tui/chat/widgets/chat_header.py +75 -0
  421. cli/tui/chat/widgets/chat_input.py +362 -0
  422. cli/tui/chat/widgets/injection_popup.py +161 -0
  423. cli/tui/chat/widgets/message_display.py +287 -0
  424. cli/tui/chat/widgets/session_sidebar.py +214 -0
  425. cli/tui/chat/widgets/welcome_screen.py +290 -0
  426. cli/tui/screens/__init__.py +0 -0
  427. cli/tui/screens/agents.py +344 -0
  428. cli/tui/screens/base.py +301 -0
  429. cli/tui/screens/content.py +508 -0
  430. cli/tui/screens/dashboard.py +89 -0
  431. cli/tui/screens/models.py +96 -0
  432. cli/tui/screens/nav_screen.py +186 -0
  433. cli/tui/screens/schemas.py +522 -0
  434. cli/tui/screens/templates.py +734 -0
  435. cli/tui/screens/tool_configs.py +335 -0
  436. cli/tui/styles/__init__.py +0 -0
  437. cli/tui/widgets/__init__.py +0 -0
  438. cli/tui/widgets/agent_create_modal.py +139 -0
  439. cli/tui/widgets/agent_form_modal.py +659 -0
  440. cli/tui/widgets/agent_update_modal.py +299 -0
  441. cli/tui/widgets/base_form_modal.py +77 -0
  442. cli/tui/widgets/confirm_modal.py +75 -0
  443. cli/tui/widgets/help_modal.py +145 -0
  444. cli/tui/widgets/new_session_modal.py +328 -0
  445. cli/tui/widgets/schema_create_modal.py +271 -0
  446. cli/tui/widgets/schema_update_modal.py +188 -0
  447. cli/tui/widgets/status_footer.py +147 -0
  448. cli/tui/widgets/template_create_modal.py +502 -0
  449. cli/tui/widgets/template_update_modal.py +308 -0
  450. cli/tui/widgets/tool_config_create_modal.py +216 -0
  451. cli/tui/widgets/tool_config_update_modal.py +208 -0
@@ -0,0 +1,222 @@
1
+ """Knowledge document ingest command implementation."""
2
+
3
+ from pathlib import Path
4
+ from typing import Annotated, Optional
5
+ from uuid import UUID
6
+
7
+ import typer
8
+
9
+ from cli.commands.flag_utils import (
10
+ require_one_of,
11
+ validate_document_type,
12
+ validate_source_type,
13
+ )
14
+ from cli.commands.knowledge.documents.ingest.presenter import present_ingest_success
15
+ from cli.infrastructure.command import async_command, authenticated_client
16
+ from cli.infrastructure.file_content import FileContentError, resolve_content
17
+ from cli.infrastructure.kv_parser import parse_kv_pairs
18
+ from alloy_runtime_types.dtos.knowledge import IngestDocumentRequest
19
+
20
+
21
+ def knowledge_documents_ingest_command(
22
+ collection: UUID = typer.Option(
23
+ ...,
24
+ "-C",
25
+ "--collection",
26
+ help="Target collection UUID",
27
+ ),
28
+ title: str = typer.Option(
29
+ ...,
30
+ "-t",
31
+ "--title",
32
+ help="Document title",
33
+ ),
34
+ file: Optional[Path] = typer.Option(
35
+ None,
36
+ "-f",
37
+ "--file",
38
+ help="Path to document file",
39
+ exists=True,
40
+ readable=True,
41
+ ),
42
+ content: Optional[str] = typer.Option(
43
+ None,
44
+ "-c",
45
+ "--content",
46
+ help="Document content (or @file, or @- for stdin)",
47
+ ),
48
+ question_agent: Optional[UUID] = typer.Option(
49
+ None,
50
+ "-Q",
51
+ "--question-agent",
52
+ help="Agent UUID for question generation (required unless --skip-question-gen)",
53
+ ),
54
+ question_template: Optional[UUID] = typer.Option(
55
+ None,
56
+ "-T",
57
+ "--question-template",
58
+ help="Template UUID for question generation (required unless --skip-question-gen)",
59
+ ),
60
+ source_type: str = typer.Option(
61
+ "upload",
62
+ "-s",
63
+ "--source-type",
64
+ help="Source type: upload, paste, api_import, url_scrape",
65
+ ),
66
+ metadata: Annotated[
67
+ Optional[list[str]],
68
+ typer.Option(
69
+ "-m",
70
+ "--metadata",
71
+ help="Metadata (key=value or key:=json). Repeatable.",
72
+ ),
73
+ ] = None,
74
+ tag: Annotated[
75
+ Optional[list[str]],
76
+ typer.Option(
77
+ "--tag",
78
+ help="Hierarchical tag path (e.g., 'engineering.backend'). Repeatable.",
79
+ ),
80
+ ] = None,
81
+ document_type: str = typer.Option(
82
+ "document",
83
+ "-D",
84
+ "--document-type",
85
+ help="Document type for preprocessing: transcript, sales_call, markdown, document",
86
+ ),
87
+ external_id: Optional[str] = typer.Option(
88
+ None,
89
+ "-e",
90
+ "--external-id",
91
+ help="Caller-provided ID for idempotency and correlation (unique per org)",
92
+ ),
93
+ dry_run: bool = typer.Option(
94
+ False,
95
+ "--dry-run",
96
+ help="Only preprocess - don't persist. Useful for testing preprocessing.",
97
+ ),
98
+ skip_question_gen: bool = typer.Option(
99
+ False,
100
+ "--skip-question-gen",
101
+ help="Skip question generation during ingestion. Makes -Q and -T optional.",
102
+ ),
103
+ ) -> None:
104
+ """Ingest a document into a knowledge collection.
105
+
106
+ Document content can be provided via:
107
+ - --file: Read from a file path
108
+ - --content: Direct text, @file reference, or @- for stdin
109
+
110
+ Document types control preprocessing:
111
+ - transcript: Podcasts/videos/meetings with timestamps and natural speech
112
+ - sales_call: Sales/customer calls with speaker labels
113
+ - markdown: Markdown-formatted content
114
+ - document: General text documents (default)
115
+
116
+ Examples:
117
+ ai2 knowledge documents ingest -C <uuid> -t "API Docs" -f ./api.md -Q <agent> -T <template>
118
+ ai2 kb documents ingest -C <uuid> -t "Notes" -c "My content" -Q <agent> -T <template>
119
+ cat doc.txt | ai2 kb documents ingest -C <uuid> -t "Doc" -c @- -Q <agent> -T <template>
120
+ ai2 kb documents ingest -C <uuid> -t "Doc" -c @notes.md -Q <agent> -T <template> -m author=John
121
+ ai2 kb documents ingest -C <uuid> -t "Meeting" -f meeting.vtt -D transcript -Q <agent> -T <template>
122
+ ai2 kb documents ingest -C <uuid> -t "Test" -f doc.md --dry-run -Q <agent> -T <template>
123
+ """
124
+ require_one_of(("--file", file), ("--content", content))
125
+
126
+ _execute_ingest_from_flags(
127
+ collection=collection,
128
+ title=title,
129
+ file=file,
130
+ content=content,
131
+ question_agent=question_agent,
132
+ question_template=question_template,
133
+ source_type=source_type,
134
+ metadata=metadata,
135
+ tag=tag,
136
+ document_type=document_type,
137
+ external_id=external_id,
138
+ dry_run=dry_run,
139
+ skip_question_gen=skip_question_gen,
140
+ )
141
+
142
+
143
+ def _execute_ingest_from_flags(
144
+ collection: UUID,
145
+ title: str,
146
+ file: Optional[Path],
147
+ content: Optional[str],
148
+ question_agent: Optional[UUID],
149
+ question_template: Optional[UUID],
150
+ source_type: str,
151
+ metadata: Optional[list[str]],
152
+ tag: Optional[list[str]],
153
+ document_type: str,
154
+ external_id: Optional[str],
155
+ dry_run: bool,
156
+ skip_question_gen: bool,
157
+ ) -> None:
158
+ """Build and execute document ingestion from command flags."""
159
+ # Validate source type
160
+ validated_source_type = validate_source_type(source_type)
161
+
162
+ # Validate document type
163
+ validated_document_type = validate_document_type(document_type)
164
+
165
+ # Resolve content from file or inline
166
+ if file:
167
+ if not file.is_file():
168
+ raise typer.BadParameter(f"Not a file: {file}")
169
+ try:
170
+ document_content = file.read_text(encoding="utf-8")
171
+ except UnicodeDecodeError:
172
+ raise typer.BadParameter(
173
+ f"File is not valid UTF-8 text: {file}. Only text files are supported."
174
+ )
175
+ else:
176
+ try:
177
+ document_content = resolve_content(content) or ""
178
+ except FileContentError as e:
179
+ raise typer.BadParameter(str(e))
180
+
181
+ if not document_content.strip():
182
+ raise typer.BadParameter("Document content cannot be empty")
183
+
184
+ # Validate question generation requirements
185
+ if not skip_question_gen:
186
+ if question_agent is None:
187
+ raise typer.BadParameter(
188
+ "--question-agent (-Q) is required unless --skip-question-gen is set"
189
+ )
190
+ if question_template is None:
191
+ raise typer.BadParameter(
192
+ "--question-template (-T) is required unless --skip-question-gen is set"
193
+ )
194
+
195
+ # Parse metadata (httpie style)
196
+ parsed_metadata = parse_kv_pairs(metadata) if metadata else None
197
+
198
+ request = IngestDocumentRequest(
199
+ collection_id=collection,
200
+ title=title,
201
+ content=document_content,
202
+ question_gen_agent_id=question_agent,
203
+ question_gen_template_id=question_template,
204
+ source_type=validated_source_type,
205
+ document_type=validated_document_type, # type: ignore[arg-type]
206
+ metadata=parsed_metadata,
207
+ tags=tag,
208
+ external_id=external_id,
209
+ dry_run=dry_run,
210
+ skip_question_generation=skip_question_gen,
211
+ )
212
+
213
+ _execute_ingest(request)
214
+
215
+
216
+ @async_command
217
+ async def _execute_ingest(request: IngestDocumentRequest) -> None:
218
+ """Execute the document ingestion with the given request."""
219
+ async with authenticated_client() as (_config, client):
220
+ response = await client.ingest_document(request)
221
+
222
+ present_ingest_success(response)
@@ -0,0 +1,41 @@
1
+ """Presenter for document ingest output."""
2
+
3
+ from rich.panel import Panel
4
+ from rich.table import Table
5
+
6
+ from cli.infrastructure.formatting.fields import format_uuid
7
+ from cli.infrastructure.output import OutputService
8
+ from alloy_runtime_types.dtos.knowledge import IngestDocumentResponse
9
+
10
+
11
+ def present_ingest_success(response: IngestDocumentResponse) -> None:
12
+ """Present successful document ingestion (queued for processing).
13
+
14
+ Args:
15
+ response: Document ingestion response from server
16
+ """
17
+ output = OutputService.get()
18
+
19
+ if response.document_id is None:
20
+ output.info("Dry run complete - document not persisted")
21
+ else:
22
+ output.info("Document queued for processing")
23
+
24
+ table = Table(show_header=False, box=None, padding=(0, 1))
25
+ table.add_column("Field", style="dim")
26
+ table.add_column("Value")
27
+
28
+ if response.document_id is not None:
29
+ table.add_row("Document ID", format_uuid(response.document_id, short=False))
30
+ table.add_row("Title", response.title)
31
+ table.add_row("Status", f"[yellow]{response.status}[/yellow]")
32
+
33
+ if response.document_id is not None:
34
+ table.add_row("", "")
35
+ table.add_row(
36
+ "[dim]Next Step[/dim]",
37
+ "[dim]Poll with: ai2 knowledge documents get <id>[/dim]",
38
+ )
39
+
40
+ panel = Panel(table, title=f"Document: {response.title}", border_style="blue")
41
+ output.console.print(panel)
File without changes
@@ -0,0 +1,69 @@
1
+ """Knowledge documents list command implementation."""
2
+
3
+ import typer
4
+
5
+ from cli.commands.knowledge.documents.list.presenter import present_documents_list
6
+ from cli.infrastructure.command import async_command, authenticated_client
7
+
8
+
9
+ def knowledge_documents_list_command(
10
+ collection: str | None = typer.Option(
11
+ None,
12
+ "-C",
13
+ "--collection",
14
+ help="Filter by collection ID or name",
15
+ ),
16
+ status: str | None = typer.Option(
17
+ None,
18
+ "-s",
19
+ "--status",
20
+ help="Filter by status: pending|processing|completed|failed",
21
+ ),
22
+ limit: int = typer.Option(
23
+ 50,
24
+ "-l",
25
+ "--limit",
26
+ min=1,
27
+ max=100,
28
+ help="Max results",
29
+ ),
30
+ offset: int = typer.Option(
31
+ 0,
32
+ "--offset",
33
+ min=0,
34
+ help="Skip N results",
35
+ ),
36
+ ) -> None:
37
+ """List documents in the knowledge base.
38
+
39
+ Examples:
40
+ ai2 knowledge documents list
41
+ ai2 kb documents list -C my-collection
42
+ ai2 kb documents list -s completed -l 20
43
+ ai2 kb documents list --status failed
44
+ """
45
+ _execute_list(
46
+ collection_id=collection,
47
+ processing_status=status,
48
+ limit=limit,
49
+ offset=offset,
50
+ )
51
+
52
+
53
+ @async_command
54
+ async def _execute_list(
55
+ collection_id: str | None,
56
+ processing_status: str | None,
57
+ limit: int,
58
+ offset: int,
59
+ ) -> None:
60
+ """Execute list documents operation."""
61
+ async with authenticated_client() as (_config, client):
62
+ response = await client.list_documents(
63
+ limit=limit,
64
+ offset=offset,
65
+ collection_id=collection_id,
66
+ processing_status=processing_status,
67
+ )
68
+
69
+ present_documents_list(response)
@@ -0,0 +1,86 @@
1
+ """Presenter for knowledge documents list command output."""
2
+
3
+ from alloy_runtime_types.dtos.knowledge import ListDocumentsResponse
4
+
5
+ from cli.infrastructure.output import OutputService
6
+ from cli.infrastructure.renderers.list_renderer import ColumnConfig
7
+
8
+
9
+ def _format_status(status: str) -> str:
10
+ """Format processing status with color."""
11
+ status_colors = {
12
+ "completed": "[green]completed[/]",
13
+ "processing": "[yellow]processing[/]",
14
+ "pending": "[blue]pending[/]",
15
+ "failed": "[red]failed[/]",
16
+ }
17
+ return status_colors.get(status, status)
18
+
19
+
20
+ def present_documents_list(response: ListDocumentsResponse) -> None:
21
+ """Present list of documents with Rich table formatting.
22
+
23
+ Args:
24
+ response: List documents response from server
25
+ """
26
+ output = OutputService.get()
27
+
28
+ if not response.documents:
29
+ output.info("No documents found")
30
+ return
31
+
32
+ columns = [
33
+ ColumnConfig(
34
+ source_field="id",
35
+ header_label="ID",
36
+ compact_line=1,
37
+ compact_style="cyan",
38
+ ),
39
+ ColumnConfig(
40
+ source_field="processing_status",
41
+ header_label="Status",
42
+ formatter=_format_status,
43
+ compact_line=1,
44
+ ),
45
+ ColumnConfig(
46
+ source_field="chunks_count",
47
+ header_label="Chunks",
48
+ align="right",
49
+ compact_line=1,
50
+ compact_style="blue",
51
+ ),
52
+ ColumnConfig(
53
+ source_field="questions_generated",
54
+ header_label="Questions",
55
+ align="right",
56
+ compact_line=1,
57
+ compact_style="magenta",
58
+ ),
59
+ ColumnConfig(
60
+ source_field="updated_at",
61
+ header_label="Updated",
62
+ formatter=lambda x: x.strftime("%Y-%m-%d") if x else "-",
63
+ compact_line=1,
64
+ compact_style="dim",
65
+ ),
66
+ ColumnConfig(
67
+ source_field="title",
68
+ header_label="Title",
69
+ formatter=lambda x: x[:60] + "..." if x and len(x) > 60 else (x or "-"),
70
+ compact_line=2,
71
+ compact_style="white bold",
72
+ ),
73
+ ColumnConfig(
74
+ source_field="collection_id",
75
+ header_label="Collection",
76
+ formatter=lambda x: str(x)[:8] + "..." if x else "-",
77
+ compact_line=2,
78
+ compact_style="dim",
79
+ ),
80
+ ]
81
+
82
+ output.table(
83
+ items=response.documents, # type: ignore[arg-type]
84
+ title=f"Documents ({response.total})",
85
+ columns=columns,
86
+ )
File without changes
@@ -0,0 +1,102 @@
1
+ """Knowledge documents reingest command implementation."""
2
+
3
+ from typing import Optional
4
+ from uuid import UUID
5
+
6
+ import typer
7
+
8
+ from cli.commands.flag_utils import validate_uuid
9
+ from cli.commands.knowledge.documents.reingest.presenter import (
10
+ present_reingest_result,
11
+ )
12
+ from cli.infrastructure.command import async_command, authenticated_client
13
+ from alloy_runtime_types.dtos.knowledge import ReingestDocumentRequest
14
+
15
+
16
+ def knowledge_documents_reingest_command(
17
+ document_id: str = typer.Argument(
18
+ ...,
19
+ help="Document UUID",
20
+ ),
21
+ question_gen_agent: Optional[str] = typer.Option(
22
+ None,
23
+ "--question-gen-agent",
24
+ help="Agent UUID for question generation",
25
+ ),
26
+ question_gen_template: Optional[str] = typer.Option(
27
+ None,
28
+ "--question-gen-template",
29
+ help="Template UUID for question generation",
30
+ ),
31
+ document_type: Optional[str] = typer.Option(
32
+ None,
33
+ "--document-type",
34
+ help="Document type for preprocessing: transcript, markdown, document",
35
+ ),
36
+ dry_run: bool = typer.Option(
37
+ False,
38
+ "--dry-run",
39
+ help="Preview changes without committing",
40
+ ),
41
+ ) -> None:
42
+ """Reingest a document with new processing parameters.
43
+
44
+ Re-processes an existing document, optionally with updated question
45
+ generation agents, templates, or document type. The document's
46
+ original content is preserved; only derived data is regenerated.
47
+
48
+ Use --dry-run to preview preprocessing changes without committing.
49
+
50
+ Examples:
51
+ ai2 knowledge documents reingest <uuid>
52
+ ai2 kb documents reingest <uuid> --document-type transcript
53
+ ai2 kb documents reingest <uuid> --question-gen-agent <agent-uuid> --dry-run
54
+ """
55
+ _execute_reingest(
56
+ document_id=document_id,
57
+ question_gen_agent=question_gen_agent,
58
+ question_gen_template=question_gen_template,
59
+ document_type=document_type,
60
+ dry_run=dry_run,
61
+ )
62
+
63
+
64
+ @async_command
65
+ async def _execute_reingest(
66
+ document_id: str,
67
+ question_gen_agent: Optional[str],
68
+ question_gen_template: Optional[str],
69
+ document_type: Optional[str],
70
+ dry_run: bool,
71
+ ) -> None:
72
+ """Execute document reingest operation."""
73
+
74
+ agent_id: UUID | None = None
75
+ if question_gen_agent is not None:
76
+ agent_id = validate_uuid(question_gen_agent, "question-gen-agent")
77
+
78
+ template_id: UUID | None = None
79
+ if question_gen_template is not None:
80
+ template_id = validate_uuid(question_gen_template, "question-gen-template")
81
+
82
+ valid_types = ("transcript", "markdown", "document")
83
+ if document_type is not None and document_type not in valid_types:
84
+ raise typer.BadParameter(
85
+ f"Invalid document type '{document_type}'. "
86
+ f"Must be one of: {', '.join(valid_types)}"
87
+ )
88
+
89
+ request = ReingestDocumentRequest(
90
+ question_gen_agent_id=agent_id,
91
+ question_gen_template_id=template_id,
92
+ document_type=document_type, # type: ignore[arg-type]
93
+ dry_run=dry_run,
94
+ )
95
+
96
+ async with authenticated_client() as (_config, client):
97
+ response = await client.reingest_document(
98
+ document_id=document_id,
99
+ request=request,
100
+ )
101
+
102
+ present_reingest_result(response)
@@ -0,0 +1,70 @@
1
+ """Presenter for knowledge document reingest command output."""
2
+
3
+ from rich.panel import Panel
4
+ from rich.table import Table
5
+
6
+ from cli.infrastructure.formatting.fields import format_uuid
7
+ from cli.infrastructure.output import OutputService
8
+ from alloy_runtime_types.dtos.knowledge import ReingestDocumentResponse
9
+
10
+
11
+ def _format_reingest_status(status: str) -> str:
12
+ """Format reingest status with color."""
13
+ status_colors = {
14
+ "pending": "[yellow]pending[/]",
15
+ "dry_run_complete": "[blue]dry_run_complete[/]",
16
+ }
17
+ return status_colors.get(status, status)
18
+
19
+
20
+ def present_reingest_result(response: ReingestDocumentResponse) -> None:
21
+ """Present document reingest result.
22
+
23
+ Args:
24
+ response: ReingestDocumentResponse from server
25
+ """
26
+ output = OutputService.get()
27
+
28
+ if response.dry_run:
29
+ output.info("Dry run — no changes committed")
30
+ else:
31
+ output.success("Document queued for reingestion")
32
+
33
+ table = Table(show_header=False, box=None, padding=(0, 1))
34
+ table.add_column("Field", style="dim")
35
+ table.add_column("Value")
36
+
37
+ table.add_row("Document ID", format_uuid(response.document_id, short=False))
38
+ table.add_row("Status", _format_reingest_status(response.status))
39
+ table.add_row("Message", response.message)
40
+ table.add_row("Document Type", response.document_type)
41
+
42
+ if response.question_gen_agent_id:
43
+ table.add_row(
44
+ "Question Gen Agent",
45
+ format_uuid(response.question_gen_agent_id, short=False),
46
+ )
47
+ if response.question_gen_template_id:
48
+ table.add_row(
49
+ "Question Gen Template",
50
+ format_uuid(response.question_gen_template_id, short=False),
51
+ )
52
+
53
+ if response.dry_run and response.diff_stats:
54
+ table.add_row("Diff Stats", str(response.diff_stats))
55
+
56
+ if response.rejected:
57
+ table.add_row("Rejected", f"[red]{response.rejection_reason}[/]")
58
+
59
+ border_style = "blue" if response.dry_run else "green"
60
+ title = "Reingest Dry Run" if response.dry_run else "Document Reingested"
61
+ panel = Panel(table, title=title, border_style=border_style)
62
+ output.console.print(panel)
63
+
64
+ if response.dry_run and response.diff_unified:
65
+ diff_panel = Panel(
66
+ response.diff_unified,
67
+ title="Preprocessing Diff",
68
+ border_style="cyan",
69
+ )
70
+ output.console.print(diff_panel)
File without changes
@@ -0,0 +1,85 @@
1
+ """Knowledge documents update metadata command implementation."""
2
+
3
+ import json
4
+ from typing import Any, cast
5
+
6
+ import typer
7
+
8
+ from cli.commands.knowledge.documents.update.presenter import (
9
+ present_update_metadata_result,
10
+ )
11
+ from cli.infrastructure.command import async_command, authenticated_client
12
+ from cli.infrastructure.file_content import resolve_content_or_raise
13
+ from alloy_runtime_types.dtos.knowledge import UpdateDocumentMetadataRequest
14
+
15
+
16
+ def knowledge_documents_update_command(
17
+ document_id: str = typer.Argument(
18
+ ...,
19
+ help="Document UUID",
20
+ ),
21
+ metadata: str = typer.Option(
22
+ ...,
23
+ "--metadata",
24
+ help="Metadata JSON object (or @file.json)",
25
+ ),
26
+ metadata_mode: str = typer.Option(
27
+ "merge",
28
+ "--metadata-mode",
29
+ help="Metadata update mode: 'merge' (default) or 'replace'",
30
+ ),
31
+ ) -> None:
32
+ """Update a document's metadata.
33
+
34
+ The --metadata flag accepts a JSON string or @file reference.
35
+ Use --metadata-mode to control whether keys are merged with
36
+ existing metadata (default) or replace it entirely.
37
+
38
+ Examples:
39
+ ai2 knowledge documents update <uuid> --metadata '{"key": "value"}'
40
+ ai2 kb documents update <uuid> --metadata @meta.json --metadata-mode replace
41
+ """
42
+ _execute_update(
43
+ document_id=document_id,
44
+ metadata_raw=metadata,
45
+ metadata_mode=metadata_mode,
46
+ )
47
+
48
+
49
+ @async_command
50
+ async def _execute_update(
51
+ document_id: str,
52
+ metadata_raw: str,
53
+ metadata_mode: str,
54
+ ) -> None:
55
+ """Execute document metadata update operation."""
56
+
57
+ if metadata_mode not in ("merge", "replace"):
58
+ raise typer.BadParameter(
59
+ f"Invalid metadata mode '{metadata_mode}'. Must be 'merge' or 'replace'."
60
+ )
61
+
62
+ resolved = resolve_content_or_raise(metadata_raw)
63
+ if resolved is None:
64
+ raise typer.BadParameter("--metadata is required")
65
+
66
+ try:
67
+ parsed = json.loads(resolved)
68
+ except json.JSONDecodeError as e:
69
+ raise typer.BadParameter(f"Invalid JSON for --metadata: {e}")
70
+ if not isinstance(parsed, dict):
71
+ raise typer.BadParameter("--metadata must be a JSON object")
72
+ metadata_dict = cast(dict[str, Any], parsed)
73
+
74
+ request = UpdateDocumentMetadataRequest(
75
+ metadata=metadata_dict,
76
+ metadata_mode=metadata_mode, # type: ignore[arg-type]
77
+ )
78
+
79
+ async with authenticated_client() as (_config, client):
80
+ response = await client.update_document_metadata(
81
+ document_id=document_id,
82
+ request=request,
83
+ )
84
+
85
+ present_update_metadata_result(response)