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,377 @@
|
|
|
1
|
+
"""Shared command-layer helpers to eliminate duplication across command files.
|
|
2
|
+
|
|
3
|
+
Provides common patterns used by all CLI commands:
|
|
4
|
+
- Context extraction (formatter, services)
|
|
5
|
+
- Exit code mapping for API errors
|
|
6
|
+
- Warning emission for multi-project operations
|
|
7
|
+
- Branch resolution for --branch flag
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import secrets
|
|
12
|
+
import sys
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
import typer
|
|
16
|
+
|
|
17
|
+
from ..config_store import ConfigStore
|
|
18
|
+
from ..constants import (
|
|
19
|
+
ENV_KBC_MANAGE_API_TOKEN,
|
|
20
|
+
EXIT_JOB_TIMEOUT_TERMINATED,
|
|
21
|
+
EXIT_PERMISSION_DENIED,
|
|
22
|
+
)
|
|
23
|
+
from ..errors import ErrorCode, KeboolaApiError, PermissionDeniedError
|
|
24
|
+
from ..output import OutputFormatter
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def resolve_manage_token(*, allow_env: bool = False) -> str:
|
|
28
|
+
"""Resolve the manage token from a permitted source.
|
|
29
|
+
|
|
30
|
+
Default-deny: KBC_MANAGE_API_TOKEN is ignored unless ``allow_env=True``
|
|
31
|
+
(set by the top-level ``--allow-env-manage-token`` flag). The change
|
|
32
|
+
closes the AI-exfiltration risk where any subprocess running as the
|
|
33
|
+
same user can read the env var; the new default is "human at a TTY".
|
|
34
|
+
|
|
35
|
+
Resolution order:
|
|
36
|
+
1. ``KBC_MANAGE_API_TOKEN`` env var, IF ``allow_env`` is True. Otherwise
|
|
37
|
+
a one-shot stderr warning is emitted and the env var is ignored.
|
|
38
|
+
2. Interactive prompt with hidden input (if stdin is a TTY).
|
|
39
|
+
3. Exit 2 with an actionable error naming the opt-in flag.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
allow_env: When True, restores the legacy env-var-first behaviour
|
|
43
|
+
for the current invocation. Plumbed from the top-level
|
|
44
|
+
``--allow-env-manage-token`` flag via ``ctx.obj``.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
The manage API token.
|
|
48
|
+
|
|
49
|
+
Raises:
|
|
50
|
+
typer.Exit: If no token can be resolved (exit code 2).
|
|
51
|
+
"""
|
|
52
|
+
env_token = os.environ.get(ENV_KBC_MANAGE_API_TOKEN)
|
|
53
|
+
if env_token:
|
|
54
|
+
if allow_env:
|
|
55
|
+
return env_token
|
|
56
|
+
typer.echo(
|
|
57
|
+
f"Warning: {ENV_KBC_MANAGE_API_TOKEN} found in environment "
|
|
58
|
+
"but ignored. Pass --allow-env-manage-token to opt in.",
|
|
59
|
+
err=True,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
is_tty = hasattr(sys.stdin, "isatty") and sys.stdin.isatty()
|
|
63
|
+
if is_tty:
|
|
64
|
+
return typer.prompt("Manage API token", hide_input=True)
|
|
65
|
+
|
|
66
|
+
typer.echo(
|
|
67
|
+
"Error: No manage token available. Run interactively, or pass "
|
|
68
|
+
f"--allow-env-manage-token to read {ENV_KBC_MANAGE_API_TOKEN} "
|
|
69
|
+
"from env.",
|
|
70
|
+
err=True,
|
|
71
|
+
)
|
|
72
|
+
raise typer.Exit(code=2)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def get_formatter(ctx: typer.Context) -> OutputFormatter:
|
|
76
|
+
"""Retrieve the OutputFormatter from the Typer context."""
|
|
77
|
+
return ctx.obj["formatter"]
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def get_service(ctx: typer.Context, key: str) -> Any:
|
|
81
|
+
"""Retrieve a service from the Typer context."""
|
|
82
|
+
return ctx.obj[key]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def map_error_to_exit_code(exc: KeboolaApiError) -> int:
|
|
86
|
+
"""Map a KeboolaApiError to a CLI exit code.
|
|
87
|
+
|
|
88
|
+
- INVALID_TOKEN -> 3 (authentication error)
|
|
89
|
+
- TIMEOUT / CONNECTION_ERROR / RETRY_EXHAUSTED / QUEUE_JOB_TIMEOUT -> 4
|
|
90
|
+
(network/retryable; QUEUE_JOB_TIMEOUT means local gave up AND the
|
|
91
|
+
remote-kill attempt also failed, so the job may still be running)
|
|
92
|
+
- JOB_TIMEOUT_TERMINATED -> EXIT_JOB_TIMEOUT_TERMINATED (7)
|
|
93
|
+
(local --timeout elapsed and we successfully cancelled the remote
|
|
94
|
+
job; scripts can distinguish "we killed it" from "it failed on its own")
|
|
95
|
+
- Everything else -> 1 (general error)
|
|
96
|
+
"""
|
|
97
|
+
if exc.error_code in ("INVALID_TOKEN", "MISSING_MASTER_TOKEN"):
|
|
98
|
+
return 3
|
|
99
|
+
if exc.error_code in (
|
|
100
|
+
"TIMEOUT",
|
|
101
|
+
"CONNECTION_ERROR",
|
|
102
|
+
"RETRY_EXHAUSTED",
|
|
103
|
+
"QUEUE_JOB_TIMEOUT",
|
|
104
|
+
):
|
|
105
|
+
return 4
|
|
106
|
+
if exc.error_code == "JOB_TIMEOUT_TERMINATED":
|
|
107
|
+
return EXIT_JOB_TIMEOUT_TERMINATED
|
|
108
|
+
return 1
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def emit_project_warnings(formatter: OutputFormatter, result: dict) -> None:
|
|
112
|
+
"""Emit warnings from multi-project operation results.
|
|
113
|
+
|
|
114
|
+
Iterates the 'errors' list in the result dict (if present) and prints
|
|
115
|
+
each entry as a warning via the formatter.
|
|
116
|
+
"""
|
|
117
|
+
for err in result.get("errors", []):
|
|
118
|
+
alias = err.get("project_alias", "unknown")
|
|
119
|
+
message = err.get("message", "Unknown error")
|
|
120
|
+
formatter.warning(f"Project '{alias}': {message}")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _is_help_request(ctx: typer.Context) -> bool:
|
|
124
|
+
"""Check if the current invocation is a --help request.
|
|
125
|
+
|
|
126
|
+
The group callback fires before Click parses subcommand arguments,
|
|
127
|
+
so --help for a subcommand (e.g. 'branch delete --help') is still
|
|
128
|
+
in sys.argv at this point. We allow help through even for blocked commands.
|
|
129
|
+
|
|
130
|
+
Also respects Click's resilient_parsing mode (tab completions).
|
|
131
|
+
"""
|
|
132
|
+
import sys
|
|
133
|
+
|
|
134
|
+
if "--help" in sys.argv or "-h" in sys.argv:
|
|
135
|
+
return True
|
|
136
|
+
return bool(ctx.resilient_parsing)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def check_cli_permission(ctx: typer.Context, group_name: str) -> None:
|
|
140
|
+
"""Check CLI command permissions using the active policy.
|
|
141
|
+
|
|
142
|
+
Called from sub-app callbacks. Constructs operation name as
|
|
143
|
+
'{group_name}.{subcommand}' and checks against the permission engine.
|
|
144
|
+
Always allows --help through (no API calls made).
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
ctx: Typer context (must have permission_engine in obj).
|
|
148
|
+
group_name: The sub-app name (e.g., 'branch', 'config').
|
|
149
|
+
"""
|
|
150
|
+
if _is_help_request(ctx):
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
engine = ctx.obj.get("permission_engine")
|
|
154
|
+
if engine is None or not engine.active:
|
|
155
|
+
return
|
|
156
|
+
|
|
157
|
+
subcommand = ctx.invoked_subcommand
|
|
158
|
+
if subcommand is None:
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
operation = f"{group_name}.{subcommand}"
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
engine.check_or_raise(operation)
|
|
165
|
+
except PermissionDeniedError as exc:
|
|
166
|
+
formatter = get_formatter(ctx)
|
|
167
|
+
formatter.error(message=exc.message, error_code=ErrorCode.PERMISSION_DENIED)
|
|
168
|
+
raise typer.Exit(code=EXIT_PERMISSION_DENIED) from None
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def resolve_project_alias(
|
|
172
|
+
ctx: typer.Context,
|
|
173
|
+
formatter: OutputFormatter,
|
|
174
|
+
explicit: str | None,
|
|
175
|
+
) -> str:
|
|
176
|
+
"""Resolve the effective project alias for a single-project operation.
|
|
177
|
+
|
|
178
|
+
Precedence (first match wins):
|
|
179
|
+
1. ``explicit`` (typically the CLI ``--project`` flag)
|
|
180
|
+
2. ``KBAGENT_PROJECT`` env var
|
|
181
|
+
3. Persisted pin (``config.default_project`` set by ``kbagent project use``)
|
|
182
|
+
4. Sole registered project when exactly one exists (convenience)
|
|
183
|
+
5. Exit code 5 with a CONFIG_ERROR if none of the above resolves
|
|
184
|
+
|
|
185
|
+
Use this from write/destructive command paths where implicit fan-out
|
|
186
|
+
(``resolve_projects(None)`` returning every project) would be surprising
|
|
187
|
+
or unsafe. Read paths should keep their existing fan-out behavior.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
ctx: Typer context (must contain ``project_service``).
|
|
191
|
+
formatter: Output formatter for structured error emission.
|
|
192
|
+
explicit: The value of the CLI --project flag, or None.
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
The resolved project alias (guaranteed to be registered).
|
|
196
|
+
"""
|
|
197
|
+
from ..errors import ConfigError as _ConfigError
|
|
198
|
+
|
|
199
|
+
service = get_service(ctx, "project_service")
|
|
200
|
+
try:
|
|
201
|
+
alias, _source = service.resolve_pinned_alias(explicit=explicit)
|
|
202
|
+
except _ConfigError as exc:
|
|
203
|
+
formatter.error(message=exc.message, error_code=ErrorCode.CONFIG_ERROR)
|
|
204
|
+
raise typer.Exit(code=5) from None
|
|
205
|
+
return alias
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def validate_branch_requires_project(
|
|
209
|
+
formatter: OutputFormatter,
|
|
210
|
+
branch: int | None,
|
|
211
|
+
project: str | None,
|
|
212
|
+
) -> None:
|
|
213
|
+
"""Validate that --branch is always accompanied by --project.
|
|
214
|
+
|
|
215
|
+
Raises:
|
|
216
|
+
typer.Exit: With code 2 if branch is set but project is not.
|
|
217
|
+
"""
|
|
218
|
+
if branch is not None and not project:
|
|
219
|
+
formatter.error(
|
|
220
|
+
message="--branch requires --project (branch ID is per-project)",
|
|
221
|
+
error_code=ErrorCode.INVALID_ARGUMENT,
|
|
222
|
+
)
|
|
223
|
+
raise typer.Exit(code=2) from None
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def resolve_branch(
|
|
227
|
+
config_store: ConfigStore,
|
|
228
|
+
formatter: OutputFormatter,
|
|
229
|
+
project: str | None,
|
|
230
|
+
branch: int | None,
|
|
231
|
+
*,
|
|
232
|
+
ignore_active_branch: bool = False,
|
|
233
|
+
) -> tuple[str | None, int | None]:
|
|
234
|
+
"""Resolve the effective branch and project.
|
|
235
|
+
|
|
236
|
+
Resolution order:
|
|
237
|
+
1. Explicit --branch always wins (no change)
|
|
238
|
+
2. If no --branch, check active_branch_id from config for the resolved project
|
|
239
|
+
3. If active branch found, use it and print info message in human mode
|
|
240
|
+
|
|
241
|
+
When an active branch is resolved from config, --project is also set
|
|
242
|
+
to the project alias (branch is per-project).
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
config_store: Config store for looking up project configs.
|
|
246
|
+
formatter: Output formatter for info messages.
|
|
247
|
+
project: Explicit --project alias or None.
|
|
248
|
+
branch: Explicit --branch integer or None.
|
|
249
|
+
ignore_active_branch: When True, the implicit active_branch_id from
|
|
250
|
+
config is ignored and the production endpoint (branch_id=None) is
|
|
251
|
+
used unless --branch was passed explicitly. An info message is
|
|
252
|
+
printed so the user can see the active dev branch was skipped.
|
|
253
|
+
Intended for storage READ commands -- the Storage API
|
|
254
|
+
branch-scoped endpoint returns only locally-modified resources,
|
|
255
|
+
which for a freshly created dev branch is an empty set. Explicit
|
|
256
|
+
--branch still overrides.
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
Tuple of (effective_project, effective_branch_id).
|
|
260
|
+
"""
|
|
261
|
+
if branch is not None:
|
|
262
|
+
return project, branch
|
|
263
|
+
|
|
264
|
+
if project is not None:
|
|
265
|
+
proj_config = config_store.get_project(project)
|
|
266
|
+
if proj_config and proj_config.active_branch_id is not None:
|
|
267
|
+
if ignore_active_branch:
|
|
268
|
+
if not formatter.json_mode:
|
|
269
|
+
formatter.err_console.print(
|
|
270
|
+
f"[bold blue]Info:[/bold blue] Using production branch for read "
|
|
271
|
+
f"(active dev branch '{proj_config.active_branch_id}' ignored; "
|
|
272
|
+
f"pass --branch {proj_config.active_branch_id} to override)"
|
|
273
|
+
)
|
|
274
|
+
return project, None
|
|
275
|
+
if not formatter.json_mode:
|
|
276
|
+
formatter.err_console.print(
|
|
277
|
+
f"[bold blue]Info:[/bold blue] Using active branch "
|
|
278
|
+
f"(ID: {proj_config.active_branch_id}) for project '{project}'"
|
|
279
|
+
)
|
|
280
|
+
return project, proj_config.active_branch_id
|
|
281
|
+
else:
|
|
282
|
+
config = config_store.load()
|
|
283
|
+
active_projects = [
|
|
284
|
+
(alias, proj)
|
|
285
|
+
for alias, proj in config.projects.items()
|
|
286
|
+
if proj.active_branch_id is not None
|
|
287
|
+
]
|
|
288
|
+
if len(active_projects) == 1:
|
|
289
|
+
alias, proj = active_projects[0]
|
|
290
|
+
if ignore_active_branch:
|
|
291
|
+
if not formatter.json_mode:
|
|
292
|
+
formatter.err_console.print(
|
|
293
|
+
f"[bold blue]Info:[/bold blue] Using production branch for read "
|
|
294
|
+
f"(active dev branch '{proj.active_branch_id}' on project '{alias}' "
|
|
295
|
+
f"ignored; pass --branch {proj.active_branch_id} to override)"
|
|
296
|
+
)
|
|
297
|
+
return alias, None
|
|
298
|
+
if not formatter.json_mode:
|
|
299
|
+
formatter.err_console.print(
|
|
300
|
+
f"[bold blue]Info:[/bold blue] Using active branch "
|
|
301
|
+
f"(ID: {proj.active_branch_id}) for project '{alias}'"
|
|
302
|
+
)
|
|
303
|
+
return alias, proj.active_branch_id
|
|
304
|
+
|
|
305
|
+
return project, None
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
_CONFIRM_CODE_LENGTH = 4
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def require_random_code_confirmation(action_description: str) -> None:
|
|
312
|
+
"""Require the user to type a random hex code to confirm a high-risk action.
|
|
313
|
+
|
|
314
|
+
Prevents AI agents from programmatically approving production-affecting
|
|
315
|
+
writes (Developer Portal updates, permission policy changes). The agent
|
|
316
|
+
cannot predict the code and cannot type it into stdin.
|
|
317
|
+
|
|
318
|
+
Behaviour:
|
|
319
|
+
- No TTY -> raise typer.Exit(EXIT_PERMISSION_DENIED).
|
|
320
|
+
- TTY + correct code -> return None (caller proceeds).
|
|
321
|
+
- TTY + wrong code / EOF / interrupt -> raise typer.Exit(EXIT_PERMISSION_DENIED).
|
|
322
|
+
|
|
323
|
+
Args:
|
|
324
|
+
action_description: Short verb phrase shown in the prompt
|
|
325
|
+
(e.g. "patch keboola.ex-foo", "update permission policy").
|
|
326
|
+
"""
|
|
327
|
+
is_tty = hasattr(sys.stdin, "isatty") and sys.stdin.isatty()
|
|
328
|
+
if not is_tty:
|
|
329
|
+
sys.stderr.write(
|
|
330
|
+
f"\nRefusing to {action_description}: this action requires a "
|
|
331
|
+
"real terminal so a human can type the confirmation code. "
|
|
332
|
+
"There is no --yes bypass by design.\n"
|
|
333
|
+
)
|
|
334
|
+
raise typer.Exit(code=EXIT_PERMISSION_DENIED)
|
|
335
|
+
|
|
336
|
+
code = secrets.token_hex(_CONFIRM_CODE_LENGTH)
|
|
337
|
+
sys.stderr.write(f"\nTo {action_description}, type this code: {code}\n")
|
|
338
|
+
sys.stderr.write("Confirmation: ")
|
|
339
|
+
sys.stderr.flush()
|
|
340
|
+
|
|
341
|
+
try:
|
|
342
|
+
user_input = input().strip()
|
|
343
|
+
except (EOFError, KeyboardInterrupt):
|
|
344
|
+
raise typer.Exit(code=EXIT_PERMISSION_DENIED) from None
|
|
345
|
+
|
|
346
|
+
if user_input != code:
|
|
347
|
+
sys.stderr.write("Confirmation failed. Aborting.\n")
|
|
348
|
+
raise typer.Exit(code=EXIT_PERMISSION_DENIED)
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def resolve_identity_alias(ctx: typer.Context, explicit: str | None) -> str:
|
|
352
|
+
"""Resolve the dev-portal identity alias for this invocation.
|
|
353
|
+
|
|
354
|
+
Order: explicit --identity flag > default from config > error.
|
|
355
|
+
"""
|
|
356
|
+
if explicit:
|
|
357
|
+
return explicit
|
|
358
|
+
config_store: ConfigStore = get_service(ctx, "config_store")
|
|
359
|
+
default = config_store.load().default_dev_portal_identity
|
|
360
|
+
if not default:
|
|
361
|
+
raise typer.BadParameter(
|
|
362
|
+
"No Developer Portal identity selected. Pass --identity <alias>, "
|
|
363
|
+
"or set a default via `kbagent dev-portal identity use <alias>`."
|
|
364
|
+
)
|
|
365
|
+
return default
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def get_dev_portal_service(ctx: typer.Context):
|
|
369
|
+
"""Build a DeveloperPortalService bound to the current ConfigStore."""
|
|
370
|
+
from ..dev_portal_client import DeveloperPortalClient
|
|
371
|
+
from ..services.dev_portal_service import DeveloperPortalService
|
|
372
|
+
|
|
373
|
+
config_store: ConfigStore = get_service(ctx, "config_store")
|
|
374
|
+
return DeveloperPortalService(
|
|
375
|
+
config_store=config_store,
|
|
376
|
+
client_factory=lambda identity: DeveloperPortalClient(identity),
|
|
377
|
+
)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Helper for resolving text input from --text, --file, or --stdin.
|
|
2
|
+
|
|
3
|
+
Shared by `branch metadata-set` and `project description-set`. Enforces
|
|
4
|
+
mutual exclusivity so the CLI fails fast with a clear error instead of
|
|
5
|
+
silently picking one source.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from ..errors import ConfigError
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def resolve_text_input(
|
|
17
|
+
*,
|
|
18
|
+
text: str | None,
|
|
19
|
+
file: Path | None,
|
|
20
|
+
stdin: bool,
|
|
21
|
+
) -> str:
|
|
22
|
+
"""Resolve a single string value from exactly one of three sources.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
text: Inline string value, or None.
|
|
26
|
+
file: Path to a file to read UTF-8 text from, or None.
|
|
27
|
+
stdin: If True, read the value from standard input.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
The resolved string.
|
|
31
|
+
|
|
32
|
+
Raises:
|
|
33
|
+
ConfigError: If zero or more than one source is provided, or if
|
|
34
|
+
the given file cannot be read.
|
|
35
|
+
"""
|
|
36
|
+
sources = [text is not None, file is not None, stdin]
|
|
37
|
+
provided = sum(sources)
|
|
38
|
+
if provided == 0:
|
|
39
|
+
raise ConfigError("Specify exactly one of --text, --file, or --stdin.")
|
|
40
|
+
if provided > 1:
|
|
41
|
+
raise ConfigError("--text, --file, and --stdin are mutually exclusive.")
|
|
42
|
+
|
|
43
|
+
if text is not None:
|
|
44
|
+
return text
|
|
45
|
+
if file is not None:
|
|
46
|
+
if not file.is_file():
|
|
47
|
+
raise ConfigError(f"Input file not found: {file}")
|
|
48
|
+
return file.read_text(encoding="utf-8")
|
|
49
|
+
return sys.stdin.read()
|