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,384 @@
1
+ """Data App endpoints (lifecycle, secrets, password)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from fastapi import APIRouter, Depends, HTTPException, Query
10
+ from pydantic import BaseModel
11
+
12
+ from ..dependencies import ServiceRegistry, get_manage_token, get_registry
13
+
14
+ router = APIRouter(prefix="/data-apps", tags=["data-apps"])
15
+
16
+
17
+ def _resolve_git_pat(git_pat_env: str | None, git_pat_file: str | None) -> str | None:
18
+ """Resolve git PAT from env var name or file path to the actual token value."""
19
+ if git_pat_env is not None:
20
+ value = os.environ.get(git_pat_env, "")
21
+ if not value:
22
+ raise HTTPException(
23
+ status_code=400,
24
+ detail=f"Environment variable {git_pat_env!r} is unset or empty.",
25
+ )
26
+ return value
27
+ if git_pat_file is not None:
28
+ try:
29
+ return Path(git_pat_file).read_text(encoding="utf-8").strip()
30
+ except OSError as exc:
31
+ raise HTTPException(
32
+ status_code=400,
33
+ detail=f"Cannot read PAT file {git_pat_file!r}: {exc}",
34
+ ) from exc
35
+ return None
36
+
37
+
38
+ class DataAppCreate(BaseModel):
39
+ name: str
40
+ description: str = ""
41
+ slug: str
42
+ git_repo: str
43
+ git_branch: str = "main"
44
+ git_public: bool = False
45
+ git_username: str | None = None
46
+ git_pat_plaintext: str | None = None
47
+ git_pat_encrypted: str | None = None
48
+ auth: str = "password"
49
+ size: str = "tiny"
50
+ auto_suspend_after_seconds: int = 900
51
+ type: str = "python-js"
52
+ branch_id: int | None = None
53
+ deploy: bool = True
54
+ wait: bool = False
55
+ timeout_seconds: float = 600.0
56
+ keep_on_failure: bool = False
57
+ dry_run: bool = False
58
+
59
+
60
+ class SecretsSet(BaseModel):
61
+ secrets: dict[str, str]
62
+ branch_id: int | None = None
63
+ allow_plaintext_on_encrypt_failure: bool = False
64
+ dry_run: bool = False
65
+
66
+
67
+ class SecretsRemove(BaseModel):
68
+ keys: list[str]
69
+ branch_id: int | None = None
70
+ dry_run: bool = False
71
+
72
+
73
+ class RepoValidate(BaseModel):
74
+ git_repo: str
75
+ git_branch: str = "main"
76
+ git_public: bool = True
77
+ git_pat_env: str | None = None
78
+ git_pat_file: str | None = None
79
+ type: str = "python-js"
80
+ strict: bool = False
81
+
82
+
83
+ class GitCredentialCreate(BaseModel):
84
+ type: str
85
+ permissions: str
86
+ public_key: str | None = None
87
+ name: str | None = None
88
+
89
+
90
+ @router.get("", summary="List data apps across projects")
91
+ def list_apps(
92
+ project: list[str] | None = Query(None),
93
+ branch_id: int | None = None,
94
+ registry: ServiceRegistry = Depends(get_registry),
95
+ ) -> dict[str, Any]:
96
+ """List data apps in one or more projects. Mirrors `kbagent data-app list`."""
97
+ return registry.data_app.list_data_apps(aliases=project, branch_id=branch_id)
98
+
99
+
100
+ @router.get("/{project}/{app_id}", summary="Get data app detail")
101
+ def detail(
102
+ project: str,
103
+ app_id: str,
104
+ branch_id: int | None = None,
105
+ registry: ServiceRegistry = Depends(get_registry),
106
+ ) -> dict[str, Any]:
107
+ """Fetch detail for a single data app. Mirrors `kbagent data-app detail`."""
108
+ return registry.data_app.get_data_app(alias=project, app_id=app_id, branch_id=branch_id)
109
+
110
+
111
+ @router.post("/{project}", summary="Create a data app")
112
+ def create(
113
+ project: str, body: DataAppCreate, registry: ServiceRegistry = Depends(get_registry)
114
+ ) -> dict[str, Any]:
115
+ """Create a new data app, optionally deploy and wait. Mirrors `kbagent data-app create`."""
116
+ return registry.data_app.create_data_app(
117
+ alias=project,
118
+ name=body.name,
119
+ description=body.description,
120
+ slug=body.slug,
121
+ git_repo=body.git_repo,
122
+ git_branch=body.git_branch,
123
+ git_public=body.git_public,
124
+ git_username=body.git_username,
125
+ git_pat_plaintext=body.git_pat_plaintext,
126
+ git_pat_encrypted=body.git_pat_encrypted,
127
+ auth=body.auth,
128
+ size=body.size,
129
+ auto_suspend_after_seconds=body.auto_suspend_after_seconds,
130
+ type_=body.type,
131
+ branch_id=body.branch_id,
132
+ deploy=body.deploy,
133
+ wait=body.wait,
134
+ timeout_seconds=body.timeout_seconds,
135
+ keep_on_failure=body.keep_on_failure,
136
+ dry_run=body.dry_run,
137
+ )
138
+
139
+
140
+ @router.post("/{project}/{app_id}/deploy", summary="Deploy a data app version")
141
+ def deploy(
142
+ project: str,
143
+ app_id: str,
144
+ config_version: int | None = None,
145
+ wait: bool = False,
146
+ timeout_seconds: float = 600.0,
147
+ branch_id: int | None = None,
148
+ registry: ServiceRegistry = Depends(get_registry),
149
+ ) -> dict[str, Any]:
150
+ """Deploy the configured version of a data app. Mirrors `kbagent data-app deploy`."""
151
+ return registry.data_app.deploy_data_app(
152
+ alias=project,
153
+ app_id=app_id,
154
+ config_version=str(config_version) if config_version is not None else None,
155
+ wait=wait,
156
+ timeout_seconds=timeout_seconds,
157
+ branch_id=branch_id,
158
+ )
159
+
160
+
161
+ @router.post("/{project}/{app_id}/start", summary="Start a data app")
162
+ def start(
163
+ project: str,
164
+ app_id: str,
165
+ wait: bool = False,
166
+ timeout_seconds: float = 600.0,
167
+ registry: ServiceRegistry = Depends(get_registry),
168
+ ) -> dict[str, Any]:
169
+ """Start a deployed data app. Mirrors `kbagent data-app start`."""
170
+ return registry.data_app.start_data_app(
171
+ alias=project, app_id=app_id, wait=wait, timeout_seconds=timeout_seconds
172
+ )
173
+
174
+
175
+ @router.post("/{project}/{app_id}/stop", summary="Stop a data app")
176
+ def stop(
177
+ project: str,
178
+ app_id: str,
179
+ wait: bool = False,
180
+ timeout_seconds: float = 600.0,
181
+ registry: ServiceRegistry = Depends(get_registry),
182
+ ) -> dict[str, Any]:
183
+ """Stop a running data app. Mirrors `kbagent data-app stop`."""
184
+ return registry.data_app.stop_data_app(
185
+ alias=project, app_id=app_id, wait=wait, timeout_seconds=timeout_seconds
186
+ )
187
+
188
+
189
+ @router.delete("/{project}/{app_id}", summary="Delete a data app")
190
+ def delete(
191
+ project: str, app_id: str, registry: ServiceRegistry = Depends(get_registry)
192
+ ) -> dict[str, Any]:
193
+ """Delete a data app and its configuration. Mirrors `kbagent data-app delete`."""
194
+ return registry.data_app.delete_data_app(alias=project, app_id=app_id)
195
+
196
+
197
+ @router.get("/{project}/{app_id}/password", summary="Get data app access password")
198
+ def password(
199
+ project: str,
200
+ app_id: str,
201
+ manage_token: str | None = Depends(get_manage_token),
202
+ registry: ServiceRegistry = Depends(get_registry),
203
+ ) -> dict[str, Any]:
204
+ """Fetch the password for a password-protected data app. Mirrors `kbagent data-app password`."""
205
+ if not manage_token:
206
+ raise HTTPException(status_code=401, detail="Missing X-Manage-Token header.")
207
+ return registry.data_app.get_data_app_password(
208
+ alias=project, app_id=app_id, manage_token=manage_token
209
+ )
210
+
211
+
212
+ @router.get("/{project}/{app_id}/logs", summary="Tail data app container logs")
213
+ def logs(
214
+ project: str,
215
+ app_id: str,
216
+ lines: int | None = None,
217
+ since: str | None = None,
218
+ registry: ServiceRegistry = Depends(get_registry),
219
+ ) -> dict[str, Any]:
220
+ """Tail container logs for a data app. Mirrors `kbagent data-app logs`.
221
+
222
+ Query params ``lines`` and ``since`` are mutually exclusive (the
223
+ service raises ``KeboolaApiError(INVALID_ARGUMENT)`` if both are
224
+ set). ``since`` must be ISO 8601 WITH timezone.
225
+
226
+ Default-behavior asymmetry (deliberate): the CLI applies a 500-line
227
+ default when neither ``--lines`` nor ``--since`` is set (terminal
228
+ UX: predictable output for an operator at a tty). The REST route
229
+ and the service preserve the raw API contract: ``lines=None,
230
+ since=None`` returns the full current container buffer. REST
231
+ consumers (data-app dashboards, monitoring agents) typically want
232
+ the whole buffer; pass ``?lines=N`` for a cap that matches the CLI.
233
+
234
+ Note: the response ``text`` field can echo runtime secrets the app
235
+ printed to stdout/stderr; the envelope is reproduced verbatim with
236
+ no masking. Callers consuming this over SSE / scheduled-agent
237
+ persisted runs should consider secret hygiene.
238
+ """
239
+ return registry.data_app.get_app_logs(alias=project, app_id=app_id, lines=lines, since=since)
240
+
241
+
242
+ @router.get("/{project}/{app_id}/secrets", summary="List data app secrets")
243
+ def secrets_list(
244
+ project: str,
245
+ app_id: str,
246
+ branch_id: int | None = None,
247
+ show_fingerprint: bool = False,
248
+ registry: ServiceRegistry = Depends(get_registry),
249
+ ) -> dict[str, Any]:
250
+ """List secret keys configured on a data app. Mirrors `kbagent data-app secrets-list`."""
251
+ return registry.data_app.list_data_app_secrets(
252
+ alias=project,
253
+ app_id=app_id,
254
+ branch_id=branch_id,
255
+ show_fingerprint=show_fingerprint,
256
+ )
257
+
258
+
259
+ @router.get("/{project}/{app_id}/secrets/{key:path}", summary="Get a single data app secret")
260
+ def secrets_get(
261
+ project: str,
262
+ app_id: str,
263
+ key: str,
264
+ branch_id: int | None = None,
265
+ registry: ServiceRegistry = Depends(get_registry),
266
+ ) -> dict[str, Any]:
267
+ """Read a single secret value on a data app. Mirrors `kbagent data-app secrets-get`."""
268
+ return registry.data_app.get_data_app_secret(
269
+ alias=project, app_id=app_id, key=key, branch_id=branch_id
270
+ )
271
+
272
+
273
+ @router.put("/{project}/{app_id}/secrets", summary="Set data app secrets")
274
+ def secrets_set(
275
+ project: str,
276
+ app_id: str,
277
+ body: SecretsSet,
278
+ registry: ServiceRegistry = Depends(get_registry),
279
+ ) -> dict[str, Any]:
280
+ """Set or update encrypted secrets on a data app. Mirrors `kbagent data-app secrets-set`."""
281
+ return registry.data_app.set_data_app_secrets(
282
+ alias=project,
283
+ app_id=app_id,
284
+ secrets=body.secrets,
285
+ branch_id=body.branch_id,
286
+ allow_plaintext_on_encrypt_failure=body.allow_plaintext_on_encrypt_failure,
287
+ dry_run=body.dry_run,
288
+ )
289
+
290
+
291
+ @router.post("/{project}/{app_id}/secrets/remove", summary="Remove data app secrets")
292
+ def secrets_remove(
293
+ project: str,
294
+ app_id: str,
295
+ body: SecretsRemove,
296
+ registry: ServiceRegistry = Depends(get_registry),
297
+ ) -> dict[str, Any]:
298
+ """Remove one or more secrets from a data app. Mirrors `kbagent data-app secrets-remove`."""
299
+ return registry.data_app.remove_data_app_secrets(
300
+ alias=project,
301
+ app_id=app_id,
302
+ keys=body.keys,
303
+ branch_id=body.branch_id,
304
+ dry_run=body.dry_run,
305
+ )
306
+
307
+
308
+ @router.post("/validate-repo", summary="Validate a data app git repo")
309
+ def validate_repo(
310
+ body: RepoValidate, registry: ServiceRegistry = Depends(get_registry)
311
+ ) -> dict[str, Any]:
312
+ """Validate that a git repo is a deployable data app. Mirrors `kbagent data-app validate-repo`."""
313
+ git_pat = _resolve_git_pat(body.git_pat_env, body.git_pat_file)
314
+ return registry.repo_validate.validate_repo(
315
+ git_repo=body.git_repo,
316
+ git_branch=body.git_branch,
317
+ git_public=body.git_public,
318
+ git_pat=git_pat,
319
+ type_=body.type,
320
+ strict=body.strict,
321
+ )
322
+
323
+
324
+ @router.get("/{project}/{app_id}/git-repo", summary="Get a data app's git repository")
325
+ def git_repo(
326
+ project: str, app_id: str, registry: ServiceRegistry = Depends(get_registry)
327
+ ) -> dict[str, Any]:
328
+ """Show clone URLs of the app's configured git repo. Mirrors `kbagent data-app git-repo`.
329
+
330
+ Returns 409 until the app has been deployed at least once (the git block is
331
+ synced from the Storage config into the Data Science app record at deploy
332
+ time).
333
+ """
334
+ return registry.data_app_git.get_data_app_git_repo(alias=project, app_id=app_id)
335
+
336
+
337
+ @router.get("/{project}/{app_id}/git-repo/branches", summary="List a data app's git branches")
338
+ def git_branches(
339
+ project: str, app_id: str, registry: ServiceRegistry = Depends(get_registry)
340
+ ) -> dict[str, Any]:
341
+ """List remote branches of the app's git repo. Mirrors `kbagent data-app git-branches`."""
342
+ return registry.data_app_git.list_data_app_git_branches(alias=project, app_id=app_id)
343
+
344
+
345
+ @router.get("/{project}/{app_id}/git-repo/entrypoints", summary="List a data app's git entrypoints")
346
+ def git_entrypoints(
347
+ project: str, app_id: str, registry: ServiceRegistry = Depends(get_registry)
348
+ ) -> dict[str, Any]:
349
+ """List root-level .py entrypoints. Mirrors `kbagent data-app git-entrypoints`."""
350
+ return registry.data_app_git.list_data_app_git_entrypoints(alias=project, app_id=app_id)
351
+
352
+
353
+ @router.get("/{project}/{app_id}/git-repo/credentials", summary="List managed git credentials")
354
+ def git_credentials(
355
+ project: str, app_id: str, registry: ServiceRegistry = Depends(get_registry)
356
+ ) -> dict[str, Any]:
357
+ """List credentials of the app's MANAGED git repo. Mirrors `kbagent data-app git-credentials`.
358
+
359
+ The credential secret is never returned here; needs an admin storage token.
360
+ """
361
+ return registry.data_app_git.list_data_app_git_credentials(alias=project, app_id=app_id)
362
+
363
+
364
+ @router.post("/{project}/{app_id}/git-repo/credentials", summary="Create a managed git credential")
365
+ def git_credentials_create(
366
+ project: str,
367
+ app_id: str,
368
+ body: GitCredentialCreate,
369
+ registry: ServiceRegistry = Depends(get_registry),
370
+ ) -> dict[str, Any]:
371
+ """Mint a git credential for the app's MANAGED git repo.
372
+
373
+ Mirrors `kbagent data-app git-credentials-create`. For ``type=http_token``
374
+ the response carries a one-time ``secret``. Needs an admin storage token;
375
+ external repos return 409.
376
+ """
377
+ return registry.data_app_git.create_data_app_git_credential(
378
+ alias=project,
379
+ app_id=app_id,
380
+ type_=body.type,
381
+ permissions=body.permissions,
382
+ public_key=body.public_key,
383
+ name=body.name,
384
+ )
@@ -0,0 +1,67 @@
1
+ """Developer Portal reads (list/get).
2
+
3
+ Only the read surface is exposed over REST. The write commands
4
+ (`create`, `patch`, `upload-icon`, `publish`, `deprecate`) are deliberately
5
+ CLI-only: they require a human to type a random confirmation code on a real
6
+ TTY, which has no meaning over HTTP. Identity management
7
+ (`identity add/edit/remove/...`) is likewise CLI-only because it handles
8
+ login credentials that must not travel over this API. See
9
+ `plugins/kbagent/skills/kbagent/references/dev-portal-workflow.md`.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from typing import Any
15
+
16
+ from fastapi import APIRouter, Depends, HTTPException
17
+
18
+ from ..dependencies import ServiceRegistry, get_registry
19
+
20
+ router = APIRouter(prefix="/dev-portal", tags=["dev-portal"])
21
+
22
+
23
+ def _resolve_identity(registry: ServiceRegistry, identity: str | None) -> str:
24
+ """Resolve the identity alias: explicit query param, else the configured default."""
25
+ if identity:
26
+ return identity
27
+ default = registry.dev_portal.current_identity()
28
+ if not default:
29
+ raise HTTPException(
30
+ status_code=400,
31
+ detail=(
32
+ "No Developer Portal identity selected. Pass ?identity=<alias>, "
33
+ "or set a default via `kbagent dev-portal identity use <alias>`."
34
+ ),
35
+ )
36
+ return default
37
+
38
+
39
+ @router.get("/apps", summary="List Developer Portal apps for a vendor")
40
+ def list_apps(
41
+ vendor: str,
42
+ identity: str | None = None,
43
+ registry: ServiceRegistry = Depends(get_registry),
44
+ ) -> list[dict[str, Any]]:
45
+ """List all apps for a vendor. Mirrors `kbagent dev-portal list --vendor`."""
46
+ alias = _resolve_identity(registry, identity)
47
+ return registry.dev_portal.list_apps(alias, vendor)
48
+
49
+
50
+ @router.get("/apps/{app}", summary="Get one Developer Portal app")
51
+ def get_app(
52
+ app: str,
53
+ identity: str | None = None,
54
+ registry: ServiceRegistry = Depends(get_registry),
55
+ ) -> dict[str, Any]:
56
+ """Full portal entry for one app. `app` is VENDOR.APP_ID, e.g. keboola.ex-foo.
57
+
58
+ Mirrors `kbagent dev-portal get --app`.
59
+ """
60
+ if "." not in app:
61
+ raise HTTPException(
62
+ status_code=400,
63
+ detail=f"app must be in VENDOR.APP_ID form (e.g. keboola.ex-foo), got: {app!r}",
64
+ )
65
+ vendor, _ = app.split(".", 1)
66
+ alias = _resolve_identity(registry, identity)
67
+ return registry.dev_portal.get_app(alias, vendor, app)
@@ -0,0 +1,35 @@
1
+ """Encryption endpoint."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from fastapi import APIRouter, Depends
8
+ from pydantic import BaseModel
9
+
10
+ from ..dependencies import ServiceRegistry, get_registry
11
+
12
+ router = APIRouter(prefix="/encrypt", tags=["encrypt"])
13
+
14
+
15
+ class EncryptRequest(BaseModel):
16
+ project: str
17
+ component_id: str
18
+ values: dict[str, str]
19
+
20
+
21
+ @router.post("/values", summary="Encrypt secret values")
22
+ def encrypt_values(
23
+ body: EncryptRequest, registry: ServiceRegistry = Depends(get_registry)
24
+ ) -> dict[str, Any]:
25
+ """Encrypt one or more values for a specific project + component pair.
26
+
27
+ Returns the same keys with `KBC::ProjectSecure::...` ciphertexts. Use
28
+ this before writing secret values into a configuration so they are
29
+ never persisted in plaintext. Mirrors `kbagent encrypt values`.
30
+ """
31
+ return registry.encrypt.encrypt(
32
+ alias=body.project,
33
+ component_id=body.component_id,
34
+ input_data=body.values,
35
+ )
@@ -0,0 +1,179 @@
1
+ """Feature-flag endpoints (stack catalogue / project / user) -- all require a manage token.
2
+
3
+ 1:1 mirror of the `kbagent feature` command group. Every operation hits the
4
+ Manage API and therefore needs the per-request Manage token alongside the
5
+ standard bearer token, exactly like the `members` and `org` routers.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Any
11
+
12
+ from fastapi import APIRouter, Depends, HTTPException
13
+ from pydantic import BaseModel
14
+
15
+ from ..dependencies import ServiceRegistry, get_manage_token, get_registry
16
+
17
+ router = APIRouter(prefix="/feature", tags=["feature"])
18
+
19
+ # Every feature endpoint hits the Manage API, so each needs the per-request
20
+ # Manage token alongside the bearer token. Declaring the joint requirement
21
+ # here surfaces it as a separate scheme in the Swagger UI "Authorize" dialog.
22
+ _NEEDS_MANAGE_TOKEN: dict[str, Any] = {"security": [{"BearerAuth": [], "ManageToken": []}]}
23
+
24
+
25
+ class ProjectFeatureBody(BaseModel):
26
+ feature: str
27
+ dry_run: bool = False
28
+
29
+
30
+ class UserFeatureBody(BaseModel):
31
+ email: str
32
+ feature: str
33
+ dry_run: bool = False
34
+
35
+
36
+ def _require_manage(token: str | None) -> str:
37
+ if not token:
38
+ raise HTTPException(status_code=401, detail="Missing X-Manage-Token header.")
39
+ return token
40
+
41
+
42
+ @router.get("/{project}/list", summary="Stack feature catalogue", openapi_extra=_NEEDS_MANAGE_TOKEN)
43
+ def list_features(
44
+ project: str,
45
+ manage_token: str | None = Depends(get_manage_token),
46
+ registry: ServiceRegistry = Depends(get_registry),
47
+ ) -> dict[str, Any]:
48
+ """Every feature defined on the stack `project` points at. The alias only
49
+ resolves the stack URL -- the catalogue is stack-wide. Mirrors
50
+ `kbagent feature list`.
51
+ """
52
+ return registry.feature.list_stack_features(
53
+ manage_token=_require_manage(manage_token), alias=project
54
+ )
55
+
56
+
57
+ @router.get(
58
+ "/{project}/project-show",
59
+ summary="Project's assigned features",
60
+ openapi_extra=_NEEDS_MANAGE_TOKEN,
61
+ )
62
+ def project_show(
63
+ project: str,
64
+ manage_token: str | None = Depends(get_manage_token),
65
+ registry: ServiceRegistry = Depends(get_registry),
66
+ ) -> dict[str, Any]:
67
+ """Features assigned to `project`. Mirrors `kbagent feature project-show`."""
68
+ return registry.feature.list_project_features(
69
+ manage_token=_require_manage(manage_token), alias=project
70
+ )
71
+
72
+
73
+ @router.post(
74
+ "/{project}/project-add",
75
+ summary="Enable a feature on a project",
76
+ openapi_extra=_NEEDS_MANAGE_TOKEN,
77
+ )
78
+ def project_add(
79
+ project: str,
80
+ body: ProjectFeatureBody,
81
+ manage_token: str | None = Depends(get_manage_token),
82
+ registry: ServiceRegistry = Depends(get_registry),
83
+ ) -> dict[str, Any]:
84
+ """Enable a feature on `project`. Pass `dry_run=true` to preview. Mirrors
85
+ `kbagent feature project-add`.
86
+ """
87
+ return registry.feature.add_project_feature(
88
+ manage_token=_require_manage(manage_token),
89
+ alias=project,
90
+ feature=body.feature,
91
+ dry_run=body.dry_run,
92
+ )
93
+
94
+
95
+ @router.post(
96
+ "/{project}/project-remove",
97
+ summary="Disable a feature on a project",
98
+ openapi_extra=_NEEDS_MANAGE_TOKEN,
99
+ )
100
+ def project_remove(
101
+ project: str,
102
+ body: ProjectFeatureBody,
103
+ manage_token: str | None = Depends(get_manage_token),
104
+ registry: ServiceRegistry = Depends(get_registry),
105
+ ) -> dict[str, Any]:
106
+ """Disable a feature on `project` (destructive). Pass `dry_run=true` to
107
+ preview. Mirrors `kbagent feature project-remove`.
108
+ """
109
+ return registry.feature.remove_project_feature(
110
+ manage_token=_require_manage(manage_token),
111
+ alias=project,
112
+ feature=body.feature,
113
+ dry_run=body.dry_run,
114
+ )
115
+
116
+
117
+ @router.get(
118
+ "/{project}/user-show",
119
+ summary="User's assigned features",
120
+ openapi_extra=_NEEDS_MANAGE_TOKEN,
121
+ )
122
+ def user_show(
123
+ project: str,
124
+ email: str,
125
+ manage_token: str | None = Depends(get_manage_token),
126
+ registry: ServiceRegistry = Depends(get_registry),
127
+ ) -> dict[str, Any]:
128
+ """Features assigned to `email` on the alias's stack. Mirrors
129
+ `kbagent feature user-show`.
130
+ """
131
+ return registry.feature.list_user_features(
132
+ manage_token=_require_manage(manage_token), alias=project, email=email
133
+ )
134
+
135
+
136
+ @router.post(
137
+ "/{project}/user-add",
138
+ summary="Enable a feature on a user",
139
+ openapi_extra=_NEEDS_MANAGE_TOKEN,
140
+ )
141
+ def user_add(
142
+ project: str,
143
+ body: UserFeatureBody,
144
+ manage_token: str | None = Depends(get_manage_token),
145
+ registry: ServiceRegistry = Depends(get_registry),
146
+ ) -> dict[str, Any]:
147
+ """Enable a feature on a user. Pass `dry_run=true` to preview. Mirrors
148
+ `kbagent feature user-add`.
149
+ """
150
+ return registry.feature.add_user_feature(
151
+ manage_token=_require_manage(manage_token),
152
+ alias=project,
153
+ email=body.email,
154
+ feature=body.feature,
155
+ dry_run=body.dry_run,
156
+ )
157
+
158
+
159
+ @router.post(
160
+ "/{project}/user-remove",
161
+ summary="Disable a feature on a user",
162
+ openapi_extra=_NEEDS_MANAGE_TOKEN,
163
+ )
164
+ def user_remove(
165
+ project: str,
166
+ body: UserFeatureBody,
167
+ manage_token: str | None = Depends(get_manage_token),
168
+ registry: ServiceRegistry = Depends(get_registry),
169
+ ) -> dict[str, Any]:
170
+ """Disable a feature on a user (destructive). Pass `dry_run=true` to
171
+ preview. Mirrors `kbagent feature user-remove`.
172
+ """
173
+ return registry.feature.remove_user_feature(
174
+ manage_token=_require_manage(manage_token),
175
+ alias=project,
176
+ email=body.email,
177
+ feature=body.feature,
178
+ dry_run=body.dry_run,
179
+ )