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,68 @@
1
+ """Presenter for agent update output."""
2
+
3
+ from cli.commands.agents.update.fields import (
4
+ format_generation_params,
5
+ format_models,
6
+ format_tags,
7
+ format_tools,
8
+ )
9
+ from cli.infrastructure.formatting.fields import (
10
+ format_datetime,
11
+ format_optional,
12
+ format_uuid,
13
+ )
14
+ from cli.infrastructure.output import OutputService
15
+ from cli.infrastructure.renderers.entity_renderer import FieldConfig
16
+ from alloy_runtime_types.dtos.agents import UpdateAgentResponse
17
+
18
+
19
+ def present_agent_update_success(response: UpdateAgentResponse) -> None:
20
+ """Present successful agent update.
21
+
22
+ Args:
23
+ response: Agent update response from server
24
+ """
25
+ output = OutputService.get()
26
+ output.success("Agent updated successfully")
27
+
28
+ fields = [
29
+ FieldConfig("id", "ID", lambda v: format_uuid(v, short=False)),
30
+ FieldConfig("name", "Name"),
31
+ FieldConfig("description", "Description", format_optional),
32
+ FieldConfig("models", "Models", lambda _: format_models(response)),
33
+ FieldConfig("tools", "Tools", lambda _: format_tools(response)),
34
+ FieldConfig("tags", "Tags", lambda _: format_tags(response)),
35
+ FieldConfig(
36
+ "generation_params",
37
+ "Generation Params",
38
+ lambda _: format_generation_params(response),
39
+ ),
40
+ FieldConfig(
41
+ "system_instruction_version_id",
42
+ "System Prompt ID",
43
+ lambda v: format_optional(v, lambda x: format_uuid(x, short=False)),
44
+ ),
45
+ FieldConfig(
46
+ "input_schema_id",
47
+ "Input Schema ID",
48
+ lambda v: format_optional(v, lambda x: format_uuid(x, short=False)),
49
+ ),
50
+ FieldConfig(
51
+ "output_schema_id",
52
+ "Output Schema ID",
53
+ lambda v: format_optional(v, lambda x: format_uuid(x, short=False)),
54
+ ),
55
+ FieldConfig(
56
+ "organization_id",
57
+ "Organization ID",
58
+ lambda v: format_optional(v, lambda x: format_uuid(x, short=True)),
59
+ ),
60
+ FieldConfig(
61
+ "user_id",
62
+ "User ID",
63
+ lambda v: format_optional(v, lambda x: format_uuid(x, short=True)),
64
+ ),
65
+ FieldConfig("updated_at", "Updated At", format_datetime),
66
+ ]
67
+
68
+ output.entity(response, f"Agent: {response.name}", fields)
File without changes
File without changes
@@ -0,0 +1,144 @@
1
+ """Audio transcribe command implementation."""
2
+
3
+ import mimetypes
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ import typer
8
+
9
+ from cli.commands.audio.transcribe.presenter import present_transcription
10
+ from cli.infrastructure.command import async_command, authenticated_client
11
+
12
+ MAX_FILE_SIZE_BYTES = 25 * 1024 * 1024 # 25 MB
13
+
14
+
15
+ def resolve_audio_input(file: str) -> tuple[bytes, str, str]:
16
+ """Validate and read audio input from a file path or stdin.
17
+
18
+ Args:
19
+ file: File path to an audio file, or '-' for stdin.
20
+
21
+ Returns:
22
+ Tuple of (file_bytes, filename, content_type).
23
+
24
+ Raises:
25
+ typer.BadParameter: If the file is not found, not readable,
26
+ or exceeds the 25MB size limit.
27
+ """
28
+ if file == "-":
29
+ return _read_stdin()
30
+
31
+ return _read_file(file)
32
+
33
+
34
+ def _read_stdin() -> tuple[bytes, str, str]:
35
+ """Read audio bytes from stdin.
36
+
37
+ Returns:
38
+ Tuple of (file_bytes, filename, content_type).
39
+
40
+ Raises:
41
+ typer.BadParameter: If stdin data exceeds size limit.
42
+ """
43
+ file_bytes = sys.stdin.buffer.read()
44
+ if len(file_bytes) > MAX_FILE_SIZE_BYTES:
45
+ size_mb = len(file_bytes) / (1024 * 1024)
46
+ raise typer.BadParameter(
47
+ f"Stdin data ({size_mb:.1f} MB) exceeds the 25 MB upload limit."
48
+ )
49
+ return file_bytes, "audio.wav", "audio/wav"
50
+
51
+
52
+ def _read_file(file: str) -> tuple[bytes, str, str]:
53
+ """Read and validate an audio file from disk.
54
+
55
+ Args:
56
+ file: Path to the audio file.
57
+
58
+ Returns:
59
+ Tuple of (file_bytes, filename, content_type).
60
+
61
+ Raises:
62
+ typer.BadParameter: If the file is not found, not a regular file,
63
+ or exceeds the 25MB size limit.
64
+ """
65
+ path = Path(file).expanduser()
66
+
67
+ if not path.exists():
68
+ raise typer.BadParameter(f"File not found: {file}")
69
+
70
+ if not path.is_file():
71
+ raise typer.BadParameter(f"Not a file: {file}")
72
+
73
+ file_size = path.stat().st_size
74
+ if file_size > MAX_FILE_SIZE_BYTES:
75
+ size_mb = file_size / (1024 * 1024)
76
+ raise typer.BadParameter(
77
+ f"File ({size_mb:.1f} MB) exceeds the 25 MB upload limit."
78
+ )
79
+
80
+ file_bytes = path.read_bytes()
81
+ filename = path.name
82
+ content_type = _detect_content_type(path)
83
+
84
+ return file_bytes, filename, content_type
85
+
86
+
87
+ def _detect_content_type(path: Path) -> str:
88
+ """Detect the MIME content type from the file extension.
89
+
90
+ Args:
91
+ path: Path to the file.
92
+
93
+ Returns:
94
+ MIME type string. Defaults to 'audio/wav' if detection fails.
95
+ """
96
+ guessed, _ = mimetypes.guess_type(str(path))
97
+ if guessed and guessed.startswith("audio/"):
98
+ return guessed
99
+ return "audio/wav"
100
+
101
+
102
+ def audio_transcribe_command(
103
+ file: str = typer.Argument(
104
+ ...,
105
+ help="Path to audio file, or '-' for stdin.",
106
+ ),
107
+ ) -> None:
108
+ """Transcribe audio to text.
109
+
110
+ Accepts a local audio file or piped stdin. The file must be under 25 MB.
111
+ Content type is auto-detected from the file extension.
112
+
113
+ Examples:
114
+ ai audio transcribe recording.wav
115
+ ai audio transcribe ~/voice-memo.mp3
116
+ cat audio.wav | ai audio transcribe -
117
+ """
118
+ file_bytes, filename, content_type = resolve_audio_input(file)
119
+ _execute_transcribe(
120
+ file_bytes=file_bytes, filename=filename, content_type=content_type
121
+ )
122
+
123
+
124
+ @async_command
125
+ async def _execute_transcribe(
126
+ file_bytes: bytes,
127
+ filename: str,
128
+ content_type: str,
129
+ ) -> None:
130
+ """Execute the transcribe operation.
131
+
132
+ Args:
133
+ file_bytes: Raw audio bytes to upload.
134
+ filename: Name of the audio file.
135
+ content_type: MIME content type of the audio.
136
+ """
137
+ async with authenticated_client() as (_config, client):
138
+ response = await client.transcribe_audio(
139
+ file_bytes,
140
+ filename=filename,
141
+ content_type=content_type,
142
+ )
143
+
144
+ present_transcription(response)
@@ -0,0 +1,15 @@
1
+ """Presenter for audio transcribe command output."""
2
+
3
+ from alloy_runtime_types.dtos.audio import AudioTranscriptResponse
4
+
5
+
6
+ def present_transcription(response: AudioTranscriptResponse) -> None:
7
+ """Print transcribed text to stdout.
8
+
9
+ Output is piping-friendly: only the transcribed text goes to stdout
10
+ with no decoration or metadata.
11
+
12
+ Args:
13
+ response: Transcription response from the server.
14
+ """
15
+ print(response.text)
File without changes
File without changes
@@ -0,0 +1,80 @@
1
+ """Login command implementation."""
2
+
3
+ import typer
4
+
5
+ from cli.infrastructure.auth_storage import save_credentials
6
+ from cli.infrastructure.command import async_command
7
+ from cli.infrastructure.config import get_api_url
8
+ from cli.infrastructure.console import Confirm, Prompt, get_console
9
+ from cli.infrastructure.output import print_info, print_success
10
+ from cli.infrastructure.provider_setup import prompt_provider_credentials
11
+ from alloy_runtime_sdk.api_client.client import ApiClient
12
+ from alloy_runtime_types.dtos.auth import LoginRequest
13
+
14
+
15
+ def login_command(
16
+ email: str | None = typer.Option(None, "-e", "--email", help="Email address"),
17
+ password: str | None = typer.Option(
18
+ None, "--password", help="Password (avoid: exposes in shell history)"
19
+ ),
20
+ ) -> None:
21
+ """Login and save API key.
22
+
23
+ First-time users will be prompted to set up provider credentials.
24
+
25
+ Examples:
26
+
27
+ alloy login
28
+ alloy login -e user@example.com
29
+ """
30
+ _execute_login(email, password)
31
+
32
+
33
+ @async_command
34
+ async def _execute_login(
35
+ email: str | None,
36
+ password: str | None,
37
+ ) -> None:
38
+ """Execute the login operation.
39
+
40
+ Args:
41
+ email: Optional email (will prompt if not provided)
42
+ password: Optional password (will prompt if not provided)
43
+ """
44
+ console = get_console()
45
+ api_url = get_api_url()
46
+
47
+ # Prompt for credentials if not provided
48
+ if not email:
49
+ email = Prompt.ask("[cyan]Email[/cyan]")
50
+
51
+ if not password:
52
+ password = Prompt.ask("[cyan]Password[/cyan]", password=True)
53
+
54
+ console.print("\n[blue]Authenticating...[/blue]")
55
+
56
+ # Create unauthenticated client
57
+ async with ApiClient(base_url=api_url, api_key="") as client:
58
+ response = await client.login(LoginRequest(email=email, password=password))
59
+
60
+ # Save credentials
61
+ save_credentials(api_url, response.api_key)
62
+
63
+ print_success(f"Logged in as {response.name} ({response.email})")
64
+ print_info("API key saved to ~/.alloy-runtime/config")
65
+ print_info(f"Organization: {response.organization_id}")
66
+
67
+ # Prompt for provider credentials if new user
68
+ if response.is_new_user:
69
+ console.print("\n[yellow]Welcome! This is your first login.[/yellow]")
70
+ if Confirm.ask(
71
+ "Would you like to set up AI provider credentials now?", default=True
72
+ ):
73
+ await prompt_provider_credentials(api_url, response.api_key)
74
+ else:
75
+ console.print(
76
+ "\n[dim]You can set up provider credentials later with:[/dim]"
77
+ )
78
+ console.print(
79
+ "[dim] alloy credentials update <credential-id> --value <api-key>[/dim]"
80
+ )
File without changes
@@ -0,0 +1,115 @@
1
+ """Signup command implementation."""
2
+
3
+ import typer
4
+
5
+ from cli.infrastructure.auth_storage import save_credentials
6
+ from cli.infrastructure.command import async_command
7
+ from cli.infrastructure.config import get_api_url
8
+ from cli.infrastructure.console import Confirm, Prompt, get_console
9
+ from cli.infrastructure.output import print_info, print_success
10
+ from cli.infrastructure.provider_setup import prompt_provider_credentials
11
+ from alloy_runtime_sdk.api_client.client import ApiClient
12
+ from alloy_runtime_types.dtos.auth import SignupRequest
13
+
14
+
15
+ def signup_command(
16
+ email: str | None = typer.Option(None, "-e", "--email", help="Email address"),
17
+ password: str | None = typer.Option(
18
+ None, "--password", help="Password (avoid: exposes in shell history)"
19
+ ),
20
+ name: str | None = typer.Option(None, "-n", "--name", help="Display name"),
21
+ org_name: str | None = typer.Option(
22
+ None,
23
+ "--org",
24
+ help="Organization name",
25
+ ),
26
+ ) -> None:
27
+ """Create a new Alloy Runtime account.
28
+
29
+ You'll be prompted to set up provider credentials after signup.
30
+
31
+ Examples:
32
+
33
+ ai signup
34
+ ai signup -e user@example.com -n "John Doe" --org "My Company"
35
+ """
36
+ _execute_signup(email, password, name, org_name)
37
+
38
+
39
+ @async_command
40
+ async def _execute_signup(
41
+ email: str | None,
42
+ password: str | None,
43
+ name: str | None,
44
+ org_name: str | None,
45
+ ) -> None:
46
+ """Execute the signup operation.
47
+
48
+ Args:
49
+ email: Optional email (will prompt if not provided)
50
+ password: Optional password (will prompt if not provided)
51
+ name: Optional name (will prompt if not provided)
52
+ org_name: Optional organization name
53
+ """
54
+ console = get_console()
55
+ api_url = get_api_url()
56
+
57
+ # Prompt for required fields if not provided
58
+ if not email:
59
+ email = Prompt.ask("[cyan]Email address[/cyan]")
60
+
61
+ if not name:
62
+ name = Prompt.ask("[cyan]Your name[/cyan]")
63
+
64
+ if not password:
65
+ while True:
66
+ password = Prompt.ask("[cyan]Password[/cyan] (min 8 chars)", password=True)
67
+ confirm = Prompt.ask("[cyan]Confirm password[/cyan]", password=True)
68
+
69
+ if password != confirm:
70
+ console.print("[red]Passwords do not match. Please try again.[/red]\n")
71
+ continue
72
+
73
+ if password and len(password) < 8:
74
+ console.print("[red]Password must be at least 8 characters.[/red]\n")
75
+ continue
76
+
77
+ break
78
+
79
+ console.print("\n[blue]Creating your account...[/blue]")
80
+
81
+ # Create unauthenticated client
82
+ async with ApiClient(base_url=api_url, api_key="") as client:
83
+ response = await client.signup(
84
+ SignupRequest(
85
+ email=email,
86
+ password=password,
87
+ name=name,
88
+ organization_name=org_name,
89
+ )
90
+ )
91
+
92
+ # Save credentials
93
+ save_credentials(api_url, response.api_key)
94
+
95
+ console.print()
96
+ print_success(f"Account created for {response.name}")
97
+ print_info(f"Email: {response.email}")
98
+ print_info(f"Organization ID: {response.organization_id}")
99
+ print_info("API key saved to ~/.alloy-runtime/config")
100
+
101
+ # Prompt for provider setup
102
+ console.print(
103
+ "\n[bold yellow]Let's set up your AI provider credentials[/bold yellow]"
104
+ )
105
+ console.print(
106
+ "[dim]You'll need API keys from providers like OpenAI, Anthropic, or Google.[/dim]\n"
107
+ )
108
+
109
+ if Confirm.ask("Set up provider credentials now?", default=True):
110
+ await prompt_provider_credentials(api_url, response.api_key)
111
+ else:
112
+ console.print("\n[dim]You can add provider credentials later with:[/dim]")
113
+ console.print(
114
+ "[dim] ai credentials update <credential-id> --value <api-key>[/dim]"
115
+ )
@@ -0,0 +1 @@
1
+ # Empty per project convention - import directly from submodules
@@ -0,0 +1 @@
1
+ # Empty per project convention - import directly from submodules
@@ -0,0 +1 @@
1
+ # Empty per project convention - import directly from submodules
@@ -0,0 +1,57 @@
1
+ """Billing project costs by agent command implementation."""
2
+
3
+ from typing import Optional
4
+ from uuid import UUID
5
+
6
+ import typer
7
+
8
+ from cli.commands.billing.costs.by_agent.presenter import present_billing_costs_by_agent
9
+ from cli.commands.flag_utils import validate_uuid
10
+ from cli.infrastructure.command import async_command, authenticated_client
11
+
12
+
13
+ def billing_costs_by_agent_command(
14
+ project_id: str = typer.Argument(
15
+ ...,
16
+ help="Billing project UUID",
17
+ ),
18
+ start_date: Optional[str] = typer.Option(
19
+ None,
20
+ "--from",
21
+ help="Start date (ISO 8601, defaults to 30 days ago)",
22
+ ),
23
+ end_date: Optional[str] = typer.Option(
24
+ None,
25
+ "--to",
26
+ help="End date (ISO 8601, defaults to now)",
27
+ ),
28
+ ) -> None:
29
+ """Get cost breakdown by agent for a billing project.
30
+
31
+ Examples:
32
+ ai billing costs by-agent 123e4567-89ab...
33
+ ai billing costs by-agent 123e4567-89ab... --from 2026-01-01 --to 2026-01-31
34
+ """
35
+ project_uuid = validate_uuid(project_id, "billing project")
36
+ _execute_costs_by_agent(
37
+ project_id=project_uuid,
38
+ start_date=start_date,
39
+ end_date=end_date,
40
+ )
41
+
42
+
43
+ @async_command
44
+ async def _execute_costs_by_agent(
45
+ project_id: UUID,
46
+ start_date: Optional[str],
47
+ end_date: Optional[str],
48
+ ) -> None:
49
+ """Execute get billing project costs by agent operation."""
50
+ async with authenticated_client() as (_config, client):
51
+ response = await client.get_billing_project_costs_by_agent(
52
+ project_id,
53
+ start_date=start_date,
54
+ end_date=end_date,
55
+ )
56
+
57
+ present_billing_costs_by_agent(response)
@@ -0,0 +1,81 @@
1
+ """Presenter for billing project costs by agent command output."""
2
+
3
+ from decimal import Decimal
4
+
5
+ from alloy_runtime_types.dtos.billing import ProjectCostByAgent
6
+
7
+ from cli.infrastructure.output import OutputService
8
+ from cli.infrastructure.renderers.list_renderer import ColumnConfig
9
+
10
+
11
+ def _format_cost(cost: Decimal | None) -> str:
12
+ """Format cost for display."""
13
+ if cost is None:
14
+ return "-"
15
+ return f"${cost:.4f}"
16
+
17
+
18
+ def present_billing_costs_by_agent(entries: list[ProjectCostByAgent]) -> None:
19
+ """Present billing project cost breakdown by agent."""
20
+ output = OutputService.get()
21
+
22
+ if not entries:
23
+ output.info("No cost data by agent found")
24
+ return
25
+
26
+ columns = [
27
+ ColumnConfig(
28
+ source_field="agent_name",
29
+ header_label="Agent",
30
+ compact_line=1,
31
+ compact_style="green",
32
+ ),
33
+ ColumnConfig(
34
+ source_field="agent_id",
35
+ header_label="Agent ID",
36
+ formatter=lambda x: str(x) if x else "(transient)",
37
+ compact_line=2,
38
+ compact_style="dim",
39
+ ),
40
+ ColumnConfig(
41
+ source_field="execution_count",
42
+ header_label="Executions",
43
+ formatter=str,
44
+ compact_line=1,
45
+ compact_style="white",
46
+ ),
47
+ ColumnConfig(
48
+ source_field="total_tokens",
49
+ header_label="Total Tokens",
50
+ formatter=lambda x: f"{x:,}",
51
+ compact_line=2,
52
+ compact_style="dim",
53
+ ),
54
+ ColumnConfig(
55
+ source_field="llm_cost_usd",
56
+ header_label="LLM Cost",
57
+ formatter=_format_cost,
58
+ compact_line=2,
59
+ compact_style="dim",
60
+ ),
61
+ ColumnConfig(
62
+ source_field="rag_cost_usd",
63
+ header_label="RAG Cost",
64
+ formatter=_format_cost,
65
+ compact_line=2,
66
+ compact_style="dim",
67
+ ),
68
+ ColumnConfig(
69
+ source_field="total_cost_usd",
70
+ header_label="Total Cost",
71
+ formatter=_format_cost,
72
+ compact_line=1,
73
+ compact_style="green bold",
74
+ ),
75
+ ]
76
+
77
+ output.table(
78
+ items=entries, # type: ignore[arg-type]
79
+ title=f"Costs by Agent ({len(entries)} agents)",
80
+ columns=columns,
81
+ )
@@ -0,0 +1 @@
1
+ # Empty per project convention - import directly from submodules
@@ -0,0 +1,57 @@
1
+ """Billing project costs by model command implementation."""
2
+
3
+ from typing import Optional
4
+ from uuid import UUID
5
+
6
+ import typer
7
+
8
+ from cli.commands.billing.costs.by_model.presenter import present_billing_costs_by_model
9
+ from cli.commands.flag_utils import validate_uuid
10
+ from cli.infrastructure.command import async_command, authenticated_client
11
+
12
+
13
+ def billing_costs_by_model_command(
14
+ project_id: str = typer.Argument(
15
+ ...,
16
+ help="Billing project UUID",
17
+ ),
18
+ start_date: Optional[str] = typer.Option(
19
+ None,
20
+ "--from",
21
+ help="Start date (ISO 8601, defaults to 30 days ago)",
22
+ ),
23
+ end_date: Optional[str] = typer.Option(
24
+ None,
25
+ "--to",
26
+ help="End date (ISO 8601, defaults to now)",
27
+ ),
28
+ ) -> None:
29
+ """Get cost breakdown by model for a billing project.
30
+
31
+ Examples:
32
+ ai billing costs by-model 123e4567-89ab...
33
+ ai billing costs by-model 123e4567-89ab... --from 2026-01-01 --to 2026-01-31
34
+ """
35
+ project_uuid = validate_uuid(project_id, "billing project")
36
+ _execute_costs_by_model(
37
+ project_id=project_uuid,
38
+ start_date=start_date,
39
+ end_date=end_date,
40
+ )
41
+
42
+
43
+ @async_command
44
+ async def _execute_costs_by_model(
45
+ project_id: UUID,
46
+ start_date: Optional[str],
47
+ end_date: Optional[str],
48
+ ) -> None:
49
+ """Execute get billing project costs by model operation."""
50
+ async with authenticated_client() as (_config, client):
51
+ response = await client.get_billing_project_costs_by_model(
52
+ project_id,
53
+ start_date=start_date,
54
+ end_date=end_date,
55
+ )
56
+
57
+ present_billing_costs_by_model(response)