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,145 @@
|
|
|
1
|
+
"""Encrypt command -- encrypt secret values via Keboola Encryption API.
|
|
2
|
+
|
|
3
|
+
Thin CLI layer: parses arguments, calls EncryptService, formats output.
|
|
4
|
+
No business logic belongs here.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
import typer
|
|
13
|
+
|
|
14
|
+
from ..errors import ConfigError, ErrorCode, KeboolaApiError
|
|
15
|
+
from ._helpers import (
|
|
16
|
+
check_cli_permission,
|
|
17
|
+
get_formatter,
|
|
18
|
+
get_service,
|
|
19
|
+
map_error_to_exit_code,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
encrypt_app = typer.Typer(
|
|
23
|
+
help="Encrypt secret values via Keboola Encryption API (one-way, no decrypt)"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@encrypt_app.callback(invoke_without_command=True)
|
|
28
|
+
def _encrypt_permission_check(ctx: typer.Context) -> None:
|
|
29
|
+
check_cli_permission(ctx, "encrypt")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@encrypt_app.command("values")
|
|
33
|
+
def encrypt_values(
|
|
34
|
+
ctx: typer.Context,
|
|
35
|
+
project: str = typer.Option(
|
|
36
|
+
...,
|
|
37
|
+
"--project",
|
|
38
|
+
help="Project alias",
|
|
39
|
+
),
|
|
40
|
+
component_id: str = typer.Option(
|
|
41
|
+
...,
|
|
42
|
+
"--component-id",
|
|
43
|
+
help="Keboola component ID (e.g. keboola.python-transformation-v2)",
|
|
44
|
+
),
|
|
45
|
+
input_data: str = typer.Option(
|
|
46
|
+
...,
|
|
47
|
+
"--input",
|
|
48
|
+
help="JSON to encrypt: inline JSON, @file.json, or - for stdin",
|
|
49
|
+
),
|
|
50
|
+
output_file: Path | None = typer.Option(
|
|
51
|
+
None,
|
|
52
|
+
"--output-file",
|
|
53
|
+
help="Write result to file (0600 permissions) instead of stdout",
|
|
54
|
+
),
|
|
55
|
+
) -> None:
|
|
56
|
+
"""Encrypt #-prefixed secret values for a Keboola component.
|
|
57
|
+
|
|
58
|
+
Input must be a JSON object with #-prefixed keys and string values.
|
|
59
|
+
Already-encrypted values (KBC:: prefix) pass through unchanged.
|
|
60
|
+
|
|
61
|
+
The Keboola Encryption API is one-way -- there is no decrypt endpoint.
|
|
62
|
+
Encrypted values can only be used by Keboola components at runtime.
|
|
63
|
+
|
|
64
|
+
Examples:
|
|
65
|
+
kbagent encrypt values --project my-proj --component-id keboola.ex-db-snowflake --input '{"#password": "secret"}'
|
|
66
|
+
echo '{"#token": "abc"}' | kbagent encrypt values --project my-proj --component-id keboola.ex-db-snowflake --input -
|
|
67
|
+
kbagent encrypt values --project my-proj --component-id keboola.ex-db-snowflake --input @secrets.json --output-file encrypted.json
|
|
68
|
+
"""
|
|
69
|
+
formatter = get_formatter(ctx)
|
|
70
|
+
service = get_service(ctx, "encrypt_service")
|
|
71
|
+
|
|
72
|
+
# Parse input
|
|
73
|
+
try:
|
|
74
|
+
parsed = _parse_input(input_data)
|
|
75
|
+
except (json.JSONDecodeError, FileNotFoundError, ValueError) as exc:
|
|
76
|
+
formatter.error(message=str(exc), error_code=ErrorCode.INPUT_ERROR)
|
|
77
|
+
raise typer.Exit(code=2) from None
|
|
78
|
+
|
|
79
|
+
if not isinstance(parsed, dict):
|
|
80
|
+
formatter.error(
|
|
81
|
+
message="Input must be a JSON object (dict), not " + type(parsed).__name__,
|
|
82
|
+
error_code=ErrorCode.INPUT_ERROR,
|
|
83
|
+
)
|
|
84
|
+
raise typer.Exit(code=2) from None
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
result = service.encrypt(alias=project, component_id=component_id, input_data=parsed)
|
|
88
|
+
except ConfigError as exc:
|
|
89
|
+
formatter.error(message=exc.message, error_code=ErrorCode.CONFIG_ERROR)
|
|
90
|
+
raise typer.Exit(code=5) from None
|
|
91
|
+
except KeboolaApiError as exc:
|
|
92
|
+
exit_code = map_error_to_exit_code(exc)
|
|
93
|
+
formatter.error(
|
|
94
|
+
message=exc.message,
|
|
95
|
+
error_code=exc.error_code,
|
|
96
|
+
retryable=exc.retryable,
|
|
97
|
+
)
|
|
98
|
+
raise typer.Exit(code=exit_code) from None
|
|
99
|
+
|
|
100
|
+
if output_file:
|
|
101
|
+
# Atomic create with 0600 -- avoids the race window between
|
|
102
|
+
# write_text() (which uses the user's umask, often 0644) and a
|
|
103
|
+
# subsequent chmod (issue #269 sec-06). On the chmod-after-write
|
|
104
|
+
# pattern, another local user could read the encrypted secrets in
|
|
105
|
+
# the brief window before the chmod ran.
|
|
106
|
+
payload = json.dumps(result, indent=2).encode("utf-8")
|
|
107
|
+
fd = os.open(
|
|
108
|
+
str(output_file),
|
|
109
|
+
os.O_WRONLY | os.O_CREAT | os.O_TRUNC,
|
|
110
|
+
0o600,
|
|
111
|
+
)
|
|
112
|
+
try:
|
|
113
|
+
os.write(fd, payload)
|
|
114
|
+
finally:
|
|
115
|
+
os.close(fd)
|
|
116
|
+
if not formatter.json_mode:
|
|
117
|
+
formatter.console.print(f"Encrypted values written to {output_file}")
|
|
118
|
+
|
|
119
|
+
formatter.output(result, lambda c, d: c.print_json(json.dumps(d, indent=2)))
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _parse_input(raw: str) -> dict:
|
|
123
|
+
"""Parse input from inline JSON, @file, or stdin.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
raw: One of:
|
|
127
|
+
- "-" to read from stdin
|
|
128
|
+
- "@path/to/file.json" to read from a file
|
|
129
|
+
- Inline JSON string
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
Parsed JSON as a dict.
|
|
133
|
+
|
|
134
|
+
Raises:
|
|
135
|
+
json.JSONDecodeError: If JSON parsing fails.
|
|
136
|
+
FileNotFoundError: If @file does not exist.
|
|
137
|
+
"""
|
|
138
|
+
if raw == "-":
|
|
139
|
+
return json.loads(sys.stdin.read())
|
|
140
|
+
if raw.startswith("@"):
|
|
141
|
+
file_path = Path(raw[1:])
|
|
142
|
+
if not file_path.is_file():
|
|
143
|
+
raise FileNotFoundError(f"Input file not found: {file_path}")
|
|
144
|
+
return json.loads(file_path.read_text(encoding="utf-8"))
|
|
145
|
+
return json.loads(raw)
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
"""Feature-flag management commands (super-admin Manage API).
|
|
2
|
+
|
|
3
|
+
Thin CLI layer: parses arguments, calls FeatureService, formats output.
|
|
4
|
+
No business logic belongs here.
|
|
5
|
+
|
|
6
|
+
All operations require a super-admin Manage API token. It is read from an
|
|
7
|
+
interactive hidden prompt by default (never persisted, never a CLI argument);
|
|
8
|
+
pass the top-level --allow-env-manage-token to read KBC_MANAGE_API_TOKEN from
|
|
9
|
+
env (CI/CD). See ``resolve_manage_token`` for the full default-deny policy.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from typing import Any, NoReturn
|
|
15
|
+
|
|
16
|
+
import typer
|
|
17
|
+
from rich.console import Console
|
|
18
|
+
from rich.table import Table
|
|
19
|
+
|
|
20
|
+
from ..errors import ConfigError, ErrorCode, KeboolaApiError
|
|
21
|
+
from ._helpers import (
|
|
22
|
+
check_cli_permission,
|
|
23
|
+
get_formatter,
|
|
24
|
+
get_service,
|
|
25
|
+
map_error_to_exit_code,
|
|
26
|
+
resolve_manage_token,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
feature_app = typer.Typer(help="Feature flag management (requires super-admin Manage API token)")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@feature_app.callback(invoke_without_command=True)
|
|
33
|
+
def _feature_permission_check(ctx: typer.Context) -> None:
|
|
34
|
+
check_cli_permission(ctx, "feature")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _handle_errors(formatter: Any, exc: Exception) -> NoReturn:
|
|
38
|
+
"""Map a ConfigError / KeboolaApiError to a structured error + Exit."""
|
|
39
|
+
if isinstance(exc, ConfigError):
|
|
40
|
+
formatter.error(error_code=ErrorCode.CONFIG_ERROR, message=exc.message)
|
|
41
|
+
raise typer.Exit(code=5) from None
|
|
42
|
+
if isinstance(exc, KeboolaApiError):
|
|
43
|
+
exit_code = map_error_to_exit_code(exc)
|
|
44
|
+
formatter.error(error_code=exc.error_code, message=exc.message, retryable=exc.retryable)
|
|
45
|
+
raise typer.Exit(code=exit_code) from None
|
|
46
|
+
raise exc
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _format_feature_catalogue(console: Console, data: dict[str, Any]) -> None:
|
|
50
|
+
"""Render the stack feature catalogue as a Rich table."""
|
|
51
|
+
features = data.get("features") or []
|
|
52
|
+
title = f"Features on {data.get('stack_url', '')} ({len(features)} total)"
|
|
53
|
+
console.print(_feature_table(title, features))
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# Optional feature columns, in display order: (data key, header, Rich style).
|
|
57
|
+
_OPTIONAL_FEATURE_COLUMNS: tuple[tuple[str, str, str | None], ...] = (
|
|
58
|
+
("title", "Title", None),
|
|
59
|
+
("type", "Type", "dim"),
|
|
60
|
+
("description", "Description", "dim"),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _present_optional_columns(
|
|
65
|
+
features: list[dict[str, Any]],
|
|
66
|
+
) -> list[tuple[str, str, str | None]]:
|
|
67
|
+
"""Return the optional columns at least one feature actually populates.
|
|
68
|
+
|
|
69
|
+
Project/user feature arrays come back from the Manage API as bare strings
|
|
70
|
+
(normalised to name-only), so Title/Type/Description would be uniformly
|
|
71
|
+
empty -- we drop those columns rather than render dead space. The stack
|
|
72
|
+
catalogue, which carries metadata, keeps whichever columns it populates.
|
|
73
|
+
"""
|
|
74
|
+
return [
|
|
75
|
+
col
|
|
76
|
+
for col in _OPTIONAL_FEATURE_COLUMNS
|
|
77
|
+
if any(str(feat.get(col[0], "")).strip() for feat in features)
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _feature_table(title: str, features: list[dict[str, Any]]) -> Table:
|
|
82
|
+
"""Build a Rich table for a feature list, omitting empty optional columns."""
|
|
83
|
+
table = Table(title=title)
|
|
84
|
+
table.add_column("Name", style="bold cyan")
|
|
85
|
+
columns = _present_optional_columns(features)
|
|
86
|
+
for _key, header, style in columns:
|
|
87
|
+
table.add_column(header, style=style)
|
|
88
|
+
for feat in features:
|
|
89
|
+
row = [feat.get("name", ""), *(str(feat.get(key, "")) for key, _, _ in columns)]
|
|
90
|
+
table.add_row(*row)
|
|
91
|
+
return table
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _format_assigned_features(console: Console, data: dict[str, Any]) -> None:
|
|
95
|
+
"""Render features assigned to a project or user as a Rich table."""
|
|
96
|
+
features = data.get("features") or []
|
|
97
|
+
owner = (
|
|
98
|
+
f"project [cyan]{data.get('alias')}[/cyan] (id={data.get('project_id')})"
|
|
99
|
+
if "project_id" in data
|
|
100
|
+
else f"user [cyan]{data.get('email')}[/cyan]"
|
|
101
|
+
)
|
|
102
|
+
if not features:
|
|
103
|
+
console.print(f"No features assigned to {owner}.")
|
|
104
|
+
return
|
|
105
|
+
console.print(_feature_table(f"Features assigned to {owner} ({len(features)} total)", features))
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _format_write_result(console: Console, data: dict[str, Any]) -> None:
|
|
109
|
+
"""Render the outcome of an add/remove operation."""
|
|
110
|
+
status = data.get("status", "")
|
|
111
|
+
feature = data.get("feature", "")
|
|
112
|
+
target = (
|
|
113
|
+
f"project [cyan]{data.get('alias')}[/cyan] (id={data.get('project_id')})"
|
|
114
|
+
if "project_id" in data
|
|
115
|
+
else f"user [cyan]{data.get('email')}[/cyan]"
|
|
116
|
+
)
|
|
117
|
+
if status == "dry_run":
|
|
118
|
+
verb = "add to" if data.get("action") == "add" else "remove from"
|
|
119
|
+
console.print(
|
|
120
|
+
f"[bold yellow]DRY RUN[/bold yellow] would {verb} {target}: feature [bold]{feature}[/bold]"
|
|
121
|
+
)
|
|
122
|
+
elif status == "added":
|
|
123
|
+
console.print(f"[bold green]Added[/bold green] feature [bold]{feature}[/bold] to {target}.")
|
|
124
|
+
elif status == "removed":
|
|
125
|
+
console.print(f"[bold red]Removed[/bold red] feature [bold]{feature}[/bold] from {target}.")
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
# ── Stack catalogue ───────────────────────────────────────────────────
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@feature_app.command("list")
|
|
132
|
+
def feature_list(
|
|
133
|
+
ctx: typer.Context,
|
|
134
|
+
project: str = typer.Option(
|
|
135
|
+
..., "--project", "-p", help="Project alias (used to resolve the stack URL)"
|
|
136
|
+
),
|
|
137
|
+
) -> None:
|
|
138
|
+
"""List all feature flags defined on the stack."""
|
|
139
|
+
formatter = get_formatter(ctx)
|
|
140
|
+
manage_token = resolve_manage_token(allow_env=ctx.obj["allow_env_manage_token"])
|
|
141
|
+
service = get_service(ctx, "feature_service")
|
|
142
|
+
try:
|
|
143
|
+
result = service.list_stack_features(manage_token=manage_token, alias=project)
|
|
144
|
+
except (ConfigError, KeboolaApiError) as exc:
|
|
145
|
+
_handle_errors(formatter, exc)
|
|
146
|
+
formatter.output(result, _format_feature_catalogue)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
# ── Project features ──────────────────────────────────────────────────
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@feature_app.command("project-show")
|
|
153
|
+
def feature_project_show(
|
|
154
|
+
ctx: typer.Context,
|
|
155
|
+
project: str = typer.Option(..., "--project", "-p", help="Project alias"),
|
|
156
|
+
) -> None:
|
|
157
|
+
"""Show feature flags assigned to a project."""
|
|
158
|
+
formatter = get_formatter(ctx)
|
|
159
|
+
manage_token = resolve_manage_token(allow_env=ctx.obj["allow_env_manage_token"])
|
|
160
|
+
service = get_service(ctx, "feature_service")
|
|
161
|
+
try:
|
|
162
|
+
result = service.list_project_features(manage_token=manage_token, alias=project)
|
|
163
|
+
except (ConfigError, KeboolaApiError) as exc:
|
|
164
|
+
_handle_errors(formatter, exc)
|
|
165
|
+
formatter.output(result, _format_assigned_features)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@feature_app.command("project-add")
|
|
169
|
+
def feature_project_add(
|
|
170
|
+
ctx: typer.Context,
|
|
171
|
+
project: str = typer.Option(..., "--project", "-p", help="Project alias"),
|
|
172
|
+
feature: str = typer.Option(..., "--feature", "-f", help="Feature name to enable"),
|
|
173
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="Preview without making changes"),
|
|
174
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation prompt"),
|
|
175
|
+
) -> None:
|
|
176
|
+
"""Enable a feature flag on a project."""
|
|
177
|
+
formatter = get_formatter(ctx)
|
|
178
|
+
if (
|
|
179
|
+
not dry_run
|
|
180
|
+
and not formatter.json_mode
|
|
181
|
+
and not yes
|
|
182
|
+
and not typer.confirm(f"Add feature '{feature}' to project {project}?")
|
|
183
|
+
):
|
|
184
|
+
formatter.console.print("Aborted.")
|
|
185
|
+
raise typer.Exit(code=0)
|
|
186
|
+
manage_token = resolve_manage_token(allow_env=ctx.obj["allow_env_manage_token"])
|
|
187
|
+
service = get_service(ctx, "feature_service")
|
|
188
|
+
try:
|
|
189
|
+
result = service.add_project_feature(
|
|
190
|
+
manage_token=manage_token, alias=project, feature=feature, dry_run=dry_run
|
|
191
|
+
)
|
|
192
|
+
except (ConfigError, KeboolaApiError) as exc:
|
|
193
|
+
_handle_errors(formatter, exc)
|
|
194
|
+
formatter.output(result, _format_write_result)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@feature_app.command("project-remove")
|
|
198
|
+
def feature_project_remove(
|
|
199
|
+
ctx: typer.Context,
|
|
200
|
+
project: str = typer.Option(..., "--project", "-p", help="Project alias"),
|
|
201
|
+
feature: str = typer.Option(..., "--feature", "-f", help="Feature name to disable"),
|
|
202
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="Preview without making changes"),
|
|
203
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation prompt"),
|
|
204
|
+
) -> None:
|
|
205
|
+
"""Disable a feature flag on a project (destructive)."""
|
|
206
|
+
formatter = get_formatter(ctx)
|
|
207
|
+
if (
|
|
208
|
+
not dry_run
|
|
209
|
+
and not formatter.json_mode
|
|
210
|
+
and not yes
|
|
211
|
+
and not typer.confirm(
|
|
212
|
+
f"Remove feature '{feature}' from project {project}? This is destructive."
|
|
213
|
+
)
|
|
214
|
+
):
|
|
215
|
+
formatter.console.print("Aborted.")
|
|
216
|
+
raise typer.Exit(code=0)
|
|
217
|
+
manage_token = resolve_manage_token(allow_env=ctx.obj["allow_env_manage_token"])
|
|
218
|
+
service = get_service(ctx, "feature_service")
|
|
219
|
+
try:
|
|
220
|
+
result = service.remove_project_feature(
|
|
221
|
+
manage_token=manage_token, alias=project, feature=feature, dry_run=dry_run
|
|
222
|
+
)
|
|
223
|
+
except (ConfigError, KeboolaApiError) as exc:
|
|
224
|
+
_handle_errors(formatter, exc)
|
|
225
|
+
formatter.output(result, _format_write_result)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
# ── User features ─────────────────────────────────────────────────────
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
@feature_app.command("user-show")
|
|
232
|
+
def feature_user_show(
|
|
233
|
+
ctx: typer.Context,
|
|
234
|
+
project: str = typer.Option(
|
|
235
|
+
..., "--project", "-p", help="Project alias (used to resolve the stack URL)"
|
|
236
|
+
),
|
|
237
|
+
email: str = typer.Option(..., "--email", "-e", help="User email address"),
|
|
238
|
+
) -> None:
|
|
239
|
+
"""Show feature flags assigned to a user."""
|
|
240
|
+
formatter = get_formatter(ctx)
|
|
241
|
+
manage_token = resolve_manage_token(allow_env=ctx.obj["allow_env_manage_token"])
|
|
242
|
+
service = get_service(ctx, "feature_service")
|
|
243
|
+
try:
|
|
244
|
+
result = service.list_user_features(manage_token=manage_token, alias=project, email=email)
|
|
245
|
+
except (ConfigError, KeboolaApiError) as exc:
|
|
246
|
+
_handle_errors(formatter, exc)
|
|
247
|
+
formatter.output(result, _format_assigned_features)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
@feature_app.command("user-add")
|
|
251
|
+
def feature_user_add(
|
|
252
|
+
ctx: typer.Context,
|
|
253
|
+
project: str = typer.Option(
|
|
254
|
+
..., "--project", "-p", help="Project alias (used to resolve the stack URL)"
|
|
255
|
+
),
|
|
256
|
+
email: str = typer.Option(..., "--email", "-e", help="User email address"),
|
|
257
|
+
feature: str = typer.Option(..., "--feature", "-f", help="Feature name to enable"),
|
|
258
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="Preview without making changes"),
|
|
259
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation prompt"),
|
|
260
|
+
) -> None:
|
|
261
|
+
"""Enable a feature flag on a user."""
|
|
262
|
+
formatter = get_formatter(ctx)
|
|
263
|
+
if (
|
|
264
|
+
not dry_run
|
|
265
|
+
and not formatter.json_mode
|
|
266
|
+
and not yes
|
|
267
|
+
and not typer.confirm(f"Add feature '{feature}' to user {email}?")
|
|
268
|
+
):
|
|
269
|
+
formatter.console.print("Aborted.")
|
|
270
|
+
raise typer.Exit(code=0)
|
|
271
|
+
manage_token = resolve_manage_token(allow_env=ctx.obj["allow_env_manage_token"])
|
|
272
|
+
service = get_service(ctx, "feature_service")
|
|
273
|
+
try:
|
|
274
|
+
result = service.add_user_feature(
|
|
275
|
+
manage_token=manage_token, alias=project, email=email, feature=feature, dry_run=dry_run
|
|
276
|
+
)
|
|
277
|
+
except (ConfigError, KeboolaApiError) as exc:
|
|
278
|
+
_handle_errors(formatter, exc)
|
|
279
|
+
formatter.output(result, _format_write_result)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
@feature_app.command("user-remove")
|
|
283
|
+
def feature_user_remove(
|
|
284
|
+
ctx: typer.Context,
|
|
285
|
+
project: str = typer.Option(
|
|
286
|
+
..., "--project", "-p", help="Project alias (used to resolve the stack URL)"
|
|
287
|
+
),
|
|
288
|
+
email: str = typer.Option(..., "--email", "-e", help="User email address"),
|
|
289
|
+
feature: str = typer.Option(..., "--feature", "-f", help="Feature name to disable"),
|
|
290
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="Preview without making changes"),
|
|
291
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation prompt"),
|
|
292
|
+
) -> None:
|
|
293
|
+
"""Disable a feature flag on a user (destructive)."""
|
|
294
|
+
formatter = get_formatter(ctx)
|
|
295
|
+
if (
|
|
296
|
+
not dry_run
|
|
297
|
+
and not formatter.json_mode
|
|
298
|
+
and not yes
|
|
299
|
+
and not typer.confirm(f"Remove feature '{feature}' from user {email}? This is destructive.")
|
|
300
|
+
):
|
|
301
|
+
formatter.console.print("Aborted.")
|
|
302
|
+
raise typer.Exit(code=0)
|
|
303
|
+
manage_token = resolve_manage_token(allow_env=ctx.obj["allow_env_manage_token"])
|
|
304
|
+
service = get_service(ctx, "feature_service")
|
|
305
|
+
try:
|
|
306
|
+
result = service.remove_user_feature(
|
|
307
|
+
manage_token=manage_token, alias=project, email=email, feature=feature, dry_run=dry_run
|
|
308
|
+
)
|
|
309
|
+
except (ConfigError, KeboolaApiError) as exc:
|
|
310
|
+
_handle_errors(formatter, exc)
|
|
311
|
+
formatter.output(result, _format_write_result)
|