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,328 @@
1
+ """New session modal for creating chat sessions.
2
+
3
+ Provides a modal screen that allows users to create a new chat session,
4
+ selecting either an agent or a direct model to use.
5
+ """
6
+
7
+ from dataclasses import dataclass
8
+ from typing import Any
9
+ from uuid import UUID
10
+
11
+ from textual import on
12
+ from textual.app import ComposeResult
13
+ from textual.binding import Binding
14
+ from textual.containers import Horizontal, Vertical, VerticalScroll
15
+ from textual.screen import ModalScreen
16
+ from textual.widgets import (
17
+ Button,
18
+ Input,
19
+ Label,
20
+ Static,
21
+ TabbedContent,
22
+ TabPane,
23
+ TextArea,
24
+ )
25
+
26
+ from alloy_runtime_sdk.api_client.client import ApiClient
27
+ from alloy_runtime_types.dtos.models import ModelListItemResponse
28
+
29
+ from cli.infrastructure.forms.agent_picker import AgentPicker
30
+ from cli.infrastructure.forms.base_picker import SingleSelectPicker
31
+
32
+
33
+ @dataclass
34
+ class NewSessionConfig:
35
+ """Configuration for a new chat session.
36
+
37
+ Supports two modes:
38
+ - Agent mode: Use agent_id/agent_name (model comes from agent config)
39
+ - Model mode: Use provider_key/provider_model_name directly
40
+ """
41
+
42
+ name: str | None
43
+ # Agent mode fields
44
+ agent_id: UUID | None = None
45
+ agent_name: str | None = None
46
+ # Model mode fields
47
+ provider_key: str | None = None
48
+ provider_model_name: str | None = None
49
+ system_instruction: str | None = None
50
+
51
+ @property
52
+ def is_model_mode(self) -> bool:
53
+ """Check if this config uses direct model mode."""
54
+ return self.provider_key is not None and self.provider_model_name is not None
55
+
56
+ @property
57
+ def is_agent_mode(self) -> bool:
58
+ """Check if this config uses agent mode."""
59
+ return self.agent_id is not None
60
+
61
+
62
+ class SingleModelPicker(SingleSelectPicker[ModelListItemResponse]):
63
+ """Single-select model picker for the new session modal.
64
+
65
+ Unlike ModelPicker which allows multi-select for fallback models,
66
+ this picker only allows selecting one model for direct model chat.
67
+ """
68
+
69
+ def __init__(
70
+ self,
71
+ client: ApiClient,
72
+ placeholder: str = "Search models (e.g., gpt-4, claude, sonnet)...",
73
+ **kwargs: Any,
74
+ ) -> None:
75
+ """Initialize the model picker.
76
+
77
+ Args:
78
+ client: ApiClient instance for API calls
79
+ placeholder: Placeholder text for search input
80
+ """
81
+ super().__init__(
82
+ client=client,
83
+ placeholder=placeholder,
84
+ empty_message="No model selected",
85
+ **kwargs,
86
+ )
87
+
88
+ @property
89
+ def selected_model(self) -> ModelListItemResponse | None:
90
+ """Get the selected model."""
91
+ return self._selected_item
92
+
93
+ @property
94
+ def selected_provider_key(self) -> str | None:
95
+ """Get the provider key of the selected model."""
96
+ if self._selected_item is None:
97
+ return None
98
+ return self._selected_item.provider_key
99
+
100
+ @property
101
+ def selected_model_name(self) -> str | None:
102
+ """Get the model name of the selected model."""
103
+ if self._selected_item is None:
104
+ return None
105
+ return self._selected_item.provider_model_name
106
+
107
+ async def _fetch_items(self, query: str) -> list[ModelListItemResponse]:
108
+ """Fetch models matching query from the API."""
109
+ response = await self._client.list_models(
110
+ search=query,
111
+ configured_only=True,
112
+ limit=50,
113
+ )
114
+ return list(response.models)
115
+
116
+ def _format_item(self, item: ModelListItemResponse) -> str:
117
+ """Format model for display in option list."""
118
+ return f"{item.provider_key}:{item.provider_model_name}"
119
+
120
+ def _get_item_id(self, item: ModelListItemResponse) -> str:
121
+ """Get unique identifier for a model."""
122
+ return f"{item.provider_key}:{item.provider_model_name}"
123
+
124
+
125
+ class NewSessionModal(ModalScreen[NewSessionConfig | None]):
126
+ """Modal for creating a new chat session.
127
+
128
+ Allows users to optionally name the session and select either:
129
+ - An agent (uses agent's system instruction and models)
130
+ - A model directly (optionally with a custom system instruction)
131
+
132
+ Returns NewSessionConfig on create, or None if cancelled.
133
+
134
+ Usage:
135
+ def on_session_created(result: NewSessionConfig | None):
136
+ if result is not None:
137
+ # Create session with result
138
+ pass
139
+
140
+ self.app.push_screen(
141
+ NewSessionModal(client=server_client),
142
+ on_session_created
143
+ )
144
+ """
145
+
146
+ BINDINGS = [
147
+ Binding("escape", "cancel", "Cancel", show=True),
148
+ ]
149
+
150
+ DEFAULT_CSS = """
151
+ NewSessionModal {
152
+ align: center middle;
153
+ }
154
+
155
+ NewSessionModal > Vertical {
156
+ width: 80;
157
+ max-height: 40;
158
+ background: $surface;
159
+ border: thick $primary;
160
+ padding: 1 2;
161
+ }
162
+
163
+ NewSessionModal .modal-title {
164
+ text-align: center;
165
+ text-style: bold;
166
+ margin-bottom: 1;
167
+ }
168
+
169
+ NewSessionModal .section-label {
170
+ margin-top: 1;
171
+ text-style: bold;
172
+ }
173
+
174
+ NewSessionModal .help-text {
175
+ color: $text-muted;
176
+ margin-bottom: 1;
177
+ }
178
+
179
+ NewSessionModal .button-row {
180
+ margin-top: 1;
181
+ height: auto;
182
+ align: center middle;
183
+ }
184
+
185
+ NewSessionModal .button-row Button {
186
+ margin: 0 1;
187
+ }
188
+
189
+ NewSessionModal #session-name {
190
+ margin-bottom: 1;
191
+ }
192
+
193
+ NewSessionModal TabbedContent {
194
+ height: auto;
195
+ max-height: 25;
196
+ }
197
+
198
+ NewSessionModal TabPane {
199
+ height: auto;
200
+ padding: 1;
201
+ }
202
+
203
+ NewSessionModal #system-instruction {
204
+ height: 5;
205
+ margin-top: 1;
206
+ }
207
+
208
+ NewSessionModal #model-tab-scroll {
209
+ height: 100%;
210
+ max-height: 20;
211
+ }
212
+ """
213
+
214
+ def __init__(
215
+ self,
216
+ client: ApiClient,
217
+ **kwargs: Any,
218
+ ) -> None:
219
+ """Initialize the new session modal.
220
+
221
+ Args:
222
+ client: ApiClient instance for API calls
223
+ """
224
+ super().__init__(**kwargs)
225
+ self._client = client
226
+ self._active_tab: str = "agent"
227
+
228
+ def compose(self) -> ComposeResult:
229
+ with Vertical():
230
+ yield Label("New Chat Session", classes="modal-title")
231
+
232
+ yield Static("Session Name (optional):", classes="section-label")
233
+ yield Input(
234
+ placeholder="Enter a name for this chat...",
235
+ id="session-name",
236
+ )
237
+
238
+ with TabbedContent(id="mode-tabs"):
239
+ with TabPane("Agent", id="agent-tab"):
240
+ yield Static(
241
+ "Choose an agent to chat with. The agent's system instruction "
242
+ "and configured models will be used.",
243
+ classes="help-text",
244
+ )
245
+ yield AgentPicker(
246
+ client=self._client,
247
+ placeholder="Search for an agent...",
248
+ id="agent-picker",
249
+ )
250
+
251
+ with TabPane("Model", id="model-tab"):
252
+ with VerticalScroll(id="model-tab-scroll"):
253
+ yield Static(
254
+ "Chat directly with a model. Optionally provide a system instruction.",
255
+ classes="help-text",
256
+ )
257
+ yield SingleModelPicker(
258
+ client=self._client,
259
+ placeholder="Search for a model...",
260
+ id="model-picker",
261
+ )
262
+ yield Static(
263
+ "System Instruction (optional):", classes="section-label"
264
+ )
265
+ yield TextArea(
266
+ id="system-instruction",
267
+ )
268
+
269
+ with Horizontal(classes="button-row"):
270
+ yield Button("Create", id="create-btn", variant="success")
271
+ yield Button("Cancel", id="cancel-btn", variant="default")
272
+
273
+ def on_mount(self) -> None:
274
+ """Focus the session name input on mount."""
275
+ self.query_one("#session-name", Input).focus()
276
+
277
+ @on(TabbedContent.TabActivated)
278
+ def on_tab_activated(self, event: TabbedContent.TabActivated) -> None:
279
+ """Track which tab is active."""
280
+ if event.pane.id == "agent-tab":
281
+ self._active_tab = "agent"
282
+ elif event.pane.id == "model-tab":
283
+ self._active_tab = "model"
284
+
285
+ @on(Button.Pressed, "#create-btn")
286
+ def on_create_pressed(self, event: Button.Pressed) -> None:
287
+ """Create session and dismiss with config."""
288
+ name_input = self.query_one("#session-name", Input)
289
+ name = name_input.value.strip() or None
290
+
291
+ if self._active_tab == "agent":
292
+ # Agent mode
293
+ agent_picker = self.query_one("#agent-picker", AgentPicker)
294
+ agent_id_str = agent_picker.selected_agent_id
295
+ agent_id = UUID(agent_id_str) if agent_id_str else None
296
+ agent_name = agent_picker.selected_agent_name
297
+
298
+ config = NewSessionConfig(
299
+ name=name,
300
+ agent_id=agent_id,
301
+ agent_name=agent_name,
302
+ )
303
+ else:
304
+ # Model mode
305
+ model_picker = self.query_one("#model-picker", SingleModelPicker)
306
+ system_input = self.query_one("#system-instruction", TextArea)
307
+
308
+ provider_key = model_picker.selected_provider_key
309
+ model_name = model_picker.selected_model_name
310
+ system_instruction = system_input.text.strip() or None
311
+
312
+ config = NewSessionConfig(
313
+ name=name,
314
+ provider_key=provider_key,
315
+ provider_model_name=model_name,
316
+ system_instruction=system_instruction,
317
+ )
318
+
319
+ self.dismiss(config)
320
+
321
+ @on(Button.Pressed, "#cancel-btn")
322
+ def on_cancel_pressed(self, event: Button.Pressed) -> None:
323
+ """Cancel and dismiss with None."""
324
+ self.dismiss(None)
325
+
326
+ def action_cancel(self) -> None:
327
+ """Cancel via escape key."""
328
+ self.dismiss(None)
@@ -0,0 +1,271 @@
1
+ """Schema creation modal for creating new validation schemas.
2
+
3
+ Provides a comprehensive modal screen that allows users to create a new schema
4
+ with support for JSON Schema or Regex formats, including JSON example generation.
5
+ """
6
+
7
+ import json
8
+ import re
9
+ from dataclasses import dataclass
10
+ from typing import TYPE_CHECKING, Any
11
+
12
+ from textual import on, work
13
+ from textual.app import ComposeResult
14
+ from textual.containers import Horizontal, Vertical, VerticalScroll
15
+ from textual.widgets import (
16
+ Button,
17
+ Input,
18
+ Label,
19
+ RadioButton,
20
+ RadioSet,
21
+ Static,
22
+ TextArea,
23
+ )
24
+
25
+ from cli.infrastructure.scope_utils import get_scope_from_radioset
26
+ from cli.tui.widgets.base_form_modal import BaseFormModal
27
+
28
+ from alloy_runtime_sdk.api_client.client import ApiClient
29
+ from alloy_runtime_types.dtos.schemas import CreateSchemaRequest
30
+ from alloy_runtime_types.enums.ownership_scope import OwnershipScope
31
+ from alloy_runtime_types.enums.schema_enums import SchemaFormat
32
+
33
+ from cli.infrastructure.forms.json_schema_builder import JsonSchemaBuilder
34
+
35
+ if TYPE_CHECKING:
36
+ from cli.tui.app import AlloyRuntimeApp
37
+
38
+
39
+ @dataclass
40
+ class SchemaCreateResult:
41
+ """Result of successful schema creation."""
42
+
43
+ schema_id: str
44
+ schema_name: str
45
+ version: int
46
+
47
+
48
+ class SchemaCreateModal(BaseFormModal[SchemaCreateResult]):
49
+ """Modal for creating a new schema.
50
+
51
+ Provides a comprehensive form with:
52
+ - Required fields: name, schema format, definition
53
+ - Schema definition via TextArea (paste JSON schema or regex)
54
+ - JSON Schema generation from JSON example
55
+ - Optional fields: description
56
+ - Scope selection (user/organization/global)
57
+
58
+ Usage:
59
+ def on_schema_created(result: SchemaCreateResult | None):
60
+ if result is not None:
61
+ # Schema was created successfully
62
+ self.notify(f"Created schema: {result.schema_name}")
63
+ self._do_search("") # Refresh list
64
+
65
+ client = await self.app.store.get_client()
66
+ self.app.push_screen(
67
+ SchemaCreateModal(client=client),
68
+ on_schema_created
69
+ )
70
+ """
71
+
72
+ app: "AlloyRuntimeApp"
73
+
74
+ def __init__(
75
+ self,
76
+ client: ApiClient,
77
+ **kwargs: Any,
78
+ ) -> None:
79
+ """Initialize the schema creation modal.
80
+
81
+ Args:
82
+ client: ApiClient instance for API calls
83
+ """
84
+ super().__init__(**kwargs)
85
+ self._client = client
86
+
87
+ def compose(self) -> ComposeResult:
88
+ with VerticalScroll(id="schema-create-modal"):
89
+ yield Label("Create New Schema", classes="modal-title")
90
+
91
+ # ===== REQUIRED FIELDS =====
92
+ yield Static("[bold]Required[/]", classes="form-section")
93
+
94
+ yield Static(
95
+ "Name (lowercase, dots/dashes/underscores)", classes="field-label"
96
+ )
97
+ yield Input(
98
+ placeholder="e.g., user.profile, api.response-format",
99
+ id="name-input",
100
+ )
101
+
102
+ yield Static("Schema Format", classes="field-label")
103
+ with RadioSet(id="format-radios"):
104
+ yield RadioButton("JSON Schema", id="format-json-schema", value=True)
105
+ yield RadioButton("Regex", id="format-regex")
106
+
107
+ yield Static("Scope", classes="field-label")
108
+ with RadioSet(id="scope-radios"):
109
+ yield RadioButton("User (personal)", id="scope-user", value=True)
110
+ yield RadioButton("Organization", id="scope-org")
111
+ yield RadioButton("Global", id="scope-global")
112
+
113
+ # ===== SCHEMA DEFINITION =====
114
+ yield Static("[bold]Schema Definition[/]", classes="form-section")
115
+
116
+ yield Static(
117
+ "Definition (paste JSON Schema or Regex pattern)", classes="field-label"
118
+ )
119
+ yield TextArea(
120
+ id="definition-textarea",
121
+ classes="definition-input",
122
+ )
123
+
124
+ # JSON Schema builder section (only visible for JSON Schema format)
125
+ with Vertical(id="json-builder-section"):
126
+ yield Static(
127
+ "Or: Generate from JSON example (JSON Schema only)",
128
+ classes="field-label",
129
+ )
130
+ yield JsonSchemaBuilder(id="schema-builder")
131
+ yield Button(
132
+ "Use Generated Schema",
133
+ id="use-generated-btn",
134
+ variant="primary",
135
+ disabled=True,
136
+ )
137
+
138
+ # ===== OPTIONAL FIELDS =====
139
+ yield Static("[bold]Optional[/]", classes="form-section")
140
+
141
+ yield Static("Description", classes="field-label")
142
+ yield Input(
143
+ placeholder="Brief description of this schema's purpose",
144
+ id="description-input",
145
+ )
146
+
147
+ # Error display
148
+ yield Static("", id="error-display", classes="error-display")
149
+
150
+ with Horizontal(classes="button-row"):
151
+ yield Button("Create Schema", id="create-btn", variant="success")
152
+ yield Button("Cancel", id="cancel-btn", variant="default")
153
+
154
+ def on_mount(self) -> None:
155
+ """Focus the name input on mount."""
156
+ self.query_one("#name-input", Input).focus()
157
+
158
+ @on(RadioSet.Changed, "#format-radios")
159
+ def on_format_changed(self, event: RadioSet.Changed) -> None:
160
+ """Handle schema format change - show/hide JSON builder."""
161
+ json_builder_section = self.query_one("#json-builder-section", Vertical)
162
+ if event.pressed.id == "format-json-schema":
163
+ json_builder_section.display = True
164
+ else:
165
+ json_builder_section.display = False
166
+ # Clear the builder when switching to regex
167
+ self.query_one("#schema-builder", JsonSchemaBuilder).clear()
168
+
169
+ @on(JsonSchemaBuilder.SchemaChanged)
170
+ def on_schema_builder_changed(self, event: JsonSchemaBuilder.SchemaChanged) -> None:
171
+ """Enable/disable the 'Use Generated Schema' button based on builder state."""
172
+ use_btn = self.query_one("#use-generated-btn", Button)
173
+ use_btn.disabled = event.schema is None
174
+
175
+ @on(Button.Pressed, "#use-generated-btn")
176
+ def on_use_generated_pressed(self, event: Button.Pressed) -> None:
177
+ """Copy the generated schema to the definition textarea."""
178
+ builder = self.query_one("#schema-builder", JsonSchemaBuilder)
179
+ if builder.generated_schema:
180
+ schema_str = json.dumps(builder.generated_schema, indent=2)
181
+ self.query_one("#definition-textarea", TextArea).text = schema_str
182
+ self.app.notify(
183
+ "Generated schema copied to definition", severity="information"
184
+ )
185
+
186
+ @on(Button.Pressed, "#create-btn")
187
+ def on_create_pressed(self, event: Button.Pressed) -> None:
188
+ """Handle create button press."""
189
+ self.action_submit()
190
+
191
+ @on(Button.Pressed, "#cancel-btn")
192
+ def on_cancel_pressed(self, event: Button.Pressed) -> None:
193
+ """Handle cancel button press."""
194
+ self.dismiss(None)
195
+
196
+ def action_submit(self) -> None:
197
+ """Handle form submission."""
198
+ try:
199
+ self._validate_fields()
200
+ self._hide_error()
201
+ self._finalize_submission()
202
+ except ValueError as e:
203
+ self._show_error(str(e))
204
+
205
+ def _validate_fields(self) -> None:
206
+ """Validate all required fields."""
207
+ name = self.query_one("#name-input", Input).value.strip()
208
+ if not name:
209
+ raise ValueError("Name is required")
210
+
211
+ if not re.match(r"^[a-z0-9._-]+$", name):
212
+ raise ValueError(
213
+ "Name must be lowercase alphanumeric with dots, dashes, or underscores only"
214
+ )
215
+
216
+ definition = self.query_one("#definition-textarea", TextArea).text.strip()
217
+ if not definition:
218
+ raise ValueError(
219
+ "Schema definition is required. Paste a JSON Schema or regex pattern."
220
+ )
221
+
222
+ # Validate JSON schema format
223
+ schema_format = self._get_schema_format()
224
+ if schema_format == SchemaFormat.JSON_SCHEMA:
225
+ try:
226
+ json.loads(definition)
227
+ except json.JSONDecodeError as e:
228
+ raise ValueError(f"Invalid JSON in schema definition: {e.msg}")
229
+
230
+ def _get_schema_format(self) -> SchemaFormat:
231
+ """Get schema format from RadioSet."""
232
+ radios = self.query_one("#format-radios", RadioSet)
233
+ pressed = radios.pressed_button
234
+ if pressed is None or pressed.id == "format-json-schema":
235
+ return SchemaFormat.JSON_SCHEMA
236
+ return SchemaFormat.REGEX
237
+
238
+ def _get_scope(self) -> OwnershipScope:
239
+ """Get scope from RadioSet."""
240
+ return get_scope_from_radioset(self.query_one("#scope-radios", RadioSet))
241
+
242
+ def _build_request(self) -> CreateSchemaRequest:
243
+ """Build the API request."""
244
+ name = self.query_one("#name-input", Input).value.strip()
245
+ definition = self.query_one("#definition-textarea", TextArea).text.strip()
246
+ description = self.query_one("#description-input", Input).value.strip() or None
247
+
248
+ return CreateSchemaRequest(
249
+ name=name,
250
+ schema_format=self._get_schema_format(),
251
+ schema_definition=definition,
252
+ description=description,
253
+ scope=self._get_scope(),
254
+ )
255
+
256
+ @work(exclusive=True)
257
+ async def _finalize_submission(self) -> None:
258
+ """Submit to API."""
259
+ try:
260
+ request = self._build_request()
261
+ response = await self._client.create_schema(request)
262
+
263
+ result = SchemaCreateResult(
264
+ schema_id=str(response.id),
265
+ schema_name=response.schema_name,
266
+ version=response.version,
267
+ )
268
+ self.dismiss(result)
269
+
270
+ except Exception as e:
271
+ self._show_error(f"Failed to create schema: {e}")