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,204 @@
|
|
|
1
|
+
"""Flow + flow-schedule endpoints (conditional flows / keboola.flow only)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
|
|
10
|
+
from ...services.flow_validation import find_unreachable_phases, validate_conditional_flow
|
|
11
|
+
from ..dependencies import ServiceRegistry, get_registry
|
|
12
|
+
|
|
13
|
+
router = APIRouter(prefix="/flows", tags=["flows"])
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class FlowCreate(BaseModel):
|
|
17
|
+
name: str
|
|
18
|
+
description: str = ""
|
|
19
|
+
phases: list[dict[str, Any]] | None = None
|
|
20
|
+
tasks: list[dict[str, Any]] | None = None
|
|
21
|
+
branch_id: int | None = None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class FlowUpdate(BaseModel):
|
|
25
|
+
name: str | None = None
|
|
26
|
+
description: str | None = None
|
|
27
|
+
phases: list[dict[str, Any]] | None = None
|
|
28
|
+
tasks: list[dict[str, Any]] | None = None
|
|
29
|
+
branch_id: int | None = None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class FlowSchedule(BaseModel):
|
|
33
|
+
cron_tab: str
|
|
34
|
+
timezone: str = "UTC"
|
|
35
|
+
enabled: bool = True
|
|
36
|
+
schedule_name: str | None = None
|
|
37
|
+
branch_id: int | None = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class FlowValidate(BaseModel):
|
|
41
|
+
phases: list[dict[str, Any]] = []
|
|
42
|
+
tasks: list[dict[str, Any]] = []
|
|
43
|
+
project: str | None = None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# NOTE: /validate and /{project}/schema are declared BEFORE the /{project}
|
|
47
|
+
# and /{project}/{config_id} routes -- FastAPI matches in declaration order,
|
|
48
|
+
# so the literal segments must win over the path parameters.
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@router.post("/validate", summary="Validate a conditional-flow definition")
|
|
52
|
+
def validate(
|
|
53
|
+
body: FlowValidate, registry: ServiceRegistry = Depends(get_registry)
|
|
54
|
+
) -> dict[str, Any]:
|
|
55
|
+
"""Validate phases/tasks (schema + semantic checks). Mirrors `kbagent flow validate`.
|
|
56
|
+
|
|
57
|
+
With ``project`` the live keboola.flow JSON Schema is fetched from the stack
|
|
58
|
+
for structural validation; a fetch failure degrades to semantic-only and is
|
|
59
|
+
recorded in ``notes``. Without ``project`` only semantic checks run.
|
|
60
|
+
"""
|
|
61
|
+
schema: dict[str, Any] | None = None
|
|
62
|
+
notes: list[str] = []
|
|
63
|
+
if body.project:
|
|
64
|
+
fetch = registry.flow.fetch_flow_schema(body.project)
|
|
65
|
+
schema = fetch.schema
|
|
66
|
+
if schema is None:
|
|
67
|
+
notes.append(f"structural schema validation skipped: {fetch.reason}")
|
|
68
|
+
else:
|
|
69
|
+
notes.append(
|
|
70
|
+
"structural schema validation skipped: no schema source "
|
|
71
|
+
"(pass 'project' to fetch the live schema from the stack)"
|
|
72
|
+
)
|
|
73
|
+
errors = validate_conditional_flow(body.phases, body.tasks, schema)
|
|
74
|
+
warnings = [
|
|
75
|
+
f"Phase '{pid}' is unreachable from the entry phase"
|
|
76
|
+
for pid in find_unreachable_phases(body.phases)
|
|
77
|
+
]
|
|
78
|
+
return {"valid": not errors, "errors": errors, "warnings": warnings, "notes": notes}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@router.get("/{project}/schema", summary="Fetch the live conditional-flow JSON Schema")
|
|
82
|
+
def get_schema(project: str, registry: ServiceRegistry = Depends(get_registry)) -> dict[str, Any]:
|
|
83
|
+
"""Dump the keboola.flow JSON Schema served by the stack. Mirrors `kbagent flow schema --full`."""
|
|
84
|
+
fetch = registry.flow.fetch_flow_schema(project)
|
|
85
|
+
if fetch.schema is None:
|
|
86
|
+
raise HTTPException(
|
|
87
|
+
status_code=502,
|
|
88
|
+
detail=f"Could not fetch the conditional-flow schema: {fetch.reason}",
|
|
89
|
+
)
|
|
90
|
+
return {"format": "json-schema", "schema": fetch.schema}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@router.get("", summary="List flows across projects")
|
|
94
|
+
def list_flows(
|
|
95
|
+
project: list[str] | None = Query(None),
|
|
96
|
+
branch_id: int | None = None,
|
|
97
|
+
with_schedules: bool = False,
|
|
98
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
99
|
+
) -> dict[str, Any]:
|
|
100
|
+
"""List flows in one or more projects. Mirrors `kbagent flow list`."""
|
|
101
|
+
return registry.flow.list_flows(
|
|
102
|
+
aliases=project, branch_id=branch_id, with_schedules=with_schedules
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@router.get("/{project}/{config_id}", summary="Get flow detail")
|
|
107
|
+
def detail(
|
|
108
|
+
project: str,
|
|
109
|
+
config_id: str,
|
|
110
|
+
branch_id: int | None = None,
|
|
111
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
112
|
+
) -> dict[str, Any]:
|
|
113
|
+
"""Fetch a single flow configuration. Mirrors `kbagent flow detail`."""
|
|
114
|
+
return registry.flow.get_flow_detail(alias=project, config_id=config_id, branch_id=branch_id)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@router.post("/{project}", summary="Create a new flow")
|
|
118
|
+
def create(
|
|
119
|
+
project: str, body: FlowCreate, registry: ServiceRegistry = Depends(get_registry)
|
|
120
|
+
) -> dict[str, Any]:
|
|
121
|
+
"""Create a new flow configuration. Mirrors `kbagent flow new`."""
|
|
122
|
+
return registry.flow.create_flow(
|
|
123
|
+
alias=project,
|
|
124
|
+
name=body.name,
|
|
125
|
+
description=body.description,
|
|
126
|
+
phases=body.phases,
|
|
127
|
+
tasks=body.tasks,
|
|
128
|
+
branch_id=body.branch_id,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@router.patch("/{project}/{config_id}", summary="Update an existing flow")
|
|
133
|
+
def update(
|
|
134
|
+
project: str,
|
|
135
|
+
config_id: str,
|
|
136
|
+
body: FlowUpdate,
|
|
137
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
138
|
+
) -> dict[str, Any]:
|
|
139
|
+
"""Update name, description, or phases/tasks of a flow. Mirrors `kbagent flow update`."""
|
|
140
|
+
return registry.flow.update_flow(
|
|
141
|
+
alias=project,
|
|
142
|
+
config_id=config_id,
|
|
143
|
+
name=body.name,
|
|
144
|
+
description=body.description,
|
|
145
|
+
phases=body.phases,
|
|
146
|
+
tasks=body.tasks,
|
|
147
|
+
branch_id=body.branch_id,
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@router.delete("/{project}/{config_id}", summary="Delete a flow")
|
|
152
|
+
def delete(
|
|
153
|
+
project: str,
|
|
154
|
+
config_id: str,
|
|
155
|
+
branch_id: int | None = None,
|
|
156
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
157
|
+
) -> dict[str, Any]:
|
|
158
|
+
"""Delete a flow configuration. Mirrors `kbagent flow delete`."""
|
|
159
|
+
return registry.flow.delete_flow(alias=project, config_id=config_id, branch_id=branch_id)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@router.get("/{project}/{config_id}/schedules", summary="List schedules for a flow")
|
|
163
|
+
def list_schedules(
|
|
164
|
+
project: str,
|
|
165
|
+
config_id: str,
|
|
166
|
+
branch_id: int | None = None,
|
|
167
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
168
|
+
) -> dict[str, Any]:
|
|
169
|
+
"""List cron schedules attached to a flow."""
|
|
170
|
+
return registry.flow.list_flow_schedules(
|
|
171
|
+
alias=project, config_id=config_id, branch_id=branch_id
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@router.post("/{project}/{config_id}/schedule", summary="Set a cron schedule on a flow")
|
|
176
|
+
def set_schedule(
|
|
177
|
+
project: str,
|
|
178
|
+
config_id: str,
|
|
179
|
+
body: FlowSchedule,
|
|
180
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
181
|
+
) -> dict[str, Any]:
|
|
182
|
+
"""Attach or update a cron schedule on a flow. Mirrors `kbagent flow schedule`."""
|
|
183
|
+
return registry.flow.set_flow_schedule(
|
|
184
|
+
alias=project,
|
|
185
|
+
config_id=config_id,
|
|
186
|
+
cron_tab=body.cron_tab,
|
|
187
|
+
timezone=body.timezone,
|
|
188
|
+
enabled=body.enabled,
|
|
189
|
+
schedule_name=body.schedule_name,
|
|
190
|
+
branch_id=body.branch_id,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@router.delete("/{project}/{config_id}/schedule", summary="Remove a flow schedule")
|
|
195
|
+
def remove_schedule(
|
|
196
|
+
project: str,
|
|
197
|
+
config_id: str,
|
|
198
|
+
branch_id: int | None = None,
|
|
199
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
200
|
+
) -> dict[str, Any]:
|
|
201
|
+
"""Remove the cron schedule from a flow. Mirrors `kbagent flow schedule-remove`."""
|
|
202
|
+
return registry.flow.remove_flow_schedule(
|
|
203
|
+
alias=project, config_id=config_id, branch_id=branch_id
|
|
204
|
+
)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Health, version, changelog, and doctor endpoints."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from fastapi import APIRouter, Depends
|
|
8
|
+
|
|
9
|
+
from ... import __version__
|
|
10
|
+
from ...changelog import CHANGELOG, get_changelog
|
|
11
|
+
from ..dependencies import ServiceRegistry, get_registry
|
|
12
|
+
|
|
13
|
+
router = APIRouter(tags=["health"])
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@router.get("/health/ping", summary="Liveness check")
|
|
17
|
+
def ping() -> dict[str, Any]:
|
|
18
|
+
"""Unauthenticated liveness check."""
|
|
19
|
+
return {"status": "ok", "version": __version__}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@router.get("/health/auth-info", summary="Show authentication scheme")
|
|
23
|
+
def auth_info() -> dict[str, Any]:
|
|
24
|
+
"""Public info about authentication scheme (no secrets disclosed)."""
|
|
25
|
+
return {
|
|
26
|
+
"scheme": "bearer",
|
|
27
|
+
"header": "Authorization",
|
|
28
|
+
"note": (
|
|
29
|
+
"Send 'Authorization: Bearer <token>'. The token is printed to "
|
|
30
|
+
"stdout when 'kbagent serve' starts."
|
|
31
|
+
),
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@router.get("/version", summary="Show kbagent versions")
|
|
36
|
+
def version(registry: ServiceRegistry = Depends(get_registry)) -> dict[str, Any]:
|
|
37
|
+
"""Versions of kbagent, MCP server, and Python."""
|
|
38
|
+
return registry.version.get_versions()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@router.get("/changelog", summary="List release notes")
|
|
42
|
+
def changelog(limit: int | None = None) -> dict[str, Any]:
|
|
43
|
+
"""Return release entries; pass ``?limit=N`` for the latest N."""
|
|
44
|
+
items = get_changelog(limit=limit) if limit is not None and limit > 0 else dict(CHANGELOG)
|
|
45
|
+
return {
|
|
46
|
+
"entries": [{"version": v, "highlights": notes} for v, notes in items.items()],
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@router.get("/doctor", summary="Run health diagnostics")
|
|
51
|
+
def doctor(registry: ServiceRegistry = Depends(get_registry)) -> dict[str, Any]:
|
|
52
|
+
"""Run kbagent doctor health checks."""
|
|
53
|
+
return registry.doctor.run_checks()
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"""Job endpoints (list, detail, run, terminate) + SSE log/status streaming."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from collections.abc import AsyncIterator
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from fastapi import APIRouter, Depends, Query
|
|
10
|
+
from pydantic import BaseModel
|
|
11
|
+
from sse_starlette.sse import EventSourceResponse
|
|
12
|
+
|
|
13
|
+
from ...constants import DEFAULT_JOB_MODE, DEFAULT_LOG_TAIL_LINES, DEFAULT_POLL_STRATEGY
|
|
14
|
+
from ..dependencies import ServiceRegistry, get_registry
|
|
15
|
+
from ..sse import json_event
|
|
16
|
+
|
|
17
|
+
router = APIRouter(prefix="/jobs", tags=["jobs"])
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class JobRun(BaseModel):
|
|
21
|
+
component_id: str
|
|
22
|
+
config_id: str
|
|
23
|
+
config_row_ids: list[str] | None = None
|
|
24
|
+
branch_id: int | None = None
|
|
25
|
+
variable_values_id: str | None = None
|
|
26
|
+
no_variables: bool = False
|
|
27
|
+
mode: str = DEFAULT_JOB_MODE
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class JobTerminate(BaseModel):
|
|
31
|
+
job_ids: list[str] | None = None
|
|
32
|
+
status: str | None = None
|
|
33
|
+
component_id: str | None = None
|
|
34
|
+
config_id: str | None = None
|
|
35
|
+
branch_id: int | None = None
|
|
36
|
+
limit: int | None = None
|
|
37
|
+
dry_run: bool = False
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@router.get("", summary="List jobs across projects")
|
|
41
|
+
def list_jobs(
|
|
42
|
+
project: list[str] | None = Query(None),
|
|
43
|
+
component_id: str | None = None,
|
|
44
|
+
config_id: str | None = None,
|
|
45
|
+
status: str | None = None,
|
|
46
|
+
limit: int = 50,
|
|
47
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
48
|
+
) -> dict[str, Any]:
|
|
49
|
+
"""List jobs across one or more projects. Mirrors `kbagent job list`."""
|
|
50
|
+
return registry.job.list_jobs(
|
|
51
|
+
aliases=project,
|
|
52
|
+
component_id=component_id,
|
|
53
|
+
config_id=config_id,
|
|
54
|
+
status=status,
|
|
55
|
+
limit=limit,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@router.get("/{project}/{job_id}", summary="Get job detail")
|
|
60
|
+
def detail(
|
|
61
|
+
project: str, job_id: str, registry: ServiceRegistry = Depends(get_registry)
|
|
62
|
+
) -> dict[str, Any]:
|
|
63
|
+
"""Fetch detail for a single job. Mirrors `kbagent job detail`."""
|
|
64
|
+
return registry.job.get_job_detail(alias=project, job_id=job_id)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@router.post("/{project}/run", summary="Run a component configuration")
|
|
68
|
+
def run(
|
|
69
|
+
project: str,
|
|
70
|
+
body: JobRun,
|
|
71
|
+
wait: bool = False,
|
|
72
|
+
timeout: float = 300.0,
|
|
73
|
+
poll_strategy: str = DEFAULT_POLL_STRATEGY,
|
|
74
|
+
log_tail_lines: int = DEFAULT_LOG_TAIL_LINES,
|
|
75
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
76
|
+
) -> dict[str, Any]:
|
|
77
|
+
"""Run a component configuration and optionally wait for completion. Mirrors `kbagent job run`."""
|
|
78
|
+
return registry.job.run_job(
|
|
79
|
+
alias=project,
|
|
80
|
+
component_id=body.component_id,
|
|
81
|
+
config_id=body.config_id,
|
|
82
|
+
config_row_ids=body.config_row_ids,
|
|
83
|
+
wait=wait,
|
|
84
|
+
timeout=timeout,
|
|
85
|
+
branch_id=body.branch_id,
|
|
86
|
+
variable_values_id=body.variable_values_id,
|
|
87
|
+
no_variables=body.no_variables,
|
|
88
|
+
poll_strategy=poll_strategy,
|
|
89
|
+
log_tail_lines=log_tail_lines,
|
|
90
|
+
mode=body.mode,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@router.post("/{project}/terminate", summary="Terminate running jobs")
|
|
95
|
+
def terminate(
|
|
96
|
+
project: str, body: JobTerminate, registry: ServiceRegistry = Depends(get_registry)
|
|
97
|
+
) -> dict[str, Any]:
|
|
98
|
+
"""Terminate jobs by id or filter. Mirrors `kbagent job terminate`."""
|
|
99
|
+
if body.job_ids:
|
|
100
|
+
return registry.job.terminate_jobs(
|
|
101
|
+
alias=project, job_ids=body.job_ids, dry_run=body.dry_run
|
|
102
|
+
)
|
|
103
|
+
matched = registry.job.resolve_job_ids_by_filter(
|
|
104
|
+
alias=project,
|
|
105
|
+
status=body.status,
|
|
106
|
+
component_id=body.component_id,
|
|
107
|
+
config_id=body.config_id,
|
|
108
|
+
branch_id=body.branch_id,
|
|
109
|
+
limit=body.limit if body.limit is not None else 100,
|
|
110
|
+
)
|
|
111
|
+
# resolve_job_ids_by_filter returns full job dicts; extract string IDs
|
|
112
|
+
job_ids = [str(j.get("id")) for j in matched if j.get("id")]
|
|
113
|
+
return registry.job.terminate_jobs(alias=project, job_ids=job_ids, dry_run=body.dry_run)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@router.get("/{project}/{job_id}/stream", summary="Stream job status and logs (SSE)")
|
|
117
|
+
async def stream_job(
|
|
118
|
+
project: str,
|
|
119
|
+
job_id: str,
|
|
120
|
+
poll_interval: float = 2.0,
|
|
121
|
+
log_tail_lines: int = 50,
|
|
122
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
123
|
+
) -> EventSourceResponse:
|
|
124
|
+
"""SSE stream of job status transitions and recent log events.
|
|
125
|
+
|
|
126
|
+
Emits one event per poll while the job is non-terminal; emits a final
|
|
127
|
+
``status`` event when the job reaches a terminal state, then ends.
|
|
128
|
+
"""
|
|
129
|
+
projects = registry.job.resolve_projects([project])
|
|
130
|
+
proj = projects[project]
|
|
131
|
+
|
|
132
|
+
async def gen() -> AsyncIterator[dict[str, str]]:
|
|
133
|
+
last_status: str | None = None
|
|
134
|
+
last_event_id: int | None = None
|
|
135
|
+
while True:
|
|
136
|
+
try:
|
|
137
|
+
detail = await asyncio.to_thread(
|
|
138
|
+
registry.job.get_job_detail, alias=project, job_id=job_id
|
|
139
|
+
)
|
|
140
|
+
except Exception as exc:
|
|
141
|
+
yield json_event({"error": str(exc)}, event="error")
|
|
142
|
+
return
|
|
143
|
+
current_status = str(detail.get("status") or "")
|
|
144
|
+
if current_status != last_status:
|
|
145
|
+
yield json_event({"status": current_status, "job": detail}, event="status")
|
|
146
|
+
last_status = current_status
|
|
147
|
+
|
|
148
|
+
run_id = str(detail.get("runId") or detail.get("id") or job_id)
|
|
149
|
+
try:
|
|
150
|
+
client = registry.job._client_factory(proj.stack_url, proj.token)
|
|
151
|
+
try:
|
|
152
|
+
events = await asyncio.to_thread(
|
|
153
|
+
client.fetch_job_events, run_id, limit=log_tail_lines
|
|
154
|
+
)
|
|
155
|
+
finally:
|
|
156
|
+
client.close()
|
|
157
|
+
except Exception:
|
|
158
|
+
events = []
|
|
159
|
+
for ev in events or []:
|
|
160
|
+
ev_id_raw = ev.get("id")
|
|
161
|
+
try:
|
|
162
|
+
ev_id = int(ev_id_raw) if ev_id_raw is not None else None
|
|
163
|
+
except (TypeError, ValueError):
|
|
164
|
+
ev_id = None
|
|
165
|
+
if ev_id is None or last_event_id is None or ev_id > last_event_id:
|
|
166
|
+
yield json_event(ev, event="log")
|
|
167
|
+
if ev_id is not None and (last_event_id is None or ev_id > last_event_id):
|
|
168
|
+
last_event_id = ev_id
|
|
169
|
+
|
|
170
|
+
if current_status in {"success", "error", "warning", "terminated", "cancelled"}:
|
|
171
|
+
yield json_event({"final": current_status, "job": detail}, event="done")
|
|
172
|
+
return
|
|
173
|
+
await asyncio.sleep(poll_interval)
|
|
174
|
+
|
|
175
|
+
return EventSourceResponse(gen())
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Kai (Keboola AI) chat endpoints."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from fastapi import APIRouter, Depends
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
|
|
10
|
+
from ..dependencies import ServiceRegistry, get_registry
|
|
11
|
+
|
|
12
|
+
router = APIRouter(prefix="/kai", tags=["kai"])
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class KaiMessage(BaseModel):
|
|
16
|
+
message: str
|
|
17
|
+
chat_id: str | None = None
|
|
18
|
+
project: str | None = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@router.get("/ping", summary="Kai liveness probe")
|
|
22
|
+
def ping(
|
|
23
|
+
project: str | None = None, registry: ServiceRegistry = Depends(get_registry)
|
|
24
|
+
) -> dict[str, Any]:
|
|
25
|
+
"""Quick reachability check against the Kai endpoint for `project`."""
|
|
26
|
+
return registry.kai.ping(registry.kai.resolve_alias(project))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@router.get("/preflight", summary="Inspect Kai readiness")
|
|
30
|
+
def preflight(
|
|
31
|
+
project: str | None = None, registry: ServiceRegistry = Depends(get_registry)
|
|
32
|
+
) -> dict[str, Any]:
|
|
33
|
+
"""Inspect the configured token's Kai readiness without raising.
|
|
34
|
+
|
|
35
|
+
Used by the UI to render a single, informative warning ('use the master
|
|
36
|
+
"owner" token + enable AI Agent Chat') instead of letting /ping or /chat
|
|
37
|
+
blow up with KAI_NOT_ENABLED on every interaction.
|
|
38
|
+
"""
|
|
39
|
+
return registry.kai.preflight(registry.kai.resolve_alias(project))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@router.post("/ask", summary="Single-shot Kai question")
|
|
43
|
+
def ask(body: KaiMessage, registry: ServiceRegistry = Depends(get_registry)) -> dict[str, Any]:
|
|
44
|
+
"""Stateless one-off question (no chat history). Mirrors `kbagent kai ask`."""
|
|
45
|
+
return registry.kai.ask(alias=registry.kai.resolve_alias(body.project), message=body.message)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@router.post("/chat", summary="Continue a Kai chat")
|
|
49
|
+
def chat(body: KaiMessage, registry: ServiceRegistry = Depends(get_registry)) -> dict[str, Any]:
|
|
50
|
+
"""Send a message in a stateful chat (omit `chat_id` to start a new one).
|
|
51
|
+
Mirrors `kbagent kai chat`.
|
|
52
|
+
"""
|
|
53
|
+
return registry.kai.chat_message(
|
|
54
|
+
alias=registry.kai.resolve_alias(body.project),
|
|
55
|
+
message=body.message,
|
|
56
|
+
chat_id=body.chat_id,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@router.get("/history", summary="List recent Kai chats")
|
|
61
|
+
def history(
|
|
62
|
+
project: str | None = None,
|
|
63
|
+
limit: int = 10,
|
|
64
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
65
|
+
) -> dict[str, Any]:
|
|
66
|
+
"""List the most recent `limit` chats for `project`. Mirrors
|
|
67
|
+
`kbagent kai history`.
|
|
68
|
+
"""
|
|
69
|
+
return registry.kai.get_history(alias=registry.kai.resolve_alias(project), limit=limit)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@router.get("/chat/{chat_id}", summary="Replay one chat")
|
|
73
|
+
def chat_detail(
|
|
74
|
+
chat_id: str,
|
|
75
|
+
project: str | None = None,
|
|
76
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
77
|
+
) -> dict[str, Any]:
|
|
78
|
+
"""Fetch the full message history for one chat (used to restore a
|
|
79
|
+
conversation after the user navigates away and back)."""
|
|
80
|
+
return registry.kai.get_chat_detail(alias=registry.kai.resolve_alias(project), chat_id=chat_id)
|