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.
- keboola_agent_cli/__init__.py +34 -0
- keboola_agent_cli/__main__.py +5 -0
- keboola_agent_cli/_ui_dist/assets/arc-DhFYIddx.js +2 -0
- keboola_agent_cli/_ui_dist/assets/arc-DhFYIddx.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/architecture-7EHR7CIX-hNCijx_H.js +1 -0
- keboola_agent_cli/_ui_dist/assets/architectureDiagram-3BPJPVTR-C6hUlprM.js +37 -0
- keboola_agent_cli/_ui_dist/assets/architectureDiagram-3BPJPVTR-C6hUlprM.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/array-BifhSqXX.js +2 -0
- keboola_agent_cli/_ui_dist/assets/array-BifhSqXX.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/blockDiagram-GPEHLZMM-DC7qY9i4.js +133 -0
- keboola_agent_cli/_ui_dist/assets/blockDiagram-GPEHLZMM-DC7qY9i4.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/c4Diagram-AAUBKEIU-5Lh44evt.js +11 -0
- keboola_agent_cli/_ui_dist/assets/c4Diagram-AAUBKEIU-5Lh44evt.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/channel-DBMrXlxx.js +2 -0
- keboola_agent_cli/_ui_dist/assets/channel-DBMrXlxx.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-2J33WTMH-Coy82EBh.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-2J33WTMH-Coy82EBh.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-3OPIFGDE-BQC5CRHI.js +63 -0
- keboola_agent_cli/_ui_dist/assets/chunk-3OPIFGDE-BQC5CRHI.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-4BX2VUAB-DUuEt70o.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-4BX2VUAB-DUuEt70o.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-55IACEB6-BvR-6chF.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-55IACEB6-BvR-6chF.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-5ZQYHXKU-BjcTN7ul.js +3 -0
- keboola_agent_cli/_ui_dist/assets/chunk-5ZQYHXKU-BjcTN7ul.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-727SXJPM-C0zxqqRN.js +207 -0
- keboola_agent_cli/_ui_dist/assets/chunk-727SXJPM-C0zxqqRN.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-AQP2D5EJ-CXf7rIlZ.js +232 -0
- keboola_agent_cli/_ui_dist/assets/chunk-AQP2D5EJ-CXf7rIlZ.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-BSJP7CBP-Oj_FO9Q7.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-BSJP7CBP-Oj_FO9Q7.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-CSCIHK7Q-CcTsLrFc.js +124 -0
- keboola_agent_cli/_ui_dist/assets/chunk-CSCIHK7Q-CcTsLrFc.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-FMBD7UC4-FH-zLkkW.js +16 -0
- keboola_agent_cli/_ui_dist/assets/chunk-FMBD7UC4-FH-zLkkW.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-L5ZTLDWV-B1Ky_e7O.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-L5ZTLDWV-B1Ky_e7O.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-ND2GUHAM-BHz1rpbm.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-ND2GUHAM-BHz1rpbm.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-NNHCCRGN-DlpIbxXb.js +160 -0
- keboola_agent_cli/_ui_dist/assets/chunk-NNHCCRGN-DlpIbxXb.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-NZK2D7GU-tnrSoegS.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-NZK2D7GU-tnrSoegS.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-O5CBEL6O-DxxqDH0l.js +71 -0
- keboola_agent_cli/_ui_dist/assets/chunk-O5CBEL6O-DxxqDH0l.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-QZHKN3VN-CSjc2gjj.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-QZHKN3VN-CSjc2gjj.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/classDiagram-4FO5ZUOK-BuZcZu85.js +2 -0
- keboola_agent_cli/_ui_dist/assets/classDiagram-4FO5ZUOK-BuZcZu85.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/classDiagram-v2-Q7XG4LA2-BuZcZu85.js +2 -0
- keboola_agent_cli/_ui_dist/assets/classDiagram-v2-Q7XG4LA2-BuZcZu85.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/cose-bilkent-S5V4N54A-Y0L8LDMa.js +2 -0
- keboola_agent_cli/_ui_dist/assets/cose-bilkent-S5V4N54A-Y0L8LDMa.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/cytoscape.esm-C8YCVR3_.js +322 -0
- keboola_agent_cli/_ui_dist/assets/cytoscape.esm-C8YCVR3_.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/dagre-BM42HDAG-UZ-9BTqF.js +5 -0
- keboola_agent_cli/_ui_dist/assets/dagre-BM42HDAG-UZ-9BTqF.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/dagre-Bx709z4p.js +2 -0
- keboola_agent_cli/_ui_dist/assets/dagre-Bx709z4p.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/defaultLocale-C8Fc0cco.js +2 -0
- keboola_agent_cli/_ui_dist/assets/defaultLocale-C8Fc0cco.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/diagram-2AECGRRQ-DoDQ60wi.js +44 -0
- keboola_agent_cli/_ui_dist/assets/diagram-2AECGRRQ-DoDQ60wi.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/diagram-5GNKFQAL-CMGFxpUs.js +11 -0
- keboola_agent_cli/_ui_dist/assets/diagram-5GNKFQAL-CMGFxpUs.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/diagram-KO2AKTUF-1uGDa-Iu.js +4 -0
- keboola_agent_cli/_ui_dist/assets/diagram-KO2AKTUF-1uGDa-Iu.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/diagram-LMA3HP47-XtFH7B51.js +25 -0
- keboola_agent_cli/_ui_dist/assets/diagram-LMA3HP47-XtFH7B51.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/diagram-OG6HWLK6-B4_Te1T5.js +25 -0
- keboola_agent_cli/_ui_dist/assets/diagram-OG6HWLK6-B4_Te1T5.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/dist-Di6zmlv0.js +2 -0
- keboola_agent_cli/_ui_dist/assets/dist-Di6zmlv0.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/erDiagram-TEJ5UH35-NjQkrdFt.js +86 -0
- keboola_agent_cli/_ui_dist/assets/erDiagram-TEJ5UH35-NjQkrdFt.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/eventmodeling-FCH6USID-BrJMIks8.js +1 -0
- keboola_agent_cli/_ui_dist/assets/flowDiagram-I6XJVG4X-CIr8DWl7.js +163 -0
- keboola_agent_cli/_ui_dist/assets/flowDiagram-I6XJVG4X-CIr8DWl7.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/ganttDiagram-6RSMTGT7-C1VY_xbQ.js +293 -0
- keboola_agent_cli/_ui_dist/assets/ganttDiagram-6RSMTGT7-C1VY_xbQ.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/gitGraph-WXDBUCRP-COacYjo-.js +1 -0
- keboola_agent_cli/_ui_dist/assets/gitGraphDiagram-PVQCEYII-DQT8-kg2.js +107 -0
- keboola_agent_cli/_ui_dist/assets/gitGraphDiagram-PVQCEYII-DQT8-kg2.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/graphlib-B8gBHxth.js +2 -0
- keboola_agent_cli/_ui_dist/assets/graphlib-B8gBHxth.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/index-CMq50kkV.css +1 -0
- keboola_agent_cli/_ui_dist/assets/index-D8W97DAz.js +118 -0
- keboola_agent_cli/_ui_dist/assets/index-D8W97DAz.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/info-J43DQDTF-DdCTRIzU.js +1 -0
- keboola_agent_cli/_ui_dist/assets/infoDiagram-5YYISTIA-C77rsoTp.js +3 -0
- keboola_agent_cli/_ui_dist/assets/infoDiagram-5YYISTIA-C77rsoTp.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/init-D6jRqBbL.js +2 -0
- keboola_agent_cli/_ui_dist/assets/init-D6jRqBbL.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/ishikawaDiagram-YF4QCWOH-BcTbXaLy.js +71 -0
- keboola_agent_cli/_ui_dist/assets/ishikawaDiagram-YF4QCWOH-BcTbXaLy.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/journeyDiagram-JHISSGLW-BejeAJQ_.js +140 -0
- keboola_agent_cli/_ui_dist/assets/journeyDiagram-JHISSGLW-BejeAJQ_.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/kanban-definition-UN3LZRKU-BRNz_UrH.js +90 -0
- keboola_agent_cli/_ui_dist/assets/kanban-definition-UN3LZRKU-BRNz_UrH.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/katex-C4eR7coU.js +258 -0
- keboola_agent_cli/_ui_dist/assets/katex-C4eR7coU.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/line-CzAQKFbJ.js +2 -0
- keboola_agent_cli/_ui_dist/assets/line-CzAQKFbJ.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/linear-DUNFFdck.js +2 -0
- keboola_agent_cli/_ui_dist/assets/linear-DUNFFdck.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/mermaid-parser.core-CpuBOkFa.js +5 -0
- keboola_agent_cli/_ui_dist/assets/mermaid-parser.core-CpuBOkFa.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/mindmap-definition-RKZ34NQL-9EJQNjH0.js +97 -0
- keboola_agent_cli/_ui_dist/assets/mindmap-definition-RKZ34NQL-9EJQNjH0.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/ordinal-hYBb2elL.js +2 -0
- keboola_agent_cli/_ui_dist/assets/ordinal-hYBb2elL.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/packet-YPE3B663-DLiiw_B2.js +1 -0
- keboola_agent_cli/_ui_dist/assets/path-BWPyau1x.js +2 -0
- keboola_agent_cli/_ui_dist/assets/path-BWPyau1x.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/pie-LRSECV5Y-CRoO8G1g.js +1 -0
- keboola_agent_cli/_ui_dist/assets/pieDiagram-4H26LBE5-XH4cy6Cb.js +31 -0
- keboola_agent_cli/_ui_dist/assets/pieDiagram-4H26LBE5-XH4cy6Cb.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/quadrantDiagram-W4KKPZXB-fdhc93U8.js +8 -0
- keboola_agent_cli/_ui_dist/assets/quadrantDiagram-W4KKPZXB-fdhc93U8.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/radar-GUYGQ44K-DAlLVJHm.js +1 -0
- keboola_agent_cli/_ui_dist/assets/requirementDiagram-4Y6WPE33-a94eP3R9.js +85 -0
- keboola_agent_cli/_ui_dist/assets/requirementDiagram-4Y6WPE33-a94eP3R9.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/rough.esm-CSKSodPl.js +2 -0
- keboola_agent_cli/_ui_dist/assets/rough.esm-CSKSodPl.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/sankeyDiagram-5OEKKPKP-jcBa02sp.js +41 -0
- keboola_agent_cli/_ui_dist/assets/sankeyDiagram-5OEKKPKP-jcBa02sp.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/sequenceDiagram-3UESZ5HK-A5-GGM-e.js +163 -0
- keboola_agent_cli/_ui_dist/assets/sequenceDiagram-3UESZ5HK-A5-GGM-e.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/src-ZI-V_AF0.js +2 -0
- keboola_agent_cli/_ui_dist/assets/src-ZI-V_AF0.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/stateDiagram-AJRCARHV-BKAA5rqE.js +2 -0
- keboola_agent_cli/_ui_dist/assets/stateDiagram-AJRCARHV-BKAA5rqE.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/stateDiagram-v2-BHNVJYJU-DnJwJBsE.js +2 -0
- keboola_agent_cli/_ui_dist/assets/stateDiagram-v2-BHNVJYJU-DnJwJBsE.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/timeline-definition-PNZ67QCA-Cy39jp8b.js +121 -0
- keboola_agent_cli/_ui_dist/assets/timeline-definition-PNZ67QCA-Cy39jp8b.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/treeView-BLDUP644-DbLYl23-.js +1 -0
- keboola_agent_cli/_ui_dist/assets/treemap-LRROVOQU-Bp0eGlOt.js +1 -0
- keboola_agent_cli/_ui_dist/assets/vennDiagram-CIIHVFJN-BGECKubd.js +35 -0
- keboola_agent_cli/_ui_dist/assets/vennDiagram-CIIHVFJN-BGECKubd.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/wardley-L42UT6IY-D4yH4jqS.js +1 -0
- keboola_agent_cli/_ui_dist/assets/wardleyDiagram-YWT4CUSO-D6XRG3cZ.js +79 -0
- keboola_agent_cli/_ui_dist/assets/wardleyDiagram-YWT4CUSO-D6XRG3cZ.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/xychartDiagram-2RQKCTM6-DRre-pfZ.js +8 -0
- keboola_agent_cli/_ui_dist/assets/xychartDiagram-2RQKCTM6-DRre-pfZ.js.map +1 -0
- keboola_agent_cli/_ui_dist/index.html +50 -0
- keboola_agent_cli/ai_client.py +83 -0
- keboola_agent_cli/auto_update.py +550 -0
- keboola_agent_cli/changelog.py +1198 -0
- keboola_agent_cli/cli.py +448 -0
- keboola_agent_cli/client.py +3422 -0
- keboola_agent_cli/commands/__init__.py +0 -0
- keboola_agent_cli/commands/_data_app_git.py +343 -0
- keboola_agent_cli/commands/_helpers.py +377 -0
- keboola_agent_cli/commands/_metadata_input.py +49 -0
- keboola_agent_cli/commands/_semantic_layer_crud.py +632 -0
- keboola_agent_cli/commands/_semantic_layer_helpers.py +44 -0
- keboola_agent_cli/commands/_semantic_layer_reference_data.py +247 -0
- keboola_agent_cli/commands/agent.py +968 -0
- keboola_agent_cli/commands/branch.py +423 -0
- keboola_agent_cli/commands/changelog.py +168 -0
- keboola_agent_cli/commands/component.py +216 -0
- keboola_agent_cli/commands/config.py +2442 -0
- keboola_agent_cli/commands/context.py +1481 -0
- keboola_agent_cli/commands/data_app.py +1279 -0
- keboola_agent_cli/commands/dev_portal.py +584 -0
- keboola_agent_cli/commands/doctor.py +37 -0
- keboola_agent_cli/commands/encrypt.py +145 -0
- keboola_agent_cli/commands/feature.py +311 -0
- keboola_agent_cli/commands/flow.py +948 -0
- keboola_agent_cli/commands/http_client.py +157 -0
- keboola_agent_cli/commands/init.py +279 -0
- keboola_agent_cli/commands/job.py +661 -0
- keboola_agent_cli/commands/kai.py +301 -0
- keboola_agent_cli/commands/lineage.py +1464 -0
- keboola_agent_cli/commands/org.py +292 -0
- keboola_agent_cli/commands/permissions.py +360 -0
- keboola_agent_cli/commands/project.py +1192 -0
- keboola_agent_cli/commands/repl.py +243 -0
- keboola_agent_cli/commands/schedule.py +340 -0
- keboola_agent_cli/commands/search.py +178 -0
- keboola_agent_cli/commands/semantic_layer.py +939 -0
- keboola_agent_cli/commands/serve.py +272 -0
- keboola_agent_cli/commands/sharing.py +340 -0
- keboola_agent_cli/commands/storage.py +2630 -0
- keboola_agent_cli/commands/stream.py +266 -0
- keboola_agent_cli/commands/sync.py +1277 -0
- keboola_agent_cli/commands/tool.py +206 -0
- keboola_agent_cli/commands/version.py +186 -0
- keboola_agent_cli/commands/workspace.py +635 -0
- keboola_agent_cli/config_store.py +582 -0
- keboola_agent_cli/constants.py +528 -0
- keboola_agent_cli/data_science_client.py +342 -0
- keboola_agent_cli/dev_portal_client.py +323 -0
- keboola_agent_cli/errors.py +248 -0
- keboola_agent_cli/http_base.py +315 -0
- keboola_agent_cli/json_utils.py +126 -0
- keboola_agent_cli/lib.py +536 -0
- keboola_agent_cli/manage_client.py +324 -0
- keboola_agent_cli/metastore_client.py +214 -0
- keboola_agent_cli/models.py +427 -0
- keboola_agent_cli/output.py +1084 -0
- keboola_agent_cli/permissions.py +469 -0
- keboola_agent_cli/py.typed +3 -0
- keboola_agent_cli/result_models.py +271 -0
- keboola_agent_cli/server/__init__.py +34 -0
- keboola_agent_cli/server/agent_runner.py +1289 -0
- keboola_agent_cli/server/agents_store.py +325 -0
- keboola_agent_cli/server/app.py +764 -0
- keboola_agent_cli/server/auth.py +117 -0
- keboola_agent_cli/server/dependencies.py +149 -0
- keboola_agent_cli/server/pricing.py +303 -0
- keboola_agent_cli/server/routers/__init__.py +1 -0
- keboola_agent_cli/server/routers/agents.py +616 -0
- keboola_agent_cli/server/routers/ai_chat.py +129 -0
- keboola_agent_cli/server/routers/branches.py +133 -0
- keboola_agent_cli/server/routers/components.py +48 -0
- keboola_agent_cli/server/routers/configs.py +507 -0
- keboola_agent_cli/server/routers/data_apps.py +384 -0
- keboola_agent_cli/server/routers/dev_portal.py +67 -0
- keboola_agent_cli/server/routers/encrypt.py +35 -0
- keboola_agent_cli/server/routers/feature.py +179 -0
- keboola_agent_cli/server/routers/flows.py +204 -0
- keboola_agent_cli/server/routers/health.py +53 -0
- keboola_agent_cli/server/routers/jobs.py +175 -0
- keboola_agent_cli/server/routers/kai.py +80 -0
- keboola_agent_cli/server/routers/lineage.py +226 -0
- keboola_agent_cli/server/routers/mcp.py +70 -0
- keboola_agent_cli/server/routers/members.py +170 -0
- keboola_agent_cli/server/routers/org.py +96 -0
- keboola_agent_cli/server/routers/projects.py +106 -0
- keboola_agent_cli/server/routers/schedules.py +54 -0
- keboola_agent_cli/server/routers/search.py +30 -0
- keboola_agent_cli/server/routers/semantic_layer.py +650 -0
- keboola_agent_cli/server/routers/sharing.py +86 -0
- keboola_agent_cli/server/routers/storage.py +574 -0
- keboola_agent_cli/server/routers/stream.py +100 -0
- keboola_agent_cli/server/routers/workspaces.py +302 -0
- keboola_agent_cli/server/run_broadcaster.py +329 -0
- keboola_agent_cli/server/sse.py +25 -0
- keboola_agent_cli/services/__init__.py +0 -0
- keboola_agent_cli/services/_encryption.py +217 -0
- keboola_agent_cli/services/_semantic_layer_cascade.py +147 -0
- keboola_agent_cli/services/_semantic_layer_crud.py +382 -0
- keboola_agent_cli/services/_semantic_layer_internals.py +1078 -0
- keboola_agent_cli/services/_semantic_layer_lookup.py +181 -0
- keboola_agent_cli/services/_semantic_layer_reference_data.py +217 -0
- keboola_agent_cli/services/_sync_bindings.py +456 -0
- keboola_agent_cli/services/_sync_branch.py +191 -0
- keboola_agent_cli/services/_sync_bulk.py +228 -0
- keboola_agent_cli/services/_sync_clone.py +163 -0
- keboola_agent_cli/services/_sync_models.py +97 -0
- keboola_agent_cli/services/_sync_push_ops.py +369 -0
- keboola_agent_cli/services/_sync_storage.py +376 -0
- keboola_agent_cli/services/_sync_writeback.py +167 -0
- keboola_agent_cli/services/agent_service.py +458 -0
- keboola_agent_cli/services/base.py +175 -0
- keboola_agent_cli/services/branch_service.py +588 -0
- keboola_agent_cli/services/component_service.py +694 -0
- keboola_agent_cli/services/config_service.py +2099 -0
- keboola_agent_cli/services/data_app_git_service.py +224 -0
- keboola_agent_cli/services/data_app_service.py +2082 -0
- keboola_agent_cli/services/deep_lineage_service.py +1322 -0
- keboola_agent_cli/services/dev_portal_service.py +345 -0
- keboola_agent_cli/services/doctor_service.py +445 -0
- keboola_agent_cli/services/encrypt_service.py +87 -0
- keboola_agent_cli/services/feature_service.py +268 -0
- keboola_agent_cli/services/flow_service.py +769 -0
- keboola_agent_cli/services/flow_validation.py +188 -0
- keboola_agent_cli/services/http_forwarder_service.py +236 -0
- keboola_agent_cli/services/job_idempotency_store.py +285 -0
- keboola_agent_cli/services/job_service.py +797 -0
- keboola_agent_cli/services/kai_service.py +367 -0
- keboola_agent_cli/services/lineage_service.py +274 -0
- keboola_agent_cli/services/mcp_service.py +1498 -0
- keboola_agent_cli/services/mcp_transport.py +259 -0
- keboola_agent_cli/services/member_service.py +593 -0
- keboola_agent_cli/services/org_service.py +619 -0
- keboola_agent_cli/services/project_service.py +947 -0
- keboola_agent_cli/services/repo_validate_service.py +767 -0
- keboola_agent_cli/services/schedule_service.py +731 -0
- keboola_agent_cli/services/search_service.py +331 -0
- keboola_agent_cli/services/semantic_layer_service.py +1497 -0
- keboola_agent_cli/services/sharing_service.py +307 -0
- keboola_agent_cli/services/storage_service.py +2524 -0
- keboola_agent_cli/services/stream_service.py +395 -0
- keboola_agent_cli/services/sync_service.py +2244 -0
- keboola_agent_cli/services/variables_service.py +447 -0
- keboola_agent_cli/services/version_service.py +1038 -0
- keboola_agent_cli/services/workspace_service.py +1103 -0
- keboola_agent_cli/stream_client.py +217 -0
- keboola_agent_cli/sync/__init__.py +1 -0
- keboola_agent_cli/sync/branch_mapping.py +174 -0
- keboola_agent_cli/sync/clone.py +211 -0
- keboola_agent_cli/sync/code_extraction.py +655 -0
- keboola_agent_cli/sync/config_format.py +290 -0
- keboola_agent_cli/sync/diff_engine.py +566 -0
- keboola_agent_cli/sync/git_utils.py +93 -0
- keboola_agent_cli/sync/manifest.py +162 -0
- keboola_agent_cli/sync/naming.py +90 -0
- keboola_agent_cli/sync/secrets.py +62 -0
- keboola_agent_cli/sync/sql_split.py +134 -0
- keboola_cli-0.63.4.dist-info/METADATA +308 -0
- keboola_cli-0.63.4.dist-info/RECORD +306 -0
- keboola_cli-0.63.4.dist-info/WHEEL +4 -0
- keboola_cli-0.63.4.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,764 @@
|
|
|
1
|
+
"""FastAPI HTTP server for kbagent.
|
|
2
|
+
|
|
3
|
+
Wraps all services as REST endpoints. Designed to be consumed by the
|
|
4
|
+
web/backend Node.js BFF (which in turn serves the React UI in web/frontend).
|
|
5
|
+
|
|
6
|
+
Key design choices:
|
|
7
|
+
- Bearer token auth (random secret generated at startup, printed to stdout)
|
|
8
|
+
- Localhost-only by default (bind 127.0.0.1)
|
|
9
|
+
- Per-request X-Manage-Token header for write ops requiring manage token
|
|
10
|
+
- Reuses existing services with their existing dict return shapes
|
|
11
|
+
- SSE for streaming: job log tail, branch reset progress, kai chat
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import asyncio
|
|
17
|
+
import contextlib
|
|
18
|
+
import logging
|
|
19
|
+
import secrets
|
|
20
|
+
from contextlib import asynccontextmanager
|
|
21
|
+
|
|
22
|
+
from fastapi import FastAPI
|
|
23
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
24
|
+
from fastapi.openapi.utils import get_openapi
|
|
25
|
+
from fastapi.responses import JSONResponse
|
|
26
|
+
from starlette.exceptions import HTTPException as StarletteHTTPException
|
|
27
|
+
|
|
28
|
+
from .. import __version__
|
|
29
|
+
from ..config_store import ConfigStore, resolve_config_dir
|
|
30
|
+
from ..errors import ConfigError, ErrorCode, KeboolaApiError
|
|
31
|
+
from .agents_store import AgentStore
|
|
32
|
+
from .auth import PUBLIC_PATHS, AuthSettings, install_auth
|
|
33
|
+
from .dependencies import ServiceRegistry, install_registry
|
|
34
|
+
from .routers import (
|
|
35
|
+
agents,
|
|
36
|
+
ai_chat,
|
|
37
|
+
branches,
|
|
38
|
+
components,
|
|
39
|
+
configs,
|
|
40
|
+
data_apps,
|
|
41
|
+
dev_portal,
|
|
42
|
+
encrypt,
|
|
43
|
+
feature,
|
|
44
|
+
flows,
|
|
45
|
+
health,
|
|
46
|
+
jobs,
|
|
47
|
+
kai,
|
|
48
|
+
lineage,
|
|
49
|
+
mcp,
|
|
50
|
+
members,
|
|
51
|
+
org,
|
|
52
|
+
projects,
|
|
53
|
+
schedules,
|
|
54
|
+
search,
|
|
55
|
+
semantic_layer,
|
|
56
|
+
sharing,
|
|
57
|
+
storage,
|
|
58
|
+
stream,
|
|
59
|
+
workspaces,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
logger = logging.getLogger(__name__)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# OpenAPI tag metadata. Order matches the CLI groupings printed by
|
|
66
|
+
# ``kbagent --help`` so the Swagger UI sidebar reads top-down the same
|
|
67
|
+
# way users explore commands on the terminal:
|
|
68
|
+
#
|
|
69
|
+
# Project Management -> Configurations -> Data -> Execution ->
|
|
70
|
+
# Development -> AI & Tools -> System
|
|
71
|
+
#
|
|
72
|
+
# FastAPI uses this list both for ordering and for the per-section
|
|
73
|
+
# descriptions. The names below MUST match the ``tags=[...]`` value on
|
|
74
|
+
# each ``APIRouter`` in ``server/routers/`` -- a typo silently demotes
|
|
75
|
+
# a section to the end of the sidebar with no description.
|
|
76
|
+
OPENAPI_TAGS: list[dict[str, str]] = [
|
|
77
|
+
# ---- Project Management ----
|
|
78
|
+
{
|
|
79
|
+
"name": "projects",
|
|
80
|
+
"description": (
|
|
81
|
+
"**Project Management.** "
|
|
82
|
+
"Register, list, edit, and remove Keboola project aliases. "
|
|
83
|
+
"Mirrors `kbagent project add|list|remove|edit|status|use|current|info`."
|
|
84
|
+
),
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"name": "members",
|
|
88
|
+
"description": (
|
|
89
|
+
"**Project Management.** "
|
|
90
|
+
"Invite users, list members and pending invitations, "
|
|
91
|
+
"change roles, and remove members. "
|
|
92
|
+
"Mirrors `kbagent project invite|member-*|invitation-*`."
|
|
93
|
+
),
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"name": "org",
|
|
97
|
+
"description": (
|
|
98
|
+
"**Project Management.** "
|
|
99
|
+
"Bulk-onboard an entire organization (Manage API). Requires "
|
|
100
|
+
"the `X-Manage-Token` header on every request -- the manage "
|
|
101
|
+
"token is never persisted in config. "
|
|
102
|
+
"Mirrors `kbagent org setup|refresh`."
|
|
103
|
+
),
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"name": "feature",
|
|
107
|
+
"description": (
|
|
108
|
+
"**Project Management.** "
|
|
109
|
+
"List the stack feature-flag catalogue and enable/disable "
|
|
110
|
+
"features on projects and users (Manage API). Requires the "
|
|
111
|
+
"`X-Manage-Token` header (super-admin) on every request -- the "
|
|
112
|
+
"manage token is never persisted in config. "
|
|
113
|
+
"Mirrors `kbagent feature list|project-*|user-*`."
|
|
114
|
+
),
|
|
115
|
+
},
|
|
116
|
+
# ---- Configurations ----
|
|
117
|
+
{
|
|
118
|
+
"name": "configs",
|
|
119
|
+
"description": (
|
|
120
|
+
"**Configurations.** "
|
|
121
|
+
"Browse, search, update, and manage component configurations "
|
|
122
|
+
"and rows (variables, metadata, folder, default bucket, "
|
|
123
|
+
"OAuth URL). "
|
|
124
|
+
"Mirrors `kbagent config *`."
|
|
125
|
+
),
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"name": "components",
|
|
129
|
+
"description": (
|
|
130
|
+
"**Configurations.** "
|
|
131
|
+
"Discover components (extractors, writers, applications, "
|
|
132
|
+
"transformations) and fetch their JSON schemas. "
|
|
133
|
+
"Mirrors `kbagent component list|detail`."
|
|
134
|
+
),
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"name": "encrypt",
|
|
138
|
+
"description": (
|
|
139
|
+
"**Configurations.** "
|
|
140
|
+
"Encrypt secret values for a specific project + component "
|
|
141
|
+
"using the Keboola encryption API. "
|
|
142
|
+
"Mirrors `kbagent encrypt values`."
|
|
143
|
+
),
|
|
144
|
+
},
|
|
145
|
+
# ---- Data ----
|
|
146
|
+
{
|
|
147
|
+
"name": "storage",
|
|
148
|
+
"description": (
|
|
149
|
+
"**Data.** "
|
|
150
|
+
"Buckets, tables, columns, files. Create, upload, download, "
|
|
151
|
+
"describe, swap, delete. "
|
|
152
|
+
"Mirrors `kbagent storage *`."
|
|
153
|
+
),
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"name": "stream",
|
|
157
|
+
"description": (
|
|
158
|
+
"**Data.** "
|
|
159
|
+
"Data Streams (OpenTelemetry / OTLP) -- list, create, and "
|
|
160
|
+
"delete ingest sources and retrieve their endpoints. The OTLP "
|
|
161
|
+
"URL embeds a secret that is masked unless `reveal=true`. "
|
|
162
|
+
"Mirrors `kbagent stream list|create-source|detail|delete`."
|
|
163
|
+
),
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"name": "search",
|
|
167
|
+
"description": (
|
|
168
|
+
"**Data.** "
|
|
169
|
+
"Cross-resource search over tables, buckets, configs, "
|
|
170
|
+
"flows, data-apps, and transformations. "
|
|
171
|
+
"Mirrors `kbagent search`."
|
|
172
|
+
),
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"name": "sharing",
|
|
176
|
+
"description": (
|
|
177
|
+
"**Data.** "
|
|
178
|
+
"Share buckets across projects and inspect the sharing "
|
|
179
|
+
"graph (edges). "
|
|
180
|
+
"Mirrors `kbagent sharing *`."
|
|
181
|
+
),
|
|
182
|
+
},
|
|
183
|
+
# ---- Execution ----
|
|
184
|
+
{
|
|
185
|
+
"name": "jobs",
|
|
186
|
+
"description": (
|
|
187
|
+
"**Execution.** "
|
|
188
|
+
"Run components, inspect job history, terminate running "
|
|
189
|
+
"jobs. "
|
|
190
|
+
"Mirrors `kbagent job list|detail|run|terminate`."
|
|
191
|
+
),
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"name": "flows",
|
|
195
|
+
"description": (
|
|
196
|
+
"**Execution.** "
|
|
197
|
+
"Orchestrator and Flow CRUD, scheduling, run history. "
|
|
198
|
+
"Mirrors `kbagent flow *`."
|
|
199
|
+
),
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"name": "schedules",
|
|
203
|
+
"description": (
|
|
204
|
+
"**Execution.** "
|
|
205
|
+
"Cron-style schedules attached to flows / configurations. "
|
|
206
|
+
"Mirrors `kbagent schedule list|detail|find`."
|
|
207
|
+
),
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
"name": "data-apps",
|
|
211
|
+
"description": (
|
|
212
|
+
"**Execution.** "
|
|
213
|
+
"Streamlit / R / Python data apps -- create, deploy, "
|
|
214
|
+
"start/stop, manage secrets. "
|
|
215
|
+
"Mirrors `kbagent data-app *`."
|
|
216
|
+
),
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"name": "dev-portal",
|
|
220
|
+
"description": (
|
|
221
|
+
"**Read-only.** "
|
|
222
|
+
"Developer Portal app discovery -- list a vendor's apps, get one "
|
|
223
|
+
"app's full entry. Mirrors `kbagent dev-portal list|get`. Writes "
|
|
224
|
+
"and identity management are CLI-only (TTY-confirmed)."
|
|
225
|
+
),
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
"name": "workspaces",
|
|
229
|
+
"description": (
|
|
230
|
+
"**Execution.** "
|
|
231
|
+
"Snowflake / BigQuery workspaces -- CRUD, load tables, "
|
|
232
|
+
"run SQL via Query Service, GC orphans. "
|
|
233
|
+
"Mirrors `kbagent workspace *`."
|
|
234
|
+
),
|
|
235
|
+
},
|
|
236
|
+
# ---- Development ----
|
|
237
|
+
{
|
|
238
|
+
"name": "branches",
|
|
239
|
+
"description": (
|
|
240
|
+
"**Development.** "
|
|
241
|
+
"Dev branch lifecycle (create / use / reset / delete / "
|
|
242
|
+
"merge) and branch metadata. "
|
|
243
|
+
"Mirrors `kbagent branch *`."
|
|
244
|
+
),
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"name": "lineage",
|
|
248
|
+
"description": (
|
|
249
|
+
"**Development.** "
|
|
250
|
+
"Build and query cross-project data lineage (table-level "
|
|
251
|
+
"and column-level). "
|
|
252
|
+
"Mirrors `kbagent lineage build|show|info`."
|
|
253
|
+
),
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"name": "semantic-layer",
|
|
257
|
+
"description": (
|
|
258
|
+
"**Development.** "
|
|
259
|
+
"Model, validate, import/export, diff, promote, and build "
|
|
260
|
+
"semantic layer artifacts (datasets, metrics, "
|
|
261
|
+
"relationships, constraints, glossary). "
|
|
262
|
+
"Mirrors `kbagent semantic-layer *`."
|
|
263
|
+
),
|
|
264
|
+
},
|
|
265
|
+
# ---- AI & Tools ----
|
|
266
|
+
{
|
|
267
|
+
"name": "mcp",
|
|
268
|
+
"description": (
|
|
269
|
+
"**AI & Tools.** "
|
|
270
|
+
"List and call MCP tools across one or all projects. "
|
|
271
|
+
"Mirrors `kbagent tool list|call`."
|
|
272
|
+
),
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
"name": "kai",
|
|
276
|
+
"description": (
|
|
277
|
+
"**AI & Tools.** "
|
|
278
|
+
"Keboola AI (Kai) -- ping, preflight, single-shot ask, "
|
|
279
|
+
"chat with history. "
|
|
280
|
+
"Mirrors `kbagent kai *`."
|
|
281
|
+
),
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
"name": "ai-chat",
|
|
285
|
+
"description": (
|
|
286
|
+
"**AI & Tools.** "
|
|
287
|
+
"Server-side streaming AI chat (SSE) used by the kbagent "
|
|
288
|
+
"web UI. No CLI equivalent."
|
|
289
|
+
),
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
"name": "agents",
|
|
293
|
+
"description": (
|
|
294
|
+
"**AI & Tools.** "
|
|
295
|
+
"Scheduled / on-demand AI agent tasks. Server-only feature "
|
|
296
|
+
"(no CLI equivalent) -- the scheduler loop runs inside "
|
|
297
|
+
"`kbagent serve` and persists tasks + runs to the config "
|
|
298
|
+
"directory."
|
|
299
|
+
),
|
|
300
|
+
},
|
|
301
|
+
# ---- System ----
|
|
302
|
+
{
|
|
303
|
+
"name": "health",
|
|
304
|
+
"description": (
|
|
305
|
+
"**System.** "
|
|
306
|
+
"Liveness ping, auth-info bootstrap, version, changelog, "
|
|
307
|
+
"and doctor checks. `/health/ping` is the only public "
|
|
308
|
+
"endpoint -- everything else requires Bearer auth."
|
|
309
|
+
),
|
|
310
|
+
},
|
|
311
|
+
]
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
APP_DESCRIPTION = """\
|
|
315
|
+
HTTP API surface for **kbagent**. Wraps every CLI command as a REST
|
|
316
|
+
endpoint so the kbagent web UI and any HTTP client (curl, the
|
|
317
|
+
`kbagent http` proxy, scheduled agents, Node BFFs, ...) can drive
|
|
318
|
+
Keboola the same way the terminal does.
|
|
319
|
+
|
|
320
|
+
## Authentication
|
|
321
|
+
|
|
322
|
+
Every endpoint except `GET /health/ping`, `GET /docs`, `GET /redoc`,
|
|
323
|
+
and `GET /openapi.json` requires a bearer token. The token is
|
|
324
|
+
generated when `kbagent serve` starts and printed to stdout -- click
|
|
325
|
+
**Authorize** at the top right of this page and paste it once.
|
|
326
|
+
|
|
327
|
+
Endpoints under **org** additionally require an `X-Manage-Token`
|
|
328
|
+
header (the Keboola Manage API token). It is never persisted; pass
|
|
329
|
+
it per request.
|
|
330
|
+
|
|
331
|
+
## Layout
|
|
332
|
+
|
|
333
|
+
Sections below are grouped roughly the same way `kbagent --help` groups
|
|
334
|
+
its command tree:
|
|
335
|
+
|
|
336
|
+
- **Project Management** -- projects, members, org, feature flags
|
|
337
|
+
- **Configurations** -- configs, components, encrypt
|
|
338
|
+
- **Data** -- storage, search, sharing
|
|
339
|
+
- **Execution** -- jobs, flows, schedules, data-apps, workspaces
|
|
340
|
+
- **Development** -- branches, lineage, semantic-layer
|
|
341
|
+
- **AI & Tools** -- mcp, kai, ai-chat, agents
|
|
342
|
+
- **System** -- health
|
|
343
|
+
|
|
344
|
+
Most endpoints accept a `project` alias either in the body or as a
|
|
345
|
+
query parameter; multi-project endpoints accept `project` repeatedly.
|
|
346
|
+
"""
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def _build_custom_openapi(app: FastAPI):
|
|
350
|
+
"""Return a closure that generates the OpenAPI schema with auth schemes.
|
|
351
|
+
|
|
352
|
+
FastAPI's default ``get_openapi(...)`` does not know about the
|
|
353
|
+
``BearerAuthMiddleware`` (it's an ASGI middleware, not a per-route
|
|
354
|
+
dependency), so the generated schema has no ``securitySchemes`` and
|
|
355
|
+
Swagger UI shows no **Authorize** button. We patch the schema after
|
|
356
|
+
generation:
|
|
357
|
+
|
|
358
|
+
1. Declare a ``BearerAuth`` HTTP scheme (the global default for every
|
|
359
|
+
endpoint that is not in ``PUBLIC_PATHS``).
|
|
360
|
+
2. Declare a ``ManageToken`` API-key scheme (header ``X-Manage-Token``)
|
|
361
|
+
used by the ``org`` router and any future endpoint that requires the
|
|
362
|
+
Manage API token. Endpoints opt in by listing it in their
|
|
363
|
+
``openapi_extra={"security": [{"BearerAuth": [], "ManageToken": []}]}``.
|
|
364
|
+
3. Apply ``BearerAuth`` globally and clear ``security`` for public paths
|
|
365
|
+
so Swagger UI shows them as unsecured (matching the actual middleware
|
|
366
|
+
behavior).
|
|
367
|
+
|
|
368
|
+
The result is cached on ``app.openapi_schema`` per FastAPI convention.
|
|
369
|
+
"""
|
|
370
|
+
|
|
371
|
+
def custom_openapi() -> dict:
|
|
372
|
+
if app.openapi_schema:
|
|
373
|
+
return app.openapi_schema
|
|
374
|
+
schema = get_openapi(
|
|
375
|
+
title=app.title,
|
|
376
|
+
version=app.version,
|
|
377
|
+
description=app.description,
|
|
378
|
+
routes=app.routes,
|
|
379
|
+
tags=app.openapi_tags,
|
|
380
|
+
)
|
|
381
|
+
components = schema.setdefault("components", {})
|
|
382
|
+
components["securitySchemes"] = {
|
|
383
|
+
"BearerAuth": {
|
|
384
|
+
"type": "http",
|
|
385
|
+
"scheme": "bearer",
|
|
386
|
+
"description": (
|
|
387
|
+
"Bearer token printed to stdout when `kbagent serve` starts. "
|
|
388
|
+
"Paste it once and Swagger UI will attach it to every request."
|
|
389
|
+
),
|
|
390
|
+
},
|
|
391
|
+
"ManageToken": {
|
|
392
|
+
"type": "apiKey",
|
|
393
|
+
"in": "header",
|
|
394
|
+
"name": "X-Manage-Token",
|
|
395
|
+
"description": (
|
|
396
|
+
"Keboola Manage API token. Required only by `/org/*` "
|
|
397
|
+
"endpoints. Never persisted; passed per request."
|
|
398
|
+
),
|
|
399
|
+
},
|
|
400
|
+
}
|
|
401
|
+
schema["security"] = [{"BearerAuth": []}]
|
|
402
|
+
# Public paths in PUBLIC_PATHS are exempt from the bearer-auth
|
|
403
|
+
# middleware. Reflect that in the schema so Swagger UI does not
|
|
404
|
+
# mislabel them as locked.
|
|
405
|
+
for path in PUBLIC_PATHS:
|
|
406
|
+
path_item = schema.get("paths", {}).get(path)
|
|
407
|
+
if not path_item:
|
|
408
|
+
continue
|
|
409
|
+
for op in path_item.values():
|
|
410
|
+
if isinstance(op, dict):
|
|
411
|
+
op["security"] = []
|
|
412
|
+
app.openapi_schema = schema
|
|
413
|
+
return schema
|
|
414
|
+
|
|
415
|
+
return custom_openapi
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def _format_error(
|
|
419
|
+
message: str, error_code: ErrorCode | str, *, http_status: int = 400
|
|
420
|
+
) -> JSONResponse:
|
|
421
|
+
"""Render a kbagent-style error envelope at the given HTTP status.
|
|
422
|
+
|
|
423
|
+
``error_code`` accepts both :class:`ErrorCode` enum members (the canonical
|
|
424
|
+
surface; matches CLI error envelopes byte-for-byte) and raw strings (for
|
|
425
|
+
forward compatibility when a router wraps a third-party exception whose
|
|
426
|
+
code is not yet in the enum). The :class:`ErrorCode` mixes in ``str``, so
|
|
427
|
+
both shapes serialise as plain strings in the JSON body.
|
|
428
|
+
"""
|
|
429
|
+
return JSONResponse(
|
|
430
|
+
status_code=http_status,
|
|
431
|
+
content={
|
|
432
|
+
"status": "error",
|
|
433
|
+
"error": {
|
|
434
|
+
"code": str(error_code),
|
|
435
|
+
"message": message,
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
def create_app(
|
|
442
|
+
*,
|
|
443
|
+
config_dir: str | None = None,
|
|
444
|
+
auth_token: str | None = None,
|
|
445
|
+
cors_origins: list[str] | None = None,
|
|
446
|
+
serve_url: str | None = None,
|
|
447
|
+
ui_dist: str | None = None,
|
|
448
|
+
) -> FastAPI:
|
|
449
|
+
"""Build and configure the FastAPI application.
|
|
450
|
+
|
|
451
|
+
Args:
|
|
452
|
+
config_dir: Override config directory (matches kbagent --config-dir).
|
|
453
|
+
auth_token: Bearer token clients must send. If None, generates one.
|
|
454
|
+
cors_origins: Allowed CORS origins. Default: localhost dev ports.
|
|
455
|
+
serve_url: Self-URL (``http://host:port``) of this server. Stored on
|
|
456
|
+
the registry so agent subprocesses can be told where to call back.
|
|
457
|
+
If None, defaults are still injected into subprocess env using
|
|
458
|
+
``127.0.0.1:8001`` -- the serve_command default.
|
|
459
|
+
ui_dist: Optional absolute path to a built React ``dist/`` directory
|
|
460
|
+
(the output of ``npm run build`` in ``web/frontend``). When set,
|
|
461
|
+
the FastAPI app additionally:
|
|
462
|
+
|
|
463
|
+
1) accepts ``/api/<path>`` as an alias for the bare ``<path>``
|
|
464
|
+
(the BFF used to re-strip this prefix; in single-process mode
|
|
465
|
+
we do it server-side via an ASGI path-rewrite middleware),
|
|
466
|
+
2) mounts the dist directory at ``/`` so static assets and the
|
|
467
|
+
SPA fallback are served by uvicorn directly,
|
|
468
|
+
3) intercepts ``GET /`` to inject ``window.__KBAGENT_TOKEN`` into
|
|
469
|
+
``index.html`` so the SPA boots already authenticated -- no
|
|
470
|
+
BFF and no manual paste step.
|
|
471
|
+
|
|
472
|
+
If the path does not exist, the UI mount is skipped silently and
|
|
473
|
+
a warning is logged so ``--ui`` typos don't break the API path.
|
|
474
|
+
|
|
475
|
+
Returns:
|
|
476
|
+
Configured FastAPI app ready for uvicorn.
|
|
477
|
+
"""
|
|
478
|
+
resolved_token = auth_token or secrets.token_urlsafe(32)
|
|
479
|
+
|
|
480
|
+
@asynccontextmanager
|
|
481
|
+
async def _lifespan(app_: FastAPI):
|
|
482
|
+
# Start the agent scheduler loop in the background once services
|
|
483
|
+
# exist (registry is installed below before include_router calls).
|
|
484
|
+
from .agent_runner import scheduler_loop
|
|
485
|
+
|
|
486
|
+
scheduler_task = None
|
|
487
|
+
store = getattr(app_.state, "agent_store", None)
|
|
488
|
+
registry_ = getattr(app_.state, "registry", None)
|
|
489
|
+
if store is not None and registry_ is not None:
|
|
490
|
+
scheduler_task = asyncio.create_task(scheduler_loop(store, registry_))
|
|
491
|
+
logger.info("Agent scheduler task spawned")
|
|
492
|
+
try:
|
|
493
|
+
yield
|
|
494
|
+
finally:
|
|
495
|
+
if scheduler_task is not None:
|
|
496
|
+
scheduler_task.cancel()
|
|
497
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
498
|
+
await scheduler_task
|
|
499
|
+
|
|
500
|
+
app = FastAPI(
|
|
501
|
+
lifespan=_lifespan, # type: ignore[arg-type]
|
|
502
|
+
title="kbagent serve",
|
|
503
|
+
description=APP_DESCRIPTION,
|
|
504
|
+
version=__version__,
|
|
505
|
+
docs_url="/docs",
|
|
506
|
+
redoc_url="/redoc",
|
|
507
|
+
openapi_tags=OPENAPI_TAGS,
|
|
508
|
+
swagger_ui_parameters={
|
|
509
|
+
# Keep the bearer token across page refreshes so the user only
|
|
510
|
+
# has to paste it once per browser session. The token is held in
|
|
511
|
+
# the Swagger UI in-memory store (and localStorage when persist
|
|
512
|
+
# is true) -- safe for a localhost-only dev tool, and a major
|
|
513
|
+
# ergonomics win for exploring the API.
|
|
514
|
+
"persistAuthorization": True,
|
|
515
|
+
# Default tag ordering follows ``openapi_tags`` above; this
|
|
516
|
+
# toggle just keeps Swagger from re-sorting operations within
|
|
517
|
+
# each tag alphabetically (we want them in router declaration
|
|
518
|
+
# order, which usually mirrors a logical workflow).
|
|
519
|
+
"operationsSorter": None,
|
|
520
|
+
"docExpansion": "none",
|
|
521
|
+
},
|
|
522
|
+
)
|
|
523
|
+
app.openapi = _build_custom_openapi(app) # type: ignore[method-assign]
|
|
524
|
+
|
|
525
|
+
app.add_middleware(
|
|
526
|
+
CORSMiddleware,
|
|
527
|
+
allow_origins=cors_origins
|
|
528
|
+
or [
|
|
529
|
+
"http://localhost:5173", # Vite dev default
|
|
530
|
+
"http://localhost:8000", # Node BFF
|
|
531
|
+
"http://127.0.0.1:5173",
|
|
532
|
+
"http://127.0.0.1:8000",
|
|
533
|
+
],
|
|
534
|
+
allow_credentials=True,
|
|
535
|
+
allow_methods=["*"],
|
|
536
|
+
allow_headers=["*"],
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
install_auth(app, AuthSettings(token=resolved_token))
|
|
540
|
+
|
|
541
|
+
resolved_dir, source = resolve_config_dir(cli_config_dir=config_dir)
|
|
542
|
+
config_store = ConfigStore(config_dir=resolved_dir, source=source)
|
|
543
|
+
registry = ServiceRegistry(
|
|
544
|
+
config_store=config_store,
|
|
545
|
+
serve_url=serve_url,
|
|
546
|
+
serve_token=resolved_token,
|
|
547
|
+
)
|
|
548
|
+
install_registry(app, registry)
|
|
549
|
+
|
|
550
|
+
app.state.agent_store = AgentStore(resolved_dir)
|
|
551
|
+
|
|
552
|
+
from .run_broadcaster import install_broadcaster
|
|
553
|
+
|
|
554
|
+
install_broadcaster(app)
|
|
555
|
+
|
|
556
|
+
@app.exception_handler(ConfigError)
|
|
557
|
+
async def _config_error_handler(_request, exc: ConfigError):
|
|
558
|
+
return _format_error(str(exc), ErrorCode.CONFIG_ERROR, http_status=400)
|
|
559
|
+
|
|
560
|
+
@app.exception_handler(KeboolaApiError)
|
|
561
|
+
async def _api_error_handler(_request, exc: KeboolaApiError):
|
|
562
|
+
code = getattr(exc, "error_code", ErrorCode.API_ERROR)
|
|
563
|
+
msg = getattr(exc, "message", str(exc)) or str(exc)
|
|
564
|
+
return _format_error(msg, code, http_status=502)
|
|
565
|
+
|
|
566
|
+
@app.exception_handler(StarletteHTTPException)
|
|
567
|
+
async def _starlette_handler(_request, exc: StarletteHTTPException):
|
|
568
|
+
return _format_error(
|
|
569
|
+
exc.detail or "HTTP error", ErrorCode.HTTP_ERROR, http_status=exc.status_code
|
|
570
|
+
)
|
|
571
|
+
|
|
572
|
+
@app.exception_handler(Exception)
|
|
573
|
+
async def _generic_handler(_request, exc: Exception):
|
|
574
|
+
logger.exception("Unhandled error: %s", exc)
|
|
575
|
+
return _format_error(str(exc) or repr(exc), ErrorCode.INTERNAL_ERROR, http_status=500)
|
|
576
|
+
|
|
577
|
+
app.include_router(health.router)
|
|
578
|
+
app.include_router(projects.router)
|
|
579
|
+
app.include_router(members.router)
|
|
580
|
+
app.include_router(feature.router)
|
|
581
|
+
app.include_router(configs.router)
|
|
582
|
+
app.include_router(components.router)
|
|
583
|
+
app.include_router(storage.router)
|
|
584
|
+
app.include_router(stream.router)
|
|
585
|
+
app.include_router(jobs.router)
|
|
586
|
+
app.include_router(branches.router)
|
|
587
|
+
app.include_router(workspaces.router)
|
|
588
|
+
app.include_router(flows.router)
|
|
589
|
+
app.include_router(schedules.router)
|
|
590
|
+
app.include_router(lineage.router)
|
|
591
|
+
app.include_router(sharing.router)
|
|
592
|
+
app.include_router(data_apps.router)
|
|
593
|
+
app.include_router(dev_portal.router)
|
|
594
|
+
app.include_router(mcp.router)
|
|
595
|
+
app.include_router(kai.router)
|
|
596
|
+
app.include_router(ai_chat.router)
|
|
597
|
+
app.include_router(encrypt.router)
|
|
598
|
+
app.include_router(search.router)
|
|
599
|
+
app.include_router(semantic_layer.router)
|
|
600
|
+
app.include_router(org.router)
|
|
601
|
+
app.include_router(agents.router)
|
|
602
|
+
|
|
603
|
+
app.state.auth_token = resolved_token
|
|
604
|
+
|
|
605
|
+
if ui_dist:
|
|
606
|
+
_install_ui(app, ui_dist=ui_dist, token=resolved_token)
|
|
607
|
+
|
|
608
|
+
return app
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
_SESSION_COOKIE_NAME = "kbagent_session"
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
def _install_ui(app: FastAPI, *, ui_dist: str, token: str) -> None:
|
|
615
|
+
"""Mount the built React SPA at ``/`` and bridge ``/api/*`` to bare routes.
|
|
616
|
+
|
|
617
|
+
Three pieces:
|
|
618
|
+
|
|
619
|
+
1) **Path-rewrite middleware** for ``/api/<rest>`` -> ``<rest>``. Mounted
|
|
620
|
+
BEFORE auth so the auth header check runs against the rewritten path
|
|
621
|
+
(auth doesn't care about path, but PUBLIC_PATHS exact-matches do).
|
|
622
|
+
2) **Cookie-setting** ``GET /`` and ``GET /index.html``: read the built
|
|
623
|
+
``index.html``, return it with a ``Set-Cookie: kbagent_session=<token>;
|
|
624
|
+
HttpOnly; SameSite=Strict; Path=/`` header. Public (no auth) so the
|
|
625
|
+
SPA can bootstrap. The browser then attaches the cookie to every
|
|
626
|
+
same-origin REST + SSE request automatically. The token is HttpOnly
|
|
627
|
+
(no JS access -- XSS-resistant), SameSite=Strict (no cross-origin
|
|
628
|
+
sends -- CSRF-resistant), and lives only for the browser session.
|
|
629
|
+
|
|
630
|
+
This replaces the older "inject ``window.__KBAGENT_TOKEN`` into a
|
|
631
|
+
``<script>`` tag" approach. The injected token landed in the JS heap
|
|
632
|
+
(XSS-readable) and the EventSource fallback (``?_kbagent_token=...``
|
|
633
|
+
query param) put it into uvicorn's access log -- both attack surfaces
|
|
634
|
+
are gone with the cookie-only design.
|
|
635
|
+
3) **StaticFiles mount at ``/``** with ``html=True`` so missing paths fall
|
|
636
|
+
through to ``index.html`` for SPA client-side routing.
|
|
637
|
+
|
|
638
|
+
The mount is appended *after* all API routers, so any registered route
|
|
639
|
+
(``/projects``, ``/configs``, ``/agents``, ...) wins over a hypothetical
|
|
640
|
+
static file with the same name.
|
|
641
|
+
"""
|
|
642
|
+
from pathlib import Path
|
|
643
|
+
|
|
644
|
+
from fastapi.responses import HTMLResponse
|
|
645
|
+
from fastapi.staticfiles import StaticFiles
|
|
646
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
647
|
+
|
|
648
|
+
dist = Path(ui_dist).expanduser().resolve()
|
|
649
|
+
if not dist.exists() or not (dist / "index.html").exists():
|
|
650
|
+
logger.warning(
|
|
651
|
+
"UI dist path %s missing index.html -- skipping UI mount. "
|
|
652
|
+
"Did you run `make web-build`?",
|
|
653
|
+
dist,
|
|
654
|
+
)
|
|
655
|
+
return
|
|
656
|
+
|
|
657
|
+
class _ApiAliasMiddleware(BaseHTTPMiddleware):
|
|
658
|
+
"""Rewrite ``/api/<path>`` -> ``/<path>`` so the SPA can keep its
|
|
659
|
+
existing ``/api/*`` calls unchanged when the BFF is removed.
|
|
660
|
+
|
|
661
|
+
Done in middleware (not via APIRouter prefix) so we can flip a single
|
|
662
|
+
scope key and reuse every existing router unchanged.
|
|
663
|
+
"""
|
|
664
|
+
|
|
665
|
+
async def dispatch(self, request, call_next): # type: ignore[override]
|
|
666
|
+
path = request.scope.get("path", "")
|
|
667
|
+
if path.startswith("/api/") or path == "/api":
|
|
668
|
+
stripped = path[4:] or "/"
|
|
669
|
+
request.scope["path"] = stripped
|
|
670
|
+
# raw_path is bytes; some Starlette internals consult it.
|
|
671
|
+
request.scope["raw_path"] = stripped.encode("utf-8")
|
|
672
|
+
return await call_next(request)
|
|
673
|
+
|
|
674
|
+
app.add_middleware(_ApiAliasMiddleware)
|
|
675
|
+
|
|
676
|
+
# Allow unauthenticated access to the bootstrap HTML so the browser can
|
|
677
|
+
# load it and pick up the session cookie. Static assets (JS/CSS/icons)
|
|
678
|
+
# under /assets/* are also public -- they carry no secrets. The shared
|
|
679
|
+
# PUBLIC_PATHS set is already imported at module scope; the SPA's
|
|
680
|
+
# extended public surface is bolted on via ``_allow_static_through_auth``
|
|
681
|
+
# below.
|
|
682
|
+
|
|
683
|
+
@app.get("/", include_in_schema=False)
|
|
684
|
+
@app.get("/index.html", include_in_schema=False)
|
|
685
|
+
async def _serve_ui_index() -> HTMLResponse:
|
|
686
|
+
html = (dist / "index.html").read_text(encoding="utf-8")
|
|
687
|
+
response = HTMLResponse(html)
|
|
688
|
+
# Browser session cookie: HttpOnly + SameSite=Strict + Path=/. No
|
|
689
|
+
# ``Secure`` flag because kbagent serve defaults to plain http on
|
|
690
|
+
# 127.0.0.1; setting Secure would prevent the cookie from ever being
|
|
691
|
+
# set on a localhost dev install. Operators running `--host 0.0.0.0`
|
|
692
|
+
# behind TLS termination should layer Secure via the proxy.
|
|
693
|
+
# ``max_age`` deliberately omitted -> cookie is a session cookie
|
|
694
|
+
# (cleared when the browser closes), matching the token's
|
|
695
|
+
# one-per-process lifecycle.
|
|
696
|
+
response.set_cookie(
|
|
697
|
+
key=_SESSION_COOKIE_NAME,
|
|
698
|
+
value=token,
|
|
699
|
+
httponly=True,
|
|
700
|
+
samesite="strict",
|
|
701
|
+
path="/",
|
|
702
|
+
)
|
|
703
|
+
return response
|
|
704
|
+
|
|
705
|
+
# SPA fallback + assets. ``html=True`` makes StaticFiles serve index.html
|
|
706
|
+
# for unknown paths (so /workspaces, /jobs, etc. client-side routes work
|
|
707
|
+
# on direct navigation). The auth middleware will still gate API calls;
|
|
708
|
+
# static files are served before it sees them only because StaticFiles
|
|
709
|
+
# is the LAST mount and middleware runs on the unified scope -- so we
|
|
710
|
+
# widen PUBLIC_PATHS via prefix logic in the auth middleware itself.
|
|
711
|
+
# Quick path: short-circuit auth for non-/api GETs by extending the
|
|
712
|
+
# bypass list at install time.
|
|
713
|
+
_allow_static_through_auth(app)
|
|
714
|
+
|
|
715
|
+
app.mount("/", StaticFiles(directory=str(dist), html=True), name="ui")
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
def _allow_static_through_auth(app: FastAPI) -> None:
|
|
719
|
+
"""Mark the SPA's GET-only static surface as auth-public.
|
|
720
|
+
|
|
721
|
+
The auth middleware exempts ``PUBLIC_PATHS`` (docs, openapi, health). In UI
|
|
722
|
+
mode the SPA also needs ``GET /``, ``GET /index.html``, ``GET /assets/*``,
|
|
723
|
+
favicons, and the SPA's client-side routes (which resolve to index.html via
|
|
724
|
+
the StaticFiles ``html=True`` fallback) to load without a token.
|
|
725
|
+
|
|
726
|
+
Route-aware (GHSA-ffpq-prmh-3gx2): a real endpoint must authenticate; only
|
|
727
|
+
genuine client-side SPA routes fall through to the public index.html shell.
|
|
728
|
+
We ask the router whether a GET resolves to a registered route via the
|
|
729
|
+
routing match protocol, NOT a flat scan of ``app.routes``: fastapi >=0.137
|
|
730
|
+
nests included routers into a lazy tree (``_IncludedRouter``), so a flat scan
|
|
731
|
+
misses nested endpoints and would serve them unauthenticated. ``matches()``
|
|
732
|
+
is the same resolution a real request uses, so it cannot miss a live
|
|
733
|
+
endpoint. Fails CLOSED -- any error treats the path as protected, never
|
|
734
|
+
silently public.
|
|
735
|
+
|
|
736
|
+
Stashed on ``app.state`` for the auth middleware to consult (avoids an
|
|
737
|
+
import cycle with the auth module).
|
|
738
|
+
"""
|
|
739
|
+
from starlette.routing import Match
|
|
740
|
+
|
|
741
|
+
static_paths = frozenset({"/", "/index.html", "/favicon.svg", "/favicon.ico", "/manifest.json"})
|
|
742
|
+
|
|
743
|
+
def _is_ui_public(method: str, path: str) -> bool:
|
|
744
|
+
if method != "GET":
|
|
745
|
+
return False
|
|
746
|
+
# SPA shell + built assets are always public so the browser can bootstrap.
|
|
747
|
+
if path in static_paths or path.startswith("/assets/"):
|
|
748
|
+
return True
|
|
749
|
+
try:
|
|
750
|
+
scope = {"type": "http", "method": "GET", "path": path, "headers": []}
|
|
751
|
+
for route in app.router.routes:
|
|
752
|
+
if getattr(route, "name", None) == "ui": # the SPA StaticFiles catch-all
|
|
753
|
+
continue
|
|
754
|
+
match, _ = route.matches(scope)
|
|
755
|
+
if match is not Match.NONE:
|
|
756
|
+
return False # resolves to a real endpoint -> require auth
|
|
757
|
+
return True # no endpoint matched -> genuine SPA client route
|
|
758
|
+
except Exception:
|
|
759
|
+
return False # fail closed
|
|
760
|
+
|
|
761
|
+
app.state.is_ui_public = _is_ui_public
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
__all__ = ["create_app"]
|