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,272 @@
1
+ """kbagent serve - launch the FastAPI HTTP server.
2
+
3
+ Wraps all kbagent services as REST endpoints. Print the bearer token to
4
+ stdout on startup so the Node.js BFF (or any other client) can read it.
5
+
6
+ With ``--ui`` the same process additionally serves the built React SPA
7
+ from ``web/frontend/dist`` and pre-injects the bearer token into the
8
+ HTML, so end-users don't need to spawn a Node BFF or paste tokens.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import logging
14
+ import os
15
+ import secrets
16
+ import sys
17
+ from datetime import UTC, datetime
18
+ from pathlib import Path
19
+
20
+ import typer
21
+
22
+ from ..constants import ENV_CONVERSATION_ID
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+ ENV_AUTH_TOKEN = "KBAGENT_SERVE_TOKEN"
27
+ ENV_UI_DIST = "KBAGENT_UI_DIST"
28
+
29
+
30
+ def _default_conversation_id() -> str:
31
+ """Generate a conversation ID for a fresh ``kbagent serve`` session.
32
+
33
+ Format: ``serve-<UTC-timestamp>-<8 hex>``. The timestamp prefix makes
34
+ log scanning by start time trivial; the hex suffix disambiguates
35
+ rapid restarts in the same minute. The ``serve-`` prefix is how
36
+ observability dashboards filter "human-driven session" vs other
37
+ integrations.
38
+
39
+ Reads ``KBAGENT_CONVERSATION_ID`` and reuses it when present so a
40
+ caller that pre-set the var (CI, supervisor scripts, debugging across
41
+ a restart) keeps a stable session ID across kbagent serve restarts.
42
+ """
43
+ existing = os.environ.get(ENV_CONVERSATION_ID, "").strip()
44
+ if existing:
45
+ return existing
46
+ stamp = datetime.now(UTC).strftime("%Y%m%dT%H%M%SZ")
47
+ return f"serve-{stamp}-{secrets.token_hex(4)}"
48
+
49
+
50
+ def _autodetect_ui_dist() -> Path | None:
51
+ """Find the built React SPA across install layouts.
52
+
53
+ Search order (first match wins):
54
+ 1) ``$KBAGENT_UI_DIST`` env var -- explicit override, never auto-overridden.
55
+ 2) ``keboola_agent_cli/_ui_dist`` *inside the installed package* --
56
+ populated at wheel build time by the ``hatch_build.py`` hook. This
57
+ is what ``uv tool install git+...`` and ``pip install`` from PyPI
58
+ both produce when Node is available (or a maintainer pre-built it).
59
+ 3) ``<repo>/web/frontend/dist`` resolved relative to this source file --
60
+ editable installs (``uv pip install -e .``) plus local clones.
61
+ 4) ``<cwd>/web/frontend/dist`` -- last-resort for unusual layouts.
62
+
63
+ Returns ``None`` if no candidate exists; the caller then surfaces a
64
+ "no UI bundled" error with rebuild instructions tailored to the
65
+ likely install method.
66
+ """
67
+ env = os.environ.get(ENV_UI_DIST)
68
+ if env:
69
+ p = Path(env).expanduser().resolve()
70
+ return p if (p / "index.html").exists() else None
71
+
72
+ # commands/serve.py -> commands -> keboola_agent_cli/ -> _ui_dist
73
+ bundled = Path(__file__).resolve().parent.parent / "_ui_dist"
74
+ if (bundled / "index.html").exists():
75
+ return bundled
76
+
77
+ # commands/serve.py -> commands -> keboola_agent_cli -> src -> repo
78
+ repo_dist = Path(__file__).resolve().parents[3] / "web" / "frontend" / "dist"
79
+ if (repo_dist / "index.html").exists():
80
+ return repo_dist
81
+
82
+ cwd_dist = Path.cwd() / "web" / "frontend" / "dist"
83
+ if (cwd_dist / "index.html").exists():
84
+ return cwd_dist
85
+ return None
86
+
87
+
88
+ def serve_command(
89
+ host: str = typer.Option(
90
+ "127.0.0.1",
91
+ "--host",
92
+ help="Bind host. Default 127.0.0.1 (localhost-only).",
93
+ ),
94
+ port: int = typer.Option(
95
+ 8001,
96
+ "--port",
97
+ help="Bind port. Default 8001 to leave 8000 for the Node BFF.",
98
+ ),
99
+ reload: bool = typer.Option(
100
+ False,
101
+ "--reload",
102
+ help="Auto-reload on code changes (uvicorn --reload).",
103
+ ),
104
+ log_level: str = typer.Option(
105
+ "info",
106
+ "--log-level",
107
+ help="uvicorn log level: critical, error, warning, info, debug, trace.",
108
+ ),
109
+ cors_origin: list[str] = typer.Option(
110
+ None,
111
+ "--cors-origin",
112
+ help="Add a CORS origin (repeatable). Default: localhost:5173 / 8000.",
113
+ ),
114
+ config_dir: str | None = typer.Option(
115
+ None,
116
+ "--config-dir",
117
+ help="Override config directory path (matches kbagent --config-dir).",
118
+ ),
119
+ ui: bool = typer.Option(
120
+ False,
121
+ "--ui",
122
+ help=(
123
+ "Mount the built React SPA at / so a single uvicorn process serves "
124
+ "both the API and the UI. ``GET /`` sets an HttpOnly `kbagent_session` "
125
+ "cookie (SameSite=Strict, Path=/) so the browser boots already "
126
+ "authenticated -- no Node BFF, no paste step, no token in the JS "
127
+ "heap or URL. Run `make web-build` once to produce the dist/ folder."
128
+ ),
129
+ ),
130
+ ui_dist: str | None = typer.Option(
131
+ None,
132
+ "--ui-dist",
133
+ help=(
134
+ "Override the path to the built React dist/ directory. Defaults to "
135
+ "<repo>/web/frontend/dist relative to the package, or $KBAGENT_UI_DIST. "
136
+ "Implies --ui."
137
+ ),
138
+ ),
139
+ ) -> None:
140
+ """Launch the kbagent HTTP API server.
141
+
142
+ The server prints a bearer token on startup. Set ``KBAGENT_SERVE_TOKEN``
143
+ in advance to pin a value (useful for the Node BFF dev workflow).
144
+ """
145
+ # Probe the optional server extras up front so users get a friendly
146
+ # install hint instead of a Python traceback. We catch BOTH imports
147
+ # (uvicorn AND the FastAPI surface that ``..server`` pulls in) because
148
+ # a partial install (one extra present, the other not) was producing
149
+ # the legacy "ModuleNotFoundError: No module named 'fastapi'" raw
150
+ # traceback in 0.40.0 -- the previous guard only watched uvicorn.
151
+ try:
152
+ import uvicorn
153
+
154
+ from ..server import create_app
155
+ except ModuleNotFoundError as exc: # pragma: no cover
156
+ missing = exc.name or "server extras"
157
+ typer.echo(
158
+ f"\nkbagent serve requires the optional 'server' extras "
159
+ f"(missing: {missing}).\n\n"
160
+ "Reinstall with the [server] extras:\n\n"
161
+ " # If you installed via uv tool install (recommended for end users):\n"
162
+ " uv tool install --force --with 'keboola-cli[server]' \\\n"
163
+ " git+https://github.com/keboola/cli\n\n"
164
+ " # If you have a local checkout (development):\n"
165
+ " uv pip install -e '.[server]'\n",
166
+ err=True,
167
+ )
168
+ raise typer.Exit(code=1) from None
169
+
170
+ auth_token = os.environ.get(ENV_AUTH_TOKEN) or secrets.token_urlsafe(32)
171
+ os.environ[ENV_AUTH_TOKEN] = auth_token
172
+
173
+ # Generate a stable conversation ID for this serve session and export it
174
+ # to env so child processes (MCP subprocess, AI agent CLI invocations,
175
+ # scheduled `kbagent http` calls) inherit it and emit X-Conversation-ID
176
+ # on every Keboola API request. Otherwise observability shows
177
+ # "Conversation ID not set" in `kbagent doctor`.
178
+ conversation_id = _default_conversation_id()
179
+ os.environ[ENV_CONVERSATION_ID] = conversation_id
180
+
181
+ cors = list(cors_origin) if cors_origin else None
182
+
183
+ serve_url = f"http://{host}:{port}"
184
+
185
+ # Resolve --ui/--ui-dist. Either flag opts in; --ui-dist additionally
186
+ # pins the path. If --ui is on but no dist found, abort with the
187
+ # "run make web-build" hint instead of silently launching API-only.
188
+ resolved_ui_dist: str | None = None
189
+ if ui or ui_dist:
190
+ candidate = Path(ui_dist).expanduser().resolve() if ui_dist else _autodetect_ui_dist()
191
+ if candidate is None or not (candidate / "index.html").exists():
192
+ bundled = Path(__file__).resolve().parent.parent / "_ui_dist"
193
+ repo_dist = Path(__file__).resolve().parents[3] / "web" / "frontend" / "dist"
194
+ cwd_dist = Path.cwd() / "web" / "frontend" / "dist"
195
+ typer.echo(
196
+ "--ui: no built React SPA found.\n"
197
+ "\n"
198
+ " Searched these locations (none had index.html):\n"
199
+ f" 1. $KBAGENT_UI_DIST {os.environ.get(ENV_UI_DIST, '(unset)')}\n"
200
+ f" 2. installed package {bundled}\n"
201
+ f" 3. repo checkout {repo_dist}\n"
202
+ f" 4. cwd {cwd_dist}\n"
203
+ "\n"
204
+ " How to fix:\n"
205
+ " - From git checkout: `make web-build` then re-run.\n"
206
+ " - From `uv tool install git+...`: re-install with Node 20+\n"
207
+ " on PATH; the build hook will compile the SPA automatically.\n"
208
+ " - Or pin a path: `kbagent serve --ui-dist /path/to/dist`.",
209
+ err=True,
210
+ )
211
+ raise typer.Exit(code=1) from None
212
+ resolved_ui_dist = str(candidate)
213
+
214
+ app = create_app(
215
+ config_dir=config_dir,
216
+ auth_token=auth_token,
217
+ cors_origins=cors,
218
+ serve_url=serve_url,
219
+ ui_dist=resolved_ui_dist,
220
+ )
221
+
222
+ if resolved_ui_dist:
223
+ # UI mode: the user opens the browser directly; there is no BFF to
224
+ # paste the token into. The token is still printed so scripted
225
+ # callers / curl one-liners keep working.
226
+ sys.stdout.write(
227
+ "\n"
228
+ " kbagent serve (single-process UI mode)\n"
229
+ f" ├─ open: http://{host}:{port}/\n"
230
+ f" ├─ api docs: http://{host}:{port}/docs\n"
231
+ f" ├─ ui dist: {resolved_ui_dist}\n"
232
+ f" ├─ conv id: {conversation_id}\n"
233
+ f" └─ token: {auth_token}\n"
234
+ "\n"
235
+ " Browser is auto-authenticated via an HttpOnly kbagent_session cookie\n"
236
+ " set on GET /. Token never enters the JS heap or the URL.\n"
237
+ f" For curl / scripts: Authorization: Bearer {auth_token}\n"
238
+ "\n"
239
+ " For `kbagent http` in another terminal (bash/zsh):\n"
240
+ f" export KBAGENT_SERVE_URL=http://{host}:{port}\n"
241
+ f" export KBAGENT_SERVE_TOKEN={auth_token}\n"
242
+ f" export KBAGENT_CONVERSATION_ID={conversation_id}\n"
243
+ "\n"
244
+ )
245
+ else:
246
+ sys.stdout.write(
247
+ "\n"
248
+ " kbagent serve\n"
249
+ f" ├─ host: http://{host}:{port}\n"
250
+ f" ├─ docs: http://{host}:{port}/docs\n"
251
+ f" ├─ openapi: http://{host}:{port}/openapi.json\n"
252
+ f" ├─ conv id: {conversation_id}\n"
253
+ f" └─ token: {auth_token}\n"
254
+ "\n"
255
+ f" Set {ENV_AUTH_TOKEN}={auth_token} for the Node BFF.\n"
256
+ " Tip: `kbagent serve --ui` mounts the React SPA on the same port.\n"
257
+ "\n"
258
+ " For `kbagent http` in another terminal (bash/zsh):\n"
259
+ f" export KBAGENT_SERVE_URL=http://{host}:{port}\n"
260
+ f" export KBAGENT_SERVE_TOKEN={auth_token}\n"
261
+ f" export KBAGENT_CONVERSATION_ID={conversation_id}\n"
262
+ "\n"
263
+ )
264
+ sys.stdout.flush()
265
+
266
+ uvicorn.run(
267
+ app,
268
+ host=host,
269
+ port=port,
270
+ reload=reload,
271
+ log_level=log_level,
272
+ )
@@ -0,0 +1,340 @@
1
+ """Sharing commands - cross-project bucket sharing and linking.
2
+
3
+ Enable data sharing between Keboola projects:
4
+ - share/unshare: control bucket visibility (requires master token)
5
+ - link/unlink: create/remove linked buckets in target projects
6
+ - list: browse available shared buckets
7
+ """
8
+
9
+ import typer
10
+
11
+ from ..errors import ConfigError, ErrorCode, KeboolaApiError
12
+ from ._helpers import (
13
+ check_cli_permission,
14
+ emit_project_warnings,
15
+ get_formatter,
16
+ get_service,
17
+ map_error_to_exit_code,
18
+ )
19
+
20
+ sharing_app = typer.Typer(
21
+ help=(
22
+ "Cross-project bucket sharing and linking.\n\n"
23
+ "share/unshare require a master token (org membership). "
24
+ "Set via KBC_MASTER_TOKEN_{ALIAS} or KBC_MASTER_TOKEN env var.\n\n"
25
+ "list/link/unlink work with the regular project token."
26
+ )
27
+ )
28
+
29
+
30
+ @sharing_app.callback(invoke_without_command=True)
31
+ def _sharing_permission_check(ctx: typer.Context) -> None:
32
+ check_cli_permission(ctx, "sharing")
33
+
34
+
35
+ @sharing_app.command("list")
36
+ def sharing_list(
37
+ ctx: typer.Context,
38
+ project: list[str] | None = typer.Option(
39
+ None,
40
+ "--project",
41
+ help="Project alias (repeatable). Omit to query all projects.",
42
+ ),
43
+ ) -> None:
44
+ """List shared buckets available for linking.
45
+
46
+ Shows buckets shared within your organization that can be linked
47
+ into your projects. Uses the regular project token.
48
+ """
49
+ formatter = get_formatter(ctx)
50
+ service = get_service(ctx, "sharing_service")
51
+
52
+ try:
53
+ result = service.list_shared(aliases=project)
54
+ except ConfigError as exc:
55
+ formatter.error(message=exc.message, error_code=ErrorCode.CONFIG_ERROR)
56
+ raise typer.Exit(code=5) from None
57
+
58
+ if formatter.json_mode:
59
+ formatter.output(result)
60
+ else:
61
+ from rich.table import Table
62
+
63
+ buckets = result["shared_buckets"]
64
+ if not buckets:
65
+ formatter.console.print("[dim]No shared buckets found.[/dim]")
66
+ return
67
+
68
+ table = Table(title="Shared Buckets")
69
+ table.add_column("Source Project", style="bold")
70
+ table.add_column("Bucket ID", style="cyan")
71
+ table.add_column("Sharing Type", style="dim")
72
+ table.add_column("Backend", style="dim")
73
+ table.add_column("Tables", justify="right")
74
+ table.add_column("Rows", justify="right")
75
+
76
+ for b in buckets:
77
+ table.add_row(
78
+ f"{b['source_project_name']} (#{b['source_project_id']})",
79
+ b["source_bucket_id"],
80
+ b["sharing"],
81
+ b["backend"],
82
+ str(len(b.get("tables", []))),
83
+ str(b["rows_count"]),
84
+ )
85
+
86
+ formatter.console.print(table)
87
+ emit_project_warnings(formatter, result)
88
+
89
+
90
+ @sharing_app.command("share")
91
+ def sharing_share(
92
+ ctx: typer.Context,
93
+ project: str = typer.Option(
94
+ ...,
95
+ "--project",
96
+ help="Project alias that owns the bucket to share.",
97
+ ),
98
+ bucket_id: str = typer.Option(
99
+ ...,
100
+ "--bucket-id",
101
+ help="Bucket ID to share (e.g. out.c-data).",
102
+ ),
103
+ sharing_type: str = typer.Option(
104
+ ...,
105
+ "--type",
106
+ help=(
107
+ "Sharing type: 'organization' (all org members), "
108
+ "'organization-project' (all project members in org), "
109
+ "'selected-projects' (specific projects, use --target-project-ids), "
110
+ "'selected-users' (specific users, use --target-users)."
111
+ ),
112
+ ),
113
+ target_project_ids: str | None = typer.Option(
114
+ None,
115
+ "--target-project-ids",
116
+ help="Comma-separated project IDs (required for --type selected-projects).",
117
+ ),
118
+ target_users: str | None = typer.Option(
119
+ None,
120
+ "--target-users",
121
+ help="Comma-separated email addresses (required for --type selected-users).",
122
+ ),
123
+ ) -> None:
124
+ """Enable sharing on a bucket.
125
+
126
+ Requires a master token with organization membership. Set via env var:
127
+
128
+ KBC_MASTER_TOKEN_{ALIAS} (project-specific, e.g. KBC_MASTER_TOKEN_PROD)
129
+ KBC_MASTER_TOKEN (global fallback)
130
+
131
+ Falls back to the project's configured token if no master token is set.
132
+ """
133
+ formatter = get_formatter(ctx)
134
+ service = get_service(ctx, "sharing_service")
135
+
136
+ # Parse target lists
137
+ parsed_project_ids: list[int] | None = None
138
+ parsed_users: list[str] | None = None
139
+
140
+ if sharing_type == "selected-projects":
141
+ if not target_project_ids:
142
+ formatter.error(
143
+ message="--target-project-ids is required for --type selected-projects",
144
+ error_code=ErrorCode.USAGE_ERROR,
145
+ )
146
+ raise typer.Exit(code=2)
147
+ parsed_project_ids = [int(pid.strip()) for pid in target_project_ids.split(",")]
148
+
149
+ if sharing_type == "selected-users":
150
+ if not target_users:
151
+ formatter.error(
152
+ message="--target-users is required for --type selected-users",
153
+ error_code=ErrorCode.USAGE_ERROR,
154
+ )
155
+ raise typer.Exit(code=2)
156
+ parsed_users = [u.strip() for u in target_users.split(",")]
157
+
158
+ try:
159
+ result = service.share(
160
+ alias=project,
161
+ bucket_id=bucket_id,
162
+ sharing_type=sharing_type,
163
+ target_project_ids=parsed_project_ids,
164
+ target_users=parsed_users,
165
+ )
166
+ except ConfigError as exc:
167
+ formatter.error(message=exc.message, error_code=ErrorCode.CONFIG_ERROR)
168
+ raise typer.Exit(code=5) from None
169
+ except KeboolaApiError as exc:
170
+ formatter.error(message=exc.message, error_code=exc.error_code, retryable=exc.retryable)
171
+ raise typer.Exit(code=map_error_to_exit_code(exc)) from None
172
+
173
+ if formatter.json_mode:
174
+ formatter.output(result)
175
+ else:
176
+ formatter.success(result["message"])
177
+
178
+
179
+ @sharing_app.command("unshare")
180
+ def sharing_unshare(
181
+ ctx: typer.Context,
182
+ project: str = typer.Option(
183
+ ...,
184
+ "--project",
185
+ help="Project alias that owns the shared bucket.",
186
+ ),
187
+ bucket_id: str = typer.Option(
188
+ ...,
189
+ "--bucket-id",
190
+ help="Bucket ID to stop sharing (e.g. out.c-data).",
191
+ ),
192
+ ) -> None:
193
+ """Disable sharing on a bucket.
194
+
195
+ Fails if other projects still have linked buckets pointing to it.
196
+
197
+ Requires a master token (see 'sharing share --help' for env var details).
198
+ """
199
+ formatter = get_formatter(ctx)
200
+ service = get_service(ctx, "sharing_service")
201
+
202
+ try:
203
+ result = service.unshare(alias=project, bucket_id=bucket_id)
204
+ except ConfigError as exc:
205
+ formatter.error(message=exc.message, error_code=ErrorCode.CONFIG_ERROR)
206
+ raise typer.Exit(code=5) from None
207
+ except KeboolaApiError as exc:
208
+ formatter.error(message=exc.message, error_code=exc.error_code, retryable=exc.retryable)
209
+ raise typer.Exit(code=map_error_to_exit_code(exc)) from None
210
+
211
+ if formatter.json_mode:
212
+ formatter.output(result)
213
+ else:
214
+ formatter.success(result["message"])
215
+
216
+
217
+ @sharing_app.command("link")
218
+ def sharing_link(
219
+ ctx: typer.Context,
220
+ project: str = typer.Option(
221
+ ...,
222
+ "--project",
223
+ help="Target project alias where the linked bucket will be created.",
224
+ ),
225
+ source_project_id: int = typer.Option(
226
+ ...,
227
+ "--source-project-id",
228
+ help="ID of the project that owns the shared bucket.",
229
+ ),
230
+ bucket_id: str = typer.Option(
231
+ ...,
232
+ "--bucket-id",
233
+ help="Source bucket ID to link (e.g. out.c-data).",
234
+ ),
235
+ name: str | None = typer.Option(
236
+ None,
237
+ "--name",
238
+ help="Display name for the linked bucket. Auto-generated if omitted.",
239
+ ),
240
+ ) -> None:
241
+ """Link a shared bucket into a project.
242
+
243
+ Creates a read-only linked bucket in the target project that mirrors
244
+ the source bucket's tables. Uses the regular project token
245
+ (no master token needed).
246
+
247
+ Use 'sharing list' to discover available shared buckets.
248
+ """
249
+ formatter = get_formatter(ctx)
250
+ service = get_service(ctx, "sharing_service")
251
+
252
+ try:
253
+ result = service.link(
254
+ alias=project,
255
+ source_project_id=source_project_id,
256
+ source_bucket_id=bucket_id,
257
+ name=name,
258
+ )
259
+ except ConfigError as exc:
260
+ formatter.error(message=exc.message, error_code=ErrorCode.CONFIG_ERROR)
261
+ raise typer.Exit(code=5) from None
262
+ except KeboolaApiError as exc:
263
+ formatter.error(message=exc.message, error_code=exc.error_code, retryable=exc.retryable)
264
+ raise typer.Exit(code=map_error_to_exit_code(exc)) from None
265
+
266
+ if formatter.json_mode:
267
+ formatter.output(result)
268
+ else:
269
+ formatter.success(result["message"])
270
+
271
+
272
+ @sharing_app.command("unlink")
273
+ def sharing_unlink(
274
+ ctx: typer.Context,
275
+ project: str = typer.Option(
276
+ ...,
277
+ "--project",
278
+ help="Project alias containing the linked bucket.",
279
+ ),
280
+ bucket_id: str = typer.Option(
281
+ ...,
282
+ "--bucket-id",
283
+ help="Linked bucket ID to remove (e.g. in.c-shared-data).",
284
+ ),
285
+ ) -> None:
286
+ """Remove a linked bucket from a project.
287
+
288
+ Deletes the linked bucket. Does not affect the source bucket or
289
+ other projects that have linked it. Uses the regular project token.
290
+ """
291
+ formatter = get_formatter(ctx)
292
+ service = get_service(ctx, "sharing_service")
293
+
294
+ try:
295
+ result = service.unlink(alias=project, bucket_id=bucket_id)
296
+ except ConfigError as exc:
297
+ formatter.error(message=exc.message, error_code=ErrorCode.CONFIG_ERROR)
298
+ raise typer.Exit(code=5) from None
299
+ except KeboolaApiError as exc:
300
+ formatter.error(message=exc.message, error_code=exc.error_code, retryable=exc.retryable)
301
+ raise typer.Exit(code=map_error_to_exit_code(exc)) from None
302
+
303
+ if formatter.json_mode:
304
+ formatter.output(result)
305
+ else:
306
+ formatter.success(result["message"])
307
+
308
+
309
+ @sharing_app.command("edges")
310
+ def sharing_edges(
311
+ ctx: typer.Context,
312
+ project: list[str] | None = typer.Option(
313
+ None,
314
+ "--project",
315
+ help="Project alias to query (can be repeated for multiple projects).",
316
+ ),
317
+ ) -> None:
318
+ """Show cross-project data flow edges via bucket sharing.
319
+
320
+ Lists how data flows between projects through shared and linked buckets.
321
+ Each edge represents a source bucket shared to a target project.
322
+
323
+ For column-level lineage, use `lineage build` + `lineage show`.
324
+ """
325
+ formatter = get_formatter(ctx)
326
+ service = get_service(ctx, "lineage_service")
327
+
328
+ try:
329
+ result = service.get_lineage(aliases=project)
330
+ except ConfigError as exc:
331
+ formatter.error(message=exc.message, error_code=ErrorCode.CONFIG_ERROR)
332
+ raise typer.Exit(code=5) from None
333
+
334
+ if formatter.json_mode:
335
+ formatter.output(result)
336
+ else:
337
+ from ..output import format_lineage_table
338
+
339
+ format_lineage_table(formatter.console, result)
340
+ emit_project_warnings(formatter, result)