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,456 @@
1
+ """Push-time link backfill for the sync service (Phase C + Phase D).
2
+
3
+ Extracted from ``sync_service.py`` to keep that file under control. These are
4
+ free functions that take the ``SyncService`` as their first argument (for the
5
+ handful of on-disk helpers they need -- ``_read_config_file``,
6
+ ``_write_config_file``, ``_compute_config_hashes``) rather than methods, so the
7
+ typing stays explicit and the binding logic is testable in isolation.
8
+
9
+ - **Phase C** (:func:`resolve_variable_bindings`): rebind a transformation's
10
+ ``variables_id`` / ``variables_values_id`` placeholders to the ULIDs created
11
+ this push.
12
+ - **Phase D** (:func:`resolve_flow_task_bindings`): remap ``keboola.flow`` task
13
+ ``configId``s to the ULIDs created this push.
14
+
15
+ Both run after the create passes, PUT the corrected config, rewrite the local
16
+ ``_config.yml``, and refresh the manifest hashes so a re-push is clean.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import copy
22
+ import logging
23
+ from typing import TYPE_CHECKING, Any
24
+
25
+ from ..errors import ErrorCode, KeboolaApiError
26
+ from ..sync.code_extraction import merge_code_files
27
+ from ..sync.config_format import local_config_to_api
28
+ from ..sync.manifest import Manifest
29
+ from ._sync_models import (
30
+ FLOW_COMPONENT_ID,
31
+ VARIABLES_COMPONENT_ID,
32
+ CreatedConfig,
33
+ FlowBindingResult,
34
+ VariableBindingResult,
35
+ )
36
+
37
+ if TYPE_CHECKING:
38
+ from .sync_service import SyncService
39
+
40
+ logger = logging.getLogger(__name__)
41
+
42
+
43
+ # ---------------------------------------------------------------------------
44
+ # Phase C: transformation -> variables links
45
+ # ---------------------------------------------------------------------------
46
+
47
+
48
+ def resolve_variable_bindings(
49
+ service: SyncService,
50
+ client: Any,
51
+ *,
52
+ created_configs: list[CreatedConfig],
53
+ created_id_map: dict[tuple[str, str], str],
54
+ created_row_id_map: dict[str, str],
55
+ created_rows_by_parent: dict[str, list[str]],
56
+ manifest: Manifest,
57
+ branch_id: int | None,
58
+ ) -> VariableBindingResult:
59
+ """Rebind transformation -> variables links from placeholders to ULIDs.
60
+
61
+ On a fresh CREATE the transformation config is POSTed with its
62
+ ``_configuration_extra.variables_id`` / ``variables_values_id`` still set to
63
+ the externally-authored placeholder strings (``config_format`` merges
64
+ ``_configuration_extra`` into the API body verbatim). This pass, run after
65
+ the variables config and its values row have been created, resolves each
66
+ placeholder to the ULID assigned during this push, PUTs the corrected
67
+ configuration body, then rewrites the local file and refreshes the manifest
68
+ hashes so a re-push is clean (KFR-03).
69
+
70
+ Resolution is a no-op when no ``keboola.variables`` config was created this
71
+ push (the already-bound / UPDATE path). When the exact placeholder key
72
+ misses but exactly one ``keboola.variables`` config was created this push,
73
+ it binds to that one with a warning; zero or ambiguous (>1) matches
74
+ accumulate an error rather than writing a broken link.
75
+ """
76
+ result = VariableBindingResult()
77
+
78
+ created_variables_ulids = [
79
+ ulid
80
+ for (component_id, _placeholder), ulid in created_id_map.items()
81
+ if component_id == VARIABLES_COMPONENT_ID
82
+ ]
83
+
84
+ for created in created_configs:
85
+ if created.component_id == VARIABLES_COMPONENT_ID:
86
+ continue # the variables config itself never carries a link
87
+ local_data = service._read_config_file(created.config_dir)
88
+ if local_data is None:
89
+ continue
90
+ extra = local_data.get("_configuration_extra")
91
+ if not isinstance(extra, dict):
92
+ continue
93
+ vars_placeholder = extra.get("variables_id")
94
+ if not vars_placeholder or not isinstance(vars_placeholder, str):
95
+ continue
96
+ raw_vals = extra.get("variables_values_id")
97
+ vals_placeholder = raw_vals if isinstance(raw_vals, str) else ""
98
+
99
+ parent_ulid = _resolve_variables_parent(
100
+ created=created,
101
+ vars_placeholder=vars_placeholder,
102
+ created_id_map=created_id_map,
103
+ created_variables_ulids=created_variables_ulids,
104
+ errors=result.errors,
105
+ )
106
+ if parent_ulid is None:
107
+ continue
108
+
109
+ row_ulid = _resolve_variables_row(
110
+ created=created,
111
+ parent_ulid=parent_ulid,
112
+ vals_placeholder=vals_placeholder,
113
+ created_row_id_map=created_row_id_map,
114
+ created_rows_by_parent=created_rows_by_parent,
115
+ errors=result.errors,
116
+ )
117
+ # A missing-but-required values row already recorded an error.
118
+ if vals_placeholder and row_ulid is None:
119
+ continue
120
+
121
+ try:
122
+ _apply_variable_binding(
123
+ service,
124
+ client,
125
+ created=created,
126
+ local_data=local_data,
127
+ parent_ulid=parent_ulid,
128
+ row_ulid=row_ulid,
129
+ manifest=manifest,
130
+ branch_id=branch_id,
131
+ )
132
+ except KeboolaApiError as exc:
133
+ result.errors.append(
134
+ {
135
+ "change_type": "variable_link",
136
+ "error_code": ErrorCode.VARIABLE_LINK_UNRESOLVED,
137
+ "component_id": created.component_id,
138
+ "config_id": created.config_id,
139
+ "message": str(exc),
140
+ }
141
+ )
142
+ continue
143
+ result.configs_rewritten += 1
144
+
145
+ return result
146
+
147
+
148
+ def _resolve_variables_parent(
149
+ *,
150
+ created: CreatedConfig,
151
+ vars_placeholder: str,
152
+ created_id_map: dict[tuple[str, str], str],
153
+ created_variables_ulids: list[str],
154
+ errors: list[dict[str, str]],
155
+ ) -> str | None:
156
+ """Resolve a transformation's ``variables_id`` placeholder to a ULID.
157
+
158
+ Returns the ULID, or ``None`` when there is nothing to backfill
159
+ (already-bound path) or the link is ambiguous (an error is appended).
160
+ """
161
+ parent_ulid = created_id_map.get((VARIABLES_COMPONENT_ID, vars_placeholder))
162
+ if parent_ulid is not None:
163
+ return parent_ulid
164
+ if not created_variables_ulids:
165
+ # No variables config created this push: the link is either already
166
+ # a ULID (UPDATE path) or points outside this push. Leave it.
167
+ return None
168
+ if len(created_variables_ulids) == 1:
169
+ parent_ulid = created_variables_ulids[0]
170
+ logger.warning(
171
+ "Transformation %s/%s variables_id placeholder %r did not match any "
172
+ "created variables config; binding to the single keboola.variables "
173
+ "config created this push (%s).",
174
+ created.component_id,
175
+ created.config_id,
176
+ vars_placeholder,
177
+ parent_ulid,
178
+ )
179
+ return parent_ulid
180
+ errors.append(
181
+ {
182
+ "change_type": "variable_link",
183
+ "error_code": ErrorCode.VARIABLE_LINK_UNRESOLVED,
184
+ "component_id": created.component_id,
185
+ "config_id": created.config_id,
186
+ "message": (
187
+ f"Cannot resolve variables_id placeholder {vars_placeholder!r}: "
188
+ f"{len(created_variables_ulids)} keboola.variables configs were "
189
+ "created this push and none matched by placeholder. Refusing to "
190
+ "write an ambiguous variables link."
191
+ ),
192
+ }
193
+ )
194
+ return None
195
+
196
+
197
+ def _resolve_variables_row(
198
+ *,
199
+ created: CreatedConfig,
200
+ parent_ulid: str,
201
+ vals_placeholder: str,
202
+ created_row_id_map: dict[str, str],
203
+ created_rows_by_parent: dict[str, list[str]],
204
+ errors: list[dict[str, str]],
205
+ ) -> str | None:
206
+ """Resolve a transformation's ``variables_values_id`` placeholder.
207
+
208
+ Returns the row ULID, or ``None`` when no values row was created (the link
209
+ is then left unset) or the choice is ambiguous (an error is appended only
210
+ when ``vals_placeholder`` was actually requested).
211
+ """
212
+ if vals_placeholder:
213
+ mapped = created_row_id_map.get(vals_placeholder)
214
+ if mapped is not None:
215
+ return mapped
216
+ siblings = created_rows_by_parent.get(parent_ulid, [])
217
+ if len(siblings) == 1:
218
+ row_ulid = siblings[0]
219
+ if vals_placeholder:
220
+ logger.warning(
221
+ "Transformation %s/%s variables_values_id placeholder %r did not "
222
+ "match a created row; binding to the single row created under "
223
+ "variables config %s.",
224
+ created.component_id,
225
+ created.config_id,
226
+ vals_placeholder,
227
+ parent_ulid,
228
+ )
229
+ return row_ulid
230
+ if vals_placeholder:
231
+ errors.append(
232
+ {
233
+ "change_type": "variable_link",
234
+ "error_code": ErrorCode.VARIABLE_LINK_UNRESOLVED,
235
+ "component_id": created.component_id,
236
+ "config_id": created.config_id,
237
+ "message": (
238
+ f"Cannot resolve variables_values_id placeholder "
239
+ f"{vals_placeholder!r}: {len(siblings)} rows were created under "
240
+ f"variables config {parent_ulid}. Refusing to write an "
241
+ "ambiguous values link."
242
+ ),
243
+ }
244
+ )
245
+ return None
246
+
247
+
248
+ def _apply_variable_binding(
249
+ service: SyncService,
250
+ client: Any,
251
+ *,
252
+ created: CreatedConfig,
253
+ local_data: dict[str, Any],
254
+ parent_ulid: str,
255
+ row_ulid: str | None,
256
+ manifest: Manifest,
257
+ branch_id: int | None,
258
+ ) -> None:
259
+ """PUT the resolved variables link, rewrite local, refresh manifest hashes.
260
+
261
+ ``local_data`` is the pristine on-disk ``_config.yml`` dict; a deep copy is
262
+ code-merged to build the full PUT body so blocks/code stay only in their
263
+ companion files. Uses :meth:`KeboolaClient.update_config` (PUT) directly --
264
+ **not** ``set_variables``, which would create a *second* variables config.
265
+ """
266
+ merged = copy.deepcopy(local_data)
267
+ merge_code_files(created.component_id, merged, created.config_dir)
268
+ _name, _description, configuration = local_config_to_api(merged)
269
+ configuration["variables_id"] = parent_ulid
270
+ if row_ulid:
271
+ configuration["variables_values_id"] = row_ulid
272
+
273
+ client.update_config(
274
+ component_id=created.component_id,
275
+ config_id=created.config_id,
276
+ configuration=configuration,
277
+ change_description="Resolve variables link via kbagent sync push",
278
+ branch_id=branch_id,
279
+ )
280
+ logger.info(
281
+ "Resolved variables link for %s/%s -> variables_id=%s variables_values_id=%s",
282
+ created.component_id,
283
+ created.config_id,
284
+ parent_ulid,
285
+ row_ulid,
286
+ )
287
+
288
+ # Rewrite the local _configuration_extra to the ULIDs (pristine data:
289
+ # no merged blocks leak into _config.yml).
290
+ extra = local_data.setdefault("_configuration_extra", {})
291
+ extra["variables_id"] = parent_ulid
292
+ if row_ulid:
293
+ extra["variables_values_id"] = row_ulid
294
+ service._write_config_file(created.config_dir, local_data)
295
+
296
+ # config_hash includes _configuration_extra, so refresh the stored
297
+ # hashes from the post-rewrite disk state or sync diff sees a conflict.
298
+ hashes = service._compute_config_hashes(created.config_dir, created.component_id)
299
+ target_branch = branch_id or 0
300
+ for cfg in manifest.configurations:
301
+ if (
302
+ cfg.branch_id == target_branch
303
+ and cfg.component_id == created.component_id
304
+ and cfg.id == created.config_id
305
+ ):
306
+ cfg.metadata["pull_hash"] = hashes.file_hash
307
+ cfg.metadata["pull_config_hash"] = hashes.cfg_hash
308
+ cfg.metadata["pull_extra_hashes"] = hashes.extra_hashes
309
+ break
310
+
311
+
312
+ # ---------------------------------------------------------------------------
313
+ # Phase D: keboola.flow task configId remap (#426)
314
+ # ---------------------------------------------------------------------------
315
+
316
+
317
+ def resolve_flow_task_bindings(
318
+ service: SyncService,
319
+ client: Any,
320
+ *,
321
+ created_configs: list[CreatedConfig],
322
+ created_id_map: dict[tuple[str, str], str],
323
+ manifest: Manifest,
324
+ branch_id: int | None,
325
+ ) -> FlowBindingResult:
326
+ """Remap keboola.flow task ``configId``s from source ids to ULIDs (Phase D).
327
+
328
+ A ``keboola.flow`` config runs other configs via
329
+ ``configuration.tasks[].task.configId`` (job-type tasks only; on disk these
330
+ live under ``_configuration_extra.tasks``). When a flow is created in the
331
+ same push as the configs it targets -- e.g. a ``sync clone`` of a reference
332
+ project -- those task ``configId``s still point at the source config ids.
333
+ This pass (mirroring the Phase-C variable backfill) resolves each job task's
334
+ ``(componentId, configId)`` via ``created_id_map`` to the ULID assigned this
335
+ push, PUTs the corrected flow, rewrites the local ``_config.yml``, and
336
+ refreshes the manifest hashes so a re-push is clean.
337
+
338
+ A no-op when no flow was created this push, or when no task references a
339
+ config created this push (the id is left untouched -- it may legitimately
340
+ point at a pre-existing config).
341
+ """
342
+ result = FlowBindingResult()
343
+ for created in created_configs:
344
+ if created.component_id != FLOW_COMPONENT_ID:
345
+ continue
346
+ local_data = service._read_config_file(created.config_dir)
347
+ if local_data is None:
348
+ continue
349
+ extra = local_data.get("_configuration_extra")
350
+ if not isinstance(extra, dict):
351
+ continue
352
+ tasks = extra.get("tasks")
353
+ if not isinstance(tasks, list):
354
+ continue
355
+
356
+ remapped = remap_flow_tasks_in_place(tasks, created_id_map)
357
+ if not remapped:
358
+ continue
359
+
360
+ try:
361
+ _apply_flow_task_binding(
362
+ service,
363
+ client,
364
+ created=created,
365
+ local_data=local_data,
366
+ manifest=manifest,
367
+ branch_id=branch_id,
368
+ )
369
+ except KeboolaApiError as exc:
370
+ result.errors.append(
371
+ {
372
+ "change_type": "flow_task_link",
373
+ "error_code": ErrorCode.API_ERROR,
374
+ "component_id": created.component_id,
375
+ "config_id": created.config_id,
376
+ "message": str(exc),
377
+ }
378
+ )
379
+ continue
380
+ result.configs_rewritten += 1
381
+ result.tasks_remapped += remapped
382
+ return result
383
+
384
+
385
+ def remap_flow_tasks_in_place(tasks: list[Any], created_id_map: dict[tuple[str, str], str]) -> int:
386
+ """Rewrite job-task ``configId``s in a flow ``tasks`` list in place.
387
+
388
+ Returns the number of task references actually remapped. Only
389
+ ``task.type == 'job'`` entries carry a ``configId``; notification and
390
+ variable tasks are skipped. A task is rewritten only when
391
+ ``(componentId, configId)`` matches an entry created this push.
392
+ """
393
+ remapped = 0
394
+ for task_entry in tasks:
395
+ if not isinstance(task_entry, dict):
396
+ continue
397
+ task = task_entry.get("task")
398
+ if not isinstance(task, dict) or task.get("type") != "job":
399
+ continue
400
+ comp = task.get("componentId")
401
+ old_cfg = task.get("configId")
402
+ if not isinstance(comp, str) or not isinstance(old_cfg, (str, int)):
403
+ continue
404
+ new_id = created_id_map.get((comp, str(old_cfg)))
405
+ if new_id and str(old_cfg) != new_id:
406
+ task["configId"] = new_id
407
+ remapped += 1
408
+ return remapped
409
+
410
+
411
+ def _apply_flow_task_binding(
412
+ service: SyncService,
413
+ client: Any,
414
+ *,
415
+ created: CreatedConfig,
416
+ local_data: dict[str, Any],
417
+ manifest: Manifest,
418
+ branch_id: int | None,
419
+ ) -> None:
420
+ """PUT a remapped flow, rewrite local ``_config.yml``, refresh hashes.
421
+
422
+ ``local_data`` already carries the remapped task ``configId``s (the caller
423
+ mutated ``_configuration_extra.tasks`` in place). A deep copy is code-merged
424
+ to build the PUT body (no-op for flows, which carry no code) so the API
425
+ receives the corrected ``configuration.tasks``.
426
+ """
427
+ merged = copy.deepcopy(local_data)
428
+ merge_code_files(created.component_id, merged, created.config_dir)
429
+ _name, _description, configuration = local_config_to_api(merged)
430
+
431
+ client.update_config(
432
+ component_id=created.component_id,
433
+ config_id=created.config_id,
434
+ configuration=configuration,
435
+ change_description="Remap flow task configIds via kbagent sync push",
436
+ branch_id=branch_id,
437
+ )
438
+ logger.info(
439
+ "Remapped flow task configIds for %s/%s",
440
+ created.component_id,
441
+ created.config_id,
442
+ )
443
+
444
+ service._write_config_file(created.config_dir, local_data)
445
+ hashes = service._compute_config_hashes(created.config_dir, created.component_id)
446
+ target_branch = branch_id or 0
447
+ for cfg in manifest.configurations:
448
+ if (
449
+ cfg.branch_id == target_branch
450
+ and cfg.component_id == created.component_id
451
+ and cfg.id == created.config_id
452
+ ):
453
+ cfg.metadata["pull_hash"] = hashes.file_hash
454
+ cfg.metadata["pull_config_hash"] = hashes.cfg_hash
455
+ cfg.metadata["pull_extra_hashes"] = hashes.extra_hashes
456
+ break
@@ -0,0 +1,191 @@
1
+ """Git-branch <-> Keboola-dev-branch linking (from sync_service.py).
2
+
3
+ Backs ``kbagent sync branch-link / branch-unlink / branch-status`` in
4
+ git-branching mode: maps the current git branch to a Keboola dev branch in
5
+ ``.keboola/branch-mapping.json``. ``branch_link`` needs the ``SyncService`` (to
6
+ resolve the project + build a client); the read/unlink helpers are pure.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from pathlib import Path
12
+ from typing import TYPE_CHECKING, Any
13
+
14
+ from ..errors import ConfigError
15
+ from ..sync.branch_mapping import BranchMapping, load_branch_mapping, save_branch_mapping
16
+ from ..sync.manifest import load_manifest
17
+
18
+ if TYPE_CHECKING:
19
+ from .sync_service import SyncService
20
+
21
+
22
+ def branch_link(
23
+ service: SyncService,
24
+ alias: str,
25
+ project_root: Path,
26
+ branch_id: int | None = None,
27
+ branch_name: str | None = None,
28
+ ) -> dict[str, Any]:
29
+ """Link the current git branch to a Keboola development branch.
30
+
31
+ If no ``branch_id`` or ``branch_name`` is given: read the current git branch,
32
+ search for an existing Keboola branch with the same name, create one if
33
+ absent, and save the mapping. Returns a dict with the link result.
34
+ """
35
+ from ..sync.git_utils import get_current_branch
36
+
37
+ manifest = load_manifest(project_root)
38
+ if not manifest.git_branching.enabled:
39
+ raise ConfigError(
40
+ "Git-branching mode is not enabled. Run 'sync init --git-branching' first."
41
+ )
42
+
43
+ git_branch = get_current_branch(project_root)
44
+ if git_branch is None:
45
+ raise ConfigError("Cannot determine current git branch.")
46
+
47
+ default_branch = manifest.git_branching.default_branch
48
+ if git_branch == default_branch:
49
+ raise ConfigError(
50
+ f"Cannot link the default branch '{default_branch}'. "
51
+ "It is automatically linked to Keboola production."
52
+ )
53
+
54
+ # Load existing mapping
55
+ try:
56
+ mapping = load_branch_mapping(project_root)
57
+ except FileNotFoundError:
58
+ mapping = BranchMapping()
59
+ mapping.set(default_branch, None, "Main")
60
+
61
+ # Check if already linked
62
+ existing = mapping.get(git_branch)
63
+ if existing is not None:
64
+ return {
65
+ "status": "already_linked",
66
+ "git_branch": git_branch,
67
+ "keboola_branch_id": existing.keboola_id,
68
+ "keboola_branch_name": existing.name,
69
+ }
70
+
71
+ projects = service.resolve_projects([alias])
72
+ project = projects[alias]
73
+ client = service._client_factory(project.stack_url, project.token)
74
+
75
+ with client:
76
+ if branch_id:
77
+ # Link to existing branch by ID
78
+ branches = client.list_dev_branches()
79
+ branch_info = next((b for b in branches if b["id"] == branch_id), None)
80
+ if branch_info is None:
81
+ raise ConfigError(f"Keboola branch {branch_id} not found.")
82
+ kbc_branch_id = int(branch_info["id"])
83
+ kbc_branch_name = branch_info.get("name", "")
84
+ elif branch_name:
85
+ # Search by name or create
86
+ branches = client.list_dev_branches()
87
+ branch_info = next((b for b in branches if b.get("name") == branch_name), None)
88
+ if branch_info:
89
+ kbc_branch_id = int(branch_info["id"])
90
+ kbc_branch_name = branch_info.get("name", "")
91
+ else:
92
+ result = client.create_dev_branch(name=branch_name)
93
+ kbc_branch_id = int(result["id"])
94
+ kbc_branch_name = branch_name
95
+ else:
96
+ # Default: use git branch name to search/create
97
+ branches = client.list_dev_branches()
98
+ branch_info = next((b for b in branches if b.get("name") == git_branch), None)
99
+ if branch_info:
100
+ kbc_branch_id = int(branch_info["id"])
101
+ kbc_branch_name = branch_info.get("name", "")
102
+ else:
103
+ result = client.create_dev_branch(name=git_branch)
104
+ kbc_branch_id = int(result["id"])
105
+ kbc_branch_name = git_branch
106
+
107
+ mapping.set(git_branch, kbc_branch_id, kbc_branch_name)
108
+ save_branch_mapping(project_root, mapping)
109
+
110
+ return {
111
+ "status": "linked",
112
+ "git_branch": git_branch,
113
+ "keboola_branch_id": kbc_branch_id,
114
+ "keboola_branch_name": kbc_branch_name,
115
+ }
116
+
117
+
118
+ def branch_unlink(project_root: Path) -> dict[str, Any]:
119
+ """Remove the branch mapping for the current git branch."""
120
+ from ..sync.git_utils import get_current_branch
121
+
122
+ manifest = load_manifest(project_root)
123
+ if not manifest.git_branching.enabled:
124
+ raise ConfigError("Git-branching mode is not enabled.")
125
+
126
+ git_branch = get_current_branch(project_root)
127
+ if git_branch is None:
128
+ raise ConfigError("Cannot determine current git branch.")
129
+
130
+ default_branch = manifest.git_branching.default_branch
131
+ if git_branch == default_branch:
132
+ raise ConfigError(
133
+ f"Cannot unlink the default branch '{default_branch}'. "
134
+ "It is permanently linked to Keboola production."
135
+ )
136
+
137
+ mapping = load_branch_mapping(project_root)
138
+ existing = mapping.get(git_branch)
139
+ if existing is None:
140
+ return {
141
+ "status": "not_linked",
142
+ "git_branch": git_branch,
143
+ }
144
+
145
+ kbc_id = existing.keboola_id
146
+ kbc_name = existing.name
147
+ mapping.remove(git_branch)
148
+ save_branch_mapping(project_root, mapping)
149
+
150
+ return {
151
+ "status": "unlinked",
152
+ "git_branch": git_branch,
153
+ "keboola_branch_id": kbc_id,
154
+ "keboola_branch_name": kbc_name,
155
+ }
156
+
157
+
158
+ def branch_status(project_root: Path) -> dict[str, Any]:
159
+ """Show the branch mapping status for the current git branch."""
160
+ from ..sync.git_utils import get_current_branch
161
+
162
+ manifest = load_manifest(project_root)
163
+ if not manifest.git_branching.enabled:
164
+ return {"git_branching": False}
165
+
166
+ git_branch = get_current_branch(project_root)
167
+ try:
168
+ mapping = load_branch_mapping(project_root)
169
+ except FileNotFoundError:
170
+ return {
171
+ "git_branching": True,
172
+ "git_branch": git_branch,
173
+ "linked": False,
174
+ }
175
+
176
+ entry = mapping.get(git_branch) if git_branch else None
177
+ if entry is None:
178
+ return {
179
+ "git_branching": True,
180
+ "git_branch": git_branch,
181
+ "linked": False,
182
+ }
183
+
184
+ return {
185
+ "git_branching": True,
186
+ "git_branch": git_branch,
187
+ "linked": True,
188
+ "keboola_branch_id": entry.keboola_id,
189
+ "keboola_branch_name": entry.name,
190
+ "is_production": entry.is_production(),
191
+ }