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,302 @@
1
+ """Parser for macro injection syntax in chat messages.
2
+
3
+ Parses injection patterns from user messages using the same syntax as templates:
4
+ - @fragment(name) - Include a fragment template
5
+ - @text(name) - Insert text from a content part
6
+ - @json(name) - Insert JSON from a content part
7
+ - @schema(name) - Reference a schema definition
8
+
9
+ Note: Unlike template macros which use Jinja2 rendering, these are resolved
10
+ at message send time by fetching the referenced content from the API.
11
+ """
12
+
13
+ import re
14
+ from dataclasses import dataclass
15
+
16
+
17
+ @dataclass
18
+ class FragmentReference:
19
+ """A reference to a fragment template found in user input."""
20
+
21
+ identifier: str # Fragment name or UUID
22
+ raw_match: str = "" # Original matched text
23
+ start: int = 0 # Start position in message
24
+ end: int = 0 # End position in message
25
+
26
+
27
+ @dataclass
28
+ class ContentReference:
29
+ """A reference to content found in user input."""
30
+
31
+ identifier: str # Content name/UUID
32
+ is_json: bool = False # True for @json, False for @text
33
+ raw_match: str = "" # Original matched text
34
+ start: int = 0 # Start position in message
35
+ end: int = 0 # End position in message
36
+
37
+
38
+ @dataclass
39
+ class SchemaReference:
40
+ """A reference to a schema found in user input."""
41
+
42
+ identifier: str # Schema name or UUID
43
+ raw_match: str = "" # Original matched text
44
+ start: int = 0 # Start position in message
45
+ end: int = 0 # End position in message
46
+
47
+
48
+ # Pattern for @fragment(identifier)
49
+ # Supports identifiers with dots, dashes, underscores, alphanumeric, and UUIDs
50
+ # No quotes required (matches template macro syntax)
51
+ FRAGMENT_PATTERN = re.compile(
52
+ r"@fragment\(\s*([a-zA-Z0-9._-]+)\s*\)", # @fragment(identifier)
53
+ )
54
+
55
+ # Pattern for @text(identifier) - text content
56
+ TEXT_PATTERN = re.compile(
57
+ r"@text\(\s*([a-zA-Z0-9._-]+)\s*\)", # @text(identifier)
58
+ )
59
+
60
+ # Pattern for @json(identifier) - JSON content
61
+ JSON_PATTERN = re.compile(
62
+ r"@json\(\s*([a-zA-Z0-9._-]+)\s*\)", # @json(identifier)
63
+ )
64
+
65
+ # Pattern for @schema(identifier) - schema definition
66
+ SCHEMA_PATTERN = re.compile(
67
+ r"@schema\(\s*([a-zA-Z0-9._-]+)\s*\)", # @schema(identifier)
68
+ )
69
+
70
+ # Pattern for detecting partial injection triggers (for autocomplete)
71
+ # Matches: @fragment, @fragment(, @fragment(partial
72
+ PARTIAL_FRAGMENT_PATTERN = re.compile(r"@fragment(?:\(\s*([a-zA-Z0-9._-]*)?)?$")
73
+ PARTIAL_TEXT_PATTERN = re.compile(r"@text(?:\(\s*([a-zA-Z0-9._-]*)?)?$")
74
+ PARTIAL_JSON_PATTERN = re.compile(r"@json(?:\(\s*([a-zA-Z0-9._-]*)?)?$")
75
+ PARTIAL_SCHEMA_PATTERN = re.compile(r"@schema(?:\(\s*([a-zA-Z0-9._-]*)?)?$")
76
+
77
+
78
+ def parse_fragments(message: str) -> list[FragmentReference]:
79
+ """Parse all @fragment references from a message.
80
+
81
+ Args:
82
+ message: User input message
83
+
84
+ Returns:
85
+ List of FragmentReference objects found in the message
86
+ """
87
+ references: list[FragmentReference] = []
88
+
89
+ for match in FRAGMENT_PATTERN.finditer(message):
90
+ references.append(
91
+ FragmentReference(
92
+ identifier=match.group(1),
93
+ raw_match=match.group(0),
94
+ start=match.start(),
95
+ end=match.end(),
96
+ )
97
+ )
98
+
99
+ return references
100
+
101
+
102
+ def parse_content(message: str) -> list[ContentReference]:
103
+ """Parse all @text and @json references from a message.
104
+
105
+ Args:
106
+ message: User input message
107
+
108
+ Returns:
109
+ List of ContentReference objects found in the message
110
+ """
111
+ references: list[ContentReference] = []
112
+
113
+ # Parse @text(identifier)
114
+ for match in TEXT_PATTERN.finditer(message):
115
+ references.append(
116
+ ContentReference(
117
+ identifier=match.group(1),
118
+ is_json=False,
119
+ raw_match=match.group(0),
120
+ start=match.start(),
121
+ end=match.end(),
122
+ )
123
+ )
124
+
125
+ # Parse @json(identifier)
126
+ for match in JSON_PATTERN.finditer(message):
127
+ references.append(
128
+ ContentReference(
129
+ identifier=match.group(1),
130
+ is_json=True,
131
+ raw_match=match.group(0),
132
+ start=match.start(),
133
+ end=match.end(),
134
+ )
135
+ )
136
+
137
+ return references
138
+
139
+
140
+ def parse_schemas(message: str) -> list[SchemaReference]:
141
+ """Parse all @schema references from a message.
142
+
143
+ Args:
144
+ message: User input message
145
+
146
+ Returns:
147
+ List of SchemaReference objects found in the message
148
+ """
149
+ references: list[SchemaReference] = []
150
+
151
+ for match in SCHEMA_PATTERN.finditer(message):
152
+ references.append(
153
+ SchemaReference(
154
+ identifier=match.group(1),
155
+ raw_match=match.group(0),
156
+ start=match.start(),
157
+ end=match.end(),
158
+ )
159
+ )
160
+
161
+ return references
162
+
163
+
164
+ def parse_injections(
165
+ message: str,
166
+ ) -> tuple[list[FragmentReference], list[ContentReference], list[SchemaReference]]:
167
+ """Parse all injection patterns from a message.
168
+
169
+ Args:
170
+ message: User input message
171
+
172
+ Returns:
173
+ Tuple of (fragment_references, content_references, schema_references)
174
+ """
175
+ return parse_fragments(message), parse_content(message), parse_schemas(message)
176
+
177
+
178
+ def has_injections(message: str) -> bool:
179
+ """Check if a message contains any injection patterns.
180
+
181
+ This is a quick check that doesn't do full parsing.
182
+
183
+ Args:
184
+ message: User input message
185
+
186
+ Returns:
187
+ True if message contains @fragment, @text, @json, or @schema patterns
188
+ """
189
+ return (
190
+ "@fragment(" in message
191
+ or "@text(" in message
192
+ or "@json(" in message
193
+ or "@schema(" in message
194
+ )
195
+
196
+
197
+ @dataclass
198
+ class PartialInjection:
199
+ """A partial injection pattern detected for autocomplete."""
200
+
201
+ injection_type: str # "fragment", "text", "json", or "schema"
202
+ partial_identifier: str # Partially typed identifier (may be empty)
203
+ start: int # Start position of the @ in the message
204
+ end: int # Current end position (cursor)
205
+
206
+
207
+ def detect_partial_injection(
208
+ message: str, cursor_pos: int | None = None
209
+ ) -> PartialInjection | None:
210
+ """Detect if the cursor is in a partial injection pattern.
211
+
212
+ Used for autocomplete - detects when user is typing an injection.
213
+
214
+ Args:
215
+ message: User input message
216
+ cursor_pos: Cursor position (defaults to end of message)
217
+
218
+ Returns:
219
+ PartialInjection if cursor is in a partial pattern, None otherwise
220
+
221
+ Examples:
222
+ >>> detect_partial_injection("Hello @fragment")
223
+ PartialInjection(injection_type='fragment', partial_identifier='', ...)
224
+ >>> detect_partial_injection("Hello @fragment(my-te")
225
+ PartialInjection(injection_type='fragment', partial_identifier='my-te', ...)
226
+ """
227
+ if cursor_pos is None:
228
+ cursor_pos = len(message)
229
+
230
+ # Get text up to cursor
231
+ text_to_cursor = message[:cursor_pos]
232
+
233
+ # Find the last @ before cursor
234
+ last_at = text_to_cursor.rfind("@")
235
+ if last_at == -1:
236
+ return None
237
+
238
+ # Get the text from @ to cursor
239
+ partial_text = text_to_cursor[last_at:]
240
+
241
+ # Try to match each partial pattern
242
+ patterns = [
243
+ (PARTIAL_FRAGMENT_PATTERN, "fragment"),
244
+ (PARTIAL_TEXT_PATTERN, "text"),
245
+ (PARTIAL_JSON_PATTERN, "json"),
246
+ (PARTIAL_SCHEMA_PATTERN, "schema"),
247
+ ]
248
+
249
+ for pattern, injection_type in patterns:
250
+ match = pattern.match(partial_text)
251
+ if match:
252
+ # Extract partial identifier if present
253
+ partial_identifier = (
254
+ match.group(1) if match.lastindex and match.group(1) else ""
255
+ )
256
+ return PartialInjection(
257
+ injection_type=injection_type,
258
+ partial_identifier=partial_identifier,
259
+ start=last_at,
260
+ end=cursor_pos,
261
+ )
262
+
263
+ return None
264
+
265
+
266
+ def format_fragment_injection(identifier: str) -> str:
267
+ """Format a fragment injection string.
268
+
269
+ Args:
270
+ identifier: Fragment name or UUID
271
+
272
+ Returns:
273
+ Formatted injection string like @fragment(name)
274
+ """
275
+ return f"@fragment({identifier})"
276
+
277
+
278
+ def format_content_injection(identifier: str, is_json: bool = False) -> str:
279
+ """Format a content injection string.
280
+
281
+ Args:
282
+ identifier: Content identifier (name or UUID)
283
+ is_json: Whether to use @json
284
+
285
+ Returns:
286
+ Formatted injection string
287
+ """
288
+ if is_json:
289
+ return f"@json({identifier})"
290
+ return f"@text({identifier})"
291
+
292
+
293
+ def format_schema_injection(identifier: str) -> str:
294
+ """Format a schema injection string.
295
+
296
+ Args:
297
+ identifier: Schema name or UUID
298
+
299
+ Returns:
300
+ Formatted injection string
301
+ """
302
+ return f"@schema({identifier})"
@@ -0,0 +1,399 @@
1
+ """Resolver for macro injection patterns in chat messages.
2
+
3
+ This module handles:
4
+ 1. Resolving @fragment references by calling the template render API
5
+ 2. Resolving @text references by fetching text content from the content API
6
+ 3. Resolving @json references by fetching structured content from the content API
7
+ 4. Resolving @schema references by fetching schema definitions from the schema API
8
+ 5. Replacing patterns in the original message with resolved content
9
+ """
10
+
11
+ import json
12
+ import re
13
+ from collections.abc import Awaitable, Callable
14
+ from dataclasses import dataclass, field
15
+ from types import TracebackType
16
+ from typing import Any, cast
17
+
18
+ from cli.infrastructure.injection.parser import (
19
+ ContentReference,
20
+ FragmentReference,
21
+ SchemaReference,
22
+ parse_injections,
23
+ )
24
+ from alloy_runtime_sdk.api_client.client import ApiClient
25
+ from alloy_runtime_types.dtos.templates import (
26
+ RenderTemplateRequest,
27
+ )
28
+ from alloy_runtime_sdk.exceptions.errors import (
29
+ AuthenticationError,
30
+ ForbiddenError,
31
+ NotFoundError,
32
+ ServerError,
33
+ ValidationError,
34
+ )
35
+
36
+
37
+ def _empty_details() -> dict[str, Any]:
38
+ return {}
39
+
40
+
41
+ def _empty_str_list() -> list[str]:
42
+ return []
43
+
44
+
45
+ def _empty_error_list() -> list["InjectionError"]:
46
+ return []
47
+
48
+
49
+ @dataclass
50
+ class InjectionError:
51
+ """An error that occurred during injection resolution."""
52
+
53
+ pattern: str # The pattern that failed (e.g., @fragment(name))
54
+ error_type: str # Type of error (e.g., "not_found", "permission_denied")
55
+ message: str # Human-readable error message
56
+ details: dict[str, Any] = field(default_factory=_empty_details)
57
+
58
+
59
+ @dataclass
60
+ class ResolvedMessage:
61
+ """Result of resolving all injection patterns in a message."""
62
+
63
+ text: str # Final message with all patterns resolved
64
+ fragments_used: list[str] = field(default_factory=_empty_str_list)
65
+ content_used: list[str] = field(default_factory=_empty_str_list)
66
+ schemas_used: list[str] = field(default_factory=_empty_str_list)
67
+ errors: list[InjectionError] = field(default_factory=_empty_error_list)
68
+
69
+ @property
70
+ def has_errors(self) -> bool:
71
+ """Check if any errors occurred during resolution."""
72
+ return len(self.errors) > 0
73
+
74
+ @property
75
+ def is_resolved(self) -> bool:
76
+ """Check if message was successfully resolved without errors."""
77
+ return not self.has_errors
78
+
79
+
80
+ class InjectionResolver:
81
+ """Resolves macro injection patterns via API calls.
82
+
83
+ Supports the same macro syntax as templates:
84
+ - @fragment(name) - Include a fragment template
85
+ - @text(name) - Insert text from a content part
86
+ - @json(name) - Insert JSON from a content part
87
+ - @schema(name) - Reference a schema definition
88
+
89
+ This resolver can work with either:
90
+ 1. Direct initialization with api_url/api_key (creates its own client)
91
+ 2. Initialization with an existing ApiClient instance
92
+
93
+ The TUI should use the client-based initialization to share the client.
94
+ """
95
+
96
+ def __init__(
97
+ self,
98
+ api_url: str | None = None,
99
+ api_key: str | None = None,
100
+ client: ApiClient | None = None,
101
+ ):
102
+ """Initialize the resolver.
103
+
104
+ Args:
105
+ api_url: Base URL for the API (used if client not provided)
106
+ api_key: API key for authentication (used if client not provided)
107
+ client: Existing ApiClient instance (preferred for TUI)
108
+ """
109
+ if client is not None:
110
+ self._client = client
111
+ self._owns_client = False
112
+ elif api_url and api_key:
113
+ self.api_url = api_url.rstrip("/")
114
+ self.api_key = api_key
115
+ self._client = None
116
+ self._owns_client = True
117
+ else:
118
+ raise ValueError("Must provide either client or both api_url and api_key")
119
+
120
+ async def _get_client(self) -> ApiClient:
121
+ """Get or create server client."""
122
+ if self._client is None:
123
+ self._client = ApiClient(
124
+ base_url=self.api_url, api_key=self.api_key, timeout=30.0
125
+ )
126
+ # Enter the async context manager
127
+ await self._client.__aenter__()
128
+ return self._client
129
+
130
+ async def close(self) -> None:
131
+ """Close the server client if we own it."""
132
+ if self._owns_client and self._client:
133
+ aexit = cast(
134
+ Callable[
135
+ [
136
+ type[BaseException] | None,
137
+ BaseException | None,
138
+ TracebackType | None,
139
+ ],
140
+ Awaitable[None],
141
+ ],
142
+ self._client.__aexit__,
143
+ )
144
+ await aexit(None, None, None)
145
+ self._client = None
146
+
147
+ @staticmethod
148
+ def _is_valid_uuid(value: str) -> bool:
149
+ """Check if a string is a valid UUID format."""
150
+ uuid_pattern = re.compile(
151
+ r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
152
+ re.IGNORECASE,
153
+ )
154
+ return bool(uuid_pattern.match(value))
155
+
156
+ async def resolve(self, message: str) -> ResolvedMessage:
157
+ """Resolve all injection patterns in a message.
158
+
159
+ Args:
160
+ message: User message with injection patterns
161
+
162
+ Returns:
163
+ ResolvedMessage with resolved text and metadata
164
+ """
165
+ # Parse all patterns
166
+ fragment_refs, content_refs, schema_refs = parse_injections(message)
167
+
168
+ if not fragment_refs and not content_refs and not schema_refs:
169
+ # No injections found - return original message
170
+ return ResolvedMessage(text=message)
171
+
172
+ # Track what we've injected
173
+ fragments_used: list[str] = []
174
+ content_used: list[str] = []
175
+ schemas_used: list[str] = []
176
+ errors: list[InjectionError] = []
177
+
178
+ # Build replacement map: (start, end) -> replacement_text
179
+ # We'll sort by position and replace from end to start to maintain positions
180
+ replacements: list[tuple[int, int, str]] = []
181
+
182
+ # Resolve fragments
183
+ for ref in fragment_refs:
184
+ try:
185
+ rendered_text = await self._resolve_fragment(ref)
186
+ replacements.append((ref.start, ref.end, rendered_text))
187
+ fragments_used.append(ref.identifier)
188
+ except Exception as e:
189
+ error = self._create_error_from_exception(ref.raw_match, e)
190
+ errors.append(error)
191
+ # Keep the original pattern in the message if it fails
192
+ replacements.append((ref.start, ref.end, ref.raw_match))
193
+
194
+ # Resolve content
195
+ for ref in content_refs:
196
+ try:
197
+ content_text = await self._resolve_content(ref)
198
+ replacements.append((ref.start, ref.end, content_text))
199
+ content_used.append(ref.identifier)
200
+ except Exception as e:
201
+ error = self._create_error_from_exception(ref.raw_match, e)
202
+ errors.append(error)
203
+ # Keep the original pattern in the message if it fails
204
+ replacements.append((ref.start, ref.end, ref.raw_match))
205
+
206
+ # Resolve schemas
207
+ for ref in schema_refs:
208
+ try:
209
+ schema_definition = await self._resolve_schema(ref)
210
+ replacements.append((ref.start, ref.end, schema_definition))
211
+ schemas_used.append(ref.identifier)
212
+ except Exception as e:
213
+ error = self._create_error_from_exception(ref.raw_match, e)
214
+ errors.append(error)
215
+ # Keep the original pattern in the message if it fails
216
+ replacements.append((ref.start, ref.end, ref.raw_match))
217
+
218
+ # Sort replacements by start position (descending) so we replace from end to start
219
+ replacements.sort(key=lambda x: x[0], reverse=True)
220
+
221
+ # Apply replacements
222
+ result_text = message
223
+ for start, end, replacement in replacements:
224
+ result_text = result_text[:start] + replacement + result_text[end:]
225
+
226
+ return ResolvedMessage(
227
+ text=result_text,
228
+ fragments_used=fragments_used,
229
+ content_used=content_used,
230
+ schemas_used=schemas_used,
231
+ errors=errors,
232
+ )
233
+
234
+ async def _resolve_fragment(self, ref: FragmentReference) -> str:
235
+ """Resolve a fragment reference by calling the template render API.
236
+
237
+ Fragments are templates with content_type='fragment'. They are rendered
238
+ without variables (fragments don't take variables in the macro syntax).
239
+
240
+ Args:
241
+ ref: Fragment reference to resolve
242
+
243
+ Returns:
244
+ Rendered fragment text
245
+ """
246
+ client = await self._get_client()
247
+
248
+ # The render endpoint requires a UUID, so we need to resolve name -> UUID first
249
+ # Check if identifier is already a UUID
250
+ if self._is_valid_uuid(ref.identifier):
251
+ from uuid import UUID
252
+
253
+ template_uuid = UUID(ref.identifier)
254
+ else:
255
+ # Look up template by name to get UUID
256
+ template_uuid = await self._get_template_uuid_by_name(ref.identifier)
257
+
258
+ # Prepare render request (no variables for fragments in macro syntax)
259
+ request_data = RenderTemplateRequest(variables={})
260
+
261
+ # Call render API using server_client
262
+ render_response = await client.render_template(template_uuid, request_data)
263
+ return render_response.rendered_text
264
+
265
+ async def _get_template_uuid_by_name(self, name: str):
266
+ """Look up a template by name and return its UUID.
267
+
268
+ Args:
269
+ name: Template name (e.g., "my-fragment" or "public.header")
270
+
271
+ Returns:
272
+ Template UUID
273
+ """
274
+
275
+ client = await self._get_client()
276
+
277
+ # GET /templates/{name} returns template details including UUID
278
+ template_response = await client.get_template(name)
279
+ return template_response.template.id
280
+
281
+ async def _resolve_content(self, ref: ContentReference) -> str:
282
+ """Resolve a content reference by fetching from the content API.
283
+
284
+ Args:
285
+ ref: Content reference to resolve
286
+
287
+ Returns:
288
+ Content text or JSON
289
+ """
290
+ from uuid import UUID
291
+
292
+ client = await self._get_client()
293
+
294
+ # Content can be referenced by UUID or by searching
295
+ # For now, we only support UUID references
296
+ if not self._is_valid_uuid(ref.identifier):
297
+ raise ValueError(
298
+ f"Invalid content identifier: '{ref.identifier}'. "
299
+ "Content must be referenced by UUID."
300
+ )
301
+
302
+ # Fetch content part using server_client
303
+ content_part = await client.get_content_part(UUID(ref.identifier))
304
+
305
+ # Return appropriate content based on type
306
+ if ref.is_json:
307
+ # User requested JSON format (@json)
308
+ if content_part.content_structured is not None:
309
+ return json.dumps(content_part.content_structured, indent=2)
310
+ elif content_part.content_text is not None:
311
+ # Fall back to text if structured not available
312
+ return content_part.content_text
313
+ else:
314
+ raise ValueError(
315
+ f"Content {ref.identifier} has no text or structured content"
316
+ )
317
+ else:
318
+ # User requested text format (@text)
319
+ if content_part.content_text is not None:
320
+ return content_part.content_text
321
+ elif content_part.content_structured is not None:
322
+ # Fall back to JSON if text not available
323
+ return json.dumps(content_part.content_structured, indent=2)
324
+ else:
325
+ raise ValueError(
326
+ f"Content {ref.identifier} has no text or structured content"
327
+ )
328
+
329
+ async def _resolve_schema(self, ref: SchemaReference) -> str:
330
+ """Resolve a schema reference by fetching from the schema API.
331
+
332
+ Args:
333
+ ref: Schema reference to resolve
334
+
335
+ Returns:
336
+ Schema definition string
337
+ """
338
+ client = await self._get_client()
339
+
340
+ # Fetch schema using server_client (supports both name and UUID)
341
+ schema_response = await client.get_schema(ref.identifier)
342
+
343
+ return schema_response.schema_definition
344
+
345
+ def _create_error_from_exception(
346
+ self, pattern: str, exception: Exception
347
+ ) -> InjectionError:
348
+ """Create an InjectionError from an exception.
349
+
350
+ Args:
351
+ pattern: The pattern that failed
352
+ exception: The exception that was raised
353
+
354
+ Returns:
355
+ InjectionError with appropriate type and message
356
+ """
357
+ # Handle server_client exceptions
358
+ if isinstance(exception, NotFoundError):
359
+ return InjectionError(
360
+ pattern=pattern,
361
+ error_type="not_found",
362
+ message=f"Fragment or content not found: {exception!s}",
363
+ )
364
+ elif isinstance(exception, ForbiddenError):
365
+ return InjectionError(
366
+ pattern=pattern,
367
+ error_type="permission_denied",
368
+ message=f"Permission denied: {exception!s}",
369
+ )
370
+ elif isinstance(exception, ValidationError):
371
+ return InjectionError(
372
+ pattern=pattern,
373
+ error_type="validation_error",
374
+ message=f"Validation error: {exception!s}",
375
+ )
376
+ elif isinstance(exception, AuthenticationError):
377
+ return InjectionError(
378
+ pattern=pattern,
379
+ error_type="authentication_error",
380
+ message=f"Authentication failed: {exception!s}",
381
+ )
382
+ elif isinstance(exception, ServerError):
383
+ return InjectionError(
384
+ pattern=pattern,
385
+ error_type="api_error",
386
+ message=f"Server error: {exception!s}",
387
+ )
388
+ elif isinstance(exception, ValueError):
389
+ return InjectionError(
390
+ pattern=pattern,
391
+ error_type="invalid_reference",
392
+ message=str(exception),
393
+ )
394
+ else:
395
+ return InjectionError(
396
+ pattern=pattern,
397
+ error_type="unknown_error",
398
+ message=f"Unexpected error: {exception!s}",
399
+ )