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,190 @@
1
+ """Presenter for content list command output."""
2
+
3
+ import json
4
+ from datetime import datetime
5
+ from typing import Any
6
+
7
+ from alloy_runtime_types.dtos.content import ListContentPartsResponse
8
+ from alloy_runtime_types.dtos.templates import TagResponse
9
+
10
+ from cli.infrastructure.output import OutputService
11
+ from cli.infrastructure.renderers.list_renderer import ColumnConfig
12
+
13
+
14
+ def _format_tags(tags: list[TagResponse]) -> str:
15
+ """Format tags for display, truncating if too many.
16
+
17
+ Args:
18
+ tags: List of tag responses
19
+
20
+ Returns:
21
+ Formatted tags string
22
+ """
23
+ if not tags:
24
+ return "-"
25
+ tag_paths = [t.tag_path for t in tags[:3]]
26
+ result = ", ".join(tag_paths)
27
+ if len(tags) > 3:
28
+ result += f" (+{len(tags) - 3})"
29
+ return result
30
+
31
+
32
+ def _format_content_preview(
33
+ content_text: str | None, content_structured: dict[str, Any] | None
34
+ ) -> str:
35
+ """Format content preview, truncating long content.
36
+
37
+ Args:
38
+ content_text: Plain text content
39
+ content_structured: Structured JSON content
40
+
41
+ Returns:
42
+ Truncated content preview string
43
+ """
44
+ max_length = 150
45
+
46
+ if content_text:
47
+ text = content_text.replace("\n", " ").strip()
48
+ if len(text) > max_length:
49
+ return text[: max_length - 3] + "..."
50
+ return text
51
+
52
+ if content_structured:
53
+ try:
54
+ json_str = json.dumps(content_structured, ensure_ascii=False)
55
+ if len(json_str) > max_length:
56
+ return json_str[: max_length - 3] + "..."
57
+ return json_str
58
+ except (TypeError, ValueError):
59
+ return "{...}"
60
+
61
+ return "-"
62
+
63
+
64
+ def _format_model_info(part: Any) -> str:
65
+ """Format model/provider information from execution metadata.
66
+
67
+ Args:
68
+ part: Content part item response
69
+
70
+ Returns:
71
+ Formatted model info string
72
+ """
73
+ if not part.execution_metadata:
74
+ return "-"
75
+
76
+ provider = part.execution_metadata.provider_key or ""
77
+ model = part.execution_metadata.provider_model_name or ""
78
+
79
+ if provider and model:
80
+ # Truncate model name if too long
81
+ if len(model) > 25:
82
+ model = model[:22] + "..."
83
+ return f"{provider}/{model}"
84
+ elif model:
85
+ return model
86
+ elif provider:
87
+ return provider
88
+ return "-"
89
+
90
+
91
+ def _format_created_at(created_at: datetime | None) -> str:
92
+ """Format creation timestamp for display.
93
+
94
+ Args:
95
+ created_at: Creation timestamp
96
+
97
+ Returns:
98
+ Formatted date string
99
+ """
100
+ if not created_at:
101
+ return "-"
102
+ return created_at.strftime("%Y-%m-%d %H:%M")
103
+
104
+
105
+ def present_content_list(response: ListContentPartsResponse) -> None:
106
+ """Present list of content parts with Rich table formatting.
107
+
108
+ Args:
109
+ response: List content parts response from server
110
+ """
111
+ output = OutputService.get()
112
+
113
+ if not response.content_parts:
114
+ output.info("No content parts found")
115
+ return
116
+
117
+ # Define table columns with compact layout configuration
118
+ # Line 1 (primary): ID, type, model, created - machine-readable data
119
+ # Line 2 (detail): Content preview, tags - human-readable content
120
+ columns = [
121
+ ColumnConfig(
122
+ source_field="id",
123
+ header_label="ID",
124
+ compact_line=1,
125
+ compact_style="cyan",
126
+ ),
127
+ ColumnConfig(
128
+ source_field="content_type_id",
129
+ header_label="Type",
130
+ compact_line=1,
131
+ compact_style="magenta",
132
+ ),
133
+ ColumnConfig(
134
+ source_field="_model_info",
135
+ header_label="Model",
136
+ compact_line=1,
137
+ compact_style="green",
138
+ ),
139
+ ColumnConfig(
140
+ source_field="created_at",
141
+ header_label="Created",
142
+ formatter=_format_created_at,
143
+ compact_line=1,
144
+ compact_style="blue",
145
+ ),
146
+ ColumnConfig(
147
+ source_field="_content_preview",
148
+ header_label="Content",
149
+ compact_line=2,
150
+ compact_style="dim",
151
+ ),
152
+ ColumnConfig(
153
+ source_field="tags",
154
+ header_label="Tags",
155
+ formatter=_format_tags,
156
+ compact_line=2,
157
+ compact_style="dim italic",
158
+ ),
159
+ ]
160
+
161
+ # Pre-process items to add computed fields
162
+ # We need to create wrapper objects with the computed fields
163
+ class ContentPartWrapper:
164
+ """Wrapper to add computed fields for table rendering."""
165
+
166
+ def __init__(self, part: Any):
167
+ self._part = part
168
+ # Copy all original fields
169
+ self.id = part.id
170
+ self.content_type_id = part.content_type_id
171
+ self.tags = part.tags
172
+ self.created_at = part.created_at
173
+ # Add computed fields
174
+ self._content_preview = _format_content_preview(
175
+ part.content_text, part.content_structured
176
+ )
177
+ self._model_info = _format_model_info(part)
178
+
179
+ wrapped_items = [ContentPartWrapper(part) for part in response.content_parts]
180
+
181
+ # Render table
182
+ output.table(
183
+ items=wrapped_items, # type: ignore[arg-type]
184
+ title=f"Content Parts ({response.total_count})",
185
+ columns=columns,
186
+ )
187
+
188
+ # Show pagination info if there's a next page
189
+ if response.cursor:
190
+ output.status(f"Next page: ai content list --cursor '{response.cursor}'")
File without changes
File without changes
@@ -0,0 +1,165 @@
1
+ """Credentials create command implementation."""
2
+
3
+ from datetime import datetime
4
+ from typing import Any, Optional
5
+
6
+ import typer
7
+
8
+ from cli.commands.credentials.create.presenter import (
9
+ present_org_credential_create_success,
10
+ )
11
+ from cli.commands.flag_utils import validate_provider
12
+ from cli.commands.shared_flags import (
13
+ CREDENTIAL_VALUE,
14
+ DESCRIPTION,
15
+ NAME,
16
+ PROVIDER,
17
+ )
18
+ from cli.infrastructure.command import async_command, authenticated_client
19
+ from alloy_runtime_types.dtos.organization_credentials import (
20
+ CreateOrganizationCredentialRequest,
21
+ )
22
+ from alloy_runtime_types.enums.provider import Provider
23
+
24
+
25
+ def credentials_create_command(
26
+ credential_type_id: str = typer.Option(
27
+ ...,
28
+ "--type",
29
+ help="Credential type: org_provider or org_tool",
30
+ ),
31
+ name: str = NAME,
32
+ plaintext_value: str = CREDENTIAL_VALUE,
33
+ provider: Optional[str] = PROVIDER,
34
+ tool_id: Optional[str] = typer.Option(
35
+ None,
36
+ "--tool-id",
37
+ help="Tool identifier (mutually exclusive with -p)",
38
+ ),
39
+ description: Optional[str] = DESCRIPTION,
40
+ expires_at: Optional[str] = typer.Option(
41
+ None,
42
+ "--expires-at",
43
+ help="Expiration ISO 8601 (2025-12-31T23:59:59Z)",
44
+ ),
45
+ configuration: Optional[str] = typer.Option(
46
+ None,
47
+ "--configuration",
48
+ help="Type-specific config as JSON",
49
+ ),
50
+ ) -> None:
51
+ """Create an organization credential (BYOK).
52
+
53
+ Requires organization owner or admin role.
54
+
55
+ SECURITY: Use env var to avoid shell history exposure:
56
+ ai credentials create --value "$OPENAI_API_KEY" ...
57
+
58
+ Examples:
59
+
60
+ # Provider credential
61
+ ai credentials create --type org_provider -n "OpenAI" --value "sk-..." -p openai
62
+
63
+ # Tool credential
64
+ ai credentials create --type org_tool -n "Search" --value "key" --tool-id web_search
65
+ """
66
+ _execute_create(
67
+ credential_type_id=credential_type_id,
68
+ name=name,
69
+ plaintext_value=plaintext_value,
70
+ provider=provider,
71
+ tool_id=tool_id,
72
+ description=description,
73
+ expires_at=expires_at,
74
+ configuration=configuration,
75
+ )
76
+
77
+
78
+ @async_command
79
+ async def _execute_create(
80
+ credential_type_id: str,
81
+ name: str,
82
+ plaintext_value: str,
83
+ provider: Optional[str],
84
+ tool_id: Optional[str],
85
+ description: Optional[str],
86
+ expires_at: Optional[str],
87
+ configuration: Optional[str],
88
+ ) -> None:
89
+ """Execute the credential creation operation.
90
+
91
+ Args:
92
+ credential_type_id: Credential type ID
93
+ name: Friendly name
94
+ plaintext_value: API key value
95
+ provider: Optional provider key string
96
+ tool_id: Optional tool identifier
97
+ description: Optional description
98
+ expires_at: Optional expiration datetime (ISO 8601 string)
99
+ configuration: Optional configuration JSON string
100
+
101
+ Raises:
102
+ typer.BadParameter: If validation fails (provider, tool_id, XOR, datetime, JSON)
103
+ ConfigurationError: If environment variables are missing
104
+ AuthenticationError: If API key is invalid
105
+ ForbiddenError: User lacks permission (not owner/admin)
106
+ ValidationError: If request data is invalid
107
+ ConflictError: If duplicate credential exists
108
+ ServerError: If server returns 5xx error
109
+ """
110
+ # Validate XOR constraint: exactly one of provider or tool_id (or neither for generic)
111
+ has_provider = provider is not None
112
+ has_tool_id = tool_id is not None
113
+
114
+ if has_provider and has_tool_id:
115
+ raise typer.BadParameter(
116
+ "Cannot provide both --provider and --tool-id. Choose one or neither."
117
+ )
118
+
119
+ # Validate and convert provider string to enum if provided
120
+ provider_enum: Optional[Provider] = None
121
+ if provider:
122
+ provider_enum = validate_provider(provider)
123
+
124
+ # Parse expires_at if provided
125
+ expires_datetime: Optional[datetime] = None
126
+ if expires_at:
127
+ try:
128
+ expires_datetime = datetime.fromisoformat(expires_at.replace("Z", "+00:00"))
129
+ except ValueError:
130
+ raise typer.BadParameter(
131
+ f"Invalid datetime format: '{expires_at}'. Must be ISO 8601 format (e.g., '2025-12-31T23:59:59Z')"
132
+ )
133
+
134
+ # Parse configuration JSON if provided
135
+ configuration_dict: Optional[dict[str, Any]] = None
136
+ if configuration:
137
+ import json
138
+
139
+ try:
140
+ configuration_dict = json.loads(configuration)
141
+ if not isinstance(configuration_dict, dict):
142
+ raise typer.BadParameter(
143
+ f"Configuration must be a JSON object, got {type(configuration_dict).__name__}"
144
+ )
145
+ except json.JSONDecodeError as e:
146
+ raise typer.BadParameter(f"Invalid JSON in --configuration: {e}")
147
+
148
+ # Build request
149
+ request = CreateOrganizationCredentialRequest(
150
+ credential_type_id=credential_type_id,
151
+ name=name,
152
+ plaintext_value=plaintext_value,
153
+ provider_key=provider_enum,
154
+ tool_id=tool_id,
155
+ description=description,
156
+ expires_at=expires_datetime,
157
+ configuration=configuration_dict,
158
+ )
159
+
160
+ # Execute API call with authenticated client
161
+ async with authenticated_client() as (_config, client):
162
+ response = await client.create_organization_credential(request)
163
+
164
+ # Present success output
165
+ present_org_credential_create_success(response)
@@ -0,0 +1,38 @@
1
+ """Field definitions for organization credential presentation."""
2
+
3
+ from typing import Any, cast
4
+
5
+ from cli.infrastructure.formatting.fields import format_datetime
6
+ from cli.infrastructure.renderers.entity_renderer import FieldConfig
7
+
8
+
9
+ def format_optional_datetime(value: Any) -> str:
10
+ """Format optional datetime value."""
11
+ return format_datetime(value) if value else "(not set)"
12
+
13
+
14
+ def format_optional_provider(value: Any) -> str:
15
+ """Format optional provider value."""
16
+ return value.value if value else "(not set)"
17
+
18
+
19
+ def format_optional_string(value: Any) -> str:
20
+ """Format optional string value."""
21
+ return cast(str, value) if value else "(not set)"
22
+
23
+
24
+ ORG_CREDENTIAL_FIELDS: list[FieldConfig] = [
25
+ FieldConfig("id", "ID", lambda v: str(v)),
26
+ FieldConfig("name", "Name"),
27
+ FieldConfig("description", "Description", format_optional_string),
28
+ FieldConfig("credential_type_id", "Credential Type"),
29
+ FieldConfig("provider_key", "Provider", format_optional_provider),
30
+ FieldConfig("tool_id", "Tool ID", format_optional_string),
31
+ FieldConfig("value_prefix", "Value Prefix", format_optional_string),
32
+ FieldConfig("organization_id", "Organization ID", lambda v: str(v)),
33
+ FieldConfig("is_active", "Is Active", lambda v: "Yes" if v else "No"),
34
+ FieldConfig("created_at", "Created", format_datetime),
35
+ FieldConfig("updated_at", "Updated", format_datetime),
36
+ FieldConfig("last_used_at", "Last Used", format_optional_datetime),
37
+ FieldConfig("expires_at", "Expires", format_optional_datetime),
38
+ ]
@@ -0,0 +1,20 @@
1
+ """Presenter for credentials create command output."""
2
+
3
+ from cli.commands.credentials.create.fields import ORG_CREDENTIAL_FIELDS
4
+ from cli.infrastructure.output import OutputService
5
+ from alloy_runtime_types.dtos.organization_credentials import (
6
+ OrganizationCredentialResponse,
7
+ )
8
+
9
+
10
+ def present_org_credential_create_success(
11
+ credential: OrganizationCredentialResponse,
12
+ ) -> None:
13
+ """Present a successful organization credential creation with Rich formatting.
14
+
15
+ Args:
16
+ credential: Created organization credential data from server
17
+ """
18
+ output = OutputService.get()
19
+ output.success(f"Created organization credential: {credential.name}")
20
+ output.entity(credential, "Organization Credential Details", ORG_CREDENTIAL_FIELDS)
File without changes
@@ -0,0 +1,53 @@
1
+ """Credential update command implementation."""
2
+
3
+ from typing import Optional
4
+ from uuid import UUID
5
+
6
+ import typer
7
+
8
+ from cli.commands.credentials.update.presenter import present_credential_update_success
9
+ from cli.commands.shared_flags import (
10
+ CREDENTIAL_VALUE_OPTIONAL,
11
+ DESCRIPTION,
12
+ NAME_OPTIONAL,
13
+ )
14
+ from cli.infrastructure.command import async_command, authenticated_client
15
+ from alloy_runtime_types.dtos.credentials import UpdateCredentialRequest
16
+
17
+
18
+ def credentials_update_command(
19
+ credential_id: UUID = typer.Argument(..., help="Credential UUID"),
20
+ name: Optional[str] = NAME_OPTIONAL,
21
+ description: Optional[str] = DESCRIPTION,
22
+ value: Optional[str] = CREDENTIAL_VALUE_OPTIONAL,
23
+ ) -> None:
24
+ """Update a credential (system or organization).
25
+
26
+ At least one flag required.
27
+
28
+ Examples:
29
+
30
+ ai credentials update <uuid> -n "Production Key"
31
+ ai credentials update <uuid> -d "Updated" --value "sk_new"
32
+ """
33
+ _execute_update(credential_id, name, description, value)
34
+
35
+
36
+ @async_command
37
+ async def _execute_update(
38
+ credential_id: UUID,
39
+ name: Optional[str],
40
+ description: Optional[str],
41
+ value: Optional[str],
42
+ ) -> None:
43
+ """Execute the credential update operation."""
44
+ request = UpdateCredentialRequest(
45
+ name=name,
46
+ description=description,
47
+ plain_text_api_key=value,
48
+ )
49
+
50
+ async with authenticated_client() as (_config, client):
51
+ response = await client.update_credential(credential_id, request)
52
+
53
+ present_credential_update_success(response)
@@ -0,0 +1,71 @@
1
+ """Field definitions for credential presentation."""
2
+
3
+ from typing import Any
4
+
5
+ from cli.infrastructure.formatting.fields import (
6
+ format_boolean,
7
+ format_cost,
8
+ format_datetime,
9
+ )
10
+ from cli.infrastructure.renderers.entity_renderer import FieldConfig
11
+
12
+
13
+ def format_optional_datetime(value: Any) -> str:
14
+ """Format optional datetime value."""
15
+ return format_datetime(value) if value else "(not set)"
16
+
17
+
18
+ def format_optional_limit(value: Any) -> str:
19
+ """Format optional limit value."""
20
+ return str(value) if value is not None else "(not set)"
21
+
22
+
23
+ def format_optional_provider(value: Any) -> str:
24
+ """Format optional provider enum value."""
25
+ return value.value if value is not None else "(not set)"
26
+
27
+
28
+ def format_optional_boolean(value: Any) -> str:
29
+ """Format optional boolean value."""
30
+ return format_boolean(value) if value is not None else "(not set)"
31
+
32
+
33
+ def format_optional_cost(value: Any) -> str:
34
+ """Format optional cost value."""
35
+ return format_cost(value) if value is not None else "(not set)"
36
+
37
+
38
+ CREDENTIAL_FIELDS: list[FieldConfig] = [
39
+ FieldConfig("id", "ID", lambda v: str(v)),
40
+ FieldConfig("name", "Name"),
41
+ FieldConfig("description", "Description"),
42
+ FieldConfig("credential_type_id", "Credential Type"),
43
+ FieldConfig("provider_key", "Provider", format_optional_provider),
44
+ FieldConfig("is_active", "Is Active", format_boolean),
45
+ FieldConfig("value_prefix", "Value Prefix"),
46
+ # Organization credential field
47
+ FieldConfig(
48
+ "organization_id", "Organization ID", lambda v: str(v) if v else "(system)"
49
+ ),
50
+ # System credential fields (optional)
51
+ FieldConfig("priority", "Priority", format_optional_limit),
52
+ FieldConfig("is_default", "Is Default", format_optional_boolean),
53
+ FieldConfig("daily_request_limit", "Daily Request Limit", format_optional_limit),
54
+ FieldConfig(
55
+ "requests_per_minute_limit", "Requests/Minute Limit", format_optional_limit
56
+ ),
57
+ FieldConfig(
58
+ "monthly_cost_limit_usd", "Monthly Cost Limit (USD)", format_optional_cost
59
+ ),
60
+ FieldConfig(
61
+ "current_daily_requests", "Current Daily Requests", format_optional_limit
62
+ ),
63
+ FieldConfig(
64
+ "current_monthly_cost_usd", "Current Monthly Cost (USD)", format_optional_cost
65
+ ),
66
+ # Timestamps
67
+ FieldConfig("created_at", "Created", format_datetime),
68
+ FieldConfig("updated_at", "Updated", format_datetime),
69
+ FieldConfig("last_used_at", "Last Used", format_optional_datetime),
70
+ FieldConfig("expires_at", "Expires", format_optional_datetime),
71
+ ]
@@ -0,0 +1,16 @@
1
+ """Presenter for credential update command output."""
2
+
3
+ from cli.commands.credentials.update.fields import CREDENTIAL_FIELDS
4
+ from cli.infrastructure.output import OutputService
5
+ from alloy_runtime_types.dtos.credentials import CredentialResponse
6
+
7
+
8
+ def present_credential_update_success(credential: CredentialResponse) -> None:
9
+ """Present a successful credential update with Rich formatting.
10
+
11
+ Args:
12
+ credential: Updated credential data from server
13
+ """
14
+ output = OutputService.get()
15
+ output.success(f"Updated credential: {credential.name}")
16
+ output.entity(credential, "Credential Details", CREDENTIAL_FIELDS)