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,192 @@
1
+ """Unified output service for CLI presentation.
2
+
3
+ This module provides format-aware output that can switch between
4
+ Rich terminal output and JSON for scripting/piping.
5
+ """
6
+
7
+ from enum import Enum
8
+
9
+ from pydantic import BaseModel
10
+
11
+ from cli.infrastructure.console import get_console
12
+ from cli.infrastructure.formatters.base import Formatter
13
+ from cli.infrastructure.formatters.compact_formatter import CompactFormatter
14
+ from cli.infrastructure.formatters.json_formatter import JsonFormatter
15
+ from cli.infrastructure.formatters.lines_formatter import LinesFormatter
16
+ from cli.infrastructure.renderers.entity_renderer import FieldConfig
17
+ from cli.infrastructure.renderers.list_renderer import ColumnConfig
18
+
19
+
20
+ class OutputFormat(Enum):
21
+ """Supported output formats."""
22
+
23
+ COMPACT = "compact" # Default: 2-line blocks, copy-friendly, no borders
24
+ JSON = "json" # Machine-readable JSON
25
+ LINES = "lines" # Key-value lines format
26
+
27
+
28
+ class OutputService:
29
+ """Format-aware output service.
30
+
31
+ Usage:
32
+ output = OutputService.get() # Uses global format setting
33
+ output.success("Operation completed")
34
+ output.entity(response, "User Details", USER_FIELDS)
35
+ output.table(items, "Users", USER_COLUMNS)
36
+ """
37
+
38
+ _instance: "OutputService | None" = None
39
+ _format: OutputFormat = OutputFormat.COMPACT
40
+
41
+ def __init__(self, format: OutputFormat = OutputFormat.COMPACT):
42
+ """Initialize output service with specified format.
43
+
44
+ Args:
45
+ format: Output format (compact, json, or lines)
46
+ """
47
+ self.format = format
48
+ self._console = get_console()
49
+ self._formatter = self._create_formatter(format)
50
+
51
+ @classmethod
52
+ def configure(cls, format: OutputFormat) -> None:
53
+ """Configure global output format. Call once at app startup.
54
+
55
+ Args:
56
+ format: Output format to use globally
57
+ """
58
+ cls._format = format
59
+ cls._instance = None # Reset singleton
60
+
61
+ @classmethod
62
+ def get(cls) -> "OutputService":
63
+ """Get the configured output service instance.
64
+
65
+ Returns:
66
+ Singleton OutputService instance
67
+ """
68
+ if cls._instance is None:
69
+ cls._instance = cls(cls._format)
70
+ return cls._instance
71
+
72
+ def _create_formatter(self, format: OutputFormat) -> Formatter:
73
+ """Create formatter instance for the specified format.
74
+
75
+ Args:
76
+ format: Output format
77
+
78
+ Returns:
79
+ Formatter instance
80
+ """
81
+ if format == OutputFormat.JSON:
82
+ return JsonFormatter()
83
+ if format == OutputFormat.LINES:
84
+ return LinesFormatter()
85
+ # Default: COMPACT
86
+ return CompactFormatter(self._console)
87
+
88
+ # Message methods
89
+ def success(self, message: str) -> None:
90
+ """Print a success message.
91
+
92
+ Args:
93
+ message: Success message to display
94
+ """
95
+ output = self._formatter.format_success(message)
96
+ self._console.print(output)
97
+
98
+ def info(self, message: str) -> None:
99
+ """Print an informational message.
100
+
101
+ Args:
102
+ message: Info message to display
103
+ """
104
+ output = self._formatter.format_info(message)
105
+ self._console.print(output)
106
+
107
+ def warning(self, message: str) -> None:
108
+ """Print a warning message.
109
+
110
+ Args:
111
+ message: Warning message to display
112
+ """
113
+ output = self._formatter.format_warning(message)
114
+ self._console.print(output)
115
+
116
+ def status(self, message: str) -> None:
117
+ """Print a status message (dim, no icon).
118
+
119
+ Args:
120
+ message: Status message to display
121
+ """
122
+ self._console.print(f"[dim]{message}[/dim]")
123
+
124
+ # Entity rendering
125
+ def entity(
126
+ self, entity: BaseModel, title: str | None, fields: list[FieldConfig]
127
+ ) -> None:
128
+ """Render a single entity (Pydantic model).
129
+
130
+ Args:
131
+ entity: Pydantic model instance to render
132
+ title: Display title for the entity
133
+ fields: Field configurations defining what to show
134
+ """
135
+ from cli.infrastructure.renderers.entity_renderer import EntityRenderer
136
+
137
+ renderer = EntityRenderer(self._formatter)
138
+ renderer.render(entity, title, fields)
139
+
140
+ # List rendering
141
+ def table(
142
+ self, items: list[BaseModel], title: str | None, columns: list[ColumnConfig]
143
+ ) -> None:
144
+ """Render a list of entities as a table.
145
+
146
+ Args:
147
+ items: List of Pydantic model instances
148
+ title: Optional table title
149
+ columns: Column configurations
150
+ """
151
+ from cli.infrastructure.renderers.list_renderer import ListRenderer
152
+
153
+ renderer = ListRenderer(self._formatter)
154
+ renderer.render(items, columns, title)
155
+
156
+ # Raw console access for complex cases
157
+ @property
158
+ def console(self):
159
+ """Get the underlying console for complex rendering needs.
160
+
161
+ Returns:
162
+ Rich Console instance
163
+ """
164
+ return self._console
165
+
166
+
167
+ # Convenience functions for direct usage (uses global singleton)
168
+ def print_success(message: str) -> None:
169
+ """Print a success message with checkmark.
170
+
171
+ Args:
172
+ message: Success message to display
173
+ """
174
+ OutputService.get().success(message)
175
+
176
+
177
+ def print_info(message: str) -> None:
178
+ """Print an informational message.
179
+
180
+ Args:
181
+ message: Info message to display
182
+ """
183
+ OutputService.get().info(message)
184
+
185
+
186
+ def print_warning(message: str) -> None:
187
+ """Print a warning message.
188
+
189
+ Args:
190
+ message: Warning message to display
191
+ """
192
+ OutputService.get().warning(message)
@@ -0,0 +1,81 @@
1
+ """Provider credentials setup service.
2
+
3
+ Shared logic for prompting users to configure AI provider API keys.
4
+ Used by both login and signup flows.
5
+ """
6
+
7
+ from cli.infrastructure.console import Confirm, Prompt, get_console
8
+ from alloy_runtime_sdk.api_client.client import ApiClient
9
+ from alloy_runtime_types.dtos.organization_credentials import (
10
+ CreateOrganizationCredentialRequest,
11
+ )
12
+ from alloy_runtime_types.enums.provider import Provider
13
+
14
+ # Provider definitions: (display_name, Provider enum, env_hint, example_prefix)
15
+ PROVIDERS = [
16
+ ("X.Ai", Provider.XAI, "XAI_API_KEY", "..."),
17
+ ("Google", Provider.GOOGLE, "GOOGLE_API_KEY", "..."),
18
+ ("Anthropic", Provider.ANTHROPIC, "ANTHROPIC_API_KEY", "sk-ant-..."),
19
+ ("OpenAI", Provider.OPENAI, "OPENAI_API_KEY", "sk-..."),
20
+ ("OpenRouter", Provider.OPENROUTER, "OPENROUTER_API_KEY", "sk-or-..."),
21
+ ]
22
+
23
+
24
+ async def prompt_provider_credentials(api_url: str, api_key: str) -> int:
25
+ """Prompt user to configure provider API keys.
26
+
27
+ Args:
28
+ api_url: API server base URL
29
+ api_key: User's API key for authenticated requests
30
+
31
+ Returns:
32
+ Number of providers successfully configured
33
+ """
34
+ console = get_console()
35
+
36
+ console.print("\n[bold cyan]AI Provider Credentials Setup[/bold cyan]")
37
+ console.print(
38
+ "[dim]Configure API keys for AI providers to start using the platform.[/dim]\n"
39
+ )
40
+
41
+ configured_count = 0
42
+
43
+ async with ApiClient(base_url=api_url, api_key=api_key) as client:
44
+ for display_name, provider_enum, _env_hint, example_prefix in PROVIDERS:
45
+ if Confirm.ask(
46
+ f"Configure [cyan]{display_name}[/cyan] API key?", default=False
47
+ ):
48
+ api_key_value = Prompt.ask(
49
+ f"[cyan]{display_name} API Key[/cyan] (e.g., {example_prefix})",
50
+ password=True,
51
+ )
52
+
53
+ if api_key_value and api_key_value.strip():
54
+ try:
55
+ request = CreateOrganizationCredentialRequest(
56
+ credential_type_id="org_provider",
57
+ name=f"{display_name} API Key",
58
+ plaintext_value=api_key_value.strip(),
59
+ provider_key=provider_enum,
60
+ description=f"{display_name} API key configured during signup",
61
+ )
62
+ await client.create_organization_credential(request)
63
+ console.print(
64
+ f"[green]✓[/green] {display_name} credential saved!"
65
+ )
66
+ configured_count += 1
67
+ except Exception as e:
68
+ console.print(
69
+ f"[red]✗[/red] Failed to save {display_name} credential: {e}"
70
+ )
71
+
72
+ if configured_count > 0:
73
+ console.print(f"\n[green]Configured {configured_count} provider(s)![/green]")
74
+ console.print("[green]You're ready to use Alloy Runtime![/green]")
75
+ else:
76
+ console.print("\n[yellow]No providers configured.[/yellow]")
77
+ console.print(
78
+ "[dim]You can add them later with the credentials commands.[/dim]"
79
+ )
80
+
81
+ return configured_count
File without changes
@@ -0,0 +1,77 @@
1
+ """Entity renderer for displaying single entity details.
2
+
3
+ Renders single Pydantic model instances using configured field formatters.
4
+ """
5
+
6
+ from dataclasses import dataclass
7
+ from typing import Any, Callable
8
+
9
+ from pydantic import BaseModel
10
+
11
+ from cli.infrastructure.console import get_console
12
+ from cli.infrastructure.formatters.base import Formatter
13
+
14
+
15
+ @dataclass
16
+ class FieldConfig:
17
+ """Configuration for a single field to render.
18
+
19
+ Attributes:
20
+ source_field: Attribute name on the Pydantic model
21
+ display_label: Human-readable label for display
22
+ formatter: Optional function to format the field value
23
+ """
24
+
25
+ source_field: str
26
+ display_label: str
27
+ formatter: Callable[[Any], str] | None = None
28
+
29
+
30
+ class EntityRenderer:
31
+ """Renders single entity details using a formatter.
32
+
33
+ Extracts data from Pydantic models according to field configurations
34
+ and renders using the provided formatter.
35
+ """
36
+
37
+ def __init__(self, formatter: Formatter):
38
+ """Initialize renderer with a formatter.
39
+
40
+ Args:
41
+ formatter: Formatter instance (CompactFormatter, JsonFormatter, etc.)
42
+ """
43
+ self.formatter = formatter
44
+
45
+ def render(
46
+ self, entity: BaseModel, title: str | None, fields: list[FieldConfig]
47
+ ) -> None:
48
+ """Render entity details to console.
49
+
50
+ Args:
51
+ entity: Pydantic model instance to render
52
+ title: Display title for the entity (optional)
53
+ fields: List of field configurations defining what to show
54
+ """
55
+ # Extract data from entity using field configs
56
+ data: dict[str, Any] = {}
57
+ for field_config in fields:
58
+ # Safely extract field value using getattr
59
+ value = getattr(entity, field_config.source_field, None)
60
+
61
+ # Apply formatter if provided
62
+ if field_config.formatter:
63
+ value = field_config.formatter(value)
64
+ elif value is not None:
65
+ value = str(value)
66
+ # If value is None and no formatter, leave as None
67
+ # (formatter will handle it with "(not set)" placeholder)
68
+
69
+ data[field_config.display_label] = value
70
+
71
+ # Format using the configured formatter
72
+ output = self.formatter.format_entity(data, title)
73
+
74
+ # Print using shared console
75
+ # Output can be a Rich renderable (Table, Panel, etc.) or a string
76
+ console = get_console()
77
+ console.print(output)
@@ -0,0 +1,114 @@
1
+ """List renderer for displaying collections with pagination.
2
+
3
+ Renders lists of Pydantic model instances using configured column formatters.
4
+ """
5
+
6
+ from collections.abc import Sequence
7
+ from dataclasses import dataclass
8
+ from typing import Any, Callable
9
+
10
+ from pydantic import BaseModel
11
+
12
+ from cli.infrastructure.console import get_console
13
+ from cli.infrastructure.formatters.base import ColumnMeta, Formatter
14
+
15
+
16
+ @dataclass
17
+ class ColumnConfig:
18
+ """Configuration for a table column.
19
+
20
+ Attributes:
21
+ source_field: Attribute name on the Pydantic model
22
+ header_label: Column header text
23
+ formatter: Optional function to format the column value
24
+ width: Optional fixed column width
25
+ align: Text alignment - "left", "center", or "right"
26
+ compact_line: For compact format - 1 = primary line (metadata),
27
+ 2 = detail line (content/description). Default is 1.
28
+ compact_style: Rich style for compact format (e.g., "cyan bold", "dim")
29
+ """
30
+
31
+ source_field: str
32
+ header_label: str
33
+ formatter: Callable[[Any], str] | None = None
34
+ width: int | None = None
35
+ align: str = "left"
36
+ compact_line: int = 1
37
+ compact_style: str | None = None
38
+
39
+
40
+ class ListRenderer:
41
+ """Renders lists/collections with pagination support.
42
+
43
+ Extracts data from lists of Pydantic models according to column configurations
44
+ and renders using the provided formatter.
45
+ """
46
+
47
+ def __init__(self, formatter: Formatter):
48
+ """Initialize renderer with a formatter.
49
+
50
+ Args:
51
+ formatter: Formatter instance (CompactFormatter, JsonFormatter, etc.)
52
+ """
53
+ self.formatter = formatter
54
+
55
+ def render(
56
+ self,
57
+ items: Sequence[BaseModel],
58
+ columns: list[ColumnConfig],
59
+ title: str | None = None,
60
+ cursor: str | None = None,
61
+ total_count: int | None = None,
62
+ ) -> None:
63
+ """Render paginated list to console.
64
+
65
+ Args:
66
+ items: Sequence of Pydantic model instances to render
67
+ columns: Column configurations defining what to show
68
+ title: Optional table title
69
+ cursor: Pagination cursor for next page
70
+ total_count: Total items across all pages
71
+ """
72
+ # Extract data from items using column configs
73
+ rows: list[dict[str, Any]] = []
74
+ for item in items:
75
+ row: dict[str, Any] = {}
76
+ for col in columns:
77
+ # Safely extract column value using getattr
78
+ value = getattr(item, col.source_field, None)
79
+
80
+ # Apply formatter if provided
81
+ if col.formatter:
82
+ value = col.formatter(value)
83
+ elif value is not None:
84
+ value = str(value)
85
+ # If value is None and no formatter, leave as None
86
+ # (formatter will handle it with "(not set)" placeholder)
87
+
88
+ row[col.header_label] = value
89
+ rows.append(row)
90
+
91
+ # Build column metadata for formatters that need layout info
92
+ column_meta = [
93
+ ColumnMeta(
94
+ label=col.header_label,
95
+ compact_line=col.compact_line,
96
+ compact_style=col.compact_style,
97
+ width=col.width,
98
+ )
99
+ for col in columns
100
+ ]
101
+
102
+ # Format the list using the configured formatter
103
+ output = self.formatter.format_list(rows, title, column_meta)
104
+
105
+ # Print using shared console
106
+ # Output can be a Rich renderable (Table) or a string
107
+ console = get_console()
108
+ console.print(output)
109
+
110
+ # Show pagination info if available
111
+ if cursor:
112
+ console.print(f"[dim]Next page cursor: {cursor}[/dim]")
113
+ if total_count:
114
+ console.print(f"[dim]Showing {len(items)} of {total_count} total[/dim]")
@@ -0,0 +1,47 @@
1
+ """Common formatting utilities for CLI display."""
2
+
3
+ from alloy_runtime_types.enums.ownership_scope import OwnershipScope
4
+ from textual.widgets import RadioSet
5
+
6
+
7
+ def format_scope_label(
8
+ organization_id: str | None,
9
+ user_id: str | None,
10
+ ) -> str:
11
+ """Format ownership scope as human-readable label.
12
+
13
+ Args:
14
+ organization_id: Organization UUID if org-owned
15
+ user_id: User UUID if user-owned
16
+
17
+ Returns:
18
+ Scope string: "User", "Org", or "Global"
19
+ """
20
+ if user_id:
21
+ return "User"
22
+ elif organization_id:
23
+ return "Org"
24
+ return "Global"
25
+
26
+
27
+ def get_scope_from_radioset(
28
+ radioset: RadioSet,
29
+ user_id: str = "scope-user",
30
+ org_id: str = "scope-org",
31
+ ) -> OwnershipScope:
32
+ """Extract OwnershipScope from a RadioSet widget.
33
+
34
+ Args:
35
+ radioset: The RadioSet containing scope radio buttons
36
+ user_id: ID of the user scope radio button
37
+ org_id: ID of the org scope radio button
38
+
39
+ Returns:
40
+ OwnershipScope enum value
41
+ """
42
+ pressed = radioset.pressed_button
43
+ if pressed is None or pressed.id == user_id:
44
+ return OwnershipScope.USER
45
+ elif pressed.id == org_id:
46
+ return OwnershipScope.ORGANIZATION
47
+ return OwnershipScope.GLOBAL
@@ -0,0 +1,101 @@
1
+ """Reusable loading spinner component for async operations.
2
+
3
+ Provides a context manager for displaying a spinner while waiting for
4
+ server responses or other async operations.
5
+ """
6
+
7
+ from collections.abc import AsyncIterator
8
+ from contextlib import asynccontextmanager
9
+ from types import TracebackType
10
+
11
+ from rich.console import Console
12
+ from rich.status import Status
13
+
14
+ from cli.infrastructure.console import get_console
15
+
16
+
17
+ class Spinner:
18
+ """A loading spinner for async operations.
19
+
20
+ Usage:
21
+ async with Spinner("Processing...") as spinner:
22
+ result = await some_async_operation()
23
+ spinner.update("Almost done...")
24
+
25
+ # Or with the module-level context manager:
26
+ async with spinner("Loading data..."):
27
+ data = await fetch_data()
28
+ """
29
+
30
+ def __init__(
31
+ self,
32
+ message: str = "Processing...",
33
+ spinner_style: str = "dots",
34
+ console: Console | None = None,
35
+ ):
36
+ """Initialize the spinner.
37
+
38
+ Args:
39
+ message: Initial status message to display
40
+ spinner_style: Rich spinner style (dots, line, dots2, etc.)
41
+ console: Optional console instance (uses global if not provided)
42
+ """
43
+ self.message = message
44
+ self.spinner_style = spinner_style
45
+ self._console = console or get_console()
46
+ self._status: Status | None = None
47
+
48
+ async def __aenter__(self) -> "Spinner":
49
+ """Start the spinner."""
50
+ self._status = self._console.status(
51
+ f"[dim]{self.message}[/]",
52
+ spinner=self.spinner_style,
53
+ spinner_style="cyan",
54
+ )
55
+ self._status.start()
56
+ return self
57
+
58
+ async def __aexit__(
59
+ self,
60
+ _exc_type: type[BaseException] | None,
61
+ _exc_val: BaseException | None,
62
+ _exc_tb: TracebackType | None,
63
+ ) -> None:
64
+ """Stop the spinner."""
65
+ if self._status:
66
+ self._status.stop()
67
+ self._status = None
68
+
69
+ def update(self, message: str) -> None:
70
+ """Update the spinner message.
71
+
72
+ Args:
73
+ message: New status message to display
74
+ """
75
+ if self._status:
76
+ self._status.update(f"[dim]{message}[/]")
77
+
78
+
79
+ @asynccontextmanager
80
+ async def spinner(
81
+ message: str = "Processing...",
82
+ spinner_style: str = "dots",
83
+ ) -> AsyncIterator[Spinner]:
84
+ """Context manager for displaying a loading spinner.
85
+
86
+ Usage:
87
+ async with spinner("Fetching data...") as s:
88
+ data = await fetch_data()
89
+ s.update("Processing response...")
90
+ result = process(data)
91
+
92
+ Args:
93
+ message: Initial status message to display
94
+ spinner_style: Rich spinner style
95
+
96
+ Yields:
97
+ Spinner instance for updating the message
98
+ """
99
+ s = Spinner(message=message, spinner_style=spinner_style)
100
+ async with s:
101
+ yield s
File without changes
@@ -0,0 +1,41 @@
1
+ """Clipboard utilities for TUI applications."""
2
+
3
+ import pyperclip
4
+ from typing import Literal, Protocol
5
+
6
+
7
+ SeverityLevel = Literal["information", "warning", "error"]
8
+
9
+
10
+ class SupportsNotify(Protocol):
11
+ """Minimal app protocol for clipboard notifications."""
12
+
13
+ def notify(
14
+ self,
15
+ message: str,
16
+ *,
17
+ title: str = "",
18
+ severity: SeverityLevel = "information",
19
+ timeout: float | None = None,
20
+ markup: bool = True,
21
+ ) -> None: ...
22
+
23
+
24
+ def copy_to_clipboard(app: SupportsNotify, text: str, label: str = "ID") -> bool:
25
+ """Copy text to clipboard with user notification.
26
+
27
+ Args:
28
+ app: The Textual app instance (for notifications)
29
+ text: The text to copy
30
+ label: Label for the notification message (e.g., "ID", "Version ID")
31
+
32
+ Returns:
33
+ True if copy succeeded, False otherwise
34
+ """
35
+ try:
36
+ pyperclip.copy(text)
37
+ app.notify(f"Copied {label}: {text}", severity="information")
38
+ return True
39
+ except Exception as e:
40
+ app.notify(f"Copy failed: {e}", severity="error")
41
+ return False