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,128 @@
1
+ """Tool config update command implementation."""
2
+
3
+ import json
4
+ from typing import Any, Optional, cast
5
+
6
+ import typer
7
+
8
+ from cli.commands.flag_utils import require_update_flags
9
+ from cli.commands.tool_configs.update.presenter import (
10
+ present_tool_config_update_success,
11
+ )
12
+ from cli.infrastructure.command import async_command, authenticated_client
13
+ from cli.infrastructure.file_content import FileContentError, resolve_content
14
+ from alloy_runtime_types.dtos.tool_configs import UpdateToolConfigRequest
15
+ from alloy_runtime_sdk.exceptions.errors import NotFoundError
16
+
17
+
18
+ def tool_configs_update_command(
19
+ identifier: str = typer.Argument(
20
+ ...,
21
+ help="Tool config UUID or slug",
22
+ ),
23
+ name: Optional[str] = typer.Option(
24
+ None,
25
+ "-n",
26
+ "--name",
27
+ help="New name (will regenerate slug)",
28
+ ),
29
+ config: Optional[str] = typer.Option(
30
+ None,
31
+ "-c",
32
+ "--config",
33
+ help="New configuration as JSON (or @file.json)",
34
+ ),
35
+ description: Optional[str] = typer.Option(
36
+ None,
37
+ "-d",
38
+ "--description",
39
+ help="New description",
40
+ ),
41
+ clear_description: bool = typer.Option(
42
+ False,
43
+ "--clear-description",
44
+ help="Clear the description",
45
+ ),
46
+ ) -> None:
47
+ """Update an existing tool config.
48
+
49
+ Only specified fields are updated; others are preserved.
50
+
51
+ Examples:
52
+ ai tool-configs update my-config -n "New Name"
53
+ ai tool-configs update my-config -c '{"top_k": 20}'
54
+ ai tool-configs update my-config -d "Updated description"
55
+ ai tc update my-config --clear-description
56
+ """
57
+ # Check at least one update flag provided
58
+ require_update_flags(
59
+ ("--name", name),
60
+ ("--config", config),
61
+ ("--description", description),
62
+ ("--clear-description", clear_description),
63
+ )
64
+
65
+ _execute_update(
66
+ identifier=identifier,
67
+ name=name,
68
+ config=config,
69
+ description=description,
70
+ clear_description=clear_description,
71
+ )
72
+
73
+
74
+ @async_command
75
+ async def _execute_update(
76
+ identifier: str,
77
+ name: Optional[str],
78
+ config: Optional[str],
79
+ description: Optional[str],
80
+ clear_description: bool,
81
+ ) -> None:
82
+ """Execute the tool config update operation."""
83
+ # Build update request dict
84
+ request_data: dict[str, Any] = {}
85
+
86
+ if name is not None:
87
+ request_data["name"] = name
88
+
89
+ if config is not None:
90
+ # Resolve @file reference for config
91
+ try:
92
+ config_content = resolve_content(config)
93
+ except FileContentError as e:
94
+ raise typer.BadParameter(str(e))
95
+
96
+ if not config_content:
97
+ raise typer.BadParameter("Config cannot be empty")
98
+
99
+ # Parse config as JSON
100
+ try:
101
+ config_dict_raw = json.loads(config_content)
102
+ except json.JSONDecodeError as e:
103
+ raise typer.BadParameter(f"Invalid JSON in config: {e}")
104
+
105
+ if not isinstance(config_dict_raw, dict):
106
+ raise typer.BadParameter("Config must be a JSON object")
107
+
108
+ request_data["config"] = cast(dict[str, Any], config_dict_raw)
109
+
110
+ if description is not None:
111
+ request_data["description"] = description
112
+ elif clear_description:
113
+ request_data["description"] = ""
114
+
115
+ request = UpdateToolConfigRequest(**request_data)
116
+
117
+ async with authenticated_client() as (_, client):
118
+ try:
119
+ # Try to get the tool config first to verify it exists
120
+ await client.get_tool_config(identifier)
121
+ except NotFoundError:
122
+ raise typer.BadParameter(
123
+ f"Tool config '{identifier}' not found. Use 'ai tool-configs list' to see available configs."
124
+ )
125
+
126
+ response = await client.update_tool_config(identifier, request)
127
+
128
+ present_tool_config_update_success(response)
@@ -0,0 +1,53 @@
1
+ """Presenter for tool config update command output."""
2
+
3
+ import json
4
+
5
+ from rich.panel import Panel
6
+ from rich.syntax import Syntax
7
+ from rich.table import Table
8
+
9
+ from cli.infrastructure.formatting.fields import (
10
+ format_optional,
11
+ format_uuid,
12
+ )
13
+ from cli.infrastructure.output import OutputService
14
+ from alloy_runtime_types.dtos.tool_configs import UpdateToolConfigResponse
15
+
16
+
17
+ def present_tool_config_update_success(response: UpdateToolConfigResponse) -> None:
18
+ """Present successful tool config update.
19
+
20
+ Args:
21
+ response: Tool config update response from server
22
+ """
23
+ output = OutputService.get()
24
+
25
+ output.success("Tool config updated successfully")
26
+
27
+ # Build a simple table for entity display
28
+ table = Table(show_header=False, box=None, padding=(0, 1))
29
+ table.add_column("Field", style="dim")
30
+ table.add_column("Value")
31
+
32
+ table.add_row("ID", format_uuid(response.id, short=False))
33
+ table.add_row("Slug", response.slug)
34
+ table.add_row("Name", response.name)
35
+ table.add_row("Tool ID", response.tool_id)
36
+ table.add_row("Description", format_optional(response.description))
37
+
38
+ # Format ownership
39
+ if response.user_id:
40
+ table.add_row("Scope", "User")
41
+ elif response.organization_id:
42
+ table.add_row("Scope", "Organization")
43
+ else:
44
+ table.add_row("Scope", "Global")
45
+
46
+ panel = Panel(table, title=f"Tool Config: {response.name}", border_style="green")
47
+ output.console.print(panel)
48
+
49
+ # Display config as syntax-highlighted JSON
50
+ config_json = json.dumps(response.config, indent=2)
51
+ syntax = Syntax(config_json, "json", theme="monokai", word_wrap=True)
52
+ config_panel = Panel(syntax, title="Configuration", border_style="blue")
53
+ output.console.print(config_panel)
@@ -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,42 @@
1
+ """Tools get command implementation."""
2
+
3
+ import typer
4
+
5
+ from cli.commands.tools.get.presenter import present_tool_details
6
+ from cli.infrastructure.command import async_command, authenticated_client
7
+
8
+
9
+ def tools_get_command(
10
+ tool_id: str = typer.Argument(
11
+ ...,
12
+ help="Tool identifier (e.g., 'rag_search', 'web_search')",
13
+ ),
14
+ ) -> None:
15
+ """Get details of a single tool.
16
+
17
+ Retrieves the full tool definition including the parameter_schema
18
+ which defines what configuration options the tool accepts.
19
+
20
+ Examples:
21
+ ai tools get rag_search
22
+ ai tools get web_search
23
+ """
24
+ _execute_get(tool_id=tool_id)
25
+
26
+
27
+ @async_command
28
+ async def _execute_get(tool_id: str) -> None:
29
+ """Execute get tool operation.
30
+
31
+ Args:
32
+ tool_id: Tool identifier string
33
+
34
+ Raises:
35
+ NotFoundError: If tool not found
36
+ AuthenticationError: If API key is invalid
37
+ ServerError: If server returns 5xx error
38
+ """
39
+ async with authenticated_client() as (_config, client):
40
+ response = await client.get_tool(tool_id)
41
+
42
+ present_tool_details(response)
@@ -0,0 +1,45 @@
1
+ """Presenter for tools get command output."""
2
+
3
+ import json
4
+
5
+ from typing import Any
6
+
7
+ from alloy_runtime_types.dtos.tools import ToolResponse
8
+
9
+ from cli.infrastructure.output import OutputService
10
+ from cli.infrastructure.renderers.entity_renderer import FieldConfig
11
+
12
+
13
+ def _format_parameter_schema(schema: dict[str, Any] | None) -> str:
14
+ """Format parameter_schema as pretty-printed JSON.
15
+
16
+ Args:
17
+ schema: Parameter schema dict or None
18
+
19
+ Returns:
20
+ Formatted JSON string, or '-' if no schema
21
+ """
22
+ if not schema:
23
+ return "None (no configurable parameters)"
24
+ return json.dumps(schema, indent=2, ensure_ascii=False)
25
+
26
+
27
+ def present_tool_details(response: ToolResponse) -> None:
28
+ """Present tool details including parameter schema.
29
+
30
+ Args:
31
+ response: ToolResponse from server
32
+ """
33
+ output = OutputService.get()
34
+
35
+ fields = [
36
+ FieldConfig("id", "ID"),
37
+ FieldConfig("description", "Description"),
38
+ FieldConfig(
39
+ "parameter_schema",
40
+ "Parameter Schema",
41
+ _format_parameter_schema,
42
+ ),
43
+ ]
44
+
45
+ output.entity(response, f"Tool: {response.id}", fields)
@@ -0,0 +1 @@
1
+ # Empty per project convention - import directly from submodules
@@ -0,0 +1,56 @@
1
+ """Tools list command implementation."""
2
+
3
+ from typing import Optional
4
+
5
+ import typer
6
+
7
+ from cli.commands.tools.list.presenter import present_tools_list
8
+ from cli.infrastructure.command import async_command, authenticated_client
9
+
10
+
11
+ def tools_list_command(
12
+ search: Optional[str] = typer.Option(
13
+ None,
14
+ "-s",
15
+ "--search",
16
+ help="Fuzzy search terms",
17
+ ),
18
+ limit: int = typer.Option(
19
+ 50,
20
+ "-l",
21
+ "--limit",
22
+ min=1,
23
+ max=100,
24
+ help="Max results",
25
+ ),
26
+ offset: int = typer.Option(
27
+ 0,
28
+ "--offset",
29
+ min=0,
30
+ help="Skip N results",
31
+ ),
32
+ ) -> None:
33
+ """List available tools in the registry.
34
+
35
+ Shows all tools that can be configured on agents, including
36
+ their identifiers and descriptions.
37
+
38
+ Examples:
39
+ ai tools list
40
+ ai tools list -s "search"
41
+ ai tools list -l 10 --offset 20
42
+ """
43
+ _execute_list(search=search, limit=limit, offset=offset)
44
+
45
+
46
+ @async_command
47
+ async def _execute_list(search: Optional[str], limit: int, offset: int) -> None:
48
+ """Execute list tools operation."""
49
+ async with authenticated_client() as (_config, client):
50
+ response = await client.list_tools(
51
+ search=search,
52
+ limit=limit,
53
+ offset=offset,
54
+ )
55
+
56
+ present_tools_list(response)
@@ -0,0 +1,44 @@
1
+ """Presenter for tools list command output."""
2
+
3
+ from alloy_runtime_types.dtos.tools import ListToolsResponse
4
+
5
+ from cli.infrastructure.output import OutputService
6
+ from cli.infrastructure.renderers.list_renderer import ColumnConfig
7
+
8
+
9
+ def present_tools_list(response: ListToolsResponse) -> None:
10
+ """Present list of tools with Rich table formatting."""
11
+ output = OutputService.get()
12
+
13
+ if not response.tools:
14
+ output.info("No tools found")
15
+ return
16
+
17
+ columns = [
18
+ ColumnConfig(
19
+ source_field="id",
20
+ header_label="ID",
21
+ compact_line=1,
22
+ compact_style="cyan",
23
+ ),
24
+ ColumnConfig(
25
+ source_field="description",
26
+ header_label="Description",
27
+ formatter=lambda x: x[:80] + "..." if x and len(x) > 80 else (x or "-"),
28
+ compact_line=2,
29
+ compact_style="dim",
30
+ ),
31
+ ColumnConfig(
32
+ source_field="parameter_schema",
33
+ header_label="Has Parameters",
34
+ formatter=lambda x: "Yes" if x else "No",
35
+ compact_line=1,
36
+ compact_style="green",
37
+ ),
38
+ ]
39
+
40
+ output.table(
41
+ items=response.tools, # type: ignore[arg-type]
42
+ title=f"Tools ({response.total})",
43
+ columns=columns,
44
+ )
File without changes
@@ -0,0 +1,53 @@
1
+ """Users create command implementation."""
2
+
3
+ from typing import Optional
4
+
5
+ import typer
6
+
7
+ from cli.commands.users.create.presenter import present_create_result
8
+ from cli.infrastructure.command import async_command, authenticated_client
9
+ from alloy_runtime_types.dtos.users import CreateUserRequest
10
+ from alloy_runtime_types.enums.member_role import MemberRole
11
+
12
+
13
+ def users_create_command(
14
+ email: str = typer.Option(
15
+ ...,
16
+ "-e",
17
+ "--email",
18
+ help="User email address",
19
+ ),
20
+ name: str = typer.Option(
21
+ ...,
22
+ "-n",
23
+ "--name",
24
+ help="User display name",
25
+ ),
26
+ role: Optional[str] = typer.Option(
27
+ None,
28
+ "-r",
29
+ "--role",
30
+ help="User role (member, admin, owner). Defaults to member",
31
+ ),
32
+ ) -> None:
33
+ """Create a new user.
34
+
35
+ Examples:
36
+ ai users create --email user@example.com --name "John Doe"
37
+ ai users create -e admin@example.com -n "Admin User" -r admin
38
+ """
39
+ role_enum = MemberRole(role.lower()) if role else MemberRole.MEMBER
40
+ _execute_create(email=email, name=name, role=role_enum)
41
+
42
+
43
+ @async_command
44
+ async def _execute_create(
45
+ email: str,
46
+ name: str,
47
+ role: MemberRole,
48
+ ) -> None:
49
+ request = CreateUserRequest(email=email, name=name, role_id=role)
50
+ async with authenticated_client() as (_config, client):
51
+ response = await client.create_user(request)
52
+
53
+ present_create_result(response)
@@ -0,0 +1,9 @@
1
+ """Presenter for users create command output."""
2
+
3
+ from cli.infrastructure.output import OutputService
4
+ from alloy_runtime_types.dtos.users import CreateUserResponse
5
+
6
+
7
+ def present_create_result(response: CreateUserResponse) -> None:
8
+ output = OutputService.get()
9
+ output.success(f"Created user: {response.email}")
File without changes
@@ -0,0 +1,42 @@
1
+ """Whoami command implementation."""
2
+
3
+ from cli.infrastructure.command import async_command
4
+ from cli.infrastructure.config import load_config
5
+ from cli.infrastructure.console import get_console
6
+ from alloy_runtime_sdk.api_client.client import ApiClient
7
+
8
+
9
+ def whoami_command() -> None:
10
+ """Show current authenticated user information.
11
+
12
+ Examples:
13
+
14
+ ai whoami
15
+ """
16
+ _execute_whoami()
17
+
18
+
19
+ @async_command
20
+ async def _execute_whoami() -> None:
21
+ """Execute the whoami operation."""
22
+ console = get_console()
23
+ config = load_config()
24
+
25
+ async with ApiClient(base_url=config.api_url, api_key=config.api_key) as client:
26
+ me = await client.get_me()
27
+
28
+ # Print user info
29
+ console.print(f"[cyan]Email:[/cyan] {me.email}")
30
+ console.print(f"[cyan]Name:[/cyan] {me.name}")
31
+ console.print(f"[cyan]User ID:[/cyan] {me.user_id}")
32
+
33
+ if me.organization_id:
34
+ console.print(
35
+ f"[cyan]Organization:[/cyan] {me.organization_name or me.organization_id}"
36
+ )
37
+ console.print(f"[cyan]Organization ID:[/cyan] {me.organization_id}")
38
+ else:
39
+ console.print("[dim]No organization[/dim]")
40
+
41
+ if me.is_system_admin:
42
+ console.print("[yellow]System Administrator[/yellow]")
File without changes
@@ -0,0 +1,71 @@
1
+ """CLI authentication storage for API keys and credentials.
2
+
3
+ Stores API key and URL in ~/.alloy-runtime/config file for persistent authentication.
4
+ """
5
+
6
+ import os
7
+ from pathlib import Path
8
+
9
+
10
+ CONFIG_DIR = Path.home() / ".alloy-runtime"
11
+ CONFIG_FILE = CONFIG_DIR / "config"
12
+
13
+
14
+ def save_credentials(api_url: str, api_key: str) -> None:
15
+ """Save API credentials to config file.
16
+
17
+ Creates ~/.alloy-runtime/config with API URL and key.
18
+
19
+ Args:
20
+ api_url: API server base URL (e.g., http://localhost:8000)
21
+ api_key: API authentication key
22
+
23
+ Example:
24
+ save_credentials("http://localhost:8000", "sk_live_abc123...")
25
+ """
26
+ CONFIG_DIR.mkdir(exist_ok=True, mode=0o700) # Secure directory permissions
27
+
28
+ with open(CONFIG_FILE, "w") as f:
29
+ f.write(f"ALLOY_RUNTIME_API_URL={api_url}\n")
30
+ f.write(f"ALLOY_RUNTIME_API_KEY={api_key}\n")
31
+
32
+ # Set secure file permissions (owner read/write only)
33
+ os.chmod(CONFIG_FILE, 0o600)
34
+
35
+
36
+ def load_credentials() -> tuple[str, str] | None:
37
+ """Load API credentials from config file.
38
+
39
+ Returns:
40
+ Tuple of (api_url, api_key) if config exists, None otherwise
41
+
42
+ Example:
43
+ creds = load_credentials()
44
+ if creds:
45
+ api_url, api_key = creds
46
+ """
47
+ if not CONFIG_FILE.exists():
48
+ return None
49
+
50
+ config: dict[str, str] = {}
51
+ with open(CONFIG_FILE, encoding="utf-8") as f:
52
+ for line in f:
53
+ line = line.strip()
54
+ if not line or "=" not in line:
55
+ continue
56
+ key, value = line.split("=", 1)
57
+ config[key] = value
58
+
59
+ if "ALLOY_RUNTIME_API_URL" in config and "ALLOY_RUNTIME_API_KEY" in config:
60
+ return config["ALLOY_RUNTIME_API_URL"], config["ALLOY_RUNTIME_API_KEY"]
61
+
62
+ return None
63
+
64
+
65
+ def clear_credentials() -> None:
66
+ """Remove stored credentials (logout).
67
+
68
+ Deletes the config file if it exists.
69
+ """
70
+ if CONFIG_FILE.exists():
71
+ CONFIG_FILE.unlink()
@@ -0,0 +1,36 @@
1
+ """Factory for creating configured ApiClient instances."""
2
+
3
+ from collections.abc import AsyncIterator
4
+ from contextlib import asynccontextmanager
5
+
6
+ from alloy_runtime_sdk.api_client.client import ApiClient
7
+
8
+ from cli.infrastructure.config import CLIConfig
9
+
10
+
11
+ @asynccontextmanager
12
+ async def create_client(config: CLIConfig) -> AsyncIterator[ApiClient]:
13
+ """Create a configured ApiClient instance.
14
+
15
+ This factory encapsulates the ApiClient creation and configuration,
16
+ ensuring proper async context management and resource cleanup.
17
+
18
+ Args:
19
+ config: Validated CLI configuration
20
+
21
+ Yields:
22
+ Configured ApiClient ready for API calls
23
+
24
+ Example:
25
+ ```python
26
+ config = load_config()
27
+ async with create_client(config) as client:
28
+ response = await client.update_system_credential(...)
29
+ ```
30
+ """
31
+ async with ApiClient(
32
+ base_url=config.api_url,
33
+ api_key=config.api_key,
34
+ timeout=config.timeout,
35
+ ) as client:
36
+ yield client
@@ -0,0 +1,75 @@
1
+ """Command infrastructure for consistent error handling and setup."""
2
+
3
+ import asyncio
4
+ from collections.abc import AsyncGenerator, Coroutine
5
+ from contextlib import asynccontextmanager
6
+ from functools import wraps
7
+ from typing import Any, Callable, ParamSpec
8
+
9
+ import typer
10
+
11
+ from cli.infrastructure.client_factory import create_client
12
+ from cli.infrastructure.config import CLIConfig, load_config
13
+ from cli.infrastructure.error_display import display_error
14
+ from alloy_runtime_sdk.api_client.client import ApiClient
15
+
16
+ P = ParamSpec("P")
17
+
18
+
19
+ def async_command(func: Callable[P, Coroutine[Any, Any, None]]) -> Callable[P, None]:
20
+ """Decorator for async CLI commands with standard error handling.
21
+
22
+ Usage:
23
+ @async_command
24
+ async def my_command(arg: str) -> None:
25
+ async with authenticated_client() as (config, client):
26
+ response = await client.some_method(arg)
27
+
28
+ The decorator handles:
29
+ - asyncio.run() wrapper
30
+ - Exception catching and display_error()
31
+ - typer.Exit(code=1) on failure
32
+
33
+ Args:
34
+ func: Async command function to wrap
35
+
36
+ Returns:
37
+ Wrapped sync function suitable for Typer command
38
+ """
39
+
40
+ @wraps(func)
41
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> None:
42
+ try:
43
+ asyncio.run(func(*args, **kwargs))
44
+ except (BlockingIOError, BrokenPipeError):
45
+ # Pipe closed or blocked - exit silently
46
+ # This happens when piping to programs like vim that don't immediately consume stdin
47
+ raise typer.Exit(code=0)
48
+ except Exception as e:
49
+ display_error(e)
50
+ raise typer.Exit(code=1)
51
+
52
+ return wrapper
53
+
54
+
55
+ @asynccontextmanager
56
+ async def authenticated_client() -> AsyncGenerator[tuple[CLIConfig, ApiClient], None]:
57
+ """Context manager for authenticated API operations.
58
+
59
+ Usage:
60
+ async with authenticated_client() as (config, client):
61
+ response = await client.some_method()
62
+
63
+ Handles:
64
+ - Configuration loading (raises ConfigurationError if missing)
65
+ - Client lifecycle management
66
+
67
+ Yields:
68
+ Tuple of (CLIConfig, ApiClient) for the operation
69
+
70
+ Raises:
71
+ ConfigurationError: If required configuration is missing
72
+ """
73
+ config = load_config()
74
+ async with create_client(config) as client:
75
+ yield config, client