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,584 @@
1
+ """`kbagent dev-portal` — Developer Portal command surface.
2
+
3
+ Identity management mirrors `kbagent project`; portal writes are gated by
4
+ `require_random_code_confirmation()` from _helpers — there is no `--yes`
5
+ bypass and no env-var override.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import getpass
11
+ import sys
12
+ from enum import StrEnum
13
+ from typing import TYPE_CHECKING, Any
14
+
15
+ import typer
16
+
17
+ from ..errors import ConfigError, ErrorCode, KeboolaApiError
18
+ from ..models import DeveloperPortalIdentity
19
+
20
+ if TYPE_CHECKING:
21
+ from ..output import OutputFormatter
22
+ from ..services.dev_portal_service import PendingWrite
23
+ from ._helpers import (
24
+ check_cli_permission,
25
+ get_dev_portal_service,
26
+ get_formatter,
27
+ map_error_to_exit_code,
28
+ resolve_identity_alias,
29
+ )
30
+
31
+
32
+ # CLI-layer enforcement of the role_hint enum. The Pydantic validator on
33
+ # DeveloperPortalIdentity intentionally silent-downgrades unknown values to
34
+ # "vendor" for backwards compatibility with pre-0.51.1 config.json files
35
+ # that may carry arbitrary free-text strings. That tolerance is wrong at the
36
+ # CLI surface, where the user just typed a value RIGHT NOW -- a typo should
37
+ # fail loudly, not silently land as "vendor" and confuse the next operation.
38
+ # A StrEnum option type gives the Typer-level rejection (exit 2 + usage error)
39
+ # before any model construction.
40
+ class RoleHint(StrEnum):
41
+ vendor = "vendor"
42
+ admin = "admin"
43
+
44
+
45
+ dev_portal_app = typer.Typer(
46
+ help="Keboola Developer Portal — multi-identity, production-safe writes.",
47
+ no_args_is_help=True,
48
+ )
49
+
50
+ identity_app = typer.Typer(help="Manage Developer Portal identities (login credentials).")
51
+ dev_portal_app.add_typer(identity_app, name="identity")
52
+
53
+
54
+ @dev_portal_app.callback()
55
+ def _dev_portal_callback(ctx: typer.Context) -> None:
56
+ """Permission gate for `kbagent dev-portal …`."""
57
+ check_cli_permission(ctx, "dev-portal")
58
+
59
+
60
+ @identity_app.callback()
61
+ def _identity_callback(ctx: typer.Context) -> None:
62
+ """Permission gate for `kbagent dev-portal identity …`."""
63
+ check_cli_permission(ctx, "dev-portal.identity")
64
+
65
+
66
+ def _split_app(app: str) -> tuple[str, str]:
67
+ """Split `VENDOR.APP_ID` into (vendor, app_id)."""
68
+ if "." not in app:
69
+ raise typer.BadParameter(
70
+ f"--app must be in VENDOR.APP_ID form (e.g. keboola.ex-foo), got: {app!r}"
71
+ )
72
+ vendor, _ = app.split(".", 1)
73
+ return vendor, app
74
+
75
+
76
+ def _read_password_stdin() -> str:
77
+ """Read a password from stdin.
78
+
79
+ TTY -> getpass (hidden, line-based, Enter to confirm).
80
+ Pipe/redirected -> read to EOF, strip whitespace.
81
+ Using `sys.stdin.read()` unconditionally would hang interactively
82
+ until the user sent EOF (Ctrl-D); getpass on TTY fixes that.
83
+ """
84
+ if sys.stdin.isatty():
85
+ return getpass.getpass("Password: ").strip()
86
+ return sys.stdin.read().strip()
87
+
88
+
89
+ # ----- Identity subcommands -----
90
+
91
+
92
+ @identity_app.command(
93
+ "add", help="Add a Developer Portal identity (verifies creds before persisting)."
94
+ )
95
+ def identity_add(
96
+ ctx: typer.Context,
97
+ alias: str = typer.Option(..., "--alias"),
98
+ username: str = typer.Option(..., "--username"),
99
+ password: str | None = typer.Option(None, "--password"),
100
+ password_stdin: bool = typer.Option(
101
+ False,
102
+ "--password-stdin",
103
+ help="Read password from stdin. On a TTY this is a hidden prompt (Enter to confirm); on a pipe it reads until EOF (e.g. `echo $PASS | … --password-stdin`).",
104
+ ),
105
+ role_hint: RoleHint = typer.Option(
106
+ RoleHint.vendor,
107
+ "--role-hint",
108
+ help="Identity role: 'vendor' (default) or 'admin'. Routes write commands to different apps-api endpoints -- admin uses PATCH /admin/apps/{app} which accepts complexity/categories/forwardToken/processTimeout/etc. that the vendor endpoint forbids.",
109
+ ),
110
+ vendor: str | None = typer.Option(None, "--vendor"),
111
+ portal_url: str = typer.Option(
112
+ "https://apps-api.keboola.com",
113
+ "--portal-url",
114
+ ),
115
+ ) -> None:
116
+ formatter = get_formatter(ctx)
117
+ if password_stdin:
118
+ password = _read_password_stdin()
119
+ if not password:
120
+ raise typer.BadParameter("Pass --password or --password-stdin.")
121
+ identity = DeveloperPortalIdentity(
122
+ username=username,
123
+ password=password,
124
+ role_hint=role_hint,
125
+ vendor=vendor,
126
+ portal_url=portal_url,
127
+ )
128
+ svc = get_dev_portal_service(ctx)
129
+ try:
130
+ svc.add_identity(alias, identity)
131
+ except (ConfigError, KeboolaApiError) as exc:
132
+ formatter.error(
133
+ message=str(exc),
134
+ error_code=getattr(exc, "error_code", ErrorCode.CONFIG_ERROR),
135
+ )
136
+ raise typer.Exit(
137
+ code=map_error_to_exit_code(exc) if isinstance(exc, KeboolaApiError) else 5
138
+ ) from None
139
+ formatter.output({"status": "ok", "alias": alias, "username": username})
140
+
141
+
142
+ @identity_app.command("list", help="List configured Developer Portal identities.")
143
+ def identity_list(ctx: typer.Context) -> None:
144
+ formatter = get_formatter(ctx)
145
+ svc = get_dev_portal_service(ctx)
146
+ identities = svc.list_identities()
147
+ default = svc.current_identity()
148
+ rows = [
149
+ {
150
+ "alias": alias,
151
+ "username": ident.username,
152
+ "vendor": ident.vendor or "",
153
+ "role_hint": ident.role_hint,
154
+ "portal_url": ident.portal_url,
155
+ "default": alias == default,
156
+ }
157
+ for alias, ident in identities.items()
158
+ ]
159
+ formatter.output(rows)
160
+
161
+
162
+ @identity_app.command("remove", help="Remove a Developer Portal identity.")
163
+ def identity_remove(
164
+ ctx: typer.Context,
165
+ alias: str = typer.Option(..., "--alias"),
166
+ ) -> None:
167
+ formatter = get_formatter(ctx)
168
+ svc = get_dev_portal_service(ctx)
169
+ try:
170
+ svc.remove_identity(alias)
171
+ except ConfigError as exc:
172
+ formatter.error(message=str(exc), error_code=ErrorCode.CONFIG_ERROR)
173
+ raise typer.Exit(code=5) from None
174
+ formatter.output({"status": "ok", "removed": alias})
175
+
176
+
177
+ @identity_app.command("edit", help="Edit fields on a Developer Portal identity (or rename it).")
178
+ def identity_edit(
179
+ ctx: typer.Context,
180
+ alias: str = typer.Option(..., "--alias"),
181
+ username: str | None = typer.Option(None, "--username"),
182
+ password: str | None = typer.Option(None, "--password"),
183
+ password_stdin: bool = typer.Option(False, "--password-stdin"),
184
+ role_hint: RoleHint | None = typer.Option(
185
+ None,
186
+ "--role-hint",
187
+ ),
188
+ vendor: str | None = typer.Option(None, "--vendor"),
189
+ new_alias: str | None = typer.Option(None, "--new-alias"),
190
+ ) -> None:
191
+ formatter = get_formatter(ctx)
192
+ svc = get_dev_portal_service(ctx)
193
+ if password_stdin:
194
+ password = _read_password_stdin()
195
+ try:
196
+ if new_alias:
197
+ svc.rename_identity(alias, new_alias)
198
+ alias = new_alias
199
+ svc.edit_identity(
200
+ alias,
201
+ username=username,
202
+ password=password,
203
+ role_hint=role_hint,
204
+ vendor=vendor,
205
+ )
206
+ except ConfigError as exc:
207
+ formatter.error(message=str(exc), error_code=ErrorCode.CONFIG_ERROR)
208
+ raise typer.Exit(code=5) from None
209
+ formatter.output({"status": "ok", "alias": alias})
210
+
211
+
212
+ @identity_app.command("use", help="Set the default Developer Portal identity.")
213
+ def identity_use(
214
+ ctx: typer.Context,
215
+ alias: str = typer.Argument(..., help="Identity alias to set as default"),
216
+ ) -> None:
217
+ formatter = get_formatter(ctx)
218
+ svc = get_dev_portal_service(ctx)
219
+ try:
220
+ svc.use_identity(alias)
221
+ except ConfigError as exc:
222
+ formatter.error(message=str(exc), error_code=ErrorCode.CONFIG_ERROR)
223
+ raise typer.Exit(code=5) from None
224
+ formatter.output({"status": "ok", "default": alias})
225
+
226
+
227
+ @identity_app.command("current", help="Show the alias of the default Developer Portal identity.")
228
+ def identity_current(ctx: typer.Context) -> None:
229
+ formatter = get_formatter(ctx)
230
+ svc = get_dev_portal_service(ctx)
231
+ formatter.output({"default": svc.current_identity()})
232
+
233
+
234
+ @identity_app.command("verify", help="Probe a Developer Portal identity by logging in.")
235
+ def identity_verify(
236
+ ctx: typer.Context,
237
+ identity: str | None = typer.Option(None, "--identity"),
238
+ ) -> None:
239
+ formatter = get_formatter(ctx)
240
+ svc = get_dev_portal_service(ctx)
241
+ alias = resolve_identity_alias(ctx, identity)
242
+ try:
243
+ info = svc.verify_identity(alias)
244
+ except KeboolaApiError as exc:
245
+ formatter.error(message=str(exc), error_code=exc.error_code)
246
+ raise typer.Exit(code=map_error_to_exit_code(exc)) from None
247
+ formatter.output({"status": "ok", **info})
248
+
249
+
250
+ # ----- Read commands -----
251
+
252
+
253
+ @dev_portal_app.command("list", help="List Developer Portal apps for a vendor.")
254
+ def list_apps(
255
+ ctx: typer.Context,
256
+ vendor: str = typer.Option(..., "--vendor"),
257
+ identity: str | None = typer.Option(None, "--identity"),
258
+ ) -> None:
259
+ formatter = get_formatter(ctx)
260
+ svc = get_dev_portal_service(ctx)
261
+ alias = resolve_identity_alias(ctx, identity)
262
+ try:
263
+ apps = svc.list_apps(alias, vendor)
264
+ except KeboolaApiError as exc:
265
+ formatter.error(message=str(exc), error_code=exc.error_code)
266
+ raise typer.Exit(code=map_error_to_exit_code(exc)) from None
267
+ formatter.output(apps)
268
+
269
+
270
+ @dev_portal_app.command("get", help="Show the full Developer Portal entry for one app.")
271
+ def get_app_cmd(
272
+ ctx: typer.Context,
273
+ app: str = typer.Option(..., "--app", help="VENDOR.APP_ID, e.g. keboola.ex-foo"),
274
+ identity: str | None = typer.Option(None, "--identity"),
275
+ ) -> None:
276
+ formatter = get_formatter(ctx)
277
+ svc = get_dev_portal_service(ctx)
278
+ alias = resolve_identity_alias(ctx, identity)
279
+ vendor, app_id = _split_app(app)
280
+ try:
281
+ result = svc.get_app(alias, vendor, app_id)
282
+ except KeboolaApiError as exc:
283
+ formatter.error(message=str(exc), error_code=exc.error_code)
284
+ raise typer.Exit(code=map_error_to_exit_code(exc)) from None
285
+ formatter.output(result)
286
+
287
+
288
+ # ----- Write commands -----
289
+
290
+ import json # noqa: E402
291
+ from dataclasses import asdict # noqa: E402
292
+ from pathlib import Path # noqa: E402
293
+
294
+ from ..constants import EXIT_PERMISSION_DENIED # noqa: E402
295
+ from ._helpers import require_random_code_confirmation # noqa: E402
296
+
297
+
298
+ def _assert_tty(action_description: str) -> None:
299
+ """Refuse immediately on non-TTY; called before any payload I/O.
300
+
301
+ This is the *first* guard in every write command so that CI/CD shells
302
+ and AI agents are rejected before any file or stdin access happens.
303
+ The full random-code prompt fires later (after the preview) on TTY.
304
+ """
305
+ is_tty = hasattr(sys.stdin, "isatty") and sys.stdin.isatty()
306
+ if not is_tty:
307
+ sys.stderr.write(
308
+ f"\nRefusing to {action_description}: this action requires a "
309
+ "real terminal so a human can type the confirmation code. "
310
+ "There is no --yes bypass by design.\n"
311
+ )
312
+ raise typer.Exit(code=EXIT_PERMISSION_DENIED)
313
+
314
+
315
+ def _load_payload(data: str | None) -> dict:
316
+ if data is None:
317
+ raise typer.BadParameter("--data is required")
318
+ if data == "-":
319
+ return json.loads(sys.stdin.read())
320
+ return json.loads(Path(data).read_text())
321
+
322
+
323
+ def _render_pending(formatter: OutputFormatter, pending: PendingWrite) -> None:
324
+ """Write a stderr-only preview of the pending write."""
325
+ from ..services.dev_portal_service import (
326
+ PendingCreate,
327
+ PendingDeprecate,
328
+ PendingIconUpload,
329
+ PendingPatch,
330
+ PendingPublish,
331
+ )
332
+
333
+ err = formatter.err_console
334
+ if isinstance(pending, PendingPatch):
335
+ err.print(f"[bold]PATCH[/bold] /vendors/{pending.vendor}/apps/{pending.app_id}")
336
+ for d in pending.diff:
337
+ err.print(f" [yellow]{d.key}[/yellow]: {d.current!r} -> {d.new!r}")
338
+ if not pending.diff:
339
+ err.print(" [dim]no field-level changes (payload matches current state)[/dim]")
340
+ elif isinstance(pending, PendingCreate):
341
+ err.print(f"[bold]POST[/bold] /vendors/{pending.vendor}/apps")
342
+ err.print_json(json.dumps(pending.payload))
343
+ elif isinstance(pending, PendingIconUpload):
344
+ err.print(
345
+ f"[bold]UPLOAD ICON[/bold] {pending.png_path} -> "
346
+ f"{pending.vendor}/{pending.app_id} ({len(pending.png_bytes)} bytes)"
347
+ )
348
+ elif isinstance(pending, PendingPublish):
349
+ err.print(
350
+ f"[bold red]PUBLISH[/bold red] /vendors/{pending.vendor}/apps/"
351
+ f"{pending.app_id}/publish (requests Keboola review)"
352
+ )
353
+ elif isinstance(pending, PendingDeprecate):
354
+ err.print(
355
+ f"[bold red]DEPRECATE[/bold red] /vendors/{pending.vendor}/apps/"
356
+ f"{pending.app_id}/deprecate (hides app, blocks new configs)"
357
+ )
358
+
359
+
360
+ def _pending_as_json(pending: PendingWrite) -> dict[str, Any]:
361
+ """Serialise a pending write for --json --dry-run output."""
362
+ raw = asdict(pending)
363
+ if "png_bytes" in raw:
364
+ raw["png_bytes"] = f"<{len(raw['png_bytes'])} bytes>"
365
+ if "png_path" in raw:
366
+ raw["png_path"] = str(raw["png_path"])
367
+ return {"status": "dry-run", "pending": raw}
368
+
369
+
370
+ @dev_portal_app.command(
371
+ "create",
372
+ help="Create (register) a new app in the Developer Portal. Requires TTY confirm; --dry-run for preview.",
373
+ )
374
+ def create_cmd(
375
+ ctx: typer.Context,
376
+ vendor: str = typer.Option(..., "--vendor"),
377
+ data: str = typer.Option(..., "--data", help="Path to JSON payload, or '-' for stdin"),
378
+ identity: str | None = typer.Option(None, "--identity"),
379
+ dry_run: bool = typer.Option(False, "--dry-run"),
380
+ ) -> None:
381
+ if not dry_run:
382
+ _assert_tty(f"create app in vendor '{vendor}'")
383
+ formatter = get_formatter(ctx)
384
+ svc = get_dev_portal_service(ctx)
385
+ alias = resolve_identity_alias(ctx, identity)
386
+ try:
387
+ pending = svc.prepare_create(alias, vendor, _load_payload(data))
388
+ except KeboolaApiError as exc:
389
+ formatter.error(message=str(exc), error_code=exc.error_code)
390
+ raise typer.Exit(code=map_error_to_exit_code(exc)) from None
391
+ _render_pending(formatter, pending)
392
+ if dry_run:
393
+ formatter.output(_pending_as_json(pending))
394
+ return
395
+ require_random_code_confirmation(f"create app in vendor '{vendor}'")
396
+ try:
397
+ result = svc.apply(pending)
398
+ except KeboolaApiError as exc:
399
+ formatter.error(message=str(exc), error_code=exc.error_code)
400
+ raise typer.Exit(code=map_error_to_exit_code(exc)) from None
401
+ formatter.output({"status": "ok", "created": result})
402
+
403
+
404
+ @dev_portal_app.command(
405
+ "patch",
406
+ help="Patch one or more properties of an existing Developer Portal app. Requires TTY confirm; --dry-run for preview.",
407
+ )
408
+ def patch_cmd(
409
+ ctx: typer.Context,
410
+ app: str = typer.Option(..., "--app"),
411
+ data: str | None = typer.Option(None, "--data"),
412
+ property_: str | None = typer.Option(None, "--property"),
413
+ value: str | None = typer.Option(None, "--value"),
414
+ value_file: str | None = typer.Option(None, "--value-file"),
415
+ identity: str | None = typer.Option(None, "--identity"),
416
+ dry_run: bool = typer.Option(
417
+ False,
418
+ "--dry-run",
419
+ help=(
420
+ "Preview the diff without writing. NOTE: still logs in and GETs the "
421
+ "current app to compute the diff, so it needs portal connectivity; "
422
+ "on a personal (MFA) identity it will prompt for an MFA code. Use a "
423
+ "service.{vendor}.{id} identity for a fully non-interactive preview."
424
+ ),
425
+ ),
426
+ ) -> None:
427
+ if not dry_run:
428
+ _assert_tty(f"patch {app}")
429
+ formatter = get_formatter(ctx)
430
+ svc = get_dev_portal_service(ctx)
431
+ alias = resolve_identity_alias(ctx, identity)
432
+ vendor, app_id = _split_app(app)
433
+
434
+ if data:
435
+ payload = _load_payload(data)
436
+ elif property_:
437
+ if value_file:
438
+ raw = Path(value_file).read_text()
439
+ elif value is not None:
440
+ raw = value
441
+ else:
442
+ raise typer.BadParameter("--property requires --value or --value-file")
443
+ try:
444
+ parsed = json.loads(raw) if raw.strip()[:1] in "[{" else raw
445
+ except json.JSONDecodeError:
446
+ parsed = raw
447
+ payload = {property_: parsed}
448
+ else:
449
+ raise typer.BadParameter("Provide --data, or --property with --value/--value-file")
450
+
451
+ try:
452
+ pending = svc.prepare_patch(alias, vendor, app_id, payload)
453
+ except KeboolaApiError as exc:
454
+ formatter.error(message=str(exc), error_code=exc.error_code)
455
+ raise typer.Exit(code=map_error_to_exit_code(exc)) from None
456
+ _render_pending(formatter, pending)
457
+ if dry_run:
458
+ formatter.output(_pending_as_json(pending))
459
+ return
460
+ require_random_code_confirmation(f"patch {app}")
461
+ try:
462
+ svc.apply(pending)
463
+ except KeboolaApiError as exc:
464
+ formatter.error(message=str(exc), error_code=exc.error_code)
465
+ raise typer.Exit(code=map_error_to_exit_code(exc)) from None
466
+ formatter.output(
467
+ {
468
+ "status": "ok",
469
+ "app": app,
470
+ "patched_keys": [d.key for d in pending.diff],
471
+ }
472
+ )
473
+
474
+
475
+ @dev_portal_app.command(
476
+ "upload-icon",
477
+ help="Upload a 128x128 PNG icon for a Developer Portal app. Requires TTY confirm; --dry-run for preview.",
478
+ )
479
+ def upload_icon_cmd(
480
+ ctx: typer.Context,
481
+ app: str = typer.Option(..., "--app"),
482
+ file: str = typer.Option(..., "--file"),
483
+ identity: str | None = typer.Option(None, "--identity"),
484
+ dry_run: bool = typer.Option(False, "--dry-run"),
485
+ ) -> None:
486
+ if not dry_run:
487
+ _assert_tty(f"upload icon for {app}")
488
+ formatter = get_formatter(ctx)
489
+ svc = get_dev_portal_service(ctx)
490
+ alias = resolve_identity_alias(ctx, identity)
491
+ vendor, app_id = _split_app(app)
492
+ try:
493
+ pending = svc.prepare_upload_icon(alias, vendor, app_id, file)
494
+ except KeboolaApiError as exc:
495
+ formatter.error(message=str(exc), error_code=exc.error_code)
496
+ raise typer.Exit(code=map_error_to_exit_code(exc)) from None
497
+ _render_pending(formatter, pending)
498
+ if dry_run:
499
+ formatter.output(_pending_as_json(pending))
500
+ return
501
+ require_random_code_confirmation(f"upload icon for {app}")
502
+ try:
503
+ result = svc.apply(pending)
504
+ except KeboolaApiError as exc:
505
+ formatter.error(message=str(exc), error_code=exc.error_code)
506
+ raise typer.Exit(code=map_error_to_exit_code(exc)) from None
507
+ formatter.output(result)
508
+
509
+
510
+ @dev_portal_app.command(
511
+ "publish",
512
+ help="Publish an app in the Developer Portal (requests Keboola review). Requires TTY confirm; --dry-run for preview.",
513
+ )
514
+ def publish_cmd(
515
+ ctx: typer.Context,
516
+ app: str = typer.Option(..., "--app"),
517
+ identity: str | None = typer.Option(None, "--identity"),
518
+ dry_run: bool = typer.Option(
519
+ False,
520
+ "--dry-run",
521
+ help=(
522
+ "Preview without writing. NOTE: still logs in and GETs the app to "
523
+ "run the publish pre-flight check, so it needs portal connectivity; "
524
+ "a personal (MFA) identity will prompt for an MFA code. Use a "
525
+ "service.{vendor}.{id} identity for a non-interactive preview."
526
+ ),
527
+ ),
528
+ ) -> None:
529
+ if not dry_run:
530
+ _assert_tty(f"publish {app}")
531
+ formatter = get_formatter(ctx)
532
+ svc = get_dev_portal_service(ctx)
533
+ alias = resolve_identity_alias(ctx, identity)
534
+ vendor, app_id = _split_app(app)
535
+ try:
536
+ pending = svc.prepare_publish(alias, vendor, app_id)
537
+ except KeboolaApiError as exc:
538
+ formatter.error(message=str(exc), error_code=exc.error_code)
539
+ raise typer.Exit(code=map_error_to_exit_code(exc)) from None
540
+ _render_pending(formatter, pending)
541
+ if dry_run:
542
+ formatter.output(_pending_as_json(pending))
543
+ return
544
+ require_random_code_confirmation(f"publish {app}")
545
+ try:
546
+ result = svc.apply(pending)
547
+ except KeboolaApiError as exc:
548
+ formatter.error(message=str(exc), error_code=exc.error_code)
549
+ raise typer.Exit(code=map_error_to_exit_code(exc)) from None
550
+ formatter.output({"status": "ok", "published": result})
551
+
552
+
553
+ @dev_portal_app.command(
554
+ "deprecate",
555
+ help="Deprecate an app in the Developer Portal (hides it, blocks new configs). Requires TTY confirm; --dry-run for preview.",
556
+ )
557
+ def deprecate_cmd(
558
+ ctx: typer.Context,
559
+ app: str = typer.Option(..., "--app"),
560
+ identity: str | None = typer.Option(None, "--identity"),
561
+ dry_run: bool = typer.Option(False, "--dry-run"),
562
+ ) -> None:
563
+ if not dry_run:
564
+ _assert_tty(f"deprecate {app}")
565
+ formatter = get_formatter(ctx)
566
+ svc = get_dev_portal_service(ctx)
567
+ alias = resolve_identity_alias(ctx, identity)
568
+ vendor, app_id = _split_app(app)
569
+ try:
570
+ pending = svc.prepare_deprecate(alias, vendor, app_id)
571
+ except KeboolaApiError as exc:
572
+ formatter.error(message=str(exc), error_code=exc.error_code)
573
+ raise typer.Exit(code=map_error_to_exit_code(exc)) from None
574
+ _render_pending(formatter, pending)
575
+ if dry_run:
576
+ formatter.output(_pending_as_json(pending))
577
+ return
578
+ require_random_code_confirmation(f"deprecate {app}")
579
+ try:
580
+ result = svc.apply(pending)
581
+ except KeboolaApiError as exc:
582
+ formatter.error(message=str(exc), error_code=exc.error_code)
583
+ raise typer.Exit(code=map_error_to_exit_code(exc)) from None
584
+ formatter.output({"status": "ok", "deprecated": result})
@@ -0,0 +1,37 @@
1
+ """Doctor command - thin CLI wrapper over DoctorService.
2
+
3
+ Delegates all health check logic to DoctorService (3-layer architecture).
4
+ """
5
+
6
+ import typer
7
+ from rich.console import Console
8
+
9
+ from ..output import format_doctor_panel
10
+ from ._helpers import get_formatter, get_service
11
+
12
+
13
+ def doctor_command(
14
+ ctx: typer.Context,
15
+ fix: bool = typer.Option(
16
+ False,
17
+ "--fix",
18
+ help="Auto-fix issues: install MCP server binary for faster startup.",
19
+ ),
20
+ ) -> None:
21
+ """Run health checks on CLI configuration and project connectivity."""
22
+ formatter = get_formatter(ctx)
23
+ doctor_service = get_service(ctx, "doctor_service")
24
+
25
+ # If --fix, run warmup before checks
26
+ if fix:
27
+ console = Console()
28
+ console.print("[bold]Running auto-fix...[/bold]")
29
+ warmup_result = doctor_service.warmup()
30
+ if warmup_result["installed"]:
31
+ console.print(f"[green]OK[/green] {warmup_result['message']}")
32
+ else:
33
+ console.print(f"[dim]{warmup_result['message']}[/dim]")
34
+ console.print()
35
+
36
+ result = doctor_service.run_checks()
37
+ formatter.output(result, format_doctor_panel)