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