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,395 @@
|
|
|
1
|
+
"""Data Streams (Stream API) service -- OTLP/HTTP source management.
|
|
2
|
+
|
|
3
|
+
Business logic for the ``kbagent stream`` command group. Wraps the Stream
|
|
4
|
+
control-plane API behind a layer that:
|
|
5
|
+
|
|
6
|
+
- resolves a kbagent project alias to its ``(stack_url, token)`` via
|
|
7
|
+
:class:`ConfigStore` (the alias is the only handle a caller needs);
|
|
8
|
+
- builds a :class:`StreamClient` through an injectable factory (testability);
|
|
9
|
+
- assembles a source's full picture for ``stream detail`` -- base + per-signal
|
|
10
|
+
OTLP endpoints, wire protocol, and the destination bucket/tables read from the
|
|
11
|
+
source's sinks;
|
|
12
|
+
- **masks the secret embedded in the OTLP endpoint URL by default**, revealing
|
|
13
|
+
it only when the caller explicitly opts in (``reveal=True``). The raw source
|
|
14
|
+
object echoed in ``--json`` output is sanitised the same way so the secret
|
|
15
|
+
never leaks unless revealed.
|
|
16
|
+
|
|
17
|
+
The Stream API authenticates with the per-project Storage API token, so -- unlike
|
|
18
|
+
the ``feature`` group -- no manage token is involved.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import logging
|
|
24
|
+
from collections.abc import Callable
|
|
25
|
+
from typing import Any
|
|
26
|
+
|
|
27
|
+
from ..config_store import ConfigStore
|
|
28
|
+
from ..constants import (
|
|
29
|
+
OTLP_BUCKET_PREFIX,
|
|
30
|
+
OTLP_PROTOCOL,
|
|
31
|
+
OTLP_SIGNAL_PATHS,
|
|
32
|
+
OTLP_SINK_COLUMNS,
|
|
33
|
+
OTLP_SINK_SIGNALS,
|
|
34
|
+
STREAM_DEFAULT_BRANCH,
|
|
35
|
+
)
|
|
36
|
+
from ..errors import ConfigError, ErrorCode, KeboolaApiError
|
|
37
|
+
from ..stream_client import StreamClient
|
|
38
|
+
|
|
39
|
+
logger = logging.getLogger(__name__)
|
|
40
|
+
|
|
41
|
+
StreamClientFactory = Callable[[str, str], StreamClient]
|
|
42
|
+
|
|
43
|
+
# Map an OTLP signal sub-path (v1/logs) to the short signal name (logs) used as
|
|
44
|
+
# the key in the assembled per-signal endpoint map.
|
|
45
|
+
_SIGNAL_NAMES: dict[str, str] = {path: path.split("/")[-1] for path in OTLP_SIGNAL_PATHS}
|
|
46
|
+
|
|
47
|
+
_SECRET_MASK = "***"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def default_stream_client_factory(stack_url: str, token: str) -> StreamClient:
|
|
51
|
+
"""Construct a :class:`StreamClient` bound to ``stack_url`` + ``token``."""
|
|
52
|
+
return StreamClient(stack_url=stack_url, token=token)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class StreamService:
|
|
56
|
+
"""Business logic for Data Streams sources (list / create / detail / delete)."""
|
|
57
|
+
|
|
58
|
+
def __init__(
|
|
59
|
+
self,
|
|
60
|
+
config_store: ConfigStore,
|
|
61
|
+
stream_client_factory: StreamClientFactory | None = None,
|
|
62
|
+
) -> None:
|
|
63
|
+
self._config_store = config_store
|
|
64
|
+
self._stream_client_factory = stream_client_factory or default_stream_client_factory
|
|
65
|
+
|
|
66
|
+
# ------------------------------------------------------------------
|
|
67
|
+
# Public API
|
|
68
|
+
# ------------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
def list_sources(self, *, alias: str, branch_id: str | None = None) -> dict[str, Any]:
|
|
71
|
+
"""List sources in the alias's project (default branch unless overridden)."""
|
|
72
|
+
stack_url, token = self._resolve_project(alias)
|
|
73
|
+
branch = branch_id or STREAM_DEFAULT_BRANCH
|
|
74
|
+
client = self._stream_client_factory(stack_url, token)
|
|
75
|
+
try:
|
|
76
|
+
raw = client.list_sources(branch)
|
|
77
|
+
return {
|
|
78
|
+
"alias": alias,
|
|
79
|
+
"branch_id": branch,
|
|
80
|
+
"sources": self._summarise_sources(raw),
|
|
81
|
+
}
|
|
82
|
+
finally:
|
|
83
|
+
client.close()
|
|
84
|
+
|
|
85
|
+
def create_source(
|
|
86
|
+
self,
|
|
87
|
+
*,
|
|
88
|
+
alias: str,
|
|
89
|
+
name: str,
|
|
90
|
+
source_type: str = "otlp",
|
|
91
|
+
branch_id: str | None = None,
|
|
92
|
+
if_not_exists: bool = False,
|
|
93
|
+
reveal: bool = False,
|
|
94
|
+
provision_sinks: bool = True,
|
|
95
|
+
) -> dict[str, Any]:
|
|
96
|
+
"""Create a source, poll the async task, and return its assembled detail.
|
|
97
|
+
|
|
98
|
+
For an ``otlp`` source, the three standard sinks (logs/metrics/traces)
|
|
99
|
+
are auto-provisioned so data actually lands in Storage -- the raw Stream
|
|
100
|
+
API ``POST /sources`` creates only the bare source, unlike the Keboola
|
|
101
|
+
UI. Pass ``provision_sinks=False`` to skip that and create a bare source.
|
|
102
|
+
|
|
103
|
+
With ``if_not_exists`` an existing source matching ``name`` (by name or
|
|
104
|
+
sourceId) is returned untouched with ``status="skipped"`` (its sinks are
|
|
105
|
+
also reconciled when ``provision_sinks`` so a half-set-up source heals).
|
|
106
|
+
"""
|
|
107
|
+
stack_url, token = self._resolve_project(alias)
|
|
108
|
+
branch = branch_id or STREAM_DEFAULT_BRANCH
|
|
109
|
+
client = self._stream_client_factory(stack_url, token)
|
|
110
|
+
try:
|
|
111
|
+
if if_not_exists:
|
|
112
|
+
existing = self._find_source(client, branch, name)
|
|
113
|
+
if existing is not None:
|
|
114
|
+
if provision_sinks and source_type == "otlp":
|
|
115
|
+
self._provision_otlp_sinks(client, branch, existing.get("sourceId", name))
|
|
116
|
+
detail = self._assemble_detail(client, branch, existing, reveal=reveal)
|
|
117
|
+
detail.update({"alias": alias, "status": "skipped"})
|
|
118
|
+
return detail
|
|
119
|
+
|
|
120
|
+
task = client.create_source(branch, name=name, source_type=source_type)
|
|
121
|
+
finished = client.wait_for_task(task)
|
|
122
|
+
source_id = self._task_source_id(finished) or name
|
|
123
|
+
if provision_sinks and source_type == "otlp":
|
|
124
|
+
self._provision_otlp_sinks(client, branch, source_id)
|
|
125
|
+
source = client.get_source(branch, source_id)
|
|
126
|
+
detail = self._assemble_detail(client, branch, source, reveal=reveal)
|
|
127
|
+
detail.update({"alias": alias, "status": "created"})
|
|
128
|
+
return detail
|
|
129
|
+
finally:
|
|
130
|
+
client.close()
|
|
131
|
+
|
|
132
|
+
def _provision_otlp_sinks(self, client: StreamClient, branch: str, source_id: str) -> None:
|
|
133
|
+
"""Create the standard logs/metrics/traces sinks for an OTLP source.
|
|
134
|
+
|
|
135
|
+
Idempotent: only signals without an existing sink are created, so a
|
|
136
|
+
re-run (or ``--if-not-exists`` against a half-provisioned source) heals
|
|
137
|
+
the set rather than erroring on a conflict.
|
|
138
|
+
"""
|
|
139
|
+
existing_signals: set[str] = set()
|
|
140
|
+
for sink in client.list_sinks(branch, source_id).get("sinks", []):
|
|
141
|
+
existing_signals.update(sink.get("allowedSignals") or [])
|
|
142
|
+
columns = [dict(col) for col in OTLP_SINK_COLUMNS]
|
|
143
|
+
for signal in OTLP_SINK_SIGNALS:
|
|
144
|
+
if signal in existing_signals:
|
|
145
|
+
continue
|
|
146
|
+
table_id = f"{OTLP_BUCKET_PREFIX}{source_id}.{signal}"
|
|
147
|
+
task = client.create_sink(
|
|
148
|
+
branch,
|
|
149
|
+
source_id,
|
|
150
|
+
name=signal.capitalize(),
|
|
151
|
+
table_id=table_id,
|
|
152
|
+
columns=columns,
|
|
153
|
+
allowed_signals=[signal],
|
|
154
|
+
)
|
|
155
|
+
client.wait_for_task(task)
|
|
156
|
+
|
|
157
|
+
def get_source_detail(
|
|
158
|
+
self,
|
|
159
|
+
*,
|
|
160
|
+
alias: str,
|
|
161
|
+
source_id: str | None = None,
|
|
162
|
+
name: str | None = None,
|
|
163
|
+
branch_id: str | None = None,
|
|
164
|
+
reveal: bool = False,
|
|
165
|
+
) -> dict[str, Any]:
|
|
166
|
+
"""Assemble the full picture for one source (endpoints + destination)."""
|
|
167
|
+
if not source_id and not name:
|
|
168
|
+
raise ConfigError("Provide a source id (positional) or --name.")
|
|
169
|
+
stack_url, token = self._resolve_project(alias)
|
|
170
|
+
branch = branch_id or STREAM_DEFAULT_BRANCH
|
|
171
|
+
client = self._stream_client_factory(stack_url, token)
|
|
172
|
+
try:
|
|
173
|
+
if source_id:
|
|
174
|
+
source = client.get_source(branch, source_id)
|
|
175
|
+
else:
|
|
176
|
+
source = self._find_source(client, branch, name or "")
|
|
177
|
+
if source is None:
|
|
178
|
+
raise KeboolaApiError(
|
|
179
|
+
message=f"No source named '{name}' in branch '{branch}'.",
|
|
180
|
+
error_code=ErrorCode.NOT_FOUND,
|
|
181
|
+
status_code=404,
|
|
182
|
+
)
|
|
183
|
+
detail = self._assemble_detail(client, branch, source, reveal=reveal)
|
|
184
|
+
detail["alias"] = alias
|
|
185
|
+
return detail
|
|
186
|
+
finally:
|
|
187
|
+
client.close()
|
|
188
|
+
|
|
189
|
+
def delete_source(
|
|
190
|
+
self,
|
|
191
|
+
*,
|
|
192
|
+
alias: str,
|
|
193
|
+
source_id: str,
|
|
194
|
+
branch_id: str | None = None,
|
|
195
|
+
dry_run: bool = False,
|
|
196
|
+
) -> dict[str, Any]:
|
|
197
|
+
"""Delete a source (async task polled to completion)."""
|
|
198
|
+
stack_url, token = self._resolve_project(alias)
|
|
199
|
+
branch = branch_id or STREAM_DEFAULT_BRANCH
|
|
200
|
+
if dry_run:
|
|
201
|
+
return {
|
|
202
|
+
"status": "dry_run",
|
|
203
|
+
"alias": alias,
|
|
204
|
+
"branch_id": branch,
|
|
205
|
+
"source_id": source_id,
|
|
206
|
+
}
|
|
207
|
+
client = self._stream_client_factory(stack_url, token)
|
|
208
|
+
try:
|
|
209
|
+
task = client.delete_source(branch, source_id)
|
|
210
|
+
client.wait_for_task(task)
|
|
211
|
+
return {
|
|
212
|
+
"status": "deleted",
|
|
213
|
+
"alias": alias,
|
|
214
|
+
"branch_id": branch,
|
|
215
|
+
"source_id": source_id,
|
|
216
|
+
}
|
|
217
|
+
finally:
|
|
218
|
+
client.close()
|
|
219
|
+
|
|
220
|
+
# ------------------------------------------------------------------
|
|
221
|
+
# Internal helpers
|
|
222
|
+
# ------------------------------------------------------------------
|
|
223
|
+
|
|
224
|
+
def _resolve_project(self, alias: str) -> tuple[str, str]:
|
|
225
|
+
"""Resolve ``alias`` to its ``(stack_url, token)``."""
|
|
226
|
+
project = self._config_store.get_project(alias)
|
|
227
|
+
if project is None:
|
|
228
|
+
raise ConfigError(
|
|
229
|
+
f"Project alias '{alias}' is not registered. Run `kbagent project list`."
|
|
230
|
+
)
|
|
231
|
+
return project.stack_url, project.token
|
|
232
|
+
|
|
233
|
+
@staticmethod
|
|
234
|
+
def _find_source(client: StreamClient, branch: str, needle: str) -> dict[str, Any] | None:
|
|
235
|
+
"""Return the first source matching ``needle`` by sourceId or name."""
|
|
236
|
+
raw = client.list_sources(branch)
|
|
237
|
+
for source in raw.get("sources", []):
|
|
238
|
+
if source.get("sourceId") == needle or source.get("name") == needle:
|
|
239
|
+
return source
|
|
240
|
+
return None
|
|
241
|
+
|
|
242
|
+
@staticmethod
|
|
243
|
+
def _task_source_id(task: dict[str, Any]) -> str | None:
|
|
244
|
+
"""Extract the created sourceId from a finished task's outputs."""
|
|
245
|
+
outputs = task.get("outputs")
|
|
246
|
+
if isinstance(outputs, dict):
|
|
247
|
+
source_id = outputs.get("sourceId")
|
|
248
|
+
if isinstance(source_id, str):
|
|
249
|
+
return source_id
|
|
250
|
+
return None
|
|
251
|
+
|
|
252
|
+
@staticmethod
|
|
253
|
+
def _summarise_sources(raw: dict[str, Any]) -> list[dict[str, Any]]:
|
|
254
|
+
"""Reduce a sources-list payload to the columns `stream list` shows.
|
|
255
|
+
|
|
256
|
+
The secret is never part of the list view -- only the base (secret-free)
|
|
257
|
+
endpoint is surfaced.
|
|
258
|
+
"""
|
|
259
|
+
out: list[dict[str, Any]] = []
|
|
260
|
+
for source in raw.get("sources", []):
|
|
261
|
+
otlp = source.get("otlp") or {}
|
|
262
|
+
http = source.get("http") or {}
|
|
263
|
+
out.append(
|
|
264
|
+
{
|
|
265
|
+
"source_id": source.get("sourceId", ""),
|
|
266
|
+
"name": source.get("name", ""),
|
|
267
|
+
"type": source.get("type", ""),
|
|
268
|
+
"description": source.get("description", ""),
|
|
269
|
+
# baseUrl is documented as the endpoint *without* the secret.
|
|
270
|
+
"base_endpoint": otlp.get("baseUrl", "") or http.get("url", ""),
|
|
271
|
+
}
|
|
272
|
+
)
|
|
273
|
+
return out
|
|
274
|
+
|
|
275
|
+
def _assemble_detail(
|
|
276
|
+
self,
|
|
277
|
+
client: StreamClient,
|
|
278
|
+
branch: str,
|
|
279
|
+
source: dict[str, Any],
|
|
280
|
+
*,
|
|
281
|
+
reveal: bool,
|
|
282
|
+
) -> dict[str, Any]:
|
|
283
|
+
"""Build the rich `stream detail` view for one source.
|
|
284
|
+
|
|
285
|
+
Fetches the source's sinks to surface the destination bucket/tables and
|
|
286
|
+
import conditions, computes per-signal OTLP endpoints, and masks the
|
|
287
|
+
secret (in every endpoint and in the echoed raw source) unless
|
|
288
|
+
``reveal`` is set.
|
|
289
|
+
"""
|
|
290
|
+
source_id = source.get("sourceId", "")
|
|
291
|
+
source_type = source.get("type", "")
|
|
292
|
+
otlp = source.get("otlp") or {}
|
|
293
|
+
http = source.get("http") or {}
|
|
294
|
+
secret = otlp.get("secret") or ""
|
|
295
|
+
base_endpoint = otlp.get("baseUrl", "")
|
|
296
|
+
full_endpoint = otlp.get("url", "") or http.get("url", "")
|
|
297
|
+
|
|
298
|
+
signal_endpoints: dict[str, str] = {}
|
|
299
|
+
if source_type == "otlp" and full_endpoint:
|
|
300
|
+
root = full_endpoint.rstrip("/")
|
|
301
|
+
for path, signal in _SIGNAL_NAMES.items():
|
|
302
|
+
signal_endpoints[signal] = f"{root}/{path}"
|
|
303
|
+
|
|
304
|
+
sinks_raw = client.list_sinks(branch, source_id)
|
|
305
|
+
destination = self._destination_from_sinks(sinks_raw)
|
|
306
|
+
import_conditions = self._import_conditions(source, sinks_raw)
|
|
307
|
+
|
|
308
|
+
endpoint_display = self._mask(full_endpoint, secret, reveal)
|
|
309
|
+
signal_display = {
|
|
310
|
+
signal: self._mask(url, secret, reveal) for signal, url in signal_endpoints.items()
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return {
|
|
314
|
+
"branch_id": branch,
|
|
315
|
+
"source_id": source_id,
|
|
316
|
+
"name": source.get("name", ""),
|
|
317
|
+
"type": source_type,
|
|
318
|
+
"description": source.get("description", ""),
|
|
319
|
+
"endpoint": endpoint_display,
|
|
320
|
+
"base_endpoint": base_endpoint,
|
|
321
|
+
"signal_endpoints": signal_display,
|
|
322
|
+
"protocol": OTLP_PROTOCOL if source_type == "otlp" else "",
|
|
323
|
+
"secret_revealed": reveal,
|
|
324
|
+
"destination": destination,
|
|
325
|
+
"import_conditions": import_conditions,
|
|
326
|
+
"sinks": sinks_raw.get("sinks", []),
|
|
327
|
+
# Echo the raw source for `--json` completeness, sanitised so the
|
|
328
|
+
# secret never leaks unless explicitly revealed.
|
|
329
|
+
"source": self._sanitise_source(source, secret, reveal),
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
@staticmethod
|
|
333
|
+
def _destination_from_sinks(sinks_raw: dict[str, Any]) -> dict[str, Any]:
|
|
334
|
+
"""Extract destination bucket + per-signal table ids from sinks."""
|
|
335
|
+
tables: dict[str, str] = {}
|
|
336
|
+
buckets: list[str] = []
|
|
337
|
+
for sink in sinks_raw.get("sinks", []):
|
|
338
|
+
table = sink.get("table") or {}
|
|
339
|
+
table_id = table.get("tableId", "")
|
|
340
|
+
if not table_id:
|
|
341
|
+
continue
|
|
342
|
+
signals = sink.get("allowedSignals") or []
|
|
343
|
+
key = signals[0] if signals else (sink.get("sinkId") or table_id)
|
|
344
|
+
tables[key] = table_id
|
|
345
|
+
bucket = table_id.rsplit(".", 1)[0] if "." in table_id else ""
|
|
346
|
+
if bucket and bucket not in buckets:
|
|
347
|
+
buckets.append(bucket)
|
|
348
|
+
return {
|
|
349
|
+
"bucket": buckets[0] if len(buckets) == 1 else "",
|
|
350
|
+
"buckets": buckets,
|
|
351
|
+
"tables": tables,
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
@staticmethod
|
|
355
|
+
def _import_conditions(
|
|
356
|
+
source: dict[str, Any], sinks_raw: dict[str, Any]
|
|
357
|
+
) -> dict[str, Any] | None:
|
|
358
|
+
"""Surface import/upload conditions if the API exposes them.
|
|
359
|
+
|
|
360
|
+
The Stream API manages import triggers (count / size / time) server-side
|
|
361
|
+
and does not always echo them on the source/sink objects. We return
|
|
362
|
+
whatever is present rather than inventing defaults (no silent defaults).
|
|
363
|
+
"""
|
|
364
|
+
for candidate in (source.get("import"), source.get("conditions")):
|
|
365
|
+
if isinstance(candidate, dict) and candidate:
|
|
366
|
+
return candidate
|
|
367
|
+
for sink in sinks_raw.get("sinks", []):
|
|
368
|
+
conditions = sink.get("conditions") or (sink.get("table") or {}).get("import")
|
|
369
|
+
if isinstance(conditions, dict) and conditions:
|
|
370
|
+
return conditions
|
|
371
|
+
return None
|
|
372
|
+
|
|
373
|
+
@staticmethod
|
|
374
|
+
def _mask(endpoint: str, secret: str, reveal: bool) -> str:
|
|
375
|
+
"""Mask the secret substring inside ``endpoint`` unless ``reveal``."""
|
|
376
|
+
if reveal or not endpoint or not secret:
|
|
377
|
+
return endpoint
|
|
378
|
+
return endpoint.replace(secret, _SECRET_MASK)
|
|
379
|
+
|
|
380
|
+
@classmethod
|
|
381
|
+
def _sanitise_source(cls, source: dict[str, Any], secret: str, reveal: bool) -> dict[str, Any]:
|
|
382
|
+
"""Return a copy of ``source`` with the secret masked unless revealed."""
|
|
383
|
+
if reveal:
|
|
384
|
+
return source
|
|
385
|
+
sanitised = dict(source)
|
|
386
|
+
for key in ("otlp", "http"):
|
|
387
|
+
block = sanitised.get(key)
|
|
388
|
+
if isinstance(block, dict):
|
|
389
|
+
masked = dict(block)
|
|
390
|
+
if masked.get("url"):
|
|
391
|
+
masked["url"] = cls._mask(masked["url"], secret, reveal)
|
|
392
|
+
if "secret" in masked:
|
|
393
|
+
masked["secret"] = _SECRET_MASK
|
|
394
|
+
sanitised[key] = masked
|
|
395
|
+
return sanitised
|