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,226 @@
1
+ """Lineage endpoints (cross-project sharing graph + deep column lineage).
2
+
3
+ The ``/browser`` family mirrors what ``kbagent lineage server`` does as a
4
+ standalone CLI subcommand -- HTML browser + data.json + query API +
5
+ Mermaid renderer -- but mounted into the FastAPI app so the React UI can
6
+ ``<iframe>`` it instead of duplicating the rendering logic.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ from pathlib import Path
13
+ from typing import Any
14
+ from urllib.parse import quote
15
+
16
+ from fastapi import APIRouter, Depends, HTTPException, Query
17
+ from fastapi.responses import HTMLResponse, PlainTextResponse
18
+ from pydantic import BaseModel
19
+
20
+ from ..dependencies import ServiceRegistry, get_registry
21
+
22
+ router = APIRouter(prefix="/lineage", tags=["lineage"])
23
+
24
+
25
+ class LineageBuild(BaseModel):
26
+ directory: str
27
+ output: str
28
+ use_ai: bool = False
29
+ refresh: bool = False
30
+
31
+
32
+ class LineageQuery(BaseModel):
33
+ load: str
34
+ upstream: str | None = None
35
+ downstream: str | None = None
36
+ project: str | None = None
37
+ depth: int = 10
38
+ format: str = "text"
39
+
40
+
41
+ @router.get("/edges", summary="List cross-project lineage edges")
42
+ def edges(
43
+ project: list[str] | None = Query(None),
44
+ registry: ServiceRegistry = Depends(get_registry),
45
+ ) -> dict[str, Any]:
46
+ """Cross-project bucket-sharing edges (LineageService)."""
47
+ return registry.lineage.get_lineage(aliases=project)
48
+
49
+
50
+ @router.post("/build", summary="Build deep column-level lineage")
51
+ def build(body: LineageBuild, registry: ServiceRegistry = Depends(get_registry)) -> dict[str, Any]:
52
+ """Build deep column-level lineage and write JSON cache to ``output``.
53
+
54
+ Auto-creates the working directory if missing -- it's empty before the
55
+ first ``sync pull`` anyway, so 404'ing on absence was just user-hostile.
56
+ """
57
+ directory = Path(body.directory).resolve()
58
+ output = Path(body.output).resolve()
59
+ try:
60
+ directory.mkdir(parents=True, exist_ok=True)
61
+ except OSError as exc:
62
+ raise HTTPException(
63
+ status_code=400,
64
+ detail=f"Cannot create working directory '{body.directory}': {exc}",
65
+ ) from exc
66
+ output.parent.mkdir(parents=True, exist_ok=True)
67
+
68
+ if body.refresh:
69
+ registry.sync.pull_all(base_dir=directory)
70
+ result = registry.deep_lineage.build_lineage(directory, generate_ai_tasks=body.use_ai)
71
+ try:
72
+ with output.open("w") as fh:
73
+ json.dump(result, fh, indent=2)
74
+ except OSError as exc:
75
+ raise HTTPException(status_code=500, detail=f"Cannot write '{body.output}': {exc}") from exc
76
+ # Tag the result with the resolved output path so the React UI can
77
+ # auto-load it without re-typing.
78
+ return {**result, "output_path": str(output)}
79
+
80
+
81
+ @router.post("/show", summary="Query a built lineage graph")
82
+ def show(body: LineageQuery, registry: ServiceRegistry = Depends(get_registry)) -> dict[str, Any]:
83
+ """Query a built lineage graph (upstream / downstream walk)."""
84
+ load_path = Path(body.load)
85
+ if not load_path.exists():
86
+ raise HTTPException(status_code=400, detail=f"Lineage cache '{body.load}' not found.")
87
+ graph = registry.deep_lineage.load_from_cache(load_path)
88
+ if body.upstream:
89
+ return registry.deep_lineage.query_upstream(
90
+ graph, body.upstream, body.project or "", body.depth
91
+ )
92
+ if body.downstream:
93
+ return registry.deep_lineage.query_downstream(
94
+ graph, body.downstream, body.project or "", body.depth
95
+ )
96
+ raise HTTPException(status_code=400, detail="Provide --upstream or --downstream.")
97
+
98
+
99
+ @router.get("/info", summary="Show lineage cache summary")
100
+ def info(
101
+ load: str = Query(...), registry: ServiceRegistry = Depends(get_registry)
102
+ ) -> dict[str, Any]:
103
+ """Return a summary of nodes/edges in a built lineage cache. Mirrors `kbagent lineage info`."""
104
+ load_path = Path(load)
105
+ if not load_path.exists():
106
+ raise HTTPException(status_code=400, detail=f"Lineage cache '{load}' not found.")
107
+ graph = registry.deep_lineage.load_from_cache(load_path)
108
+ return graph.summary()
109
+
110
+
111
+ # ── Browser surface ───────────────────────────────────────────────
112
+ # Reuses the HTML template + handler logic from `kbagent lineage server`
113
+ # (commands/lineage.py) so we don't have a second copy of the renderer.
114
+ # The CLI command serves them on a fresh http.server thread; here we
115
+ # re-host them inside FastAPI so the React UI can iframe the result.
116
+
117
+
118
+ def _rewrite_browser_html(load_path: str) -> str:
119
+ """Return the lineage browser HTML with fetch URLs rewritten.
120
+
121
+ The original template (from commands/lineage.py) hard-codes
122
+ ``/data.json`` / ``/api/query`` / ``/api/mermaid`` paths because
123
+ ``kbagent lineage server`` is the only thing serving the request.
124
+ Inside FastAPI we live under ``/lineage/...`` and the BFF proxies
125
+ that under ``/api/lineage/...`` for the browser. Rewrite the three
126
+ fetch endpoints so the same HTML works inside an iframe.
127
+
128
+ The ``load`` query param is baked into every rewritten URL so the
129
+ browser never has to know which JSON cache to read -- one HTML
130
+ response = one cache file.
131
+ """
132
+ from ...commands.lineage import _LINEAGE_HTML_TEMPLATE
133
+
134
+ enc = quote(load_path, safe="")
135
+ return (
136
+ _LINEAGE_HTML_TEMPLATE
137
+ # /data.json -> /api/lineage/data?load=...
138
+ .replace('"/data.json"', f'"/api/lineage/data?load={enc}"')
139
+ # /api/query?node=X... -> /api/lineage/walk?load=...&node=X...
140
+ .replace('"/api/query?node="', f'"/api/lineage/walk?load={enc}&node="')
141
+ # /api/mermaid?node=X... -> /api/lineage/mermaid?load=...&node=X...
142
+ .replace('"/api/mermaid?node="', f'"/api/lineage/mermaid?load={enc}&node="')
143
+ )
144
+
145
+
146
+ @router.get("/browser", response_class=HTMLResponse, summary="Open lineage browser UI")
147
+ def browser(load: str = Query(...)) -> HTMLResponse:
148
+ """Serve the interactive lineage browser HTML for a JSON cache file."""
149
+ load_path = Path(load)
150
+ if not load_path.exists():
151
+ raise HTTPException(status_code=400, detail=f"Lineage cache '{load}' not found.")
152
+ return HTMLResponse(content=_rewrite_browser_html(load))
153
+
154
+
155
+ @router.get("/data", summary="Return raw lineage JSON")
156
+ def data(load: str = Query(...)) -> Any:
157
+ """Raw lineage JSON, served verbatim from disk for the browser."""
158
+ load_path = Path(load)
159
+ if not load_path.exists():
160
+ raise HTTPException(status_code=400, detail=f"Lineage cache '{load}' not found.")
161
+ try:
162
+ raw = load_path.read_text(encoding="utf-8")
163
+ except OSError as exc:
164
+ raise HTTPException(status_code=500, detail=f"Cannot read '{load}': {exc}") from exc
165
+ # Hand the parsed dict back so FastAPI sets the right content-type and
166
+ # the browser fetch().json() call works without surprises.
167
+ return json.loads(raw)
168
+
169
+
170
+ @router.get("/walk", summary="Walk lineage graph from a node")
171
+ def walk(
172
+ load: str = Query(...),
173
+ node: str = Query(...),
174
+ direction: str = Query("downstream"),
175
+ depth: int = Query(3, ge=1, le=20),
176
+ registry: ServiceRegistry = Depends(get_registry),
177
+ ) -> dict[str, Any]:
178
+ """Walk the lineage graph upstream / downstream from a node FQN.
179
+
180
+ Used by the in-browser query bar (``/api/query`` in the standalone
181
+ server). Mirrors the CLI ``lineage show`` semantics but is GET-only
182
+ so the HTML's ``fetch()`` calls work without preflight.
183
+ """
184
+ load_path = Path(load)
185
+ if not load_path.exists():
186
+ raise HTTPException(status_code=400, detail=f"Lineage cache '{load}' not found.")
187
+ graph = registry.deep_lineage.load_from_cache(load_path)
188
+ if direction == "upstream":
189
+ return registry.deep_lineage.query_upstream(graph, node, "", depth)
190
+ return registry.deep_lineage.query_downstream(graph, node, "", depth)
191
+
192
+
193
+ @router.get("/mermaid", response_class=PlainTextResponse, summary="Render lineage as Mermaid")
194
+ def mermaid(
195
+ load: str = Query(...),
196
+ node: str = Query(...),
197
+ direction: str = Query("downstream"),
198
+ depth: int = Query(3, ge=1, le=20),
199
+ view: str = Query("flow"),
200
+ columns: str = Query("false"),
201
+ registry: ServiceRegistry = Depends(get_registry),
202
+ ) -> PlainTextResponse:
203
+ """Return a Mermaid diagram for the requested upstream/downstream walk."""
204
+ from ...services.deep_lineage_service import DeepLineageService
205
+
206
+ load_path = Path(load)
207
+ if not load_path.exists():
208
+ raise HTTPException(status_code=400, detail=f"Lineage cache '{load}' not found.")
209
+ graph = registry.deep_lineage.load_from_cache(load_path)
210
+
211
+ if direction == "upstream":
212
+ result = registry.deep_lineage.query_upstream(graph, node, "", depth)
213
+ else:
214
+ result = registry.deep_lineage.query_downstream(graph, node, "", depth)
215
+
216
+ if "error" in result:
217
+ return PlainTextResponse("graph LR\n error[" + result["error"].replace('"', "'") + "]")
218
+ edges = result.get("edges", [])
219
+ show_cols = columns == "true"
220
+ if view == "er":
221
+ code = DeepLineageService.render_er_diagram(edges, graph, node, show_columns=show_cols)
222
+ else:
223
+ code = DeepLineageService.render_mermaid(
224
+ edges, graph, direction, node, show_columns=show_cols
225
+ )
226
+ return PlainTextResponse(code)
@@ -0,0 +1,70 @@
1
+ """MCP tool endpoints (list, call across projects)."""
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="/mcp", tags=["mcp"])
13
+
14
+
15
+ class ToolCall(BaseModel):
16
+ input: dict[str, Any] | None = None
17
+ project: str | None = None
18
+ branch_id: str | None = None
19
+
20
+
21
+ @router.get("/tools", summary="List MCP tools")
22
+ def list_tools(
23
+ project: str | None = None,
24
+ branch_id: str | None = None,
25
+ registry: ServiceRegistry = Depends(get_registry),
26
+ ) -> dict[str, Any]:
27
+ """Discover every MCP tool exposed by `keboola-mcp-server` for one or
28
+ all projects. Omit `project` to fan out across every registered alias.
29
+ """
30
+ aliases = [project] if project else None
31
+ return registry.mcp.list_tools(aliases=aliases, branch_id=branch_id)
32
+
33
+
34
+ @router.get("/tools/{tool_name}/schema", summary="Fetch a tool's input schema")
35
+ def tool_schema(
36
+ tool_name: str,
37
+ project: str | None = None,
38
+ branch_id: str | None = None,
39
+ registry: ServiceRegistry = Depends(get_registry),
40
+ ) -> dict[str, Any]:
41
+ """JSON Schema for one MCP tool's `input` -- useful when wiring a form
42
+ or pre-validating before `POST /mcp/tools/{name}/call`.
43
+ """
44
+ aliases = [project] if project else None
45
+ schema = registry.mcp.get_tool_schema(tool_name=tool_name, aliases=aliases, branch_id=branch_id)
46
+ return {"tool": tool_name, "input_schema": schema or {}}
47
+
48
+
49
+ @router.post("/tools/{tool_name}/call", summary="Call an MCP tool")
50
+ def call_tool(
51
+ tool_name: str, body: ToolCall, registry: ServiceRegistry = Depends(get_registry)
52
+ ) -> dict[str, Any]:
53
+ """Invoke one MCP tool. Input is validated against the tool's schema
54
+ server-side before the MCP subprocess is spawned, so obvious mistakes
55
+ fail fast with a 4xx instead of a cryptic MCP error.
56
+ """
57
+ return registry.mcp.validate_and_call_tool(
58
+ tool_name=tool_name,
59
+ tool_input=body.input,
60
+ alias=body.project,
61
+ branch_id=body.branch_id,
62
+ )
63
+
64
+
65
+ @router.get("/server-status", summary="Check MCP server availability")
66
+ def server_status(registry: ServiceRegistry = Depends(get_registry)) -> dict[str, Any]:
67
+ """Whether the bundled `keboola-mcp-server` subprocess is reachable
68
+ (HTTP transport or stdio fallback, depending on env vars).
69
+ """
70
+ return registry.mcp.check_server_available()
@@ -0,0 +1,170 @@
1
+ """Project members endpoints (invite/list/remove/role) -- all require manage token."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import asdict, is_dataclass
6
+ from typing import Any
7
+
8
+ from fastapi import APIRouter, Depends, HTTPException
9
+ from pydantic import BaseModel
10
+
11
+ from ..dependencies import ServiceRegistry, get_manage_token, get_registry
12
+
13
+ router = APIRouter(prefix="/members", tags=["members"])
14
+
15
+ # Member endpoints all hit the Manage API, so every operation needs the
16
+ # per-request Manage token alongside the standard bearer token. Declaring
17
+ # the joint requirement here surfaces it as a separate scheme in the
18
+ # Swagger UI "Authorize" dialog.
19
+ _NEEDS_MANAGE_TOKEN: dict[str, Any] = {"security": [{"BearerAuth": [], "ManageToken": []}]}
20
+
21
+
22
+ class InviteOne(BaseModel):
23
+ email: str
24
+ role: str
25
+ reason: str | None = None
26
+ dry_run: bool = False
27
+
28
+
29
+ class CancelInvitation(BaseModel):
30
+ email: str
31
+ invitation_id: int | None = None
32
+
33
+
34
+ class RemoveMember(BaseModel):
35
+ email: str
36
+
37
+
38
+ class SetRole(BaseModel):
39
+ email: str
40
+ role: str
41
+
42
+
43
+ def _require_manage(token: str | None) -> str:
44
+ if not token:
45
+ raise HTTPException(status_code=401, detail="Missing X-Manage-Token header.")
46
+ return token
47
+
48
+
49
+ def _serialize(value: Any) -> Any:
50
+ if is_dataclass(value):
51
+ return asdict(value)
52
+ return value
53
+
54
+
55
+ @router.get("/{project}", summary="List members", openapi_extra=_NEEDS_MANAGE_TOKEN)
56
+ def list_members(
57
+ project: str,
58
+ include_pending: bool = False,
59
+ manage_token: str | None = Depends(get_manage_token),
60
+ registry: ServiceRegistry = Depends(get_registry),
61
+ ) -> dict[str, Any]:
62
+ """Active members of `project`. Pass `include_pending=true` to also include
63
+ invitations that have not yet been accepted. Mirrors
64
+ `kbagent project member-list`.
65
+ """
66
+ return registry.member.list_members(
67
+ manage_token=_require_manage(manage_token),
68
+ alias=project,
69
+ include_pending=include_pending,
70
+ )
71
+
72
+
73
+ @router.get(
74
+ "/{project}/invitations",
75
+ summary="List pending invitations",
76
+ openapi_extra=_NEEDS_MANAGE_TOKEN,
77
+ )
78
+ def list_invitations(
79
+ project: str,
80
+ manage_token: str | None = Depends(get_manage_token),
81
+ registry: ServiceRegistry = Depends(get_registry),
82
+ ) -> dict[str, Any]:
83
+ """Outstanding invitations for `project`. Mirrors
84
+ `kbagent project invitation-list`.
85
+ """
86
+ return registry.member.list_invitations(
87
+ manage_token=_require_manage(manage_token), alias=project
88
+ )
89
+
90
+
91
+ @router.post("/{project}/invite", summary="Invite a single user", openapi_extra=_NEEDS_MANAGE_TOKEN)
92
+ def invite(
93
+ project: str,
94
+ body: InviteOne,
95
+ manage_token: str | None = Depends(get_manage_token),
96
+ registry: ServiceRegistry = Depends(get_registry),
97
+ ) -> dict[str, Any]:
98
+ """Send a single invitation. Pass `dry_run=true` to validate without
99
+ actually sending. Mirrors `kbagent project invite --email --role`.
100
+ """
101
+ return registry.member.invite(
102
+ manage_token=_require_manage(manage_token),
103
+ alias=project,
104
+ email=body.email,
105
+ role=body.role,
106
+ reason=body.reason,
107
+ dry_run=body.dry_run,
108
+ )
109
+
110
+
111
+ @router.post(
112
+ "/{project}/invitations/cancel",
113
+ summary="Cancel invitation",
114
+ openapi_extra=_NEEDS_MANAGE_TOKEN,
115
+ )
116
+ def cancel_invitation(
117
+ project: str,
118
+ body: CancelInvitation,
119
+ manage_token: str | None = Depends(get_manage_token),
120
+ registry: ServiceRegistry = Depends(get_registry),
121
+ ) -> dict[str, Any]:
122
+ """Revoke an outstanding invitation. Identified by email (the API picks
123
+ the right invitation), or pin a specific `invitation_id`. Mirrors
124
+ `kbagent project invitation-cancel`.
125
+ """
126
+ return registry.member.cancel_invitation(
127
+ manage_token=_require_manage(manage_token),
128
+ alias=project,
129
+ email=body.email,
130
+ invitation_id=body.invitation_id,
131
+ )
132
+
133
+
134
+ @router.post("/{project}/remove", summary="Remove member", openapi_extra=_NEEDS_MANAGE_TOKEN)
135
+ def remove(
136
+ project: str,
137
+ body: RemoveMember,
138
+ manage_token: str | None = Depends(get_manage_token),
139
+ registry: ServiceRegistry = Depends(get_registry),
140
+ ) -> dict[str, Any]:
141
+ """Revoke a user's access to the project. Mirrors
142
+ `kbagent project member-remove`.
143
+ """
144
+ return registry.member.remove_member(
145
+ manage_token=_require_manage(manage_token),
146
+ alias=project,
147
+ email=body.email,
148
+ )
149
+
150
+
151
+ @router.post(
152
+ "/{project}/set-role",
153
+ summary="Change member role",
154
+ openapi_extra=_NEEDS_MANAGE_TOKEN,
155
+ )
156
+ def set_role(
157
+ project: str,
158
+ body: SetRole,
159
+ manage_token: str | None = Depends(get_manage_token),
160
+ registry: ServiceRegistry = Depends(get_registry),
161
+ ) -> dict[str, Any]:
162
+ """Promote / demote a member. Role is one of `admin`, `guest`,
163
+ `readOnly`, `share`. Mirrors `kbagent project member-set-role`.
164
+ """
165
+ return registry.member.set_member_role(
166
+ manage_token=_require_manage(manage_token),
167
+ alias=project,
168
+ email=body.email,
169
+ role=body.role,
170
+ )
@@ -0,0 +1,96 @@
1
+ """Organization bulk-onboarding endpoints (require manage token)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from fastapi import APIRouter, Depends, HTTPException
8
+ from pydantic import BaseModel
9
+
10
+ from ..dependencies import ServiceRegistry, get_manage_token, get_registry
11
+
12
+ router = APIRouter(prefix="/org", tags=["org"])
13
+
14
+ # Every endpoint in this router requires BOTH the standard bearer token AND
15
+ # the per-request Manage API token. Declaring it here means Swagger UI's
16
+ # "Authorize" dialog will list both schemes so users can paste each one
17
+ # once instead of guessing why a request 401s.
18
+ _NEEDS_MANAGE_TOKEN: dict[str, Any] = {"security": [{"BearerAuth": [], "ManageToken": []}]}
19
+
20
+
21
+ class OrgSetup(BaseModel):
22
+ stack_url: str
23
+ org_id: int | None = None
24
+ project_ids: list[int] | None = None
25
+ token_description: str = "kbagent"
26
+ dry_run: bool = False
27
+ token_expires_in: int | None = None
28
+
29
+
30
+ class OrgRefresh(BaseModel):
31
+ aliases: list[str] | None = None
32
+ refresh_all: bool = False
33
+ token_description: str = "kbagent"
34
+ dry_run: bool = False
35
+ force: bool = False
36
+ token_expires_in: int | None = None
37
+
38
+
39
+ @router.post("/setup", summary="Onboard org / project list", openapi_extra=_NEEDS_MANAGE_TOKEN)
40
+ def setup(
41
+ body: OrgSetup,
42
+ manage_token: str | None = Depends(get_manage_token),
43
+ registry: ServiceRegistry = Depends(get_registry),
44
+ ) -> dict[str, Any]:
45
+ """Bulk-register every project in an organization (or a fixed project ID list).
46
+
47
+ Issues a fresh storage token per project via the Manage API and persists
48
+ each project under a slug-style alias. Idempotent: existing aliases that
49
+ already point at the same project ID are skipped. Mirrors
50
+ `kbagent org setup`.
51
+ """
52
+ if not manage_token:
53
+ raise HTTPException(
54
+ status_code=401,
55
+ detail="Missing X-Manage-Token header. Org setup requires a manage token.",
56
+ )
57
+ return registry.org.setup_organization(
58
+ stack_url=body.stack_url,
59
+ manage_token=manage_token,
60
+ org_id=body.org_id,
61
+ token_description=body.token_description,
62
+ dry_run=body.dry_run,
63
+ token_expires_in=body.token_expires_in,
64
+ project_ids=body.project_ids,
65
+ )
66
+
67
+
68
+ @router.post("/refresh", summary="Re-issue storage tokens", openapi_extra=_NEEDS_MANAGE_TOKEN)
69
+ def refresh(
70
+ body: OrgRefresh,
71
+ manage_token: str | None = Depends(get_manage_token),
72
+ registry: ServiceRegistry = Depends(get_registry),
73
+ ) -> dict[str, Any]:
74
+ """Refresh the storage token for one alias, several aliases, or every project.
75
+
76
+ Useful after rotating a Manage API token or when a per-project token is
77
+ near expiry. Pass `refresh_all=true` to refresh every persisted alias,
78
+ or a list of aliases to scope the refresh. Mirrors
79
+ `kbagent project refresh --project` and `kbagent project refresh --all`.
80
+ """
81
+ if not manage_token:
82
+ raise HTTPException(
83
+ status_code=401,
84
+ detail="Missing X-Manage-Token header. Refresh requires a manage token.",
85
+ )
86
+ # Service has no refresh_all param: pass aliases=None to refresh all,
87
+ # or aliases=body.aliases to scope the refresh.
88
+ effective_aliases = None if body.refresh_all else body.aliases
89
+ return registry.org.refresh_tokens(
90
+ manage_token=manage_token,
91
+ aliases=effective_aliases,
92
+ token_description=body.token_description,
93
+ dry_run=body.dry_run,
94
+ force=body.force,
95
+ token_expires_in=body.token_expires_in,
96
+ )
@@ -0,0 +1,106 @@
1
+ """Project CRUD + status endpoints."""
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="/projects", tags=["projects"])
13
+
14
+
15
+ class ProjectCreate(BaseModel):
16
+ alias: str
17
+ stack_url: str
18
+ token: str
19
+
20
+
21
+ class ProjectEdit(BaseModel):
22
+ stack_url: str | None = None
23
+ token: str | None = None
24
+ new_alias: str | None = None
25
+ dry_run: bool = False
26
+
27
+
28
+ class ProjectDescription(BaseModel):
29
+ description: str
30
+
31
+
32
+ @router.get("", summary="List registered projects")
33
+ def list_projects(registry: ServiceRegistry = Depends(get_registry)) -> dict[str, Any]:
34
+ """All registered project aliases."""
35
+ return {"projects": registry.project.list_projects()}
36
+
37
+
38
+ @router.post("", summary="Add a project")
39
+ def add_project(
40
+ body: ProjectCreate, registry: ServiceRegistry = Depends(get_registry)
41
+ ) -> dict[str, Any]:
42
+ """Add a project. Verifies the storage token before persisting."""
43
+ return registry.project.add_project(body.alias, body.stack_url, body.token)
44
+
45
+
46
+ @router.delete("/{alias}", summary="Remove a project")
47
+ def remove_project(alias: str, registry: ServiceRegistry = Depends(get_registry)) -> dict[str, Any]:
48
+ """Remove a project by alias. Mirrors `kbagent project remove`."""
49
+ return registry.project.remove_project(alias)
50
+
51
+
52
+ @router.patch("/{alias}", summary="Edit a project")
53
+ def edit_project(
54
+ alias: str, body: ProjectEdit, registry: ServiceRegistry = Depends(get_registry)
55
+ ) -> dict[str, Any]:
56
+ """Update stack URL, token, or alias of a project. Mirrors `kbagent project edit`."""
57
+ return registry.project.edit_project(
58
+ alias=alias,
59
+ stack_url=body.stack_url,
60
+ token=body.token,
61
+ new_alias=body.new_alias,
62
+ dry_run=body.dry_run,
63
+ )
64
+
65
+
66
+ @router.get("/status", summary="Check project connectivity")
67
+ def status(
68
+ alias: str | None = None, registry: ServiceRegistry = Depends(get_registry)
69
+ ) -> dict[str, Any]:
70
+ """Connectivity check; pass ``?alias=`` to limit to one project."""
71
+ aliases = [alias] if alias else None
72
+ return {"status": registry.project.get_status(aliases=aliases)}
73
+
74
+
75
+ @router.get("/current", summary="Get the active project")
76
+ def current(registry: ServiceRegistry = Depends(get_registry)) -> dict[str, Any]:
77
+ """Currently pinned project alias. Mirrors `kbagent project current`."""
78
+ return registry.project.current_project()
79
+
80
+
81
+ @router.post("/use/{alias}", summary="Switch the active project")
82
+ def use_project(alias: str, registry: ServiceRegistry = Depends(get_registry)) -> dict[str, Any]:
83
+ """Pin a project as the current default. Mirrors `kbagent project use`."""
84
+ return registry.project.use_project(alias)
85
+
86
+
87
+ @router.get("/{alias}/info", summary="Get project metadata")
88
+ def info(alias: str, registry: ServiceRegistry = Depends(get_registry)) -> dict[str, Any]:
89
+ """Project metadata (stack, owner, tokens). Mirrors `kbagent project info`."""
90
+ return registry.project.get_info(alias)
91
+
92
+
93
+ @router.get("/{alias}/description", summary="Get the project description")
94
+ def get_description(
95
+ alias: str, registry: ServiceRegistry = Depends(get_registry)
96
+ ) -> dict[str, Any]:
97
+ return registry.branch.get_project_description(alias)
98
+
99
+
100
+ @router.put("/{alias}/description", summary="Set the project description")
101
+ def set_description(
102
+ alias: str,
103
+ body: ProjectDescription,
104
+ registry: ServiceRegistry = Depends(get_registry),
105
+ ) -> dict[str, Any]:
106
+ return registry.branch.set_project_description(alias, body.description)