keboola-cli 0.63.4__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 (306) hide show
  1. keboola_agent_cli/__init__.py +34 -0
  2. keboola_agent_cli/__main__.py +5 -0
  3. keboola_agent_cli/_ui_dist/assets/arc-DhFYIddx.js +2 -0
  4. keboola_agent_cli/_ui_dist/assets/arc-DhFYIddx.js.map +1 -0
  5. keboola_agent_cli/_ui_dist/assets/architecture-7EHR7CIX-hNCijx_H.js +1 -0
  6. keboola_agent_cli/_ui_dist/assets/architectureDiagram-3BPJPVTR-C6hUlprM.js +37 -0
  7. keboola_agent_cli/_ui_dist/assets/architectureDiagram-3BPJPVTR-C6hUlprM.js.map +1 -0
  8. keboola_agent_cli/_ui_dist/assets/array-BifhSqXX.js +2 -0
  9. keboola_agent_cli/_ui_dist/assets/array-BifhSqXX.js.map +1 -0
  10. keboola_agent_cli/_ui_dist/assets/blockDiagram-GPEHLZMM-DC7qY9i4.js +133 -0
  11. keboola_agent_cli/_ui_dist/assets/blockDiagram-GPEHLZMM-DC7qY9i4.js.map +1 -0
  12. keboola_agent_cli/_ui_dist/assets/c4Diagram-AAUBKEIU-5Lh44evt.js +11 -0
  13. keboola_agent_cli/_ui_dist/assets/c4Diagram-AAUBKEIU-5Lh44evt.js.map +1 -0
  14. keboola_agent_cli/_ui_dist/assets/channel-DBMrXlxx.js +2 -0
  15. keboola_agent_cli/_ui_dist/assets/channel-DBMrXlxx.js.map +1 -0
  16. keboola_agent_cli/_ui_dist/assets/chunk-2J33WTMH-Coy82EBh.js +2 -0
  17. keboola_agent_cli/_ui_dist/assets/chunk-2J33WTMH-Coy82EBh.js.map +1 -0
  18. keboola_agent_cli/_ui_dist/assets/chunk-3OPIFGDE-BQC5CRHI.js +63 -0
  19. keboola_agent_cli/_ui_dist/assets/chunk-3OPIFGDE-BQC5CRHI.js.map +1 -0
  20. keboola_agent_cli/_ui_dist/assets/chunk-4BX2VUAB-DUuEt70o.js +2 -0
  21. keboola_agent_cli/_ui_dist/assets/chunk-4BX2VUAB-DUuEt70o.js.map +1 -0
  22. keboola_agent_cli/_ui_dist/assets/chunk-55IACEB6-BvR-6chF.js +2 -0
  23. keboola_agent_cli/_ui_dist/assets/chunk-55IACEB6-BvR-6chF.js.map +1 -0
  24. keboola_agent_cli/_ui_dist/assets/chunk-5ZQYHXKU-BjcTN7ul.js +3 -0
  25. keboola_agent_cli/_ui_dist/assets/chunk-5ZQYHXKU-BjcTN7ul.js.map +1 -0
  26. keboola_agent_cli/_ui_dist/assets/chunk-727SXJPM-C0zxqqRN.js +207 -0
  27. keboola_agent_cli/_ui_dist/assets/chunk-727SXJPM-C0zxqqRN.js.map +1 -0
  28. keboola_agent_cli/_ui_dist/assets/chunk-AQP2D5EJ-CXf7rIlZ.js +232 -0
  29. keboola_agent_cli/_ui_dist/assets/chunk-AQP2D5EJ-CXf7rIlZ.js.map +1 -0
  30. keboola_agent_cli/_ui_dist/assets/chunk-BSJP7CBP-Oj_FO9Q7.js +2 -0
  31. keboola_agent_cli/_ui_dist/assets/chunk-BSJP7CBP-Oj_FO9Q7.js.map +1 -0
  32. keboola_agent_cli/_ui_dist/assets/chunk-CSCIHK7Q-CcTsLrFc.js +124 -0
  33. keboola_agent_cli/_ui_dist/assets/chunk-CSCIHK7Q-CcTsLrFc.js.map +1 -0
  34. keboola_agent_cli/_ui_dist/assets/chunk-FMBD7UC4-FH-zLkkW.js +16 -0
  35. keboola_agent_cli/_ui_dist/assets/chunk-FMBD7UC4-FH-zLkkW.js.map +1 -0
  36. keboola_agent_cli/_ui_dist/assets/chunk-L5ZTLDWV-B1Ky_e7O.js +2 -0
  37. keboola_agent_cli/_ui_dist/assets/chunk-L5ZTLDWV-B1Ky_e7O.js.map +1 -0
  38. keboola_agent_cli/_ui_dist/assets/chunk-ND2GUHAM-BHz1rpbm.js +2 -0
  39. keboola_agent_cli/_ui_dist/assets/chunk-ND2GUHAM-BHz1rpbm.js.map +1 -0
  40. keboola_agent_cli/_ui_dist/assets/chunk-NNHCCRGN-DlpIbxXb.js +160 -0
  41. keboola_agent_cli/_ui_dist/assets/chunk-NNHCCRGN-DlpIbxXb.js.map +1 -0
  42. keboola_agent_cli/_ui_dist/assets/chunk-NZK2D7GU-tnrSoegS.js +2 -0
  43. keboola_agent_cli/_ui_dist/assets/chunk-NZK2D7GU-tnrSoegS.js.map +1 -0
  44. keboola_agent_cli/_ui_dist/assets/chunk-O5CBEL6O-DxxqDH0l.js +71 -0
  45. keboola_agent_cli/_ui_dist/assets/chunk-O5CBEL6O-DxxqDH0l.js.map +1 -0
  46. keboola_agent_cli/_ui_dist/assets/chunk-QZHKN3VN-CSjc2gjj.js +2 -0
  47. keboola_agent_cli/_ui_dist/assets/chunk-QZHKN3VN-CSjc2gjj.js.map +1 -0
  48. keboola_agent_cli/_ui_dist/assets/classDiagram-4FO5ZUOK-BuZcZu85.js +2 -0
  49. keboola_agent_cli/_ui_dist/assets/classDiagram-4FO5ZUOK-BuZcZu85.js.map +1 -0
  50. keboola_agent_cli/_ui_dist/assets/classDiagram-v2-Q7XG4LA2-BuZcZu85.js +2 -0
  51. keboola_agent_cli/_ui_dist/assets/classDiagram-v2-Q7XG4LA2-BuZcZu85.js.map +1 -0
  52. keboola_agent_cli/_ui_dist/assets/cose-bilkent-S5V4N54A-Y0L8LDMa.js +2 -0
  53. keboola_agent_cli/_ui_dist/assets/cose-bilkent-S5V4N54A-Y0L8LDMa.js.map +1 -0
  54. keboola_agent_cli/_ui_dist/assets/cytoscape.esm-C8YCVR3_.js +322 -0
  55. keboola_agent_cli/_ui_dist/assets/cytoscape.esm-C8YCVR3_.js.map +1 -0
  56. keboola_agent_cli/_ui_dist/assets/dagre-BM42HDAG-UZ-9BTqF.js +5 -0
  57. keboola_agent_cli/_ui_dist/assets/dagre-BM42HDAG-UZ-9BTqF.js.map +1 -0
  58. keboola_agent_cli/_ui_dist/assets/dagre-Bx709z4p.js +2 -0
  59. keboola_agent_cli/_ui_dist/assets/dagre-Bx709z4p.js.map +1 -0
  60. keboola_agent_cli/_ui_dist/assets/defaultLocale-C8Fc0cco.js +2 -0
  61. keboola_agent_cli/_ui_dist/assets/defaultLocale-C8Fc0cco.js.map +1 -0
  62. keboola_agent_cli/_ui_dist/assets/diagram-2AECGRRQ-DoDQ60wi.js +44 -0
  63. keboola_agent_cli/_ui_dist/assets/diagram-2AECGRRQ-DoDQ60wi.js.map +1 -0
  64. keboola_agent_cli/_ui_dist/assets/diagram-5GNKFQAL-CMGFxpUs.js +11 -0
  65. keboola_agent_cli/_ui_dist/assets/diagram-5GNKFQAL-CMGFxpUs.js.map +1 -0
  66. keboola_agent_cli/_ui_dist/assets/diagram-KO2AKTUF-1uGDa-Iu.js +4 -0
  67. keboola_agent_cli/_ui_dist/assets/diagram-KO2AKTUF-1uGDa-Iu.js.map +1 -0
  68. keboola_agent_cli/_ui_dist/assets/diagram-LMA3HP47-XtFH7B51.js +25 -0
  69. keboola_agent_cli/_ui_dist/assets/diagram-LMA3HP47-XtFH7B51.js.map +1 -0
  70. keboola_agent_cli/_ui_dist/assets/diagram-OG6HWLK6-B4_Te1T5.js +25 -0
  71. keboola_agent_cli/_ui_dist/assets/diagram-OG6HWLK6-B4_Te1T5.js.map +1 -0
  72. keboola_agent_cli/_ui_dist/assets/dist-Di6zmlv0.js +2 -0
  73. keboola_agent_cli/_ui_dist/assets/dist-Di6zmlv0.js.map +1 -0
  74. keboola_agent_cli/_ui_dist/assets/erDiagram-TEJ5UH35-NjQkrdFt.js +86 -0
  75. keboola_agent_cli/_ui_dist/assets/erDiagram-TEJ5UH35-NjQkrdFt.js.map +1 -0
  76. keboola_agent_cli/_ui_dist/assets/eventmodeling-FCH6USID-BrJMIks8.js +1 -0
  77. keboola_agent_cli/_ui_dist/assets/flowDiagram-I6XJVG4X-CIr8DWl7.js +163 -0
  78. keboola_agent_cli/_ui_dist/assets/flowDiagram-I6XJVG4X-CIr8DWl7.js.map +1 -0
  79. keboola_agent_cli/_ui_dist/assets/ganttDiagram-6RSMTGT7-C1VY_xbQ.js +293 -0
  80. keboola_agent_cli/_ui_dist/assets/ganttDiagram-6RSMTGT7-C1VY_xbQ.js.map +1 -0
  81. keboola_agent_cli/_ui_dist/assets/gitGraph-WXDBUCRP-COacYjo-.js +1 -0
  82. keboola_agent_cli/_ui_dist/assets/gitGraphDiagram-PVQCEYII-DQT8-kg2.js +107 -0
  83. keboola_agent_cli/_ui_dist/assets/gitGraphDiagram-PVQCEYII-DQT8-kg2.js.map +1 -0
  84. keboola_agent_cli/_ui_dist/assets/graphlib-B8gBHxth.js +2 -0
  85. keboola_agent_cli/_ui_dist/assets/graphlib-B8gBHxth.js.map +1 -0
  86. keboola_agent_cli/_ui_dist/assets/index-CMq50kkV.css +1 -0
  87. keboola_agent_cli/_ui_dist/assets/index-D8W97DAz.js +118 -0
  88. keboola_agent_cli/_ui_dist/assets/index-D8W97DAz.js.map +1 -0
  89. keboola_agent_cli/_ui_dist/assets/info-J43DQDTF-DdCTRIzU.js +1 -0
  90. keboola_agent_cli/_ui_dist/assets/infoDiagram-5YYISTIA-C77rsoTp.js +3 -0
  91. keboola_agent_cli/_ui_dist/assets/infoDiagram-5YYISTIA-C77rsoTp.js.map +1 -0
  92. keboola_agent_cli/_ui_dist/assets/init-D6jRqBbL.js +2 -0
  93. keboola_agent_cli/_ui_dist/assets/init-D6jRqBbL.js.map +1 -0
  94. keboola_agent_cli/_ui_dist/assets/ishikawaDiagram-YF4QCWOH-BcTbXaLy.js +71 -0
  95. keboola_agent_cli/_ui_dist/assets/ishikawaDiagram-YF4QCWOH-BcTbXaLy.js.map +1 -0
  96. keboola_agent_cli/_ui_dist/assets/journeyDiagram-JHISSGLW-BejeAJQ_.js +140 -0
  97. keboola_agent_cli/_ui_dist/assets/journeyDiagram-JHISSGLW-BejeAJQ_.js.map +1 -0
  98. keboola_agent_cli/_ui_dist/assets/kanban-definition-UN3LZRKU-BRNz_UrH.js +90 -0
  99. keboola_agent_cli/_ui_dist/assets/kanban-definition-UN3LZRKU-BRNz_UrH.js.map +1 -0
  100. keboola_agent_cli/_ui_dist/assets/katex-C4eR7coU.js +258 -0
  101. keboola_agent_cli/_ui_dist/assets/katex-C4eR7coU.js.map +1 -0
  102. keboola_agent_cli/_ui_dist/assets/line-CzAQKFbJ.js +2 -0
  103. keboola_agent_cli/_ui_dist/assets/line-CzAQKFbJ.js.map +1 -0
  104. keboola_agent_cli/_ui_dist/assets/linear-DUNFFdck.js +2 -0
  105. keboola_agent_cli/_ui_dist/assets/linear-DUNFFdck.js.map +1 -0
  106. keboola_agent_cli/_ui_dist/assets/mermaid-parser.core-CpuBOkFa.js +5 -0
  107. keboola_agent_cli/_ui_dist/assets/mermaid-parser.core-CpuBOkFa.js.map +1 -0
  108. keboola_agent_cli/_ui_dist/assets/mindmap-definition-RKZ34NQL-9EJQNjH0.js +97 -0
  109. keboola_agent_cli/_ui_dist/assets/mindmap-definition-RKZ34NQL-9EJQNjH0.js.map +1 -0
  110. keboola_agent_cli/_ui_dist/assets/ordinal-hYBb2elL.js +2 -0
  111. keboola_agent_cli/_ui_dist/assets/ordinal-hYBb2elL.js.map +1 -0
  112. keboola_agent_cli/_ui_dist/assets/packet-YPE3B663-DLiiw_B2.js +1 -0
  113. keboola_agent_cli/_ui_dist/assets/path-BWPyau1x.js +2 -0
  114. keboola_agent_cli/_ui_dist/assets/path-BWPyau1x.js.map +1 -0
  115. keboola_agent_cli/_ui_dist/assets/pie-LRSECV5Y-CRoO8G1g.js +1 -0
  116. keboola_agent_cli/_ui_dist/assets/pieDiagram-4H26LBE5-XH4cy6Cb.js +31 -0
  117. keboola_agent_cli/_ui_dist/assets/pieDiagram-4H26LBE5-XH4cy6Cb.js.map +1 -0
  118. keboola_agent_cli/_ui_dist/assets/quadrantDiagram-W4KKPZXB-fdhc93U8.js +8 -0
  119. keboola_agent_cli/_ui_dist/assets/quadrantDiagram-W4KKPZXB-fdhc93U8.js.map +1 -0
  120. keboola_agent_cli/_ui_dist/assets/radar-GUYGQ44K-DAlLVJHm.js +1 -0
  121. keboola_agent_cli/_ui_dist/assets/requirementDiagram-4Y6WPE33-a94eP3R9.js +85 -0
  122. keboola_agent_cli/_ui_dist/assets/requirementDiagram-4Y6WPE33-a94eP3R9.js.map +1 -0
  123. keboola_agent_cli/_ui_dist/assets/rough.esm-CSKSodPl.js +2 -0
  124. keboola_agent_cli/_ui_dist/assets/rough.esm-CSKSodPl.js.map +1 -0
  125. keboola_agent_cli/_ui_dist/assets/sankeyDiagram-5OEKKPKP-jcBa02sp.js +41 -0
  126. keboola_agent_cli/_ui_dist/assets/sankeyDiagram-5OEKKPKP-jcBa02sp.js.map +1 -0
  127. keboola_agent_cli/_ui_dist/assets/sequenceDiagram-3UESZ5HK-A5-GGM-e.js +163 -0
  128. keboola_agent_cli/_ui_dist/assets/sequenceDiagram-3UESZ5HK-A5-GGM-e.js.map +1 -0
  129. keboola_agent_cli/_ui_dist/assets/src-ZI-V_AF0.js +2 -0
  130. keboola_agent_cli/_ui_dist/assets/src-ZI-V_AF0.js.map +1 -0
  131. keboola_agent_cli/_ui_dist/assets/stateDiagram-AJRCARHV-BKAA5rqE.js +2 -0
  132. keboola_agent_cli/_ui_dist/assets/stateDiagram-AJRCARHV-BKAA5rqE.js.map +1 -0
  133. keboola_agent_cli/_ui_dist/assets/stateDiagram-v2-BHNVJYJU-DnJwJBsE.js +2 -0
  134. keboola_agent_cli/_ui_dist/assets/stateDiagram-v2-BHNVJYJU-DnJwJBsE.js.map +1 -0
  135. keboola_agent_cli/_ui_dist/assets/timeline-definition-PNZ67QCA-Cy39jp8b.js +121 -0
  136. keboola_agent_cli/_ui_dist/assets/timeline-definition-PNZ67QCA-Cy39jp8b.js.map +1 -0
  137. keboola_agent_cli/_ui_dist/assets/treeView-BLDUP644-DbLYl23-.js +1 -0
  138. keboola_agent_cli/_ui_dist/assets/treemap-LRROVOQU-Bp0eGlOt.js +1 -0
  139. keboola_agent_cli/_ui_dist/assets/vennDiagram-CIIHVFJN-BGECKubd.js +35 -0
  140. keboola_agent_cli/_ui_dist/assets/vennDiagram-CIIHVFJN-BGECKubd.js.map +1 -0
  141. keboola_agent_cli/_ui_dist/assets/wardley-L42UT6IY-D4yH4jqS.js +1 -0
  142. keboola_agent_cli/_ui_dist/assets/wardleyDiagram-YWT4CUSO-D6XRG3cZ.js +79 -0
  143. keboola_agent_cli/_ui_dist/assets/wardleyDiagram-YWT4CUSO-D6XRG3cZ.js.map +1 -0
  144. keboola_agent_cli/_ui_dist/assets/xychartDiagram-2RQKCTM6-DRre-pfZ.js +8 -0
  145. keboola_agent_cli/_ui_dist/assets/xychartDiagram-2RQKCTM6-DRre-pfZ.js.map +1 -0
  146. keboola_agent_cli/_ui_dist/index.html +50 -0
  147. keboola_agent_cli/ai_client.py +83 -0
  148. keboola_agent_cli/auto_update.py +550 -0
  149. keboola_agent_cli/changelog.py +1198 -0
  150. keboola_agent_cli/cli.py +448 -0
  151. keboola_agent_cli/client.py +3422 -0
  152. keboola_agent_cli/commands/__init__.py +0 -0
  153. keboola_agent_cli/commands/_data_app_git.py +343 -0
  154. keboola_agent_cli/commands/_helpers.py +377 -0
  155. keboola_agent_cli/commands/_metadata_input.py +49 -0
  156. keboola_agent_cli/commands/_semantic_layer_crud.py +632 -0
  157. keboola_agent_cli/commands/_semantic_layer_helpers.py +44 -0
  158. keboola_agent_cli/commands/_semantic_layer_reference_data.py +247 -0
  159. keboola_agent_cli/commands/agent.py +968 -0
  160. keboola_agent_cli/commands/branch.py +423 -0
  161. keboola_agent_cli/commands/changelog.py +168 -0
  162. keboola_agent_cli/commands/component.py +216 -0
  163. keboola_agent_cli/commands/config.py +2442 -0
  164. keboola_agent_cli/commands/context.py +1481 -0
  165. keboola_agent_cli/commands/data_app.py +1279 -0
  166. keboola_agent_cli/commands/dev_portal.py +584 -0
  167. keboola_agent_cli/commands/doctor.py +37 -0
  168. keboola_agent_cli/commands/encrypt.py +145 -0
  169. keboola_agent_cli/commands/feature.py +311 -0
  170. keboola_agent_cli/commands/flow.py +948 -0
  171. keboola_agent_cli/commands/http_client.py +157 -0
  172. keboola_agent_cli/commands/init.py +279 -0
  173. keboola_agent_cli/commands/job.py +661 -0
  174. keboola_agent_cli/commands/kai.py +301 -0
  175. keboola_agent_cli/commands/lineage.py +1464 -0
  176. keboola_agent_cli/commands/org.py +292 -0
  177. keboola_agent_cli/commands/permissions.py +360 -0
  178. keboola_agent_cli/commands/project.py +1192 -0
  179. keboola_agent_cli/commands/repl.py +243 -0
  180. keboola_agent_cli/commands/schedule.py +340 -0
  181. keboola_agent_cli/commands/search.py +178 -0
  182. keboola_agent_cli/commands/semantic_layer.py +939 -0
  183. keboola_agent_cli/commands/serve.py +272 -0
  184. keboola_agent_cli/commands/sharing.py +340 -0
  185. keboola_agent_cli/commands/storage.py +2630 -0
  186. keboola_agent_cli/commands/stream.py +266 -0
  187. keboola_agent_cli/commands/sync.py +1277 -0
  188. keboola_agent_cli/commands/tool.py +206 -0
  189. keboola_agent_cli/commands/version.py +186 -0
  190. keboola_agent_cli/commands/workspace.py +635 -0
  191. keboola_agent_cli/config_store.py +582 -0
  192. keboola_agent_cli/constants.py +528 -0
  193. keboola_agent_cli/data_science_client.py +342 -0
  194. keboola_agent_cli/dev_portal_client.py +323 -0
  195. keboola_agent_cli/errors.py +248 -0
  196. keboola_agent_cli/http_base.py +315 -0
  197. keboola_agent_cli/json_utils.py +126 -0
  198. keboola_agent_cli/lib.py +536 -0
  199. keboola_agent_cli/manage_client.py +324 -0
  200. keboola_agent_cli/metastore_client.py +214 -0
  201. keboola_agent_cli/models.py +427 -0
  202. keboola_agent_cli/output.py +1084 -0
  203. keboola_agent_cli/permissions.py +469 -0
  204. keboola_agent_cli/py.typed +3 -0
  205. keboola_agent_cli/result_models.py +271 -0
  206. keboola_agent_cli/server/__init__.py +34 -0
  207. keboola_agent_cli/server/agent_runner.py +1289 -0
  208. keboola_agent_cli/server/agents_store.py +325 -0
  209. keboola_agent_cli/server/app.py +764 -0
  210. keboola_agent_cli/server/auth.py +117 -0
  211. keboola_agent_cli/server/dependencies.py +149 -0
  212. keboola_agent_cli/server/pricing.py +303 -0
  213. keboola_agent_cli/server/routers/__init__.py +1 -0
  214. keboola_agent_cli/server/routers/agents.py +616 -0
  215. keboola_agent_cli/server/routers/ai_chat.py +129 -0
  216. keboola_agent_cli/server/routers/branches.py +133 -0
  217. keboola_agent_cli/server/routers/components.py +48 -0
  218. keboola_agent_cli/server/routers/configs.py +507 -0
  219. keboola_agent_cli/server/routers/data_apps.py +384 -0
  220. keboola_agent_cli/server/routers/dev_portal.py +67 -0
  221. keboola_agent_cli/server/routers/encrypt.py +35 -0
  222. keboola_agent_cli/server/routers/feature.py +179 -0
  223. keboola_agent_cli/server/routers/flows.py +204 -0
  224. keboola_agent_cli/server/routers/health.py +53 -0
  225. keboola_agent_cli/server/routers/jobs.py +175 -0
  226. keboola_agent_cli/server/routers/kai.py +80 -0
  227. keboola_agent_cli/server/routers/lineage.py +226 -0
  228. keboola_agent_cli/server/routers/mcp.py +70 -0
  229. keboola_agent_cli/server/routers/members.py +170 -0
  230. keboola_agent_cli/server/routers/org.py +96 -0
  231. keboola_agent_cli/server/routers/projects.py +106 -0
  232. keboola_agent_cli/server/routers/schedules.py +54 -0
  233. keboola_agent_cli/server/routers/search.py +30 -0
  234. keboola_agent_cli/server/routers/semantic_layer.py +650 -0
  235. keboola_agent_cli/server/routers/sharing.py +86 -0
  236. keboola_agent_cli/server/routers/storage.py +574 -0
  237. keboola_agent_cli/server/routers/stream.py +100 -0
  238. keboola_agent_cli/server/routers/workspaces.py +302 -0
  239. keboola_agent_cli/server/run_broadcaster.py +329 -0
  240. keboola_agent_cli/server/sse.py +25 -0
  241. keboola_agent_cli/services/__init__.py +0 -0
  242. keboola_agent_cli/services/_encryption.py +217 -0
  243. keboola_agent_cli/services/_semantic_layer_cascade.py +147 -0
  244. keboola_agent_cli/services/_semantic_layer_crud.py +382 -0
  245. keboola_agent_cli/services/_semantic_layer_internals.py +1078 -0
  246. keboola_agent_cli/services/_semantic_layer_lookup.py +181 -0
  247. keboola_agent_cli/services/_semantic_layer_reference_data.py +217 -0
  248. keboola_agent_cli/services/_sync_bindings.py +456 -0
  249. keboola_agent_cli/services/_sync_branch.py +191 -0
  250. keboola_agent_cli/services/_sync_bulk.py +228 -0
  251. keboola_agent_cli/services/_sync_clone.py +163 -0
  252. keboola_agent_cli/services/_sync_models.py +97 -0
  253. keboola_agent_cli/services/_sync_push_ops.py +369 -0
  254. keboola_agent_cli/services/_sync_storage.py +376 -0
  255. keboola_agent_cli/services/_sync_writeback.py +167 -0
  256. keboola_agent_cli/services/agent_service.py +458 -0
  257. keboola_agent_cli/services/base.py +175 -0
  258. keboola_agent_cli/services/branch_service.py +588 -0
  259. keboola_agent_cli/services/component_service.py +694 -0
  260. keboola_agent_cli/services/config_service.py +2099 -0
  261. keboola_agent_cli/services/data_app_git_service.py +224 -0
  262. keboola_agent_cli/services/data_app_service.py +2082 -0
  263. keboola_agent_cli/services/deep_lineage_service.py +1322 -0
  264. keboola_agent_cli/services/dev_portal_service.py +345 -0
  265. keboola_agent_cli/services/doctor_service.py +445 -0
  266. keboola_agent_cli/services/encrypt_service.py +87 -0
  267. keboola_agent_cli/services/feature_service.py +268 -0
  268. keboola_agent_cli/services/flow_service.py +769 -0
  269. keboola_agent_cli/services/flow_validation.py +188 -0
  270. keboola_agent_cli/services/http_forwarder_service.py +236 -0
  271. keboola_agent_cli/services/job_idempotency_store.py +285 -0
  272. keboola_agent_cli/services/job_service.py +797 -0
  273. keboola_agent_cli/services/kai_service.py +367 -0
  274. keboola_agent_cli/services/lineage_service.py +274 -0
  275. keboola_agent_cli/services/mcp_service.py +1498 -0
  276. keboola_agent_cli/services/mcp_transport.py +259 -0
  277. keboola_agent_cli/services/member_service.py +593 -0
  278. keboola_agent_cli/services/org_service.py +619 -0
  279. keboola_agent_cli/services/project_service.py +947 -0
  280. keboola_agent_cli/services/repo_validate_service.py +767 -0
  281. keboola_agent_cli/services/schedule_service.py +731 -0
  282. keboola_agent_cli/services/search_service.py +331 -0
  283. keboola_agent_cli/services/semantic_layer_service.py +1497 -0
  284. keboola_agent_cli/services/sharing_service.py +307 -0
  285. keboola_agent_cli/services/storage_service.py +2524 -0
  286. keboola_agent_cli/services/stream_service.py +395 -0
  287. keboola_agent_cli/services/sync_service.py +2244 -0
  288. keboola_agent_cli/services/variables_service.py +447 -0
  289. keboola_agent_cli/services/version_service.py +1038 -0
  290. keboola_agent_cli/services/workspace_service.py +1103 -0
  291. keboola_agent_cli/stream_client.py +217 -0
  292. keboola_agent_cli/sync/__init__.py +1 -0
  293. keboola_agent_cli/sync/branch_mapping.py +174 -0
  294. keboola_agent_cli/sync/clone.py +211 -0
  295. keboola_agent_cli/sync/code_extraction.py +655 -0
  296. keboola_agent_cli/sync/config_format.py +290 -0
  297. keboola_agent_cli/sync/diff_engine.py +566 -0
  298. keboola_agent_cli/sync/git_utils.py +93 -0
  299. keboola_agent_cli/sync/manifest.py +162 -0
  300. keboola_agent_cli/sync/naming.py +90 -0
  301. keboola_agent_cli/sync/secrets.py +62 -0
  302. keboola_agent_cli/sync/sql_split.py +134 -0
  303. keboola_cli-0.63.4.dist-info/METADATA +308 -0
  304. keboola_cli-0.63.4.dist-info/RECORD +306 -0
  305. keboola_cli-0.63.4.dist-info/WHEEL +4 -0
  306. keboola_cli-0.63.4.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,661 @@
1
+ """Job commands - list history, show detail, and run jobs via Queue API.
2
+
3
+ Thin CLI layer: parses arguments, calls JobService, formats output.
4
+ No business logic belongs here.
5
+ """
6
+
7
+ from enum import StrEnum
8
+
9
+ import typer
10
+ from rich.markup import escape
11
+
12
+ from ..config_store import ConfigStore
13
+ from ..constants import (
14
+ DEFAULT_JOB_LIMIT,
15
+ DEFAULT_JOB_MODE,
16
+ DEFAULT_JOB_RUN_TIMEOUT,
17
+ DEFAULT_LOG_TAIL_LINES,
18
+ DEFAULT_POLL_STRATEGY,
19
+ KILLABLE_JOB_STATUSES,
20
+ MAX_JOB_LIMIT,
21
+ MAX_LOG_TAIL_LINES,
22
+ VALID_STATUSES,
23
+ )
24
+ from ..errors import ConfigError, ErrorCode, KeboolaApiError
25
+ from ..output import format_job_detail, format_jobs_table
26
+ from ._helpers import (
27
+ check_cli_permission,
28
+ emit_project_warnings,
29
+ get_formatter,
30
+ get_service,
31
+ map_error_to_exit_code,
32
+ resolve_branch,
33
+ validate_branch_requires_project,
34
+ )
35
+
36
+
37
+ class JobMode(StrEnum):
38
+ """Queue API job mode."""
39
+
40
+ run = "run"
41
+ debug = "debug"
42
+
43
+
44
+ class PollStrategy(StrEnum):
45
+ """Polling cadence for --wait."""
46
+
47
+ exponential = "exponential"
48
+ fixed = "fixed"
49
+
50
+
51
+ job_app = typer.Typer(help="Browse job history and run jobs")
52
+
53
+
54
+ @job_app.callback(invoke_without_command=True)
55
+ def _job_permission_check(ctx: typer.Context) -> None:
56
+ check_cli_permission(ctx, "job")
57
+
58
+
59
+ @job_app.command("list")
60
+ def job_list(
61
+ ctx: typer.Context,
62
+ project: list[str] | None = typer.Option(
63
+ None,
64
+ "--project",
65
+ help="Project alias to query (can be repeated for multiple projects)",
66
+ ),
67
+ component_id: str | None = typer.Option(
68
+ None,
69
+ "--component-id",
70
+ help="Filter by component ID (e.g. keboola.ex-db-snowflake)",
71
+ ),
72
+ config_id: str | None = typer.Option(
73
+ None,
74
+ "--config-id",
75
+ help="Filter by configuration ID (requires --component-id)",
76
+ ),
77
+ status: str | None = typer.Option(
78
+ None,
79
+ "--status",
80
+ help="Filter by job status: processing, terminated, cancelled, success, error",
81
+ ),
82
+ limit: int = typer.Option(
83
+ DEFAULT_JOB_LIMIT,
84
+ "--limit",
85
+ help=f"Maximum number of jobs to return per project (1-{MAX_JOB_LIMIT})",
86
+ ),
87
+ ) -> None:
88
+ """List jobs from connected projects."""
89
+ formatter = get_formatter(ctx)
90
+ service = get_service(ctx, "job_service")
91
+
92
+ # Validate status
93
+ if status and status not in VALID_STATUSES:
94
+ formatter.error(
95
+ message=f"Invalid status '{status}'. Valid statuses: {', '.join(VALID_STATUSES)}",
96
+ error_code=ErrorCode.INVALID_ARGUMENT,
97
+ )
98
+ raise typer.Exit(code=2)
99
+
100
+ # Validate limit range
101
+ if limit < 1 or limit > MAX_JOB_LIMIT:
102
+ formatter.error(
103
+ message=f"Invalid limit {limit}. Must be between 1 and {MAX_JOB_LIMIT}.",
104
+ error_code=ErrorCode.INVALID_ARGUMENT,
105
+ )
106
+ raise typer.Exit(code=2)
107
+
108
+ # Validate config_id requires component_id
109
+ if config_id and not component_id:
110
+ formatter.error(
111
+ message="--config-id requires --component-id to be specified.",
112
+ error_code=ErrorCode.INVALID_ARGUMENT,
113
+ )
114
+ raise typer.Exit(code=2)
115
+
116
+ try:
117
+ result = service.list_jobs(
118
+ aliases=project,
119
+ component_id=component_id,
120
+ config_id=config_id,
121
+ status=status,
122
+ limit=limit,
123
+ )
124
+ except ConfigError as exc:
125
+ formatter.error(message=exc.message, error_code=ErrorCode.CONFIG_ERROR)
126
+ raise typer.Exit(code=5) from None
127
+
128
+ if formatter.json_mode:
129
+ formatter.output(result)
130
+ else:
131
+ format_jobs_table(formatter.console, result)
132
+ emit_project_warnings(formatter, result)
133
+
134
+
135
+ @job_app.command("detail")
136
+ def job_detail(
137
+ ctx: typer.Context,
138
+ project: str = typer.Option(..., "--project", help="Project alias"),
139
+ job_id: str = typer.Option(..., "--job-id", help="Job ID"),
140
+ ) -> None:
141
+ """Show detailed information about a specific job."""
142
+ formatter = get_formatter(ctx)
143
+ service = get_service(ctx, "job_service")
144
+
145
+ try:
146
+ result = service.get_job_detail(alias=project, job_id=job_id)
147
+ formatter.output(result, format_job_detail)
148
+ except ConfigError as exc:
149
+ formatter.error(message=exc.message, error_code=ErrorCode.CONFIG_ERROR)
150
+ raise typer.Exit(code=5) from None
151
+ except KeboolaApiError as exc:
152
+ exit_code = map_error_to_exit_code(exc)
153
+ formatter.error(
154
+ message=exc.message,
155
+ error_code=exc.error_code,
156
+ project=project,
157
+ retryable=exc.retryable,
158
+ )
159
+ raise typer.Exit(code=exit_code) from None
160
+
161
+
162
+ @job_app.command("run")
163
+ def job_run(
164
+ ctx: typer.Context,
165
+ project: str = typer.Option(
166
+ ...,
167
+ "--project",
168
+ help="Project alias",
169
+ ),
170
+ component_id: str = typer.Option(
171
+ ...,
172
+ "--component-id",
173
+ help="Component ID (e.g. keboola.snowflake-transformation)",
174
+ ),
175
+ config_id: str = typer.Option(
176
+ ...,
177
+ "--config-id",
178
+ help="Configuration ID",
179
+ ),
180
+ row_id: list[str] | None = typer.Option(
181
+ None,
182
+ "--row-id",
183
+ help="Config row ID(s) to run (repeat for multiple; omit to run entire config)",
184
+ ),
185
+ wait: bool = typer.Option(
186
+ False,
187
+ "--wait",
188
+ help="Wait for job to finish (poll until terminal state)",
189
+ ),
190
+ timeout: float = typer.Option(
191
+ DEFAULT_JOB_RUN_TIMEOUT,
192
+ "--timeout",
193
+ help="Max seconds to wait when --wait is set",
194
+ ),
195
+ branch: int | None = typer.Option(
196
+ None,
197
+ "--branch",
198
+ help="Dev branch ID (overrides active branch)",
199
+ ),
200
+ mode: JobMode = typer.Option(
201
+ JobMode(DEFAULT_JOB_MODE),
202
+ "--mode",
203
+ help=(
204
+ "Queue API job mode. 'run' (default) executes the component "
205
+ "normally and writes to mapped output tables. 'debug' executes "
206
+ "the component but redirects its output to a Storage File tagged "
207
+ "'debug-<jobId>' instead of the destination buckets -- safe for "
208
+ "dry-runs and for reproducing a failing run on production "
209
+ "configuration without touching production data."
210
+ ),
211
+ ),
212
+ variable_values_id: str | None = typer.Option(
213
+ None,
214
+ "--variable-values-id",
215
+ help=(
216
+ "Explicit keboola.variables values-row ID to bind. Use when the "
217
+ "linked variables config has multiple rows and auto-resolution "
218
+ "(first row) picks the wrong one. Mutually exclusive with "
219
+ "--no-variables."
220
+ ),
221
+ ),
222
+ no_variables: bool = typer.Option(
223
+ False,
224
+ "--no-variables",
225
+ help=(
226
+ "Skip variable-values resolution entirely. Use for components "
227
+ "that do not support variables, or when intentionally running "
228
+ "against empty bindings. Mutually exclusive with "
229
+ "--variable-values-id."
230
+ ),
231
+ ),
232
+ poll_strategy: PollStrategy = typer.Option(
233
+ PollStrategy(DEFAULT_POLL_STRATEGY),
234
+ "--poll-strategy",
235
+ help=(
236
+ "Polling cadence used with --wait. 'exponential' (default) "
237
+ "starts at 2s and relaxes toward 15s as a job runs long "
238
+ "(2s x 30 -> 5s x 48 -> 15s). 'fixed' keeps a constant 1s "
239
+ "interval (useful for tests or very short jobs)."
240
+ ),
241
+ ),
242
+ log_tail_lines: int = typer.Option(
243
+ DEFAULT_LOG_TAIL_LINES,
244
+ "--log-tail-lines",
245
+ help=(
246
+ "On FAILED/WARNING/TERMINATED jobs, fetch the last N job events "
247
+ f"(from Storage Events API) and surface them as 'logTail' in "
248
+ f"JSON output or a panel in human mode. Only used with --wait. "
249
+ f"0 disables (recommended for automation pipelines); max "
250
+ f"{MAX_LOG_TAIL_LINES}."
251
+ ),
252
+ ),
253
+ idempotency_key: str | None = typer.Option(
254
+ None,
255
+ "--idempotency-key",
256
+ help=(
257
+ "Client-supplied de-duplication token (issue #427). On replay with "
258
+ "the same key, a prior still-running or non-failed job is returned "
259
+ "instead of creating a duplicate -- safe for resumed/retried build "
260
+ "steps. A prior FAILED run is re-run. Dedup is client-side (the "
261
+ "Queue API has no idempotency key) and scoped to this machine's "
262
+ "config-dir; reusing a key for a different component/config errors."
263
+ ),
264
+ ),
265
+ force_rerun: bool = typer.Option(
266
+ False,
267
+ "--force-rerun",
268
+ help="Ignore any stored --idempotency-key entry and always create a fresh job.",
269
+ ),
270
+ ) -> None:
271
+ """Run a job for a component configuration.
272
+
273
+ Creates a Queue API job and optionally waits for completion.
274
+ Use --row-id to run specific configuration rows.
275
+
276
+ When a dev branch is active (via 'branch use'), the job automatically
277
+ runs on that branch. Use --branch to override.
278
+
279
+ When the config has linked variables (configuration.variables_id),
280
+ kbagent auto-resolves a variableValuesId so the job binds to the
281
+ deployed values row. Override with --variable-values-id or skip
282
+ with --no-variables.
283
+
284
+ Queue polling uses an exponential curve by default (2s x 30 -> 5s x 48
285
+ -> 15s, total 5min before the 15s tail). If --timeout expires, kbagent
286
+ issues kill_job on the remote and exits 7 (JOB_TIMEOUT_TERMINATED) with
287
+ the cancelled job + log tail attached. If the kill itself fails, exits
288
+ 4 (QUEUE_JOB_TIMEOUT, retryable) so scripts can tell "we killed it"
289
+ from "local gave up, remote may still be running".
290
+ """
291
+ formatter = get_formatter(ctx)
292
+ service = get_service(ctx, "job_service")
293
+ config_store: ConfigStore = ctx.obj["config_store"]
294
+
295
+ if variable_values_id is not None:
296
+ variable_values_id = variable_values_id.strip()
297
+ if not variable_values_id:
298
+ formatter.error(
299
+ message=(
300
+ "--variable-values-id cannot be empty or whitespace. "
301
+ "Pass a row id, or omit the flag to auto-resolve the default row."
302
+ ),
303
+ error_code=ErrorCode.INVALID_ARGUMENT,
304
+ )
305
+ raise typer.Exit(code=2)
306
+
307
+ if variable_values_id and no_variables:
308
+ formatter.error(
309
+ message=(
310
+ "--variable-values-id and --no-variables are mutually exclusive. "
311
+ "Pass --variable-values-id to bind a specific values row, or "
312
+ "--no-variables to skip resolution, but not both."
313
+ ),
314
+ error_code=ErrorCode.INVALID_ARGUMENT,
315
+ )
316
+ raise typer.Exit(code=2)
317
+
318
+ if log_tail_lines < 0 or log_tail_lines > MAX_LOG_TAIL_LINES:
319
+ formatter.error(
320
+ message=(
321
+ f"--log-tail-lines must be between 0 and {MAX_LOG_TAIL_LINES}. "
322
+ f"Got {log_tail_lines}."
323
+ ),
324
+ error_code=ErrorCode.INVALID_ARGUMENT,
325
+ )
326
+ raise typer.Exit(code=2)
327
+
328
+ validate_branch_requires_project(formatter, branch, project)
329
+ _, effective_branch = resolve_branch(config_store, formatter, project, branch)
330
+
331
+ if not formatter.json_mode:
332
+ msg = f"Running [cyan]{component_id}[/cyan] / [cyan]{config_id}[/cyan]"
333
+ if row_id:
334
+ msg += f" (rows: {', '.join(row_id)})"
335
+ if effective_branch is not None:
336
+ msg += f" on branch [cyan]{effective_branch}[/cyan]"
337
+ if mode != DEFAULT_JOB_MODE:
338
+ msg += f" [bold yellow]mode={mode}[/bold yellow]"
339
+ if wait:
340
+ msg += f" [dim](waiting up to {timeout:.0f}s)[/dim]"
341
+ msg += "..."
342
+ formatter.console.print(msg)
343
+ if no_variables:
344
+ formatter.console.print("[dim]Skipping variable-values resolution.[/dim]")
345
+
346
+ try:
347
+ result = service.run_job(
348
+ alias=project,
349
+ component_id=component_id,
350
+ config_id=config_id,
351
+ config_row_ids=row_id,
352
+ wait=wait,
353
+ timeout=timeout,
354
+ branch_id=effective_branch,
355
+ variable_values_id=variable_values_id,
356
+ no_variables=no_variables,
357
+ poll_strategy=poll_strategy,
358
+ log_tail_lines=log_tail_lines,
359
+ mode=mode,
360
+ idempotency_key=idempotency_key,
361
+ force_rerun=force_rerun,
362
+ )
363
+ except ConfigError as exc:
364
+ formatter.error(message=exc.message, error_code=ErrorCode.CONFIG_ERROR)
365
+ raise typer.Exit(code=5) from None
366
+ except KeboolaApiError as exc:
367
+ formatter.error(
368
+ message=exc.message,
369
+ error_code=exc.error_code,
370
+ project=project,
371
+ retryable=exc.retryable,
372
+ details=exc.details or None,
373
+ )
374
+ if not formatter.json_mode:
375
+ _render_log_tail(formatter, exc.details)
376
+ raise typer.Exit(code=map_error_to_exit_code(exc)) from None
377
+
378
+ if formatter.json_mode:
379
+ formatter.output(result)
380
+ else:
381
+ if result.get("idempotent_replay"):
382
+ formatter.console.print(
383
+ "[dim]Idempotency key matched a prior run -- returning the existing "
384
+ "job (no new job created).[/dim]"
385
+ )
386
+ resolved_id = result.get("resolvedVariableValuesId")
387
+ if resolved_id:
388
+ formatter.console.print(f"[dim]Bound variable values row: {escape(resolved_id)}[/dim]")
389
+ job_id = result.get("id", "?")
390
+ status = result.get("status", "unknown")
391
+ if status in ("success", "terminated"):
392
+ formatter.console.print(f"[bold green]Job {job_id}:[/bold green] {status}")
393
+ elif status == "warning":
394
+ # "error" is intentionally absent here: the service layer raises
395
+ # QUEUE_JOB_FAILED for failed jobs, so this human-mode branch is
396
+ # only reached for non-error terminal and transient states.
397
+ formatter.console.print(f"[bold yellow]Job {job_id}:[/bold yellow] {status}")
398
+ _render_log_tail(formatter, {"logTail": result.get("logTail") or []})
399
+ else:
400
+ formatter.console.print(f"[bold blue]Job {job_id}:[/bold blue] {status}")
401
+ if not wait:
402
+ formatter.console.print(
403
+ " Use --wait to poll until completion, "
404
+ f"or: kbagent job detail --project {project} --job-id {job_id}"
405
+ )
406
+
407
+
408
+ def _render_log_tail(formatter, details: dict | None) -> None:
409
+ """Render a logTail attached to an error or result payload in human mode."""
410
+ if not details:
411
+ return
412
+ tail = details.get("logTail") or []
413
+ if not tail:
414
+ return
415
+ formatter.console.print("[bold]Log tail (last events):[/bold]")
416
+ for event in tail:
417
+ ts = event.get("created") or event.get("createdTime") or ""
418
+ event_type = event.get("type") or event.get("event") or ""
419
+ msg = event.get("message") or ""
420
+ # Trim overly long event messages so the panel stays readable.
421
+ if len(msg) > 400:
422
+ msg = msg[:400] + "..."
423
+ formatter.console.print(f" [dim]{ts}[/dim] [{event_type}] {msg}")
424
+
425
+
426
+ @job_app.command("terminate")
427
+ def job_terminate(
428
+ ctx: typer.Context,
429
+ project: str = typer.Option(
430
+ ...,
431
+ "--project",
432
+ help="Project alias",
433
+ ),
434
+ job_id: list[str] | None = typer.Option(
435
+ None,
436
+ "--job-id",
437
+ help="Job ID to terminate. Can be repeated. Mutually exclusive with --status.",
438
+ ),
439
+ status: str | None = typer.Option(
440
+ None,
441
+ "--status",
442
+ help=(
443
+ "Bulk-terminate jobs matching status. "
444
+ f"Single killable: {', '.join(sorted(KILLABLE_JOB_STATUSES))}. "
445
+ "Use 'any' to match all killable states at once (typical for runaway cleanup). "
446
+ "Recommend scoping with --component-id / --config-id / --branch."
447
+ ),
448
+ ),
449
+ component_id: str | None = typer.Option(
450
+ None,
451
+ "--component-id",
452
+ help="Filter bulk terminate by component ID",
453
+ ),
454
+ config_id: str | None = typer.Option(
455
+ None,
456
+ "--config-id",
457
+ help="Filter bulk terminate by configuration ID (requires --component-id)",
458
+ ),
459
+ limit: int = typer.Option(
460
+ DEFAULT_JOB_LIMIT,
461
+ "--limit",
462
+ help=f"Max jobs to consider when using --status (1-{MAX_JOB_LIMIT})",
463
+ ),
464
+ branch: int | None = typer.Option(
465
+ None,
466
+ "--branch",
467
+ help="Dev branch ID (filters jobs client-side; defaults to active branch if set via 'branch use')",
468
+ ),
469
+ dry_run: bool = typer.Option(
470
+ False,
471
+ "--dry-run",
472
+ help="Show what would be terminated without executing",
473
+ ),
474
+ yes: bool = typer.Option(
475
+ False,
476
+ "--yes",
477
+ "-y",
478
+ help="Skip confirmation prompt",
479
+ ),
480
+ ) -> None:
481
+ """Terminate one or more Queue API jobs (use to stop runaway or stuck jobs).
482
+
483
+ Two modes:
484
+
485
+ - Single/multiple by ID: --job-id ID [--job-id ID ...]
486
+ - Bulk by filter: --status processing [--component-id ID] [--config-id ID] [--branch ID]
487
+
488
+ Queue API kill is asynchronous: the job's desiredStatus becomes 'terminating'
489
+ and the actual status transitions to 'cancelled' (if waiting) or 'terminated'
490
+ (if processing) within a few seconds. Poll with 'kbagent job detail' to
491
+ observe the terminal state.
492
+
493
+ Jobs already in a terminal state (success/error/terminated/cancelled) are
494
+ counted as 'already_finished' — safe to re-run this command idempotently
495
+ for cleanup purposes.
496
+ """
497
+ formatter = get_formatter(ctx)
498
+ service = get_service(ctx, "job_service")
499
+ config_store: ConfigStore = ctx.obj["config_store"]
500
+
501
+ if bool(job_id) == bool(status):
502
+ formatter.error(
503
+ message="Provide either --job-id (one or more) or --status, but not both.",
504
+ error_code=ErrorCode.INVALID_ARGUMENT,
505
+ )
506
+ raise typer.Exit(code=2)
507
+
508
+ if status and status != "any" and status not in KILLABLE_JOB_STATUSES:
509
+ formatter.error(
510
+ message=(
511
+ f"Invalid --status '{status}'. Use one of: "
512
+ f"{', '.join(sorted(KILLABLE_JOB_STATUSES))} or 'any'."
513
+ ),
514
+ error_code=ErrorCode.INVALID_ARGUMENT,
515
+ )
516
+ raise typer.Exit(code=2)
517
+
518
+ if config_id and not component_id:
519
+ formatter.error(
520
+ message="--config-id requires --component-id.",
521
+ error_code=ErrorCode.INVALID_ARGUMENT,
522
+ )
523
+ raise typer.Exit(code=2)
524
+
525
+ if limit < 1 or limit > MAX_JOB_LIMIT:
526
+ formatter.error(
527
+ message=f"Invalid --limit {limit}. Must be between 1 and {MAX_JOB_LIMIT}.",
528
+ error_code=ErrorCode.INVALID_ARGUMENT,
529
+ )
530
+ raise typer.Exit(code=2)
531
+
532
+ validate_branch_requires_project(formatter, branch, project)
533
+ _, effective_branch = resolve_branch(config_store, formatter, project, branch)
534
+
535
+ # Resolve job IDs
536
+ resolved_ids: list[str]
537
+ filter_context: dict[str, object | None] | None = None
538
+ if job_id:
539
+ resolved_ids = list(job_id)
540
+ else:
541
+ # "any" means: list without status filter, then keep only killable states client-side
542
+ list_status = None if status == "any" else status
543
+ try:
544
+ matched = service.resolve_job_ids_by_filter(
545
+ alias=project,
546
+ status=list_status,
547
+ component_id=component_id,
548
+ config_id=config_id,
549
+ branch_id=effective_branch,
550
+ limit=limit,
551
+ )
552
+ if status == "any":
553
+ matched = service.filter_killable(matched)
554
+ except ConfigError as exc:
555
+ formatter.error(message=exc.message, error_code=ErrorCode.CONFIG_ERROR)
556
+ raise typer.Exit(code=5) from None
557
+ except KeboolaApiError as exc:
558
+ formatter.error(
559
+ message=exc.message,
560
+ error_code=exc.error_code,
561
+ project=project,
562
+ retryable=exc.retryable,
563
+ )
564
+ raise typer.Exit(code=map_error_to_exit_code(exc)) from None
565
+
566
+ resolved_ids = [str(j.get("id")) for j in matched if j.get("id")]
567
+ filter_context = {
568
+ "status": status,
569
+ "component_id": component_id,
570
+ "config_id": config_id,
571
+ "branch_id": effective_branch,
572
+ "matched_count": len(resolved_ids),
573
+ }
574
+
575
+ if not resolved_ids:
576
+ empty_result = {
577
+ "killed": [],
578
+ "already_finished": [],
579
+ "not_found": [],
580
+ "failed": [],
581
+ "dry_run": dry_run,
582
+ "project_alias": project,
583
+ "filter": filter_context,
584
+ }
585
+ if formatter.json_mode:
586
+ formatter.output(empty_result)
587
+ else:
588
+ formatter.console.print("[bold blue]No jobs matched.[/bold blue]")
589
+ return
590
+
591
+ # Dry-run: report without killing
592
+ if dry_run:
593
+ try:
594
+ result = service.terminate_jobs(
595
+ alias=project,
596
+ job_ids=resolved_ids,
597
+ dry_run=True,
598
+ )
599
+ except ConfigError as exc:
600
+ formatter.error(message=exc.message, error_code=ErrorCode.CONFIG_ERROR)
601
+ raise typer.Exit(code=5) from None
602
+
603
+ if filter_context is not None:
604
+ result["filter"] = filter_context
605
+ if formatter.json_mode:
606
+ formatter.output(result)
607
+ else:
608
+ formatter.console.print(
609
+ f"[bold blue]Would terminate {len(resolved_ids)} job(s):[/bold blue]"
610
+ )
611
+ for jid in resolved_ids:
612
+ formatter.console.print(f" - {jid}")
613
+ return
614
+
615
+ # Confirmation prompt (interactive only)
616
+ confirm_msg = f"Terminate {len(resolved_ids)} job(s) in project '{project}'?"
617
+ if not yes and not formatter.json_mode and not typer.confirm(confirm_msg):
618
+ formatter.console.print("Aborted.")
619
+ raise typer.Exit(code=0)
620
+
621
+ try:
622
+ result = service.terminate_jobs(
623
+ alias=project,
624
+ job_ids=resolved_ids,
625
+ )
626
+ except ConfigError as exc:
627
+ formatter.error(message=exc.message, error_code=ErrorCode.CONFIG_ERROR)
628
+ raise typer.Exit(code=5) from None
629
+ except KeboolaApiError as exc:
630
+ formatter.error(
631
+ message=exc.message,
632
+ error_code=exc.error_code,
633
+ project=project,
634
+ retryable=exc.retryable,
635
+ )
636
+ raise typer.Exit(code=map_error_to_exit_code(exc)) from None
637
+
638
+ if filter_context is not None:
639
+ result["filter"] = filter_context
640
+
641
+ if formatter.json_mode:
642
+ formatter.output(result)
643
+ else:
644
+ for entry in result["killed"]:
645
+ formatter.console.print(
646
+ f"[bold green]Killed:[/bold green] {entry['id']} "
647
+ f"(status={entry.get('status')}, desiredStatus={entry.get('desiredStatus')})"
648
+ )
649
+ for entry in result["already_finished"]:
650
+ formatter.console.print(
651
+ f"[yellow]Already finished:[/yellow] {entry['id']} ({entry.get('reason')})"
652
+ )
653
+ for jid in result["not_found"]:
654
+ formatter.console.print(f"[bold red]Not found:[/bold red] {jid}")
655
+ for f_item in result["failed"]:
656
+ formatter.console.print(
657
+ f"[bold red]Failed:[/bold red] {f_item['id']}: {f_item['error']}"
658
+ )
659
+
660
+ if result["failed"]:
661
+ raise typer.Exit(code=1)