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,632 @@
1
+ """Typer sub-apps for ``kbagent semantic-layer add|edit|remove``.
2
+
3
+ Extracted from :mod:`commands.semantic_layer` so the parent commands file
4
+ stays under the 1,200-LOC commands-file ceiling defined in CONTRIBUTING.md.
5
+ The three sub-apps are mounted onto ``semantic_layer_app`` via
6
+ ``add_typer(...)`` in the parent module; they share error handling and the
7
+ stdin-TTY probe via :mod:`commands._semantic_layer_helpers`.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import typer
13
+ from rich.console import Console
14
+
15
+ from ..errors import ErrorCode
16
+ from ._helpers import (
17
+ check_cli_permission,
18
+ get_formatter,
19
+ get_service,
20
+ )
21
+ from ._semantic_layer_helpers import _handle_service_call, _is_stdin_tty
22
+
23
+ # ---------------------------------------------------------------------------
24
+ # semantic-layer add -- one sub-subcommand per entity type
25
+ # ---------------------------------------------------------------------------
26
+
27
+
28
+ add_app = typer.Typer(
29
+ name="add",
30
+ help="Add an entity (metric, dataset, relationship, constraint, glossary).",
31
+ no_args_is_help=True,
32
+ )
33
+
34
+
35
+ @add_app.callback(invoke_without_command=True)
36
+ def _add_permission_check(ctx: typer.Context) -> None:
37
+ """Permission check for the ``add`` sub-app.
38
+
39
+ Uses the standard ``check_cli_permission`` helper which composes the
40
+ operation key as ``"semantic-layer.add.{subcommand}"`` (one per leaf).
41
+ Every leaf within ``add`` is classified ``write`` in
42
+ :mod:`permissions.OPERATION_REGISTRY`, so the gate is uniform.
43
+ """
44
+ check_cli_permission(ctx, "semantic-layer.add")
45
+
46
+
47
+ def _print_item_added(label: str): # type: ignore[no-untyped-def]
48
+ """Build a human-mode lambda that confirms one item was added."""
49
+
50
+ def _render(c: Console, d: dict) -> None:
51
+ attrs = d.get("attributes") or {}
52
+ name = attrs.get("name") or attrs.get("term", "?")
53
+ c.print(
54
+ f"[bold green]Added {label}[/bold green] [cyan]{name}[/cyan] "
55
+ f"([dim]{d.get('id', '')}[/dim])"
56
+ )
57
+
58
+ return _render
59
+
60
+
61
+ @add_app.command("metric")
62
+ def add_metric(
63
+ ctx: typer.Context,
64
+ project: str = typer.Option(..., "--project", help="Project alias"),
65
+ model: str | None = typer.Option(None, "--model", help="Model name or UUID"),
66
+ name: str = typer.Option(..., "--name", help="Metric name"),
67
+ sql: str = typer.Option(..., "--sql", help="SQL expression for the metric"),
68
+ dataset: str = typer.Option(..., "--dataset", help="Dataset tableId this metric belongs to"),
69
+ description: str = typer.Option("", "--description", help="Optional description"),
70
+ yes: bool = typer.Option(False, "--yes", "-y", help="Skip the dataset-mismatch warning"),
71
+ ) -> None:
72
+ """Add a metric to a semantic-layer model."""
73
+ formatter = get_formatter(ctx)
74
+ service = get_service(ctx, "semantic_layer_service")
75
+ result = _handle_service_call(
76
+ ctx,
77
+ service.add_metric,
78
+ alias=project,
79
+ model_name_or_uuid=model,
80
+ name=name,
81
+ sql=sql,
82
+ dataset=dataset,
83
+ description=description,
84
+ assume_yes=yes,
85
+ is_tty=_is_stdin_tty(),
86
+ confirm_cb=typer.confirm,
87
+ )
88
+ formatter.output(result, _print_item_added("metric"))
89
+
90
+
91
+ @add_app.command("dataset")
92
+ def add_dataset(
93
+ ctx: typer.Context,
94
+ project: str = typer.Option(..., "--project", help="Project alias"),
95
+ model: str | None = typer.Option(None, "--model", help="Model name or UUID"),
96
+ name: str = typer.Option(..., "--name", help="Dataset name"),
97
+ table_id: str = typer.Option(
98
+ ..., "--table-id", help="Storage tableId, e.g. out.c-bucket.table"
99
+ ),
100
+ description: str = typer.Option("", "--description"),
101
+ grain: str = typer.Option("", "--grain", help="Grain description"),
102
+ primary_key: list[str] | None = typer.Option(
103
+ None, "--primary-key", help="Repeat for multi-col PK"
104
+ ),
105
+ deep_fields: bool = typer.Option(
106
+ False,
107
+ "--deep-fields",
108
+ help="Fetch storage schema and synthesise fields[] with role heuristics.",
109
+ ),
110
+ ) -> None:
111
+ """Add a dataset (FQN derived from tableId)."""
112
+ formatter = get_formatter(ctx)
113
+ service = get_service(ctx, "semantic_layer_service")
114
+ result = _handle_service_call(
115
+ ctx,
116
+ service.add_dataset,
117
+ alias=project,
118
+ model_name_or_uuid=model,
119
+ name=name,
120
+ table_id=table_id,
121
+ description=description,
122
+ grain=grain,
123
+ primary_key=primary_key,
124
+ deep_fields=deep_fields,
125
+ )
126
+ formatter.output(result, _print_item_added("dataset"))
127
+
128
+
129
+ @add_app.command("relationship")
130
+ def add_relationship(
131
+ ctx: typer.Context,
132
+ project: str = typer.Option(..., "--project", help="Project alias"),
133
+ model: str | None = typer.Option(None, "--model", help="Model name or UUID"),
134
+ name: str = typer.Option(..., "--name", help="Relationship name"),
135
+ from_: str = typer.Option(..., "--from", help="Source dataset tableId"),
136
+ to: str = typer.Option(..., "--to", help="Target dataset tableId"),
137
+ on: str = typer.Option(..., "--on", help="Join condition"),
138
+ type_: str = typer.Option("left", "--type", help="Join type: 'left' or 'inner'."),
139
+ ) -> None:
140
+ """Add a relationship between two datasets."""
141
+ formatter = get_formatter(ctx)
142
+ service = get_service(ctx, "semantic_layer_service")
143
+ result = _handle_service_call(
144
+ ctx,
145
+ service.add_relationship,
146
+ alias=project,
147
+ model_name_or_uuid=model,
148
+ name=name,
149
+ from_=from_,
150
+ to=to,
151
+ on=on,
152
+ type_=type_,
153
+ )
154
+ formatter.output(result, _print_item_added("relationship"))
155
+
156
+
157
+ @add_app.command("constraint")
158
+ def add_constraint(
159
+ ctx: typer.Context,
160
+ project: str = typer.Option(..., "--project", help="Project alias"),
161
+ model: str | None = typer.Option(None, "--model", help="Model name or UUID"),
162
+ name: str = typer.Option(
163
+ ...,
164
+ "--name",
165
+ help=(
166
+ "Constraint name (regex ^[a-z][a-z0-9_]*$). For the 4-band "
167
+ "health convention end with _critical / _warning / _healthy / _review."
168
+ ),
169
+ ),
170
+ constraint_type: str = typer.Option(
171
+ ...,
172
+ "--constraint-type",
173
+ help=("One of: inequality|equality|range|composition|exclusion|temporal|conditional."),
174
+ ),
175
+ rule: str = typer.Option(
176
+ ...,
177
+ "--rule",
178
+ help='Rule expression STRING (e.g. "value >= 0"). NOT an object.',
179
+ ),
180
+ metrics: str = typer.Option(
181
+ ...,
182
+ "--metrics",
183
+ help="Comma-separated list of metric names this constraint applies to.",
184
+ ),
185
+ severity: str = typer.Option(
186
+ "warning", "--severity", help="One of: error|warning|info (the 3-level API enum)."
187
+ ),
188
+ ) -> None:
189
+ """Add a constraint."""
190
+ formatter = get_formatter(ctx)
191
+ service = get_service(ctx, "semantic_layer_service")
192
+ metrics_list = [m.strip() for m in metrics.split(",") if m.strip()]
193
+ if not metrics_list:
194
+ formatter.error(
195
+ message="--metrics must contain at least one metric name.",
196
+ error_code=ErrorCode.VALIDATION_ERROR,
197
+ )
198
+ raise typer.Exit(code=2)
199
+ result = _handle_service_call(
200
+ ctx,
201
+ service.add_constraint,
202
+ alias=project,
203
+ model_name_or_uuid=model,
204
+ name=name,
205
+ constraint_type=constraint_type,
206
+ rule=rule,
207
+ metrics=metrics_list,
208
+ severity=severity,
209
+ )
210
+ formatter.output(result, _print_item_added("constraint"))
211
+
212
+
213
+ @add_app.command("glossary")
214
+ def add_glossary(
215
+ ctx: typer.Context,
216
+ project: str = typer.Option(..., "--project", help="Project alias"),
217
+ model: str | None = typer.Option(None, "--model", help="Model name or UUID"),
218
+ term: str = typer.Option(..., "--term", help="Glossary term"),
219
+ definition: str = typer.Option("", "--definition", help="Optional definition"),
220
+ ) -> None:
221
+ """Add a glossary term."""
222
+ formatter = get_formatter(ctx)
223
+ service = get_service(ctx, "semantic_layer_service")
224
+ result = _handle_service_call(
225
+ ctx,
226
+ service.add_glossary,
227
+ alias=project,
228
+ model_name_or_uuid=model,
229
+ term=term,
230
+ definition=definition,
231
+ )
232
+ formatter.output(result, _print_item_added("glossary"))
233
+
234
+
235
+ # ---------------------------------------------------------------------------
236
+ # semantic-layer edit -- DELETE+POST with rollback + rename cascade
237
+ # ---------------------------------------------------------------------------
238
+
239
+
240
+ edit_app = typer.Typer(
241
+ name="edit",
242
+ help=(
243
+ "Edit a metric, dataset, constraint, relationship, or glossary term "
244
+ "(DELETE+POST with rollback)."
245
+ ),
246
+ no_args_is_help=True,
247
+ )
248
+
249
+
250
+ @edit_app.callback(invoke_without_command=True)
251
+ def _edit_permission_check(ctx: typer.Context) -> None:
252
+ """Permission check for the ``edit`` sub-app.
253
+
254
+ Every ``edit`` leaf is classified ``write`` in OPERATION_REGISTRY.
255
+ """
256
+ check_cli_permission(ctx, "semantic-layer.edit")
257
+
258
+
259
+ def _print_edit_result(label: str): # type: ignore[no-untyped-def]
260
+ """Build a human-mode renderer for edit responses."""
261
+
262
+ def _render(c: Console, d: dict) -> None:
263
+ updated = d.get("updated") or {}
264
+ attrs = updated.get("attributes") or {}
265
+ name = attrs.get("name") or attrs.get("term", "?")
266
+ if d.get("partial_state"):
267
+ c.print(
268
+ f"[bold red]PARTIAL STATE[/bold red] -- {label} edit "
269
+ f"succeeded but one or more cascade entries failed. The "
270
+ f"model is internally inconsistent until you re-run the "
271
+ f"failed cascades."
272
+ )
273
+ c.print(
274
+ f"[bold green]Updated {label}[/bold green] [cyan]{name}[/cyan] "
275
+ f"([dim]{updated.get('id', '')}[/dim])"
276
+ )
277
+ cascaded = d.get("cascaded_constraints") or []
278
+ for entry in cascaded:
279
+ status = entry.get("status", "?")
280
+ colour = "green" if status == "updated" else "red"
281
+ c.print(
282
+ f" [{colour}]cascade {status}[/{colour}] "
283
+ f"constraint [cyan]{entry.get('constraint', '?')}[/cyan]"
284
+ )
285
+ if d.get("rollback"):
286
+ c.print(f"[bold red]Rollback applied:[/bold red] {d['rollback']}")
287
+ if d.get("recovery_hint"):
288
+ c.print(f"[bold yellow]Recovery:[/bold yellow] {d['recovery_hint']}")
289
+
290
+ return _render
291
+
292
+
293
+ @edit_app.command("metric")
294
+ def edit_metric(
295
+ ctx: typer.Context,
296
+ project: str = typer.Option(..., "--project", help="Project alias"),
297
+ model: str | None = typer.Option(None, "--model", help="Model name or UUID"),
298
+ name: str = typer.Option(..., "--name", help="Current metric name"),
299
+ new_name: str | None = typer.Option(
300
+ None, "--new-name", help="Rename to this name (triggers constraint cascade)"
301
+ ),
302
+ new_sql: str | None = typer.Option(None, "--new-sql", help="Replace SQL"),
303
+ new_dataset: str | None = typer.Option(None, "--new-dataset", help="Replace dataset tableId"),
304
+ new_description: str | None = typer.Option(
305
+ None, "--new-description", help="Replace description"
306
+ ),
307
+ yes: bool = typer.Option(False, "--yes", "-y", help="Skip the rename-cascade prompt"),
308
+ ) -> None:
309
+ """Edit a metric. Rename cascades to any constraint that references it."""
310
+ formatter = get_formatter(ctx)
311
+ service = get_service(ctx, "semantic_layer_service")
312
+ result = _handle_service_call(
313
+ ctx,
314
+ service.edit_metric,
315
+ alias=project,
316
+ model_name_or_uuid=model,
317
+ current_name=name,
318
+ new_name=new_name,
319
+ new_sql=new_sql,
320
+ new_dataset=new_dataset,
321
+ new_description=new_description,
322
+ assume_yes=yes,
323
+ is_tty=_is_stdin_tty(),
324
+ confirm_cb=typer.confirm,
325
+ )
326
+ formatter.output(result, _print_edit_result("metric"))
327
+
328
+
329
+ @edit_app.command("dataset")
330
+ def edit_dataset(
331
+ ctx: typer.Context,
332
+ project: str = typer.Option(..., "--project", help="Project alias"),
333
+ model: str | None = typer.Option(None, "--model", help="Model name or UUID"),
334
+ name: str = typer.Option(..., "--name", help="Current dataset name"),
335
+ new_name: str | None = typer.Option(None, "--new-name", help="Rename to this name"),
336
+ new_description: str | None = typer.Option(
337
+ None, "--new-description", help="Replace description"
338
+ ),
339
+ new_grain: str | None = typer.Option(None, "--new-grain", help="Replace grain"),
340
+ ) -> None:
341
+ """Edit a dataset (no cascade — metric.dataset uses tableId, not name)."""
342
+ formatter = get_formatter(ctx)
343
+ service = get_service(ctx, "semantic_layer_service")
344
+ result = _handle_service_call(
345
+ ctx,
346
+ service.edit_dataset,
347
+ alias=project,
348
+ model_name_or_uuid=model,
349
+ current_name=name,
350
+ new_name=new_name,
351
+ new_description=new_description,
352
+ new_grain=new_grain,
353
+ )
354
+ formatter.output(result, _print_edit_result("dataset"))
355
+
356
+
357
+ @edit_app.command("constraint")
358
+ def edit_constraint(
359
+ ctx: typer.Context,
360
+ project: str = typer.Option(..., "--project", help="Project alias"),
361
+ model: str | None = typer.Option(None, "--model", help="Model name or UUID"),
362
+ name: str = typer.Option(..., "--name", help="Current constraint name"),
363
+ new_name: str | None = typer.Option(
364
+ None, "--new-name", help=f"Rename to this name (regex {'^[a-z][a-z0-9_]*$'!r})"
365
+ ),
366
+ new_rule: str | None = typer.Option(None, "--new-rule", help="Replace rule (STRING)"),
367
+ new_constraint_type: str | None = typer.Option(
368
+ None, "--new-constraint-type", help="Replace constraintType (closed enum)"
369
+ ),
370
+ new_severity: str | None = typer.Option(
371
+ None, "--new-severity", help="Replace severity (error|warning|info)"
372
+ ),
373
+ new_metrics: str | None = typer.Option(
374
+ None, "--new-metrics", help="Comma-separated list of metric names"
375
+ ),
376
+ ) -> None:
377
+ """Edit a constraint (DELETE+POST, with local validators)."""
378
+ formatter = get_formatter(ctx)
379
+ service = get_service(ctx, "semantic_layer_service")
380
+ metrics_list = (
381
+ [m.strip() for m in new_metrics.split(",") if m.strip()]
382
+ if new_metrics is not None
383
+ else None
384
+ )
385
+ result = _handle_service_call(
386
+ ctx,
387
+ service.edit_constraint,
388
+ alias=project,
389
+ model_name_or_uuid=model,
390
+ current_name=name,
391
+ new_name=new_name,
392
+ new_rule=new_rule,
393
+ new_constraint_type=new_constraint_type,
394
+ new_severity=new_severity,
395
+ new_metrics=metrics_list,
396
+ )
397
+ formatter.output(result, _print_edit_result("constraint"))
398
+
399
+
400
+ @edit_app.command("relationship")
401
+ def edit_relationship(
402
+ ctx: typer.Context,
403
+ project: str = typer.Option(..., "--project", help="Project alias"),
404
+ model: str | None = typer.Option(None, "--model", help="Model name or UUID"),
405
+ name: str = typer.Option(..., "--name", help="Current relationship name"),
406
+ new_name: str | None = typer.Option(None, "--new-name", help="Rename to this name"),
407
+ new_from: str | None = typer.Option(None, "--new-from", help="Replace source dataset tableId"),
408
+ new_to: str | None = typer.Option(None, "--new-to", help="Replace target dataset tableId"),
409
+ new_on: str | None = typer.Option(None, "--new-on", help="Replace join condition"),
410
+ new_type: str | None = typer.Option(
411
+ None, "--new-type", help="Replace join type (left | inner)"
412
+ ),
413
+ ) -> None:
414
+ """Edit a relationship (DELETE+POST). Validates ``--new-type`` locally."""
415
+ formatter = get_formatter(ctx)
416
+ service = get_service(ctx, "semantic_layer_service")
417
+ result = _handle_service_call(
418
+ ctx,
419
+ service.edit_relationship,
420
+ alias=project,
421
+ model_name_or_uuid=model,
422
+ current_name=name,
423
+ new_name=new_name,
424
+ new_from=new_from,
425
+ new_to=new_to,
426
+ new_on=new_on,
427
+ new_type=new_type,
428
+ )
429
+ formatter.output(result, _print_edit_result("relationship"))
430
+
431
+
432
+ @edit_app.command("glossary")
433
+ def edit_glossary(
434
+ ctx: typer.Context,
435
+ project: str = typer.Option(..., "--project", help="Project alias"),
436
+ model: str | None = typer.Option(None, "--model", help="Model name or UUID"),
437
+ term: str = typer.Option(..., "--term", help="Current glossary term"),
438
+ new_term: str | None = typer.Option(
439
+ None,
440
+ "--new-term",
441
+ help=(
442
+ "Rename the term (DESTRUCTIVE cascade: downstream consumers joining on the "
443
+ "term string will break -- pass --yes to confirm)."
444
+ ),
445
+ ),
446
+ new_definition: str | None = typer.Option(
447
+ None, "--new-definition", help="Replace the definition"
448
+ ),
449
+ yes: bool = typer.Option(
450
+ False, "--yes", "-y", help="Skip the rename-cascade prompt (required for --new-term)"
451
+ ),
452
+ ) -> None:
453
+ """Edit a glossary term. ``--new-term`` is destructive for downstream joins."""
454
+ formatter = get_formatter(ctx)
455
+ service = get_service(ctx, "semantic_layer_service")
456
+
457
+ if new_term is not None and new_term != term and not yes:
458
+ if not _is_stdin_tty():
459
+ formatter.error(
460
+ message=(
461
+ f"Refusing to rename glossary term {term!r} -> {new_term!r} "
462
+ "non-interactively without --yes (downstream consumers joining "
463
+ "on the term string will break)."
464
+ ),
465
+ error_code=ErrorCode.VALIDATION_ERROR,
466
+ )
467
+ raise typer.Exit(code=2)
468
+ if not formatter.json_mode and not typer.confirm(
469
+ f"Rename glossary term '{term}' to '{new_term}'? "
470
+ "Downstream consumers joining on the term will break."
471
+ ):
472
+ formatter.console.print("Aborted.")
473
+ raise typer.Exit(code=0)
474
+
475
+ result = _handle_service_call(
476
+ ctx,
477
+ service.edit_glossary,
478
+ alias=project,
479
+ model_name_or_uuid=model,
480
+ current_term=term,
481
+ new_term=new_term,
482
+ new_definition=new_definition,
483
+ )
484
+ formatter.output(result, _print_edit_result("glossary"))
485
+
486
+
487
+ # ---------------------------------------------------------------------------
488
+ # semantic-layer remove -- destructive, orphan-warning before delete
489
+ # ---------------------------------------------------------------------------
490
+
491
+
492
+ remove_app = typer.Typer(
493
+ name="remove",
494
+ help=("Remove a metric / dataset / constraint / relationship / glossary term (destructive)."),
495
+ no_args_is_help=True,
496
+ )
497
+
498
+
499
+ @remove_app.callback(invoke_without_command=True)
500
+ def _remove_permission_check(ctx: typer.Context) -> None:
501
+ """Permission check for the ``remove`` sub-app.
502
+
503
+ Every ``remove`` leaf is classified ``destructive`` in OPERATION_REGISTRY.
504
+ """
505
+ check_cli_permission(ctx, "semantic-layer.remove")
506
+
507
+
508
+ def _run_remove(
509
+ ctx: typer.Context,
510
+ *,
511
+ kind: str,
512
+ project: str,
513
+ model: str | None,
514
+ name: str,
515
+ yes: bool,
516
+ ) -> None:
517
+ formatter = get_formatter(ctx)
518
+ service = get_service(ctx, "semantic_layer_service")
519
+
520
+ # Always echo the orphan warning (--yes only skips the prompt).
521
+ preview = _handle_service_call(
522
+ ctx,
523
+ service.preview_remove,
524
+ alias=project,
525
+ model_name_or_uuid=model,
526
+ kind=kind,
527
+ name=name,
528
+ )
529
+
530
+ orphans = preview.get("orphaned_constraints") or []
531
+ is_tty = _is_stdin_tty()
532
+
533
+ if orphans and not formatter.json_mode:
534
+ formatter.err_console.print(
535
+ f"\n[bold yellow]Removing {kind} '{name}' will orphan "
536
+ f"{len(orphans)} constraint(s):[/bold yellow]"
537
+ )
538
+ for orph in orphans:
539
+ metrics = orph.get("metrics", [])
540
+ formatter.err_console.print(f" · {orph['name']} (metrics: {metrics})")
541
+ formatter.err_console.print(
542
+ "These constraints will have a dangling reference in DIM_METRIC_THRESHOLD.\n"
543
+ "To avoid this, remove or update the constraints first."
544
+ )
545
+
546
+ # Non-TTY without --yes: refuse with exit 2 (warning above already shown).
547
+ if not yes:
548
+ if not is_tty:
549
+ formatter.error(
550
+ message=(f"Refusing to remove {kind} {name!r} non-interactively without --yes."),
551
+ error_code=ErrorCode.VALIDATION_ERROR,
552
+ )
553
+ raise typer.Exit(code=2)
554
+ if not formatter.json_mode and not typer.confirm(f"Delete {kind} '{name}' anyway?"):
555
+ formatter.console.print("Aborted.")
556
+ raise typer.Exit(code=0)
557
+
558
+ result = _handle_service_call(
559
+ ctx,
560
+ service.remove_item,
561
+ alias=project,
562
+ model_name_or_uuid=model,
563
+ kind=kind,
564
+ name=name,
565
+ )
566
+ formatter.output(
567
+ result,
568
+ lambda c, d: c.print(
569
+ f"[bold green]Removed {kind}[/bold green] [cyan]{d['removed']['name']}[/cyan] "
570
+ f"([dim]{d['removed']['id']}[/dim])"
571
+ ),
572
+ )
573
+
574
+
575
+ @remove_app.command("metric")
576
+ def remove_metric(
577
+ ctx: typer.Context,
578
+ project: str = typer.Option(..., "--project", help="Project alias"),
579
+ model: str | None = typer.Option(None, "--model", help="Model name or UUID"),
580
+ name: str = typer.Option(..., "--name", help="Metric name"),
581
+ yes: bool = typer.Option(False, "--yes", "-y", help="Skip the confirm prompt"),
582
+ ) -> None:
583
+ """Remove a metric. Prints an orphan-warning when constraints reference it."""
584
+ _run_remove(ctx, kind="metric", project=project, model=model, name=name, yes=yes)
585
+
586
+
587
+ @remove_app.command("dataset")
588
+ def remove_dataset(
589
+ ctx: typer.Context,
590
+ project: str = typer.Option(..., "--project", help="Project alias"),
591
+ model: str | None = typer.Option(None, "--model", help="Model name or UUID"),
592
+ name: str = typer.Option(..., "--name", help="Dataset name"),
593
+ yes: bool = typer.Option(False, "--yes", "-y", help="Skip the confirm prompt"),
594
+ ) -> None:
595
+ """Remove a dataset."""
596
+ _run_remove(ctx, kind="dataset", project=project, model=model, name=name, yes=yes)
597
+
598
+
599
+ @remove_app.command("constraint")
600
+ def remove_constraint(
601
+ ctx: typer.Context,
602
+ project: str = typer.Option(..., "--project", help="Project alias"),
603
+ model: str | None = typer.Option(None, "--model", help="Model name or UUID"),
604
+ name: str = typer.Option(..., "--name", help="Constraint name"),
605
+ yes: bool = typer.Option(False, "--yes", "-y", help="Skip the confirm prompt"),
606
+ ) -> None:
607
+ """Remove a constraint."""
608
+ _run_remove(ctx, kind="constraint", project=project, model=model, name=name, yes=yes)
609
+
610
+
611
+ @remove_app.command("relationship")
612
+ def remove_relationship(
613
+ ctx: typer.Context,
614
+ project: str = typer.Option(..., "--project", help="Project alias"),
615
+ model: str | None = typer.Option(None, "--model", help="Model name or UUID"),
616
+ name: str = typer.Option(..., "--name", help="Relationship name"),
617
+ yes: bool = typer.Option(False, "--yes", "-y", help="Skip the confirm prompt"),
618
+ ) -> None:
619
+ """Remove a relationship. No orphan-check (relationships are leaf entities)."""
620
+ _run_remove(ctx, kind="relationship", project=project, model=model, name=name, yes=yes)
621
+
622
+
623
+ @remove_app.command("glossary")
624
+ def remove_glossary(
625
+ ctx: typer.Context,
626
+ project: str = typer.Option(..., "--project", help="Project alias"),
627
+ model: str | None = typer.Option(None, "--model", help="Model name or UUID"),
628
+ term: str = typer.Option(..., "--term", help="Glossary term"),
629
+ yes: bool = typer.Option(False, "--yes", "-y", help="Skip the confirm prompt"),
630
+ ) -> None:
631
+ """Remove a glossary term. No orphan-check (glossary is a leaf entity)."""
632
+ _run_remove(ctx, kind="glossary", project=project, model=model, name=term, yes=yes)
@@ -0,0 +1,44 @@
1
+ """Shared helpers for the ``semantic-layer`` command group.
2
+
3
+ Split out of :mod:`commands.semantic_layer` so that the ``add`` / ``edit`` /
4
+ ``remove`` sub-apps -- which live in :mod:`commands._semantic_layer_crud` --
5
+ can reuse the same error-handling and stdin-TTY probe without forcing a
6
+ circular import between the two command modules.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import sys
12
+
13
+ import typer
14
+
15
+ from ..errors import ConfigError, ErrorCode, KeboolaApiError
16
+ from ._helpers import get_formatter, map_error_to_exit_code
17
+
18
+
19
+ def _handle_service_call(ctx: typer.Context, func, *args, **kwargs): # type: ignore[no-untyped-def]
20
+ """Run a service call, mapping ``ConfigError`` / ``KeboolaApiError`` to exit codes.
21
+
22
+ Returns the service result on success; on failure, prints the structured
23
+ error envelope (JSON mode) or a red error line (human mode) and raises
24
+ ``typer.Exit`` with the appropriate code.
25
+ """
26
+ formatter = get_formatter(ctx)
27
+ try:
28
+ return func(*args, **kwargs)
29
+ except ConfigError as exc:
30
+ formatter.error(message=exc.message, error_code=ErrorCode.CONFIG_ERROR)
31
+ raise typer.Exit(code=5) from None
32
+ except KeboolaApiError as exc:
33
+ formatter.error(
34
+ message=exc.message,
35
+ error_code=exc.error_code,
36
+ retryable=exc.retryable,
37
+ details=exc.details,
38
+ )
39
+ raise typer.Exit(code=map_error_to_exit_code(exc)) from None
40
+
41
+
42
+ def _is_stdin_tty() -> bool:
43
+ """Return ``True`` when stdin is attached to a TTY (interactive shell)."""
44
+ return hasattr(sys.stdin, "isatty") and sys.stdin.isatty()