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,217 @@
1
+ """Keboola Stream (Data Streams) API client with retry, timeouts, token masking.
2
+
3
+ This module talks to the Keboola **Stream control-plane API** for managing
4
+ Data Streams sources and sinks (list / create / detail / delete). The base URL
5
+ is derived from the Storage API stack URL by replacing 'connection.' with
6
+ 'stream.' in the hostname (the same scheme used for 'ai.'/'queue.'), and the
7
+ request is authenticated with the per-project Storage API token
8
+ (``X-StorageApi-Token``) -- no manage token is involved.
9
+
10
+ Important: the OTLP *ingestion* endpoint (``stream-in.<region>/otlp/...``) is a
11
+ separate data-plane host and is NOT derived here -- it is returned by the API in
12
+ the source's ``otlp.url`` field. This client only speaks to the control plane.
13
+
14
+ Source-create and source-delete are asynchronous: the API returns a ``Task``
15
+ (202 Accepted); :meth:`wait_for_task` polls ``GET /v1/tasks/{taskId}`` until the
16
+ task reports ``isFinished``.
17
+
18
+ Inherits shared retry/error logic from :class:`BaseHttpClient`.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import logging
24
+ import time
25
+ from typing import Any
26
+ from urllib.parse import quote, urlparse
27
+
28
+ from .constants import STREAM_API_TIMEOUT, STREAM_TASK_POLL_INTERVAL, STREAM_TASK_TIMEOUT
29
+ from .errors import ErrorCode, KeboolaApiError
30
+ from .http_base import BaseHttpClient
31
+
32
+ logger = logging.getLogger(__name__)
33
+
34
+
35
+ class StreamClient(BaseHttpClient):
36
+ """HTTP client for the Keboola Stream (Data Streams) control-plane API.
37
+
38
+ Provides source CRUD, sink listing, and async-task polling, with the
39
+ retry/backoff (429/5xx), timeouts, and token masking inherited from
40
+ :class:`BaseHttpClient`.
41
+ """
42
+
43
+ def __init__(self, stack_url: str, token: str) -> None:
44
+ self._stack_url = stack_url.rstrip("/")
45
+ stream_base_url = self._derive_service_url(self._stack_url, "stream")
46
+ headers = {
47
+ "X-StorageApi-Token": token,
48
+ "Content-Type": "application/json",
49
+ }
50
+ super().__init__(
51
+ base_url=stream_base_url,
52
+ token=token,
53
+ headers=headers,
54
+ timeout=STREAM_API_TIMEOUT,
55
+ )
56
+
57
+ def __enter__(self) -> StreamClient:
58
+ return self
59
+
60
+ def __exit__(self, *args: Any) -> None:
61
+ self.close()
62
+
63
+ # ------------------------------------------------------------------
64
+ # Sources
65
+ # ------------------------------------------------------------------
66
+
67
+ def list_sources(self, branch_id: str) -> dict[str, Any]:
68
+ """List sources in a branch (``GET /v1/branches/{branch}/sources``)."""
69
+ path = f"/v1/branches/{quote(branch_id, safe='')}/sources"
70
+ response = self._do_request("GET", path)
71
+ return response.json()
72
+
73
+ def get_source(self, branch_id: str, source_id: str) -> dict[str, Any]:
74
+ """Fetch one source (``GET /v1/branches/{branch}/sources/{id}``)."""
75
+ path = f"/v1/branches/{quote(branch_id, safe='')}/sources/{quote(source_id, safe='')}"
76
+ response = self._do_request("GET", path)
77
+ return response.json()
78
+
79
+ def create_source(
80
+ self,
81
+ branch_id: str,
82
+ name: str,
83
+ source_type: str,
84
+ source_id: str | None = None,
85
+ description: str | None = None,
86
+ ) -> dict[str, Any]:
87
+ """Create a source. Returns the async ``Task`` (poll with wait_for_task)."""
88
+ path = f"/v1/branches/{quote(branch_id, safe='')}/sources"
89
+ payload: dict[str, Any] = {"name": name, "type": source_type}
90
+ if source_id is not None:
91
+ payload["sourceId"] = source_id
92
+ if description is not None:
93
+ payload["description"] = description
94
+ response = self._do_request("POST", path, json=payload)
95
+ return response.json()
96
+
97
+ def delete_source(self, branch_id: str, source_id: str) -> dict[str, Any]:
98
+ """Delete a source. Returns the async ``Task`` (poll with wait_for_task)."""
99
+ path = f"/v1/branches/{quote(branch_id, safe='')}/sources/{quote(source_id, safe='')}"
100
+ response = self._do_request("DELETE", path)
101
+ return response.json()
102
+
103
+ # ------------------------------------------------------------------
104
+ # Sinks
105
+ # ------------------------------------------------------------------
106
+
107
+ def list_sinks(self, branch_id: str, source_id: str) -> dict[str, Any]:
108
+ """List a source's sinks (``GET .../sources/{id}/sinks``)."""
109
+ path = f"/v1/branches/{quote(branch_id, safe='')}/sources/{quote(source_id, safe='')}/sinks"
110
+ response = self._do_request("GET", path)
111
+ return response.json()
112
+
113
+ def create_sink(
114
+ self,
115
+ branch_id: str,
116
+ source_id: str,
117
+ *,
118
+ name: str,
119
+ table_id: str,
120
+ columns: list[dict[str, Any]],
121
+ allowed_signals: list[str] | None = None,
122
+ sink_id: str | None = None,
123
+ ) -> dict[str, Any]:
124
+ """Create a table sink on a source. Returns the async ``Task``.
125
+
126
+ ``columns`` is the table mapping column list (see the Stream API
127
+ ``TableColumn`` schema). ``allowed_signals`` restricts which OTLP signals
128
+ route to this sink (logs/metrics/traces); omit to accept all.
129
+ """
130
+ path = f"/v1/branches/{quote(branch_id, safe='')}/sources/{quote(source_id, safe='')}/sinks"
131
+ payload: dict[str, Any] = {
132
+ "type": "table",
133
+ "name": name,
134
+ "table": {
135
+ "type": "keboola",
136
+ "tableId": table_id,
137
+ "mapping": {"columns": columns},
138
+ },
139
+ }
140
+ if allowed_signals is not None:
141
+ payload["allowedSignals"] = allowed_signals
142
+ if sink_id is not None:
143
+ payload["sinkId"] = sink_id
144
+ response = self._do_request("POST", path, json=payload)
145
+ return response.json()
146
+
147
+ # ------------------------------------------------------------------
148
+ # Tasks (async create/delete)
149
+ # ------------------------------------------------------------------
150
+
151
+ def get_task(self, task_id: str) -> dict[str, Any]:
152
+ """Fetch a task by id (``GET /v1/tasks/{taskId}``)."""
153
+ path = f"/v1/tasks/{quote(task_id, safe='')}"
154
+ response = self._do_request("GET", path)
155
+ return response.json()
156
+
157
+ def wait_for_task(
158
+ self,
159
+ task: dict[str, Any],
160
+ timeout: float = STREAM_TASK_TIMEOUT,
161
+ poll_interval: float = STREAM_TASK_POLL_INTERVAL,
162
+ ) -> dict[str, Any]:
163
+ """Poll a ``Task`` to completion and return the finished task.
164
+
165
+ Accepts the Task dict returned by :meth:`create_source` /
166
+ :meth:`delete_source`. Polls its canonical poll URL (the task's ``url``
167
+ reduced to a path, falling back to ``/v1/tasks/{taskId}``) until
168
+ ``isFinished`` is true, then raises :class:`KeboolaApiError` if the task
169
+ failed.
170
+ """
171
+ if task.get("isFinished"):
172
+ return self._check_task_result(task)
173
+
174
+ poll_path = self._task_poll_path(task)
175
+ deadline = time.monotonic() + timeout
176
+ latest = task
177
+ while time.monotonic() < deadline:
178
+ time.sleep(poll_interval)
179
+ response = self._do_request("GET", poll_path)
180
+ latest = response.json()
181
+ if latest.get("isFinished"):
182
+ return self._check_task_result(latest)
183
+
184
+ raise KeboolaApiError(
185
+ message=(
186
+ f"Stream task '{latest.get('taskId', '?')}' did not finish within "
187
+ f"{timeout:.0f}s (last status: {latest.get('status', 'unknown')})"
188
+ ),
189
+ error_code=ErrorCode.TIMEOUT,
190
+ retryable=True,
191
+ )
192
+
193
+ def _task_poll_path(self, task: dict[str, Any]) -> str:
194
+ """Resolve the path to poll for ``task``.
195
+
196
+ Prefers the task's ``url`` field reduced to a path relative to the
197
+ Stream base; falls back to ``/v1/tasks/{taskId}``.
198
+ """
199
+ url = task.get("url")
200
+ if isinstance(url, str) and url:
201
+ parsed = urlparse(url)
202
+ if parsed.path:
203
+ return parsed.path
204
+ task_id = task.get("taskId", "")
205
+ return f"/v1/tasks/{quote(str(task_id), safe='')}"
206
+
207
+ @staticmethod
208
+ def _check_task_result(task: dict[str, Any]) -> dict[str, Any]:
209
+ """Return a finished task, raising if it ended in error."""
210
+ error = task.get("error")
211
+ status = task.get("status")
212
+ if error or status == "error":
213
+ raise KeboolaApiError(
214
+ message=f"Stream task failed: {error or status}",
215
+ error_code=ErrorCode.API_ERROR,
216
+ )
217
+ return task
@@ -0,0 +1 @@
1
+ """Sync package: filesystem serialization for Keboola configurations."""
@@ -0,0 +1,174 @@
1
+ """Branch mapping for git-to-Keboola branch mapping.
2
+
3
+ Manages .keboola/branch-mapping.json which maps git branch names
4
+ to Keboola development branch IDs.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ from pathlib import Path
11
+ from typing import Any
12
+
13
+ from ..constants import BRANCH_MAPPING_FILENAME, KEBOOLA_DIR_NAME
14
+ from ..errors import ConfigError
15
+
16
+
17
+ def _coerce_keboola_id(raw: Any) -> int | None:
18
+ """Coerce a raw ``id`` field from JSON to ``int | None``.
19
+
20
+ Older kbagent versions (<= 0.30.3) wrote branch IDs as strings (e.g.
21
+ ``"99999"``) due to issue #267. ``None`` means production. Empty
22
+ string is also treated as production for legacy tolerance.
23
+
24
+ Raises ``ValueError`` with a descriptive message if *raw* is neither
25
+ None, empty, nor parseable as an int (e.g. a hand-edited
26
+ ``branch-mapping.json`` containing ``"id": "not-a-number"``). The
27
+ caller (typically ``BranchMapping.from_dict``) should let this
28
+ bubble up to ``load_branch_mapping`` which converts it to a
29
+ ConfigError surface (issue #269 sec-20).
30
+ """
31
+ if raw is None or raw == "":
32
+ return None
33
+ try:
34
+ return int(raw)
35
+ except (TypeError, ValueError) as exc:
36
+ raise ValueError(
37
+ f"Invalid branch ID in branch-mapping.json: {raw!r}. "
38
+ f"Expected null or an integer; got {type(raw).__name__}."
39
+ ) from exc
40
+
41
+
42
+ class BranchMappingEntry:
43
+ """A single git branch -> Keboola branch mapping."""
44
+
45
+ def __init__(self, keboola_id: int | None, name: str):
46
+ self.keboola_id = keboola_id # None = production
47
+ self.name = name
48
+
49
+ def is_production(self) -> bool:
50
+ return self.keboola_id is None
51
+
52
+ def to_dict(self) -> dict[str, Any]:
53
+ return {"id": self.keboola_id, "name": self.name}
54
+
55
+
56
+ class BranchMapping:
57
+ """Manages git-to-Keboola branch mappings."""
58
+
59
+ def __init__(self) -> None:
60
+ self.version: int = 1
61
+ self.mappings: dict[str, BranchMappingEntry] = {}
62
+
63
+ def get(self, git_branch: str) -> BranchMappingEntry | None:
64
+ return self.mappings.get(git_branch)
65
+
66
+ def set(self, git_branch: str, keboola_id: int | None, name: str) -> None:
67
+ self.mappings[git_branch] = BranchMappingEntry(keboola_id, name)
68
+
69
+ def remove(self, git_branch: str) -> bool:
70
+ if git_branch in self.mappings:
71
+ del self.mappings[git_branch]
72
+ return True
73
+ return False
74
+
75
+ def to_dict(self) -> dict[str, Any]:
76
+ return {
77
+ "version": self.version,
78
+ "mappings": {k: v.to_dict() for k, v in self.mappings.items()},
79
+ }
80
+
81
+ @classmethod
82
+ def from_dict(cls, data: dict[str, Any]) -> BranchMapping:
83
+ mapping = cls()
84
+ mapping.version = data.get("version", 1)
85
+ for git_branch, entry in data.get("mappings", {}).items():
86
+ mapping.mappings[git_branch] = BranchMappingEntry(
87
+ keboola_id=_coerce_keboola_id(entry.get("id")),
88
+ name=entry.get("name", ""),
89
+ )
90
+ return mapping
91
+
92
+
93
+ def load_branch_mapping(project_root: Path) -> BranchMapping:
94
+ """Load .keboola/branch-mapping.json.
95
+
96
+ Raises:
97
+ FileNotFoundError: If the mapping file does not exist.
98
+ ConfigError: If the JSON cannot be parsed or contains a malformed
99
+ branch ID. The descriptive message names the offending file
100
+ so the user can find and fix it. Surfacing this as ConfigError
101
+ (rather than raw ValueError) lets CLI commands catch it via
102
+ the existing ``except ConfigError`` handler and emit a clean
103
+ JSON error envelope with exit code 5 instead of a Python
104
+ traceback (issue #269 sec-20 + smoke-test follow-up).
105
+ """
106
+ path = project_root / KEBOOLA_DIR_NAME / BRANCH_MAPPING_FILENAME
107
+ if not path.exists():
108
+ raise FileNotFoundError(f"Branch mapping not found at {path}")
109
+ try:
110
+ data = json.loads(path.read_text(encoding="utf-8"))
111
+ return BranchMapping.from_dict(data)
112
+ except ValueError as exc:
113
+ # _coerce_keboola_id and json.loads both raise ValueError on
114
+ # malformed input; wrap with path context and convert to
115
+ # ConfigError so the CLI surfaces a clean error envelope.
116
+ raise ConfigError(f"Failed to parse {path}: {exc}") from exc
117
+
118
+
119
+ def save_branch_mapping(project_root: Path, mapping: BranchMapping) -> None:
120
+ """Save branch mapping to .keboola/branch-mapping.json."""
121
+ path = project_root / KEBOOLA_DIR_NAME / BRANCH_MAPPING_FILENAME
122
+ path.parent.mkdir(parents=True, exist_ok=True)
123
+ path.write_text(
124
+ json.dumps(mapping.to_dict(), indent=4, ensure_ascii=False) + "\n",
125
+ encoding="utf-8",
126
+ )
127
+
128
+
129
+ def find_sync_workspace(start: Path | None = None) -> Path | None:
130
+ """Locate the nearest enclosing sync workspace.
131
+
132
+ Walks up from *start* (or the current working directory) and returns
133
+ the first directory that contains a ``.keboola/branch-mapping.json``
134
+ file, or ``None`` if none is found before the filesystem root.
135
+ """
136
+ cursor = (start or Path.cwd()).resolve()
137
+ for candidate in [cursor, *cursor.parents]:
138
+ if (candidate / KEBOOLA_DIR_NAME / BRANCH_MAPPING_FILENAME).exists():
139
+ return candidate
140
+ return None
141
+
142
+
143
+ def cleanup_branch_id_from_mapping(branch_id: int) -> dict[str, Any] | None:
144
+ """Remove every git-branch entry that maps to *branch_id* from the
145
+ nearest enclosing sync workspace, if one exists.
146
+
147
+ Designed to be a best-effort cleanup hook for ``branch delete`` and
148
+ ``branch merge``: locates ``.keboola/branch-mapping.json`` via
149
+ :func:`find_sync_workspace`, removes any entries whose ``keboola_id``
150
+ equals *branch_id*, and persists the change. Returns a dict
151
+ describing what was unlinked, or ``None`` if no workspace was found
152
+ or no entry referenced the branch (no-op).
153
+ """
154
+ project_root = find_sync_workspace()
155
+ if project_root is None:
156
+ return None
157
+ try:
158
+ mapping = load_branch_mapping(project_root)
159
+ except (FileNotFoundError, ConfigError, ValueError):
160
+ # ValueError preserved for backward compat with callers that may
161
+ # still trigger it via custom paths; ConfigError is the new shape
162
+ # raised by load_branch_mapping itself.
163
+ return None
164
+
165
+ removed: list[str] = []
166
+ for git_branch, entry in list(mapping.mappings.items()):
167
+ if entry.keboola_id == branch_id:
168
+ mapping.remove(git_branch)
169
+ removed.append(git_branch)
170
+
171
+ if not removed:
172
+ return None
173
+ save_branch_mapping(project_root, mapping)
174
+ return {"project_root": str(project_root), "git_branches_unlinked": removed}
@@ -0,0 +1,211 @@
1
+ """Pure helpers for the ``sync clone`` composite (#426).
2
+
3
+ A clone copies a *reference* synced project tree into a fresh target, applies
4
+ declarative overrides, and re-points the manifest so a subsequent ``sync push``
5
+ CREATEs everything fresh in the target project. Cloning into a **fresh** target
6
+ project needs no id surgery: the reference's config ids do not exist in the
7
+ target remote, so the diff classifies every config as ``added`` and the push
8
+ assigns new ULIDs -- and because ``created_id_map`` is keyed by the reference id
9
+ (the manifest entry's id before writeback), the Phase-C variable links and the
10
+ Phase-D flow task ``configId``s remap reference->ULID automatically.
11
+
12
+ These functions are deliberately side-effecting but **pure of API calls**: they
13
+ only touch the on-disk tree + the in-memory manifest, so they are unit-testable
14
+ without a client. ``SyncService.clone_project`` orchestrates them and drives the
15
+ diff/push.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import shutil
21
+ from pathlib import Path
22
+ from typing import Any
23
+
24
+ import yaml
25
+
26
+ from ..constants import CONFIG_FILENAME
27
+
28
+ # Duplicated here (not imported from sync_service) to avoid a circular import --
29
+ # clone.py is imported *by* sync_service.
30
+ VARIABLES_COMPONENT_ID = "keboola.variables"
31
+
32
+ # Default branch dir name when a config's branch is not listed in the manifest
33
+ # (non-git-branching projects pull a single "main/" tree).
34
+ _DEFAULT_BRANCH_DIR = "main"
35
+
36
+
37
+ def branch_path_map(manifest: Any) -> dict[int, str]:
38
+ """Map ``branch_id -> on-disk branch dir name`` from the manifest branches."""
39
+ return {b.id: b.path for b in manifest.branches}
40
+
41
+
42
+ def _read_yaml(path: Path) -> dict[str, Any]:
43
+ loaded = yaml.safe_load(path.read_text(encoding="utf-8"))
44
+ return loaded if isinstance(loaded, dict) else {}
45
+
46
+
47
+ def _write_yaml(path: Path, data: dict[str, Any]) -> None:
48
+ path.write_text(
49
+ yaml.dump(data, sort_keys=False, allow_unicode=True, default_flow_style=False),
50
+ encoding="utf-8",
51
+ )
52
+
53
+
54
+ def copy_reference_tree(source_dir: Path, target_dir: Path) -> None:
55
+ """Copy a reference project tree to a fresh target dir (incl. code files).
56
+
57
+ Raises:
58
+ FileExistsError: if ``target_dir`` already exists (the caller decides
59
+ whether an existing target means "already cloned, just push").
60
+ """
61
+ if target_dir.exists():
62
+ raise FileExistsError(f"Target directory already exists: {target_dir}")
63
+ shutil.copytree(source_dir, target_dir)
64
+
65
+
66
+ def repoint_manifest_project(manifest: Any, *, project_id: int, api_host: str) -> None:
67
+ """Re-point the manifest's project block at the clone's target project."""
68
+ manifest.project.id = project_id
69
+ manifest.project.api_host = api_host
70
+
71
+
72
+ def _config_dir(target_dir: Path, branch_map: dict[int, str], branch_id: int, path: str) -> Path:
73
+ return target_dir / branch_map.get(branch_id, _DEFAULT_BRANCH_DIR) / path
74
+
75
+
76
+ def _remap_bucket_in_table_id(table_id: Any, bucket_map: dict[str, str]) -> Any:
77
+ """Rewrite the bucket prefix of a storage table id via ``bucket_map``.
78
+
79
+ A table id is ``<stage>.<bucket>.<table>`` whose bucket id is
80
+ ``<stage>.<bucket>``. A bucket-level reference (``in.c-foo``) is mapped
81
+ whole; a table reference (``in.c-foo.users``) has only its bucket prefix
82
+ swapped. Unmatched / non-string values pass through unchanged.
83
+ """
84
+ if not isinstance(table_id, str):
85
+ return table_id
86
+ for old, new in bucket_map.items():
87
+ if table_id == old:
88
+ return new
89
+ if table_id.startswith(old + "."):
90
+ return new + table_id[len(old) :]
91
+ return table_id
92
+
93
+
94
+ def _rewrite_buckets_in_config(config_dir: Path, bucket_map: dict[str, str]) -> int:
95
+ config_file = config_dir / CONFIG_FILENAME
96
+ if not config_file.exists():
97
+ return 0
98
+ data = _read_yaml(config_file)
99
+ rewrites = 0
100
+ for section, key in (("input", "source"), ("output", "destination")):
101
+ mapping = data.get(section)
102
+ if not isinstance(mapping, dict):
103
+ continue
104
+ for table in mapping.get("tables") or []:
105
+ if not isinstance(table, dict) or key not in table:
106
+ continue
107
+ new_value = _remap_bucket_in_table_id(table[key], bucket_map)
108
+ if new_value != table[key]:
109
+ table[key] = new_value
110
+ rewrites += 1
111
+ if rewrites:
112
+ _write_yaml(config_file, data)
113
+ return rewrites
114
+
115
+
116
+ def apply_bucket_map(target_dir: Path, manifest: Any, bucket_map: dict[str, str]) -> int:
117
+ """Rewrite bucket ids in every config's storage input/output mappings.
118
+
119
+ Returns the number of table references rewritten. A no-op when
120
+ ``bucket_map`` is empty.
121
+ """
122
+ if not bucket_map:
123
+ return 0
124
+ branch_map = branch_path_map(manifest)
125
+ rewrites = 0
126
+ for cfg in manifest.configurations:
127
+ rewrites += _rewrite_buckets_in_config(
128
+ _config_dir(target_dir, branch_map, cfg.branch_id, cfg.path), bucket_map
129
+ )
130
+ for row in cfg.rows:
131
+ rewrites += _rewrite_buckets_in_config(
132
+ _config_dir(target_dir, branch_map, cfg.branch_id, row.path), bucket_map
133
+ )
134
+ return rewrites
135
+
136
+
137
+ def _override_values_in_row(row_dir: Path, variable_values: dict[str, str]) -> int:
138
+ config_file = row_dir / CONFIG_FILENAME
139
+ if not config_file.exists():
140
+ return 0
141
+ data = _read_yaml(config_file)
142
+ # keboola.variables rows hoist their ``values`` array to the YAML top level.
143
+ values = data.get("values")
144
+ if not isinstance(values, list):
145
+ return 0
146
+ overridden = 0
147
+ for item in values:
148
+ if isinstance(item, dict) and item.get("name") in variable_values:
149
+ new_value = str(variable_values[item["name"]])
150
+ if item.get("value") != new_value:
151
+ item["value"] = new_value
152
+ overridden += 1
153
+ if overridden:
154
+ _write_yaml(config_file, data)
155
+ return overridden
156
+
157
+
158
+ def apply_variable_values(target_dir: Path, manifest: Any, variable_values: dict[str, str]) -> int:
159
+ """Override ``keboola.variables`` row values by variable name.
160
+
161
+ Returns the number of values overridden. A no-op when ``variable_values``
162
+ is empty. Values are written as strings (the Keboola variables contract).
163
+ """
164
+ if not variable_values:
165
+ return 0
166
+ branch_map = branch_path_map(manifest)
167
+ overridden = 0
168
+ for cfg in manifest.configurations:
169
+ if cfg.component_id != VARIABLES_COMPONENT_ID:
170
+ continue
171
+ for row in cfg.rows:
172
+ overridden += _override_values_in_row(
173
+ _config_dir(target_dir, branch_map, cfg.branch_id, row.path), variable_values
174
+ )
175
+ return overridden
176
+
177
+
178
+ def apply_instance_rename(target_dir: Path, manifest: Any, renames: dict[str, str]) -> int:
179
+ """Rename config-path prefixes on disk and in the manifest.
180
+
181
+ ``renames`` maps an old path prefix to a new one (e.g.
182
+ ``{"extractor/keboola.ex-http/Acme": "extractor/keboola.ex-http/Globex"}``).
183
+ For every config (and its rows) whose manifest ``path`` equals the prefix or
184
+ starts with ``prefix + "/"``, the on-disk subtree is moved once and the
185
+ manifest paths are rewritten. Returns the number of configs whose path
186
+ changed. A no-op when ``renames`` is empty.
187
+ """
188
+ if not renames:
189
+ return 0
190
+ branch_map = branch_path_map(manifest)
191
+ moved: set[tuple[str, str]] = set()
192
+ renamed = 0
193
+ for old, new in renames.items():
194
+ for cfg in manifest.configurations:
195
+ if not (cfg.path == old or cfg.path.startswith(old + "/")):
196
+ continue
197
+ branch_dir = branch_map.get(cfg.branch_id, _DEFAULT_BRANCH_DIR)
198
+ move_key = (branch_dir, old)
199
+ if move_key not in moved:
200
+ src = target_dir / branch_dir / old
201
+ dst = target_dir / branch_dir / new
202
+ if src.exists():
203
+ dst.parent.mkdir(parents=True, exist_ok=True)
204
+ shutil.move(str(src), str(dst))
205
+ moved.add(move_key)
206
+ cfg.path = new + cfg.path[len(old) :]
207
+ for row in cfg.rows:
208
+ if row.path == old or row.path.startswith(old + "/"):
209
+ row.path = new + row.path[len(old) :]
210
+ renamed += 1
211
+ return renamed