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,243 @@
|
|
|
1
|
+
"""Interactive REPL mode for kbagent.
|
|
2
|
+
|
|
3
|
+
Provides a prompt-toolkit based interactive shell where users and agents
|
|
4
|
+
can run kbagent commands without restarting the process. Supports tab
|
|
5
|
+
completion, persistent history, and colored output.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import contextlib
|
|
9
|
+
import os
|
|
10
|
+
import shlex
|
|
11
|
+
import sys
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import TypeGuard
|
|
14
|
+
|
|
15
|
+
import click
|
|
16
|
+
import platformdirs
|
|
17
|
+
import typer
|
|
18
|
+
from prompt_toolkit import PromptSession
|
|
19
|
+
from prompt_toolkit.completion import Completer, Completion
|
|
20
|
+
from prompt_toolkit.formatted_text import HTML
|
|
21
|
+
from prompt_toolkit.history import FileHistory
|
|
22
|
+
|
|
23
|
+
from .. import __version__
|
|
24
|
+
from ._helpers import get_formatter
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _is_group(cmd: object) -> TypeGuard[click.Group]:
|
|
28
|
+
"""True if ``cmd`` is a command group (duck-typed; Typer >=0.25 vendors Click)."""
|
|
29
|
+
return callable(getattr(cmd, "list_commands", None))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _build_command_tree(click_group: click.Group, prefix: str = "") -> dict:
|
|
33
|
+
"""Walk Typer/Click command tree and return a dict of command paths to help strings."""
|
|
34
|
+
tree: dict[str, str] = {}
|
|
35
|
+
try:
|
|
36
|
+
with click.Context(click_group) as ctx:
|
|
37
|
+
for name in click_group.list_commands(ctx):
|
|
38
|
+
cmd = click_group.get_command(ctx, name)
|
|
39
|
+
if cmd is None:
|
|
40
|
+
continue
|
|
41
|
+
full_name = f"{prefix}{name}"
|
|
42
|
+
help_text = cmd.get_short_help_str(limit=80)
|
|
43
|
+
tree[full_name] = help_text
|
|
44
|
+
if _is_group(cmd):
|
|
45
|
+
tree.update(_build_command_tree(cmd, f"{full_name} "))
|
|
46
|
+
except Exception as exc:
|
|
47
|
+
# Report rather than degrade silently to an empty tree.
|
|
48
|
+
sys.stderr.write(f"kbagent: failed to build REPL command tree: {exc!r}\n")
|
|
49
|
+
return tree
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class KbagentCompleter(Completer):
|
|
53
|
+
"""Tab-completion for kbagent commands inside the REPL."""
|
|
54
|
+
|
|
55
|
+
def __init__(self, command_tree: dict[str, str]) -> None:
|
|
56
|
+
self._commands = command_tree
|
|
57
|
+
|
|
58
|
+
def get_completions(self, document, complete_event):
|
|
59
|
+
text = document.text_before_cursor.strip()
|
|
60
|
+
for cmd, help_text in sorted(self._commands.items()):
|
|
61
|
+
if cmd.startswith(text):
|
|
62
|
+
# Yield the remaining part after what's already typed
|
|
63
|
+
suffix = cmd[len(text) :]
|
|
64
|
+
yield Completion(
|
|
65
|
+
suffix,
|
|
66
|
+
start_position=0,
|
|
67
|
+
display=cmd,
|
|
68
|
+
display_meta=help_text,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _get_history_path() -> Path:
|
|
73
|
+
"""Return path for persistent REPL history file.
|
|
74
|
+
|
|
75
|
+
Ensures the file exists with 0600 permissions before returning so
|
|
76
|
+
``prompt_toolkit.FileHistory`` does not create it world-readable
|
|
77
|
+
under a permissive umask. REPL command lines may include
|
|
78
|
+
``project add --token TOKEN``, so the history must not leak to
|
|
79
|
+
group/other (issue #269 sec-04).
|
|
80
|
+
"""
|
|
81
|
+
config_dir = Path(platformdirs.user_config_dir("keboola-agent-cli"))
|
|
82
|
+
config_dir.mkdir(parents=True, exist_ok=True)
|
|
83
|
+
history_path = config_dir / "repl_history"
|
|
84
|
+
if not history_path.exists():
|
|
85
|
+
# Atomic create with 0600 so FileHistory.append never widens perms.
|
|
86
|
+
fd = os.open(str(history_path), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
|
87
|
+
os.close(fd)
|
|
88
|
+
else:
|
|
89
|
+
# Pre-existing file might have been created by an older kbagent
|
|
90
|
+
# under a permissive umask -- tighten perms in place. Filesystems
|
|
91
|
+
# that do not support chmod (e.g. some network mounts) silently no-op.
|
|
92
|
+
with contextlib.suppress(OSError):
|
|
93
|
+
history_path.chmod(0o600)
|
|
94
|
+
return history_path
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _run_repl(
|
|
98
|
+
json_mode: bool,
|
|
99
|
+
verbose: bool,
|
|
100
|
+
no_color: bool,
|
|
101
|
+
config_dir: str | None,
|
|
102
|
+
deny_writes: bool = False,
|
|
103
|
+
deny_destructive: bool = False,
|
|
104
|
+
allow_env_manage_token: bool = False,
|
|
105
|
+
) -> None:
|
|
106
|
+
"""Main REPL loop.
|
|
107
|
+
|
|
108
|
+
Global flags from the outer invocation are re-applied on every command
|
|
109
|
+
executed inside the REPL. This includes the session-only firewall flags
|
|
110
|
+
``--deny-writes`` / ``--deny-destructive`` -- dropping them here would
|
|
111
|
+
silently elevate the REPL above the policy the user started it with --
|
|
112
|
+
and ``--allow-env-manage-token``, which would otherwise force re-prompts
|
|
113
|
+
on every nested ``org setup`` / ``project refresh`` / ``data-app
|
|
114
|
+
password`` even after the user opted in at the outer invocation.
|
|
115
|
+
"""
|
|
116
|
+
from ..cli import app as typer_app
|
|
117
|
+
|
|
118
|
+
# Build command tree for completion
|
|
119
|
+
click_app = typer.main.get_command(typer_app)
|
|
120
|
+
command_tree = _build_command_tree(click_app) if _is_group(click_app) else {}
|
|
121
|
+
|
|
122
|
+
# Add REPL-specific commands
|
|
123
|
+
command_tree["help"] = "Show available commands"
|
|
124
|
+
command_tree["exit"] = "Exit the REPL"
|
|
125
|
+
command_tree["quit"] = "Exit the REPL"
|
|
126
|
+
|
|
127
|
+
completer = KbagentCompleter(command_tree)
|
|
128
|
+
history = FileHistory(str(_get_history_path()))
|
|
129
|
+
|
|
130
|
+
session: PromptSession = PromptSession(
|
|
131
|
+
history=history,
|
|
132
|
+
completer=completer,
|
|
133
|
+
complete_while_typing=False,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
prompt_text = HTML("<ansiblue><b>kbagent</b></ansiblue><ansigray> > </ansigray>")
|
|
137
|
+
|
|
138
|
+
# Show banner
|
|
139
|
+
sys.stderr.write(f"\nkbagent v{__version__} -- interactive mode\n")
|
|
140
|
+
sys.stderr.write("Type 'help' for commands, 'exit' to quit.\n")
|
|
141
|
+
sys.stderr.write(f"Global flags: --json={json_mode}, --verbose={verbose}\n")
|
|
142
|
+
if deny_writes or deny_destructive:
|
|
143
|
+
active = []
|
|
144
|
+
if deny_writes:
|
|
145
|
+
active.append("--deny-writes")
|
|
146
|
+
if deny_destructive:
|
|
147
|
+
active.append("--deny-destructive")
|
|
148
|
+
sys.stderr.write(f"Session firewall: {' '.join(active)} (active for all commands)\n")
|
|
149
|
+
sys.stderr.write("\n")
|
|
150
|
+
|
|
151
|
+
while True:
|
|
152
|
+
try:
|
|
153
|
+
line = session.prompt(prompt_text).strip()
|
|
154
|
+
except (EOFError, KeyboardInterrupt):
|
|
155
|
+
sys.stderr.write("\n")
|
|
156
|
+
break
|
|
157
|
+
|
|
158
|
+
if not line:
|
|
159
|
+
continue
|
|
160
|
+
|
|
161
|
+
if line in ("exit", "quit", "q"):
|
|
162
|
+
break
|
|
163
|
+
|
|
164
|
+
if line == "help":
|
|
165
|
+
# Print command tree
|
|
166
|
+
sys.stderr.write("\nAvailable commands:\n")
|
|
167
|
+
# Show only top-level and second-level commands
|
|
168
|
+
shown = set()
|
|
169
|
+
for cmd, help_text in sorted(command_tree.items()):
|
|
170
|
+
parts = cmd.split()
|
|
171
|
+
if len(parts) <= 2 and cmd not in ("help", "exit", "quit"):
|
|
172
|
+
sys.stderr.write(f" {cmd:<35} {help_text}\n")
|
|
173
|
+
shown.add(cmd)
|
|
174
|
+
sys.stderr.write(f"\n {'help':<35} Show this help\n")
|
|
175
|
+
sys.stderr.write(f" {'exit':<35} Exit the REPL\n")
|
|
176
|
+
sys.stderr.write("\n")
|
|
177
|
+
continue
|
|
178
|
+
|
|
179
|
+
# Parse the input line into argv
|
|
180
|
+
try:
|
|
181
|
+
argv = shlex.split(line)
|
|
182
|
+
except ValueError as exc:
|
|
183
|
+
sys.stderr.write(f"Parse error: {exc}\n")
|
|
184
|
+
continue
|
|
185
|
+
|
|
186
|
+
# Build full argv with global flags
|
|
187
|
+
full_argv = []
|
|
188
|
+
if json_mode and "--json" not in argv and "-j" not in argv:
|
|
189
|
+
full_argv.append("--json")
|
|
190
|
+
if verbose and "--verbose" not in argv and "-v" not in argv:
|
|
191
|
+
full_argv.append("--verbose")
|
|
192
|
+
if no_color and "--no-color" not in argv:
|
|
193
|
+
full_argv.append("--no-color")
|
|
194
|
+
if config_dir and "--config-dir" not in argv:
|
|
195
|
+
full_argv.extend(["--config-dir", config_dir])
|
|
196
|
+
# Session firewall flags: re-applied on every REPL invocation so the
|
|
197
|
+
# user's opt-in policy survives across prompts. Forgetting to forward
|
|
198
|
+
# these would silently restore write/destructive access inside the REPL.
|
|
199
|
+
if deny_writes and "--deny-writes" not in argv:
|
|
200
|
+
full_argv.append("--deny-writes")
|
|
201
|
+
if deny_destructive and "--deny-destructive" not in argv:
|
|
202
|
+
full_argv.append("--deny-destructive")
|
|
203
|
+
if allow_env_manage_token and "--allow-env-manage-token" not in argv:
|
|
204
|
+
full_argv.append("--allow-env-manage-token")
|
|
205
|
+
full_argv.extend(argv)
|
|
206
|
+
|
|
207
|
+
# Prevent recursive REPL
|
|
208
|
+
if full_argv and full_argv[-1] == "repl":
|
|
209
|
+
sys.stderr.write("Already in REPL mode.\n")
|
|
210
|
+
continue
|
|
211
|
+
|
|
212
|
+
# Execute the command via Typer
|
|
213
|
+
try:
|
|
214
|
+
click_app = typer.main.get_command(typer_app)
|
|
215
|
+
click_app(full_argv, standalone_mode=False)
|
|
216
|
+
except SystemExit:
|
|
217
|
+
pass # Typer/Click raises SystemExit on --help or errors
|
|
218
|
+
except Exception as exc:
|
|
219
|
+
sys.stderr.write(f"Error: {exc}\n")
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def repl_command(ctx: typer.Context) -> None:
|
|
223
|
+
"""Start interactive REPL mode for kbagent."""
|
|
224
|
+
formatter = get_formatter(ctx)
|
|
225
|
+
|
|
226
|
+
if formatter.json_mode:
|
|
227
|
+
# In JSON mode, REPL doesn't make sense -- output instructions
|
|
228
|
+
formatter.output(
|
|
229
|
+
{
|
|
230
|
+
"message": "REPL mode is for interactive use. Run 'kbagent repl' without --json.",
|
|
231
|
+
}
|
|
232
|
+
)
|
|
233
|
+
return
|
|
234
|
+
|
|
235
|
+
_run_repl(
|
|
236
|
+
json_mode=ctx.obj.get("json_output", False),
|
|
237
|
+
verbose=ctx.obj.get("verbose", False),
|
|
238
|
+
no_color=ctx.obj.get("no_color", False),
|
|
239
|
+
config_dir=None, # Already resolved in ctx
|
|
240
|
+
deny_writes=ctx.obj.get("deny_writes", False),
|
|
241
|
+
deny_destructive=ctx.obj.get("deny_destructive", False),
|
|
242
|
+
allow_env_manage_token=ctx.obj.get("allow_env_manage_token", False),
|
|
243
|
+
)
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
"""Schedule discovery and audit commands.
|
|
2
|
+
|
|
3
|
+
Thin CLI layer over :class:`ScheduleService`. Three subcommands:
|
|
4
|
+
|
|
5
|
+
- ``schedule list`` — fleet-wide listing of ``keboola.scheduler`` configs.
|
|
6
|
+
- ``schedule detail`` — single-schedule detail plus parent metadata.
|
|
7
|
+
- ``schedule find`` — audit filters (cron-window, not-run-since).
|
|
8
|
+
|
|
9
|
+
All three are read-only and safe under ``--deny-writes``.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import logging
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
import typer
|
|
18
|
+
from rich.markup import escape
|
|
19
|
+
from rich.table import Table
|
|
20
|
+
|
|
21
|
+
from ..errors import ConfigError, ErrorCode, KeboolaApiError
|
|
22
|
+
from ._helpers import (
|
|
23
|
+
check_cli_permission,
|
|
24
|
+
get_formatter,
|
|
25
|
+
get_service,
|
|
26
|
+
map_error_to_exit_code,
|
|
27
|
+
resolve_branch,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
logger = logging.getLogger(__name__)
|
|
31
|
+
|
|
32
|
+
schedule_app = typer.Typer(
|
|
33
|
+
help="Discover and audit cron schedules across projects (keboola.scheduler)"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@schedule_app.callback(invoke_without_command=True)
|
|
38
|
+
def _schedule_permission_check(ctx: typer.Context) -> None:
|
|
39
|
+
check_cli_permission(ctx, "schedule")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# ---------------------------------------------------------------------------
|
|
43
|
+
# Shared helpers
|
|
44
|
+
# ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _enabled_badge(enabled: bool) -> str:
|
|
48
|
+
return "[green]yes[/green]" if enabled else "[yellow]no[/yellow]"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _format_schedule_table(
|
|
52
|
+
formatter: Any,
|
|
53
|
+
schedules: list[dict[str, Any]],
|
|
54
|
+
*,
|
|
55
|
+
extra_columns: list[str] | None = None,
|
|
56
|
+
) -> None:
|
|
57
|
+
"""Render a schedule list as a Rich table.
|
|
58
|
+
|
|
59
|
+
``extra_columns`` optionally appends columns like ``last_run_at`` that
|
|
60
|
+
only exist on the ``find`` output, keeping the base list view compact.
|
|
61
|
+
"""
|
|
62
|
+
extra_columns = extra_columns or []
|
|
63
|
+
|
|
64
|
+
columns = [
|
|
65
|
+
"Project",
|
|
66
|
+
"Schedule",
|
|
67
|
+
"Parent",
|
|
68
|
+
"Component",
|
|
69
|
+
"Cron",
|
|
70
|
+
"TZ",
|
|
71
|
+
"Enabled",
|
|
72
|
+
]
|
|
73
|
+
columns.extend(extra_columns)
|
|
74
|
+
|
|
75
|
+
tbl = Table(*columns, show_header=True, header_style="bold cyan")
|
|
76
|
+
for s in schedules:
|
|
77
|
+
row = [
|
|
78
|
+
escape(s.get("project_alias", "")),
|
|
79
|
+
escape(s.get("schedule_name", "") or s.get("schedule_id", "")),
|
|
80
|
+
escape(s.get("parent_name", "") or s.get("parent_config_id", "")),
|
|
81
|
+
escape(s.get("parent_component_id", "")),
|
|
82
|
+
escape(s.get("cron", "")),
|
|
83
|
+
escape(s.get("timezone", "")),
|
|
84
|
+
_enabled_badge(bool(s.get("enabled", False))),
|
|
85
|
+
]
|
|
86
|
+
for col in extra_columns:
|
|
87
|
+
if col == "Last run":
|
|
88
|
+
row.append(escape(s.get("last_run_at") or "never"))
|
|
89
|
+
elif col == "In window":
|
|
90
|
+
match = s.get("matches_cron_window")
|
|
91
|
+
# ``None`` means the filter was never evaluated -- render
|
|
92
|
+
# as a neutral dash instead of a green "yes" to avoid
|
|
93
|
+
# visually implying an affirmative match.
|
|
94
|
+
if match is None:
|
|
95
|
+
row.append("[dim]—[/dim]")
|
|
96
|
+
else:
|
|
97
|
+
row.append(_enabled_badge(bool(match)))
|
|
98
|
+
else:
|
|
99
|
+
row.append("")
|
|
100
|
+
tbl.add_row(*row)
|
|
101
|
+
formatter.console.print(tbl)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _emit_errors(formatter: Any, errors: list[dict[str, Any]]) -> None:
|
|
105
|
+
for err in errors:
|
|
106
|
+
formatter.warning(
|
|
107
|
+
f"Project '{err.get('project_alias', '?')}': {err.get('message', 'error')}"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
# ---------------------------------------------------------------------------
|
|
112
|
+
# schedule list
|
|
113
|
+
# ---------------------------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@schedule_app.command("list")
|
|
117
|
+
def schedule_list(
|
|
118
|
+
ctx: typer.Context,
|
|
119
|
+
project: list[str] | None = typer.Option(
|
|
120
|
+
None,
|
|
121
|
+
"--project",
|
|
122
|
+
help="Project alias (repeatable; omit for all registered projects)",
|
|
123
|
+
),
|
|
124
|
+
enabled_only: bool = typer.Option(
|
|
125
|
+
False,
|
|
126
|
+
"--enabled-only",
|
|
127
|
+
help="Only show schedules whose state is 'enabled'",
|
|
128
|
+
),
|
|
129
|
+
branch: int | None = typer.Option(
|
|
130
|
+
None, "--branch", help="Dev branch ID (requires single --project)"
|
|
131
|
+
),
|
|
132
|
+
) -> None:
|
|
133
|
+
"""List cron schedules (keboola.scheduler configs) across projects.
|
|
134
|
+
|
|
135
|
+
Each row shows: project alias, schedule ID + name, parent component ID
|
|
136
|
+
and config name, cron expression, timezone, and enabled state.
|
|
137
|
+
"""
|
|
138
|
+
formatter = get_formatter(ctx)
|
|
139
|
+
service = get_service(ctx, "schedule_service")
|
|
140
|
+
config_store = ctx.obj["config_store"]
|
|
141
|
+
|
|
142
|
+
if branch is not None and (not project or len(project) != 1):
|
|
143
|
+
formatter.error(
|
|
144
|
+
message="--branch requires exactly one --project",
|
|
145
|
+
error_code=ErrorCode.INVALID_ARGUMENT,
|
|
146
|
+
)
|
|
147
|
+
raise typer.Exit(code=2) from None
|
|
148
|
+
|
|
149
|
+
effective_branch: int | None = branch
|
|
150
|
+
if branch is None and project and len(project) == 1:
|
|
151
|
+
_, effective_branch = resolve_branch(config_store, formatter, project[0], None)
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
result = service.list_schedules(
|
|
155
|
+
aliases=project,
|
|
156
|
+
enabled_only=enabled_only,
|
|
157
|
+
branch_id=effective_branch,
|
|
158
|
+
)
|
|
159
|
+
except ConfigError as exc:
|
|
160
|
+
formatter.error(message=exc.message, error_code=ErrorCode.CONFIG_ERROR)
|
|
161
|
+
raise typer.Exit(code=5) from None
|
|
162
|
+
|
|
163
|
+
if formatter.json_mode:
|
|
164
|
+
formatter.output(result)
|
|
165
|
+
return
|
|
166
|
+
|
|
167
|
+
schedules = result.get("schedules", [])
|
|
168
|
+
if not schedules:
|
|
169
|
+
formatter.console.print("[dim]No schedules found.[/dim]")
|
|
170
|
+
else:
|
|
171
|
+
_format_schedule_table(formatter, schedules)
|
|
172
|
+
_emit_errors(formatter, result.get("errors", []))
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
# ---------------------------------------------------------------------------
|
|
176
|
+
# schedule detail
|
|
177
|
+
# ---------------------------------------------------------------------------
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@schedule_app.command("detail")
|
|
181
|
+
def schedule_detail(
|
|
182
|
+
ctx: typer.Context,
|
|
183
|
+
project: str = typer.Option(..., "--project", help="Project alias"),
|
|
184
|
+
schedule_id: str = typer.Option(
|
|
185
|
+
..., "--schedule-id", help="keboola.scheduler configuration ID"
|
|
186
|
+
),
|
|
187
|
+
branch: int | None = typer.Option(None, "--branch", help="Dev branch ID"),
|
|
188
|
+
) -> None:
|
|
189
|
+
"""Show full detail for a single cron schedule.
|
|
190
|
+
|
|
191
|
+
Returns the cron expression, timezone, enabled state, and the parent
|
|
192
|
+
configuration the schedule targets (component_id + config_id + name).
|
|
193
|
+
"""
|
|
194
|
+
formatter = get_formatter(ctx)
|
|
195
|
+
service = get_service(ctx, "schedule_service")
|
|
196
|
+
config_store = ctx.obj["config_store"]
|
|
197
|
+
_, effective_branch = resolve_branch(config_store, formatter, project, branch)
|
|
198
|
+
|
|
199
|
+
try:
|
|
200
|
+
result = service.get_schedule_detail(
|
|
201
|
+
alias=project,
|
|
202
|
+
schedule_id=schedule_id,
|
|
203
|
+
branch_id=effective_branch,
|
|
204
|
+
)
|
|
205
|
+
except ConfigError as exc:
|
|
206
|
+
formatter.error(message=exc.message, error_code=ErrorCode.CONFIG_ERROR)
|
|
207
|
+
raise typer.Exit(code=5) from None
|
|
208
|
+
except KeboolaApiError as exc:
|
|
209
|
+
formatter.error(message=exc.message, error_code=exc.error_code, retryable=exc.retryable)
|
|
210
|
+
raise typer.Exit(code=map_error_to_exit_code(exc)) from None
|
|
211
|
+
|
|
212
|
+
if formatter.json_mode:
|
|
213
|
+
formatter.output(result)
|
|
214
|
+
return
|
|
215
|
+
|
|
216
|
+
formatter.console.print(
|
|
217
|
+
f"\n[bold]{escape(result.get('schedule_name', ''))}[/bold] "
|
|
218
|
+
f"[dim](ID: {escape(result.get('schedule_id', ''))})[/dim]"
|
|
219
|
+
)
|
|
220
|
+
formatter.console.print(f" Project: {escape(result.get('project_alias', ''))}")
|
|
221
|
+
if result.get("branch_id") is not None:
|
|
222
|
+
formatter.console.print(f" Branch: {result['branch_id']}")
|
|
223
|
+
formatter.console.print(
|
|
224
|
+
f" Parent: {escape(result.get('parent_name', '') or '(unknown)')} "
|
|
225
|
+
f"[dim]{escape(result.get('parent_component_id', ''))}/"
|
|
226
|
+
f"{escape(result.get('parent_config_id', ''))}[/dim]"
|
|
227
|
+
)
|
|
228
|
+
formatter.console.print(f" Cron: {escape(result.get('cron', ''))}")
|
|
229
|
+
formatter.console.print(f" Timezone: {escape(result.get('timezone', ''))}")
|
|
230
|
+
formatter.console.print(f" Enabled: {_enabled_badge(bool(result.get('enabled', False)))}")
|
|
231
|
+
if result.get("created"):
|
|
232
|
+
formatter.console.print(f" Created: {escape(result['created'])}")
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
# ---------------------------------------------------------------------------
|
|
236
|
+
# schedule find
|
|
237
|
+
# ---------------------------------------------------------------------------
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
@schedule_app.command("find")
|
|
241
|
+
def schedule_find(
|
|
242
|
+
ctx: typer.Context,
|
|
243
|
+
project: list[str] | None = typer.Option(
|
|
244
|
+
None,
|
|
245
|
+
"--project",
|
|
246
|
+
help="Project alias (repeatable; omit for all projects)",
|
|
247
|
+
),
|
|
248
|
+
cron_window: str | None = typer.Option(
|
|
249
|
+
None,
|
|
250
|
+
"--cron-window",
|
|
251
|
+
help="Only include schedules firing entirely inside an hour window, "
|
|
252
|
+
"e.g. '02:00-04:00'. Hour-field approximation -- see gotchas.md.",
|
|
253
|
+
),
|
|
254
|
+
not_run_since: int | None = typer.Option(
|
|
255
|
+
None,
|
|
256
|
+
"--not-run-since",
|
|
257
|
+
help="Only include schedules whose parent config has not produced "
|
|
258
|
+
"a job in the last N days (or never ran). Pass 0 to force the "
|
|
259
|
+
"last_run_at lookup for every row without applying a staleness "
|
|
260
|
+
"filter. NOTE: Queue API is not branch-aware -- combining with "
|
|
261
|
+
"--branch still compares against production jobs.",
|
|
262
|
+
),
|
|
263
|
+
branch: int | None = typer.Option(
|
|
264
|
+
None, "--branch", help="Dev branch ID (requires single --project)"
|
|
265
|
+
),
|
|
266
|
+
) -> None:
|
|
267
|
+
"""Audit schedules by cron window or job-freshness.
|
|
268
|
+
|
|
269
|
+
The ``last_run_at`` and ``matches_cron_window`` columns are always
|
|
270
|
+
present in the output but populated only when the corresponding
|
|
271
|
+
filter is active. Without filters both stay ``None`` -- this avoids
|
|
272
|
+
N extra Queue API calls per project purely to populate columns the
|
|
273
|
+
caller did not ask for. Pass ``--not-run-since 0`` to force the
|
|
274
|
+
``last_run_at`` lookup for every row (N extra API calls per
|
|
275
|
+
project).
|
|
276
|
+
|
|
277
|
+
``--not-run-since`` resolves jobs via the Queue API, which has no
|
|
278
|
+
branch-awareness: ``schedule find --branch <DEV> --not-run-since N``
|
|
279
|
+
still compares against production jobs. Expect false-positive
|
|
280
|
+
"stale" matches for dev branches with freshly-deployed configs.
|
|
281
|
+
|
|
282
|
+
\b
|
|
283
|
+
Examples:
|
|
284
|
+
# Schedules that fire between 02:00 and 04:00 across every project
|
|
285
|
+
kbagent --json schedule find --cron-window "02:00-04:00"
|
|
286
|
+
|
|
287
|
+
# Schedules whose parent hasn't run in the last 90 days
|
|
288
|
+
kbagent --json schedule find --not-run-since 90
|
|
289
|
+
|
|
290
|
+
# Both filters combined (AND)
|
|
291
|
+
kbagent --json schedule find --cron-window "00:00-05:00" --not-run-since 30
|
|
292
|
+
|
|
293
|
+
# No filters -> same rows as 'schedule list' plus empty last_run_at +
|
|
294
|
+
# empty matches_cron_window columns (populated only when filters active)
|
|
295
|
+
kbagent --json schedule find
|
|
296
|
+
|
|
297
|
+
# Force last_run_at population on every row (no staleness filter)
|
|
298
|
+
kbagent --json schedule find --not-run-since 0
|
|
299
|
+
"""
|
|
300
|
+
formatter = get_formatter(ctx)
|
|
301
|
+
service = get_service(ctx, "schedule_service")
|
|
302
|
+
config_store = ctx.obj["config_store"]
|
|
303
|
+
|
|
304
|
+
if branch is not None and (not project or len(project) != 1):
|
|
305
|
+
formatter.error(
|
|
306
|
+
message="--branch requires exactly one --project",
|
|
307
|
+
error_code=ErrorCode.INVALID_ARGUMENT,
|
|
308
|
+
)
|
|
309
|
+
raise typer.Exit(code=2) from None
|
|
310
|
+
|
|
311
|
+
effective_branch: int | None = branch
|
|
312
|
+
if branch is None and project and len(project) == 1:
|
|
313
|
+
_, effective_branch = resolve_branch(config_store, formatter, project[0], None)
|
|
314
|
+
|
|
315
|
+
try:
|
|
316
|
+
result = service.find_schedules(
|
|
317
|
+
aliases=project,
|
|
318
|
+
cron_window=cron_window,
|
|
319
|
+
not_run_since_days=not_run_since,
|
|
320
|
+
branch_id=effective_branch,
|
|
321
|
+
)
|
|
322
|
+
except ConfigError as exc:
|
|
323
|
+
formatter.error(message=exc.message, error_code=ErrorCode.CONFIG_ERROR)
|
|
324
|
+
raise typer.Exit(code=5) from None
|
|
325
|
+
|
|
326
|
+
if formatter.json_mode:
|
|
327
|
+
formatter.output(result)
|
|
328
|
+
return
|
|
329
|
+
|
|
330
|
+
schedules = result.get("schedules", [])
|
|
331
|
+
if not schedules:
|
|
332
|
+
formatter.console.print("[dim]No schedules match the filters.[/dim]")
|
|
333
|
+
else:
|
|
334
|
+
extras: list[str] = []
|
|
335
|
+
if cron_window is not None:
|
|
336
|
+
extras.append("In window")
|
|
337
|
+
if not_run_since is not None:
|
|
338
|
+
extras.append("Last run")
|
|
339
|
+
_format_schedule_table(formatter, schedules, extra_columns=extras)
|
|
340
|
+
_emit_errors(formatter, result.get("errors", []))
|