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,86 @@
1
+ """Bucket sharing endpoints."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from fastapi import APIRouter, Depends, Query
8
+ from pydantic import BaseModel
9
+
10
+ from ..dependencies import ServiceRegistry, get_registry
11
+
12
+ router = APIRouter(prefix="/sharing", tags=["sharing"])
13
+
14
+
15
+ class ShareBucket(BaseModel):
16
+ bucket_id: str
17
+ type: str
18
+ target_project_ids: list[int] | None = None
19
+ target_users: list[str] | None = None
20
+
21
+
22
+ class LinkBucket(BaseModel):
23
+ source_project_id: int
24
+ bucket_id: str
25
+ name: str | None = None
26
+
27
+
28
+ @router.get("", summary="List shared buckets")
29
+ def list_shared(
30
+ project: list[str] | None = Query(None),
31
+ registry: ServiceRegistry = Depends(get_registry),
32
+ ) -> dict[str, Any]:
33
+ """List buckets shared by or to the given projects. Mirrors `kbagent sharing list`."""
34
+ return registry.sharing.list_shared(aliases=project)
35
+
36
+
37
+ @router.get("/edges", summary="List cross-project sharing edges")
38
+ def edges(
39
+ project: list[str] | None = Query(None),
40
+ registry: ServiceRegistry = Depends(get_registry),
41
+ ) -> dict[str, Any]:
42
+ """Cross-project lineage edges via shared buckets. Mirrors `kbagent sharing edges`."""
43
+ return registry.lineage.get_lineage(aliases=project)
44
+
45
+
46
+ @router.post("/{project}/share", summary="Share a bucket")
47
+ def share(
48
+ project: str, body: ShareBucket, registry: ServiceRegistry = Depends(get_registry)
49
+ ) -> dict[str, Any]:
50
+ """Expose a bucket to other projects or users. Mirrors `kbagent sharing share`."""
51
+ return registry.sharing.share(
52
+ alias=project,
53
+ bucket_id=body.bucket_id,
54
+ sharing_type=body.type,
55
+ target_project_ids=body.target_project_ids,
56
+ target_users=body.target_users,
57
+ )
58
+
59
+
60
+ @router.post("/{project}/unshare/{bucket_id:path}", summary="Unshare a bucket")
61
+ def unshare(
62
+ project: str, bucket_id: str, registry: ServiceRegistry = Depends(get_registry)
63
+ ) -> dict[str, Any]:
64
+ """Revoke sharing on a bucket. Mirrors `kbagent sharing unshare`."""
65
+ return registry.sharing.unshare(alias=project, bucket_id=bucket_id)
66
+
67
+
68
+ @router.post("/{project}/link", summary="Link a shared bucket")
69
+ def link(
70
+ project: str, body: LinkBucket, registry: ServiceRegistry = Depends(get_registry)
71
+ ) -> dict[str, Any]:
72
+ """Link a shared bucket from another project. Mirrors `kbagent sharing link`."""
73
+ return registry.sharing.link(
74
+ alias=project,
75
+ source_project_id=body.source_project_id,
76
+ source_bucket_id=body.bucket_id,
77
+ name=body.name,
78
+ )
79
+
80
+
81
+ @router.post("/{project}/unlink/{bucket_id:path}", summary="Unlink a shared bucket")
82
+ def unlink(
83
+ project: str, bucket_id: str, registry: ServiceRegistry = Depends(get_registry)
84
+ ) -> dict[str, Any]:
85
+ """Remove a linked shared bucket. Mirrors `kbagent sharing unlink`."""
86
+ return registry.sharing.unlink(alias=project, bucket_id=bucket_id)
@@ -0,0 +1,574 @@
1
+ """Storage endpoints (buckets, tables, files, describe)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import tempfile
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from fastapi import APIRouter, Depends, File, Form, HTTPException, Query, UploadFile
10
+ from fastapi.responses import FileResponse
11
+ from pydantic import BaseModel
12
+
13
+ from ..dependencies import ServiceRegistry, get_registry
14
+
15
+ router = APIRouter(prefix="/storage", tags=["storage"])
16
+
17
+
18
+ class CreateBucket(BaseModel):
19
+ stage: str
20
+ name: str
21
+ description: str | None = None
22
+ backend: str | None = None
23
+ branch_id: int | None = None
24
+
25
+
26
+ class CreateTable(BaseModel):
27
+ bucket_id: str
28
+ name: str
29
+ columns: list[str]
30
+ primary_key: list[str] | None = None
31
+ not_null_columns: list[str] | None = None
32
+ defaults: list[str] | None = None
33
+ branch_id: int | None = None
34
+ if_not_exists: bool = False
35
+
36
+
37
+ class DescribeBucket(BaseModel):
38
+ description: str
39
+ branch_id: int | None = None
40
+
41
+
42
+ class DescribeTable(BaseModel):
43
+ description: str
44
+ branch_id: int | None = None
45
+
46
+
47
+ class DescribeColumns(BaseModel):
48
+ columns: dict[str, str]
49
+ branch_id: int | None = None
50
+
51
+
52
+ class TagFile(BaseModel):
53
+ add: list[str] | None = None
54
+ remove: list[str] | None = None
55
+
56
+
57
+ class LoadFileToTable(BaseModel):
58
+ file_id: int
59
+ table_id: str
60
+ incremental: bool = False
61
+ delimiter: str = ","
62
+ enclosure: str = '"'
63
+ branch_id: int | None = None
64
+
65
+
66
+ class SwapTables(BaseModel):
67
+ target_table_id: str
68
+ branch_id: int
69
+
70
+
71
+ class CloneTable(BaseModel):
72
+ branch_id: int
73
+
74
+
75
+ @router.get("/buckets", summary="List storage buckets")
76
+ def list_buckets(
77
+ project: str | None = None,
78
+ branch_id: int | None = None,
79
+ registry: ServiceRegistry = Depends(get_registry),
80
+ ) -> dict[str, Any]:
81
+ """List storage buckets in one or more projects. Mirrors `kbagent storage buckets`."""
82
+ aliases = [project] if project else None
83
+ return registry.storage.list_buckets(aliases=aliases, branch_id=branch_id)
84
+
85
+
86
+ @router.get("/buckets/{project}/{bucket_id:path}", summary="Get bucket detail")
87
+ def bucket_detail(
88
+ project: str,
89
+ bucket_id: str,
90
+ branch_id: int | None = None,
91
+ registry: ServiceRegistry = Depends(get_registry),
92
+ ) -> dict[str, Any]:
93
+ """Fetch detail for a single bucket. Mirrors `kbagent storage bucket-detail`."""
94
+ return registry.storage.get_bucket_detail(
95
+ alias=project, bucket_id=bucket_id, branch_id=branch_id
96
+ )
97
+
98
+
99
+ @router.post("/buckets/{project}", summary="Create a bucket")
100
+ def create_bucket(
101
+ project: str, body: CreateBucket, registry: ServiceRegistry = Depends(get_registry)
102
+ ) -> dict[str, Any]:
103
+ """Create a new storage bucket. Mirrors `kbagent storage create-bucket`."""
104
+ return registry.storage.create_bucket(
105
+ alias=project,
106
+ stage=body.stage,
107
+ name=body.name,
108
+ description=body.description,
109
+ backend=body.backend,
110
+ branch_id=body.branch_id,
111
+ )
112
+
113
+
114
+ @router.delete("/buckets/{project}", summary="Delete buckets")
115
+ def delete_buckets(
116
+ project: str,
117
+ bucket_id: list[str] = Query(...),
118
+ force: bool = False,
119
+ dry_run: bool = False,
120
+ branch_id: int | None = None,
121
+ registry: ServiceRegistry = Depends(get_registry),
122
+ ) -> dict[str, Any]:
123
+ """Delete one or more storage buckets. Mirrors `kbagent storage delete-bucket`."""
124
+ return registry.storage.delete_buckets(
125
+ alias=project,
126
+ bucket_ids=bucket_id,
127
+ force=force,
128
+ dry_run=dry_run,
129
+ branch_id=branch_id,
130
+ )
131
+
132
+
133
+ @router.post("/buckets/{project}/{bucket_id:path}/describe", summary="Set bucket description")
134
+ def describe_bucket(
135
+ project: str,
136
+ bucket_id: str,
137
+ body: DescribeBucket,
138
+ registry: ServiceRegistry = Depends(get_registry),
139
+ ) -> dict[str, Any]:
140
+ """Set or update a bucket's description. Mirrors `kbagent storage describe-bucket`."""
141
+ return registry.storage.describe_bucket(
142
+ alias=project,
143
+ bucket_id=bucket_id,
144
+ description=body.description,
145
+ branch_id=body.branch_id,
146
+ )
147
+
148
+
149
+ @router.get("/tables", summary="List storage tables")
150
+ def list_tables(
151
+ project: list[str] | None = Query(None),
152
+ bucket_id: str | None = None,
153
+ branch_id: int | None = None,
154
+ registry: ServiceRegistry = Depends(get_registry),
155
+ ) -> dict[str, Any]:
156
+ """List tables across one or more projects. Mirrors `kbagent storage tables`."""
157
+ return registry.storage.list_tables(aliases=project, bucket_id=bucket_id, branch_id=branch_id)
158
+
159
+
160
+ @router.get("/table-detail/{project}/{table_id:path}", summary="Get table detail")
161
+ def table_detail(
162
+ project: str,
163
+ table_id: str,
164
+ branch_id: int | None = None,
165
+ registry: ServiceRegistry = Depends(get_registry),
166
+ ) -> dict[str, Any]:
167
+ """Fetch detail for a single table. Mirrors `kbagent storage table-detail`."""
168
+ return registry.storage.get_table_detail(alias=project, table_id=table_id, branch_id=branch_id)
169
+
170
+
171
+ @router.get("/table-preview/{project}/{table_id:path}", summary="Preview table rows")
172
+ def preview_table_v2(
173
+ project: str,
174
+ table_id: str,
175
+ limit: int = 100,
176
+ columns: list[str] | None = Query(None),
177
+ where_column: str | None = None,
178
+ where_operator: str = "eq",
179
+ where_value: list[str] | None = Query(None),
180
+ changed_since: str | None = None,
181
+ changed_until: str | None = None,
182
+ registry: ServiceRegistry = Depends(get_registry),
183
+ ) -> dict[str, Any]:
184
+ """Return up to ``limit`` rows via the synchronous data-preview endpoint.
185
+
186
+ Uses ``/v2/storage/tables/{id}/data-preview`` -- synchronous, capped at
187
+ a few hundred rows, no async export job. Storage API caps sync preview
188
+ at 30 columns max.
189
+
190
+ Lives under ``/table-preview`` (not ``/tables/.../preview``) because
191
+ ``{table_id:path}`` is greedy and would conflict with sibling routes.
192
+ """
193
+ import csv as _csv
194
+ import io as _io
195
+
196
+ projects = registry.storage.resolve_projects([project])
197
+ proj = projects[project]
198
+ client = registry.storage._client_factory(proj.stack_url, proj.token)
199
+ try:
200
+ text = client.get_table_data_preview(
201
+ table_id=table_id,
202
+ limit=limit,
203
+ columns=columns,
204
+ where_column=where_column,
205
+ where_operator=where_operator,
206
+ where_values=where_value,
207
+ changed_since=changed_since,
208
+ changed_until=changed_until,
209
+ )
210
+ finally:
211
+ client.close()
212
+ reader = _csv.reader(_io.StringIO(text))
213
+ rows = list(reader)
214
+ if not rows:
215
+ return {"header": [], "rows": [], "row_count": 0}
216
+ return {"header": rows[0], "rows": rows[1:], "row_count": len(rows) - 1}
217
+
218
+
219
+ @router.get("/table-download/{project}/{table_id:path}", summary="Download table as CSV")
220
+ def download_table_v2(
221
+ project: str,
222
+ table_id: str,
223
+ columns: list[str] | None = Query(None),
224
+ limit: int | None = None,
225
+ branch_id: int | None = None,
226
+ where_column: str | None = None,
227
+ where_operator: str = "eq",
228
+ where_value: list[str] | None = Query(None),
229
+ changed_since: str | None = None,
230
+ changed_until: str | None = None,
231
+ registry: ServiceRegistry = Depends(get_registry),
232
+ ) -> FileResponse:
233
+ """Download the table as CSV (uses async export). Optional where/changed filters."""
234
+ out_path = Path(tempfile.mkstemp(suffix=".csv", prefix="kbagent-")[1])
235
+ registry.storage.download_table(
236
+ alias=project,
237
+ table_id=table_id,
238
+ output_path=str(out_path),
239
+ columns=columns,
240
+ limit=limit,
241
+ branch_id=branch_id,
242
+ where_column=where_column,
243
+ where_operator=where_operator,
244
+ where_values=where_value,
245
+ changed_since=changed_since,
246
+ changed_until=changed_until,
247
+ )
248
+ return FileResponse(
249
+ path=str(out_path),
250
+ media_type="text/csv",
251
+ filename=f"{table_id.replace('.', '_')}.csv",
252
+ )
253
+
254
+
255
+ @router.post("/tables/{project}", summary="Create a table")
256
+ def create_table(
257
+ project: str, body: CreateTable, registry: ServiceRegistry = Depends(get_registry)
258
+ ) -> dict[str, Any]:
259
+ """Create a typed storage table. Mirrors `kbagent storage create-table`."""
260
+ return registry.storage.create_table(
261
+ alias=project,
262
+ bucket_id=body.bucket_id,
263
+ name=body.name,
264
+ columns=body.columns,
265
+ primary_key=body.primary_key,
266
+ branch_id=body.branch_id,
267
+ not_null_columns=body.not_null_columns,
268
+ defaults=body.defaults,
269
+ if_not_exists=body.if_not_exists,
270
+ )
271
+
272
+
273
+ @router.post("/tables/{project}/upload", summary="Upload data into a table")
274
+ async def upload_table(
275
+ project: str,
276
+ table_id: str = Form(...),
277
+ incremental: bool = Form(False),
278
+ branch_id: int | None = Form(None),
279
+ file: UploadFile = File(...),
280
+ registry: ServiceRegistry = Depends(get_registry),
281
+ ) -> dict[str, Any]:
282
+ """Upload a CSV file into an existing table. Mirrors `kbagent storage upload-table`."""
283
+ with tempfile.NamedTemporaryFile(delete=False, suffix=Path(file.filename or "x").suffix) as tmp:
284
+ tmp.write(await file.read())
285
+ tmp_path = Path(tmp.name)
286
+ try:
287
+ return registry.storage.upload_table(
288
+ alias=project,
289
+ table_id=table_id,
290
+ file_path=str(tmp_path),
291
+ incremental=incremental,
292
+ branch_id=branch_id,
293
+ )
294
+ finally:
295
+ tmp_path.unlink(missing_ok=True)
296
+
297
+
298
+ @router.delete("/tables/{project}", summary="Delete tables")
299
+ def delete_tables(
300
+ project: str,
301
+ table_id: list[str] = Query(...),
302
+ force: bool = False,
303
+ dry_run: bool = False,
304
+ branch_id: int | None = None,
305
+ registry: ServiceRegistry = Depends(get_registry),
306
+ ) -> dict[str, Any]:
307
+ """Delete one or more storage tables. Mirrors `kbagent storage delete-table`."""
308
+ return registry.storage.delete_tables(
309
+ alias=project,
310
+ table_ids=table_id,
311
+ force=force,
312
+ dry_run=dry_run,
313
+ branch_id=branch_id,
314
+ )
315
+
316
+
317
+ @router.post("/tables/{project}/truncate", summary="Truncate tables")
318
+ def truncate_tables(
319
+ project: str,
320
+ table_id: list[str] = Query(...),
321
+ dry_run: bool = False,
322
+ branch_id: int | None = None,
323
+ registry: ServiceRegistry = Depends(get_registry),
324
+ ) -> dict[str, Any]:
325
+ """Truncate (empty) one or more storage tables. Mirrors `kbagent storage truncate-table`."""
326
+ return registry.storage.truncate_tables(
327
+ alias=project,
328
+ table_ids=table_id,
329
+ dry_run=dry_run,
330
+ branch_id=branch_id,
331
+ )
332
+
333
+
334
+ @router.delete("/columns/{project}/{table_id:path}", summary="Delete table columns")
335
+ def delete_columns(
336
+ project: str,
337
+ table_id: str,
338
+ column: list[str] = Query(...),
339
+ force: bool = False,
340
+ dry_run: bool = False,
341
+ branch_id: int | None = None,
342
+ registry: ServiceRegistry = Depends(get_registry),
343
+ ) -> dict[str, Any]:
344
+ """Delete columns from a table. Mirrors `kbagent storage delete-column`."""
345
+ return registry.storage.delete_columns(
346
+ alias=project,
347
+ table_id=table_id,
348
+ columns=column,
349
+ force=force,
350
+ dry_run=dry_run,
351
+ branch_id=branch_id,
352
+ )
353
+
354
+
355
+ @router.post("/tables/{project}/{table_id:path}/swap", summary="Swap two tables")
356
+ def swap_tables(
357
+ project: str,
358
+ table_id: str,
359
+ body: SwapTables,
360
+ dry_run: bool = False,
361
+ registry: ServiceRegistry = Depends(get_registry),
362
+ ) -> dict[str, Any]:
363
+ """Atomically swap two storage tables. Mirrors `kbagent storage swap-tables`."""
364
+ return registry.storage.swap_tables(
365
+ alias=project,
366
+ table_id=table_id,
367
+ target_table_id=body.target_table_id,
368
+ branch_id=body.branch_id,
369
+ dry_run=dry_run,
370
+ )
371
+
372
+
373
+ @router.post(
374
+ "/tables/{project}/{table_id:path}/pull",
375
+ summary="Clone a table into a dev branch",
376
+ )
377
+ def clone_table(
378
+ project: str,
379
+ table_id: str,
380
+ body: CloneTable,
381
+ dry_run: bool = False,
382
+ registry: ServiceRegistry = Depends(get_registry),
383
+ ) -> dict[str, Any]:
384
+ """Pull (clone) a production table into a dev branch.
385
+
386
+ Mirrors `kbagent storage clone-table`.
387
+ """
388
+ return registry.storage.clone_table(
389
+ alias=project,
390
+ table_id=table_id,
391
+ branch_id=body.branch_id,
392
+ dry_run=dry_run,
393
+ )
394
+
395
+
396
+ @router.post("/tables/{project}/{table_id:path}/describe", summary="Set table description")
397
+ def describe_table(
398
+ project: str,
399
+ table_id: str,
400
+ body: DescribeTable,
401
+ registry: ServiceRegistry = Depends(get_registry),
402
+ ) -> dict[str, Any]:
403
+ """Set or update a table's description. Mirrors `kbagent storage describe-table`."""
404
+ return registry.storage.describe_table(
405
+ alias=project,
406
+ table_id=table_id,
407
+ description=body.description,
408
+ branch_id=body.branch_id,
409
+ )
410
+
411
+
412
+ @router.post("/columns/{project}/{table_id:path}/describe", summary="Set column descriptions")
413
+ def describe_columns(
414
+ project: str,
415
+ table_id: str,
416
+ body: DescribeColumns,
417
+ registry: ServiceRegistry = Depends(get_registry),
418
+ ) -> dict[str, Any]:
419
+ """Set descriptions for table columns. Mirrors `kbagent storage describe-column`."""
420
+ return registry.storage.describe_columns(
421
+ alias=project,
422
+ table_id=table_id,
423
+ columns=body.columns,
424
+ branch_id=body.branch_id,
425
+ )
426
+
427
+
428
+ # Registered AFTER the more specific /columns/.../describe route above: the
429
+ # greedy {table_id:path} would otherwise shadow that POST and swallow a
430
+ # ".../describe" suffix as part of the table id.
431
+ @router.post("/columns/{project}/{table_id:path}", summary="Add a table column")
432
+ def add_column(
433
+ project: str,
434
+ table_id: str,
435
+ column: str = Query(...),
436
+ not_null: bool = False,
437
+ default: str | None = None,
438
+ branch_id: int | None = None,
439
+ registry: ServiceRegistry = Depends(get_registry),
440
+ ) -> dict[str, Any]:
441
+ """Add a single column to a table. Mirrors `kbagent storage add-column`."""
442
+ return registry.storage.add_column(
443
+ alias=project,
444
+ table_id=table_id,
445
+ column=column,
446
+ not_null=not_null,
447
+ default=default,
448
+ branch_id=branch_id,
449
+ )
450
+
451
+
452
+ # ---- Files ----
453
+
454
+
455
+ @router.get("/files", summary="List storage files")
456
+ def list_files(
457
+ project: str,
458
+ tag: list[str] | None = Query(None),
459
+ limit: int = 50,
460
+ offset: int = 0,
461
+ query: str | None = None,
462
+ branch_id: int | None = None,
463
+ registry: ServiceRegistry = Depends(get_registry),
464
+ ) -> dict[str, Any]:
465
+ """List files in a project's Storage Files API. Mirrors `kbagent storage files`."""
466
+ return registry.storage.list_files(
467
+ alias=project,
468
+ tags=tag,
469
+ limit=limit,
470
+ offset=offset,
471
+ query=query,
472
+ branch_id=branch_id,
473
+ )
474
+
475
+
476
+ @router.post("/files/upload", summary="Upload a file to Storage")
477
+ async def upload_file(
478
+ project: str = Form(...),
479
+ name: str | None = Form(None),
480
+ permanent: bool = Form(False),
481
+ tag: list[str] = Form([]),
482
+ branch_id: int | None = Form(None),
483
+ file: UploadFile = File(...),
484
+ registry: ServiceRegistry = Depends(get_registry),
485
+ ) -> dict[str, Any]:
486
+ """Upload a file into Storage Files. Mirrors `kbagent storage file-upload`."""
487
+ with tempfile.NamedTemporaryFile(delete=False, suffix=Path(file.filename or "x").suffix) as tmp:
488
+ tmp.write(await file.read())
489
+ tmp_path = Path(tmp.name)
490
+ try:
491
+ return registry.storage.upload_file(
492
+ alias=project,
493
+ file_path=str(tmp_path),
494
+ name=name or file.filename,
495
+ tags=tag,
496
+ is_permanent=permanent,
497
+ branch_id=branch_id,
498
+ )
499
+ finally:
500
+ tmp_path.unlink(missing_ok=True)
501
+
502
+
503
+ @router.get("/files/{project}/{file_id}", summary="Get file detail")
504
+ def file_detail(
505
+ project: str,
506
+ file_id: int,
507
+ registry: ServiceRegistry = Depends(get_registry),
508
+ ) -> dict[str, Any]:
509
+ """Fetch detail for a single Storage file. Mirrors `kbagent storage file-detail`."""
510
+ return registry.storage.get_file_info(alias=project, file_id=file_id)
511
+
512
+
513
+ @router.get("/files/{project}/{file_id}/download", summary="Download a file")
514
+ def file_download(
515
+ project: str,
516
+ file_id: int,
517
+ registry: ServiceRegistry = Depends(get_registry),
518
+ ) -> FileResponse:
519
+ """Download a Storage file. Mirrors `kbagent storage file-download`."""
520
+ out_dir = Path(tempfile.mkdtemp(prefix="kbagent-file-"))
521
+ result = registry.storage.download_file(
522
+ alias=project, file_id=file_id, output_path=str(out_dir)
523
+ )
524
+ file_path = result.get("local_path") if isinstance(result, dict) else None
525
+ if not file_path or not Path(file_path).exists():
526
+ raise HTTPException(status_code=500, detail="Download produced no file.")
527
+ return FileResponse(
528
+ path=file_path, media_type="application/octet-stream", filename=Path(file_path).name
529
+ )
530
+
531
+
532
+ @router.delete("/files/{project}", summary="Delete files")
533
+ def delete_files(
534
+ project: str,
535
+ file_id: list[int] = Query(...),
536
+ dry_run: bool = False,
537
+ registry: ServiceRegistry = Depends(get_registry),
538
+ ) -> dict[str, Any]:
539
+ """Delete one or more Storage files. Mirrors `kbagent storage file-delete`."""
540
+ return registry.storage.delete_files(alias=project, file_ids=file_id, dry_run=dry_run)
541
+
542
+
543
+ @router.post("/files/{project}/{file_id}/tag", summary="Add or remove file tags")
544
+ def tag_file(
545
+ project: str,
546
+ file_id: int,
547
+ body: TagFile,
548
+ registry: ServiceRegistry = Depends(get_registry),
549
+ ) -> dict[str, Any]:
550
+ """Add or remove tags on a Storage file. Mirrors `kbagent storage file-tag`."""
551
+ return registry.storage.tag_file(
552
+ alias=project,
553
+ file_id=file_id,
554
+ add_tags=body.add,
555
+ remove_tags=body.remove,
556
+ )
557
+
558
+
559
+ @router.post("/files/{project}/load-to-table", summary="Load a file into a table")
560
+ def load_file_to_table(
561
+ project: str,
562
+ body: LoadFileToTable,
563
+ registry: ServiceRegistry = Depends(get_registry),
564
+ ) -> dict[str, Any]:
565
+ """Load a Storage file's contents into a table. Mirrors `kbagent storage load-file`."""
566
+ return registry.storage.load_file_to_table(
567
+ alias=project,
568
+ file_id=body.file_id,
569
+ table_id=body.table_id,
570
+ incremental=body.incremental,
571
+ delimiter=body.delimiter,
572
+ enclosure=body.enclosure,
573
+ branch_id=body.branch_id,
574
+ )