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.
Files changed (306) hide show
  1. keboola_agent_cli/__init__.py +34 -0
  2. keboola_agent_cli/__main__.py +5 -0
  3. keboola_agent_cli/_ui_dist/assets/arc-DhFYIddx.js +2 -0
  4. keboola_agent_cli/_ui_dist/assets/arc-DhFYIddx.js.map +1 -0
  5. keboola_agent_cli/_ui_dist/assets/architecture-7EHR7CIX-hNCijx_H.js +1 -0
  6. keboola_agent_cli/_ui_dist/assets/architectureDiagram-3BPJPVTR-C6hUlprM.js +37 -0
  7. keboola_agent_cli/_ui_dist/assets/architectureDiagram-3BPJPVTR-C6hUlprM.js.map +1 -0
  8. keboola_agent_cli/_ui_dist/assets/array-BifhSqXX.js +2 -0
  9. keboola_agent_cli/_ui_dist/assets/array-BifhSqXX.js.map +1 -0
  10. keboola_agent_cli/_ui_dist/assets/blockDiagram-GPEHLZMM-DC7qY9i4.js +133 -0
  11. keboola_agent_cli/_ui_dist/assets/blockDiagram-GPEHLZMM-DC7qY9i4.js.map +1 -0
  12. keboola_agent_cli/_ui_dist/assets/c4Diagram-AAUBKEIU-5Lh44evt.js +11 -0
  13. keboola_agent_cli/_ui_dist/assets/c4Diagram-AAUBKEIU-5Lh44evt.js.map +1 -0
  14. keboola_agent_cli/_ui_dist/assets/channel-DBMrXlxx.js +2 -0
  15. keboola_agent_cli/_ui_dist/assets/channel-DBMrXlxx.js.map +1 -0
  16. keboola_agent_cli/_ui_dist/assets/chunk-2J33WTMH-Coy82EBh.js +2 -0
  17. keboola_agent_cli/_ui_dist/assets/chunk-2J33WTMH-Coy82EBh.js.map +1 -0
  18. keboola_agent_cli/_ui_dist/assets/chunk-3OPIFGDE-BQC5CRHI.js +63 -0
  19. keboola_agent_cli/_ui_dist/assets/chunk-3OPIFGDE-BQC5CRHI.js.map +1 -0
  20. keboola_agent_cli/_ui_dist/assets/chunk-4BX2VUAB-DUuEt70o.js +2 -0
  21. keboola_agent_cli/_ui_dist/assets/chunk-4BX2VUAB-DUuEt70o.js.map +1 -0
  22. keboola_agent_cli/_ui_dist/assets/chunk-55IACEB6-BvR-6chF.js +2 -0
  23. keboola_agent_cli/_ui_dist/assets/chunk-55IACEB6-BvR-6chF.js.map +1 -0
  24. keboola_agent_cli/_ui_dist/assets/chunk-5ZQYHXKU-BjcTN7ul.js +3 -0
  25. keboola_agent_cli/_ui_dist/assets/chunk-5ZQYHXKU-BjcTN7ul.js.map +1 -0
  26. keboola_agent_cli/_ui_dist/assets/chunk-727SXJPM-C0zxqqRN.js +207 -0
  27. keboola_agent_cli/_ui_dist/assets/chunk-727SXJPM-C0zxqqRN.js.map +1 -0
  28. keboola_agent_cli/_ui_dist/assets/chunk-AQP2D5EJ-CXf7rIlZ.js +232 -0
  29. keboola_agent_cli/_ui_dist/assets/chunk-AQP2D5EJ-CXf7rIlZ.js.map +1 -0
  30. keboola_agent_cli/_ui_dist/assets/chunk-BSJP7CBP-Oj_FO9Q7.js +2 -0
  31. keboola_agent_cli/_ui_dist/assets/chunk-BSJP7CBP-Oj_FO9Q7.js.map +1 -0
  32. keboola_agent_cli/_ui_dist/assets/chunk-CSCIHK7Q-CcTsLrFc.js +124 -0
  33. keboola_agent_cli/_ui_dist/assets/chunk-CSCIHK7Q-CcTsLrFc.js.map +1 -0
  34. keboola_agent_cli/_ui_dist/assets/chunk-FMBD7UC4-FH-zLkkW.js +16 -0
  35. keboola_agent_cli/_ui_dist/assets/chunk-FMBD7UC4-FH-zLkkW.js.map +1 -0
  36. keboola_agent_cli/_ui_dist/assets/chunk-L5ZTLDWV-B1Ky_e7O.js +2 -0
  37. keboola_agent_cli/_ui_dist/assets/chunk-L5ZTLDWV-B1Ky_e7O.js.map +1 -0
  38. keboola_agent_cli/_ui_dist/assets/chunk-ND2GUHAM-BHz1rpbm.js +2 -0
  39. keboola_agent_cli/_ui_dist/assets/chunk-ND2GUHAM-BHz1rpbm.js.map +1 -0
  40. keboola_agent_cli/_ui_dist/assets/chunk-NNHCCRGN-DlpIbxXb.js +160 -0
  41. keboola_agent_cli/_ui_dist/assets/chunk-NNHCCRGN-DlpIbxXb.js.map +1 -0
  42. keboola_agent_cli/_ui_dist/assets/chunk-NZK2D7GU-tnrSoegS.js +2 -0
  43. keboola_agent_cli/_ui_dist/assets/chunk-NZK2D7GU-tnrSoegS.js.map +1 -0
  44. keboola_agent_cli/_ui_dist/assets/chunk-O5CBEL6O-DxxqDH0l.js +71 -0
  45. keboola_agent_cli/_ui_dist/assets/chunk-O5CBEL6O-DxxqDH0l.js.map +1 -0
  46. keboola_agent_cli/_ui_dist/assets/chunk-QZHKN3VN-CSjc2gjj.js +2 -0
  47. keboola_agent_cli/_ui_dist/assets/chunk-QZHKN3VN-CSjc2gjj.js.map +1 -0
  48. keboola_agent_cli/_ui_dist/assets/classDiagram-4FO5ZUOK-BuZcZu85.js +2 -0
  49. keboola_agent_cli/_ui_dist/assets/classDiagram-4FO5ZUOK-BuZcZu85.js.map +1 -0
  50. keboola_agent_cli/_ui_dist/assets/classDiagram-v2-Q7XG4LA2-BuZcZu85.js +2 -0
  51. keboola_agent_cli/_ui_dist/assets/classDiagram-v2-Q7XG4LA2-BuZcZu85.js.map +1 -0
  52. keboola_agent_cli/_ui_dist/assets/cose-bilkent-S5V4N54A-Y0L8LDMa.js +2 -0
  53. keboola_agent_cli/_ui_dist/assets/cose-bilkent-S5V4N54A-Y0L8LDMa.js.map +1 -0
  54. keboola_agent_cli/_ui_dist/assets/cytoscape.esm-C8YCVR3_.js +322 -0
  55. keboola_agent_cli/_ui_dist/assets/cytoscape.esm-C8YCVR3_.js.map +1 -0
  56. keboola_agent_cli/_ui_dist/assets/dagre-BM42HDAG-UZ-9BTqF.js +5 -0
  57. keboola_agent_cli/_ui_dist/assets/dagre-BM42HDAG-UZ-9BTqF.js.map +1 -0
  58. keboola_agent_cli/_ui_dist/assets/dagre-Bx709z4p.js +2 -0
  59. keboola_agent_cli/_ui_dist/assets/dagre-Bx709z4p.js.map +1 -0
  60. keboola_agent_cli/_ui_dist/assets/defaultLocale-C8Fc0cco.js +2 -0
  61. keboola_agent_cli/_ui_dist/assets/defaultLocale-C8Fc0cco.js.map +1 -0
  62. keboola_agent_cli/_ui_dist/assets/diagram-2AECGRRQ-DoDQ60wi.js +44 -0
  63. keboola_agent_cli/_ui_dist/assets/diagram-2AECGRRQ-DoDQ60wi.js.map +1 -0
  64. keboola_agent_cli/_ui_dist/assets/diagram-5GNKFQAL-CMGFxpUs.js +11 -0
  65. keboola_agent_cli/_ui_dist/assets/diagram-5GNKFQAL-CMGFxpUs.js.map +1 -0
  66. keboola_agent_cli/_ui_dist/assets/diagram-KO2AKTUF-1uGDa-Iu.js +4 -0
  67. keboola_agent_cli/_ui_dist/assets/diagram-KO2AKTUF-1uGDa-Iu.js.map +1 -0
  68. keboola_agent_cli/_ui_dist/assets/diagram-LMA3HP47-XtFH7B51.js +25 -0
  69. keboola_agent_cli/_ui_dist/assets/diagram-LMA3HP47-XtFH7B51.js.map +1 -0
  70. keboola_agent_cli/_ui_dist/assets/diagram-OG6HWLK6-B4_Te1T5.js +25 -0
  71. keboola_agent_cli/_ui_dist/assets/diagram-OG6HWLK6-B4_Te1T5.js.map +1 -0
  72. keboola_agent_cli/_ui_dist/assets/dist-Di6zmlv0.js +2 -0
  73. keboola_agent_cli/_ui_dist/assets/dist-Di6zmlv0.js.map +1 -0
  74. keboola_agent_cli/_ui_dist/assets/erDiagram-TEJ5UH35-NjQkrdFt.js +86 -0
  75. keboola_agent_cli/_ui_dist/assets/erDiagram-TEJ5UH35-NjQkrdFt.js.map +1 -0
  76. keboola_agent_cli/_ui_dist/assets/eventmodeling-FCH6USID-BrJMIks8.js +1 -0
  77. keboola_agent_cli/_ui_dist/assets/flowDiagram-I6XJVG4X-CIr8DWl7.js +163 -0
  78. keboola_agent_cli/_ui_dist/assets/flowDiagram-I6XJVG4X-CIr8DWl7.js.map +1 -0
  79. keboola_agent_cli/_ui_dist/assets/ganttDiagram-6RSMTGT7-C1VY_xbQ.js +293 -0
  80. keboola_agent_cli/_ui_dist/assets/ganttDiagram-6RSMTGT7-C1VY_xbQ.js.map +1 -0
  81. keboola_agent_cli/_ui_dist/assets/gitGraph-WXDBUCRP-COacYjo-.js +1 -0
  82. keboola_agent_cli/_ui_dist/assets/gitGraphDiagram-PVQCEYII-DQT8-kg2.js +107 -0
  83. keboola_agent_cli/_ui_dist/assets/gitGraphDiagram-PVQCEYII-DQT8-kg2.js.map +1 -0
  84. keboola_agent_cli/_ui_dist/assets/graphlib-B8gBHxth.js +2 -0
  85. keboola_agent_cli/_ui_dist/assets/graphlib-B8gBHxth.js.map +1 -0
  86. keboola_agent_cli/_ui_dist/assets/index-CMq50kkV.css +1 -0
  87. keboola_agent_cli/_ui_dist/assets/index-D8W97DAz.js +118 -0
  88. keboola_agent_cli/_ui_dist/assets/index-D8W97DAz.js.map +1 -0
  89. keboola_agent_cli/_ui_dist/assets/info-J43DQDTF-DdCTRIzU.js +1 -0
  90. keboola_agent_cli/_ui_dist/assets/infoDiagram-5YYISTIA-C77rsoTp.js +3 -0
  91. keboola_agent_cli/_ui_dist/assets/infoDiagram-5YYISTIA-C77rsoTp.js.map +1 -0
  92. keboola_agent_cli/_ui_dist/assets/init-D6jRqBbL.js +2 -0
  93. keboola_agent_cli/_ui_dist/assets/init-D6jRqBbL.js.map +1 -0
  94. keboola_agent_cli/_ui_dist/assets/ishikawaDiagram-YF4QCWOH-BcTbXaLy.js +71 -0
  95. keboola_agent_cli/_ui_dist/assets/ishikawaDiagram-YF4QCWOH-BcTbXaLy.js.map +1 -0
  96. keboola_agent_cli/_ui_dist/assets/journeyDiagram-JHISSGLW-BejeAJQ_.js +140 -0
  97. keboola_agent_cli/_ui_dist/assets/journeyDiagram-JHISSGLW-BejeAJQ_.js.map +1 -0
  98. keboola_agent_cli/_ui_dist/assets/kanban-definition-UN3LZRKU-BRNz_UrH.js +90 -0
  99. keboola_agent_cli/_ui_dist/assets/kanban-definition-UN3LZRKU-BRNz_UrH.js.map +1 -0
  100. keboola_agent_cli/_ui_dist/assets/katex-C4eR7coU.js +258 -0
  101. keboola_agent_cli/_ui_dist/assets/katex-C4eR7coU.js.map +1 -0
  102. keboola_agent_cli/_ui_dist/assets/line-CzAQKFbJ.js +2 -0
  103. keboola_agent_cli/_ui_dist/assets/line-CzAQKFbJ.js.map +1 -0
  104. keboola_agent_cli/_ui_dist/assets/linear-DUNFFdck.js +2 -0
  105. keboola_agent_cli/_ui_dist/assets/linear-DUNFFdck.js.map +1 -0
  106. keboola_agent_cli/_ui_dist/assets/mermaid-parser.core-CpuBOkFa.js +5 -0
  107. keboola_agent_cli/_ui_dist/assets/mermaid-parser.core-CpuBOkFa.js.map +1 -0
  108. keboola_agent_cli/_ui_dist/assets/mindmap-definition-RKZ34NQL-9EJQNjH0.js +97 -0
  109. keboola_agent_cli/_ui_dist/assets/mindmap-definition-RKZ34NQL-9EJQNjH0.js.map +1 -0
  110. keboola_agent_cli/_ui_dist/assets/ordinal-hYBb2elL.js +2 -0
  111. keboola_agent_cli/_ui_dist/assets/ordinal-hYBb2elL.js.map +1 -0
  112. keboola_agent_cli/_ui_dist/assets/packet-YPE3B663-DLiiw_B2.js +1 -0
  113. keboola_agent_cli/_ui_dist/assets/path-BWPyau1x.js +2 -0
  114. keboola_agent_cli/_ui_dist/assets/path-BWPyau1x.js.map +1 -0
  115. keboola_agent_cli/_ui_dist/assets/pie-LRSECV5Y-CRoO8G1g.js +1 -0
  116. keboola_agent_cli/_ui_dist/assets/pieDiagram-4H26LBE5-XH4cy6Cb.js +31 -0
  117. keboola_agent_cli/_ui_dist/assets/pieDiagram-4H26LBE5-XH4cy6Cb.js.map +1 -0
  118. keboola_agent_cli/_ui_dist/assets/quadrantDiagram-W4KKPZXB-fdhc93U8.js +8 -0
  119. keboola_agent_cli/_ui_dist/assets/quadrantDiagram-W4KKPZXB-fdhc93U8.js.map +1 -0
  120. keboola_agent_cli/_ui_dist/assets/radar-GUYGQ44K-DAlLVJHm.js +1 -0
  121. keboola_agent_cli/_ui_dist/assets/requirementDiagram-4Y6WPE33-a94eP3R9.js +85 -0
  122. keboola_agent_cli/_ui_dist/assets/requirementDiagram-4Y6WPE33-a94eP3R9.js.map +1 -0
  123. keboola_agent_cli/_ui_dist/assets/rough.esm-CSKSodPl.js +2 -0
  124. keboola_agent_cli/_ui_dist/assets/rough.esm-CSKSodPl.js.map +1 -0
  125. keboola_agent_cli/_ui_dist/assets/sankeyDiagram-5OEKKPKP-jcBa02sp.js +41 -0
  126. keboola_agent_cli/_ui_dist/assets/sankeyDiagram-5OEKKPKP-jcBa02sp.js.map +1 -0
  127. keboola_agent_cli/_ui_dist/assets/sequenceDiagram-3UESZ5HK-A5-GGM-e.js +163 -0
  128. keboola_agent_cli/_ui_dist/assets/sequenceDiagram-3UESZ5HK-A5-GGM-e.js.map +1 -0
  129. keboola_agent_cli/_ui_dist/assets/src-ZI-V_AF0.js +2 -0
  130. keboola_agent_cli/_ui_dist/assets/src-ZI-V_AF0.js.map +1 -0
  131. keboola_agent_cli/_ui_dist/assets/stateDiagram-AJRCARHV-BKAA5rqE.js +2 -0
  132. keboola_agent_cli/_ui_dist/assets/stateDiagram-AJRCARHV-BKAA5rqE.js.map +1 -0
  133. keboola_agent_cli/_ui_dist/assets/stateDiagram-v2-BHNVJYJU-DnJwJBsE.js +2 -0
  134. keboola_agent_cli/_ui_dist/assets/stateDiagram-v2-BHNVJYJU-DnJwJBsE.js.map +1 -0
  135. keboola_agent_cli/_ui_dist/assets/timeline-definition-PNZ67QCA-Cy39jp8b.js +121 -0
  136. keboola_agent_cli/_ui_dist/assets/timeline-definition-PNZ67QCA-Cy39jp8b.js.map +1 -0
  137. keboola_agent_cli/_ui_dist/assets/treeView-BLDUP644-DbLYl23-.js +1 -0
  138. keboola_agent_cli/_ui_dist/assets/treemap-LRROVOQU-Bp0eGlOt.js +1 -0
  139. keboola_agent_cli/_ui_dist/assets/vennDiagram-CIIHVFJN-BGECKubd.js +35 -0
  140. keboola_agent_cli/_ui_dist/assets/vennDiagram-CIIHVFJN-BGECKubd.js.map +1 -0
  141. keboola_agent_cli/_ui_dist/assets/wardley-L42UT6IY-D4yH4jqS.js +1 -0
  142. keboola_agent_cli/_ui_dist/assets/wardleyDiagram-YWT4CUSO-D6XRG3cZ.js +79 -0
  143. keboola_agent_cli/_ui_dist/assets/wardleyDiagram-YWT4CUSO-D6XRG3cZ.js.map +1 -0
  144. keboola_agent_cli/_ui_dist/assets/xychartDiagram-2RQKCTM6-DRre-pfZ.js +8 -0
  145. keboola_agent_cli/_ui_dist/assets/xychartDiagram-2RQKCTM6-DRre-pfZ.js.map +1 -0
  146. keboola_agent_cli/_ui_dist/index.html +50 -0
  147. keboola_agent_cli/ai_client.py +83 -0
  148. keboola_agent_cli/auto_update.py +550 -0
  149. keboola_agent_cli/changelog.py +1198 -0
  150. keboola_agent_cli/cli.py +448 -0
  151. keboola_agent_cli/client.py +3422 -0
  152. keboola_agent_cli/commands/__init__.py +0 -0
  153. keboola_agent_cli/commands/_data_app_git.py +343 -0
  154. keboola_agent_cli/commands/_helpers.py +377 -0
  155. keboola_agent_cli/commands/_metadata_input.py +49 -0
  156. keboola_agent_cli/commands/_semantic_layer_crud.py +632 -0
  157. keboola_agent_cli/commands/_semantic_layer_helpers.py +44 -0
  158. keboola_agent_cli/commands/_semantic_layer_reference_data.py +247 -0
  159. keboola_agent_cli/commands/agent.py +968 -0
  160. keboola_agent_cli/commands/branch.py +423 -0
  161. keboola_agent_cli/commands/changelog.py +168 -0
  162. keboola_agent_cli/commands/component.py +216 -0
  163. keboola_agent_cli/commands/config.py +2442 -0
  164. keboola_agent_cli/commands/context.py +1481 -0
  165. keboola_agent_cli/commands/data_app.py +1279 -0
  166. keboola_agent_cli/commands/dev_portal.py +584 -0
  167. keboola_agent_cli/commands/doctor.py +37 -0
  168. keboola_agent_cli/commands/encrypt.py +145 -0
  169. keboola_agent_cli/commands/feature.py +311 -0
  170. keboola_agent_cli/commands/flow.py +948 -0
  171. keboola_agent_cli/commands/http_client.py +157 -0
  172. keboola_agent_cli/commands/init.py +279 -0
  173. keboola_agent_cli/commands/job.py +661 -0
  174. keboola_agent_cli/commands/kai.py +301 -0
  175. keboola_agent_cli/commands/lineage.py +1464 -0
  176. keboola_agent_cli/commands/org.py +292 -0
  177. keboola_agent_cli/commands/permissions.py +360 -0
  178. keboola_agent_cli/commands/project.py +1192 -0
  179. keboola_agent_cli/commands/repl.py +243 -0
  180. keboola_agent_cli/commands/schedule.py +340 -0
  181. keboola_agent_cli/commands/search.py +178 -0
  182. keboola_agent_cli/commands/semantic_layer.py +939 -0
  183. keboola_agent_cli/commands/serve.py +272 -0
  184. keboola_agent_cli/commands/sharing.py +340 -0
  185. keboola_agent_cli/commands/storage.py +2630 -0
  186. keboola_agent_cli/commands/stream.py +266 -0
  187. keboola_agent_cli/commands/sync.py +1277 -0
  188. keboola_agent_cli/commands/tool.py +206 -0
  189. keboola_agent_cli/commands/version.py +186 -0
  190. keboola_agent_cli/commands/workspace.py +635 -0
  191. keboola_agent_cli/config_store.py +582 -0
  192. keboola_agent_cli/constants.py +528 -0
  193. keboola_agent_cli/data_science_client.py +342 -0
  194. keboola_agent_cli/dev_portal_client.py +323 -0
  195. keboola_agent_cli/errors.py +248 -0
  196. keboola_agent_cli/http_base.py +315 -0
  197. keboola_agent_cli/json_utils.py +126 -0
  198. keboola_agent_cli/lib.py +536 -0
  199. keboola_agent_cli/manage_client.py +324 -0
  200. keboola_agent_cli/metastore_client.py +214 -0
  201. keboola_agent_cli/models.py +427 -0
  202. keboola_agent_cli/output.py +1084 -0
  203. keboola_agent_cli/permissions.py +469 -0
  204. keboola_agent_cli/py.typed +3 -0
  205. keboola_agent_cli/result_models.py +271 -0
  206. keboola_agent_cli/server/__init__.py +34 -0
  207. keboola_agent_cli/server/agent_runner.py +1289 -0
  208. keboola_agent_cli/server/agents_store.py +325 -0
  209. keboola_agent_cli/server/app.py +764 -0
  210. keboola_agent_cli/server/auth.py +117 -0
  211. keboola_agent_cli/server/dependencies.py +149 -0
  212. keboola_agent_cli/server/pricing.py +303 -0
  213. keboola_agent_cli/server/routers/__init__.py +1 -0
  214. keboola_agent_cli/server/routers/agents.py +616 -0
  215. keboola_agent_cli/server/routers/ai_chat.py +129 -0
  216. keboola_agent_cli/server/routers/branches.py +133 -0
  217. keboola_agent_cli/server/routers/components.py +48 -0
  218. keboola_agent_cli/server/routers/configs.py +507 -0
  219. keboola_agent_cli/server/routers/data_apps.py +384 -0
  220. keboola_agent_cli/server/routers/dev_portal.py +67 -0
  221. keboola_agent_cli/server/routers/encrypt.py +35 -0
  222. keboola_agent_cli/server/routers/feature.py +179 -0
  223. keboola_agent_cli/server/routers/flows.py +204 -0
  224. keboola_agent_cli/server/routers/health.py +53 -0
  225. keboola_agent_cli/server/routers/jobs.py +175 -0
  226. keboola_agent_cli/server/routers/kai.py +80 -0
  227. keboola_agent_cli/server/routers/lineage.py +226 -0
  228. keboola_agent_cli/server/routers/mcp.py +70 -0
  229. keboola_agent_cli/server/routers/members.py +170 -0
  230. keboola_agent_cli/server/routers/org.py +96 -0
  231. keboola_agent_cli/server/routers/projects.py +106 -0
  232. keboola_agent_cli/server/routers/schedules.py +54 -0
  233. keboola_agent_cli/server/routers/search.py +30 -0
  234. keboola_agent_cli/server/routers/semantic_layer.py +650 -0
  235. keboola_agent_cli/server/routers/sharing.py +86 -0
  236. keboola_agent_cli/server/routers/storage.py +574 -0
  237. keboola_agent_cli/server/routers/stream.py +100 -0
  238. keboola_agent_cli/server/routers/workspaces.py +302 -0
  239. keboola_agent_cli/server/run_broadcaster.py +329 -0
  240. keboola_agent_cli/server/sse.py +25 -0
  241. keboola_agent_cli/services/__init__.py +0 -0
  242. keboola_agent_cli/services/_encryption.py +217 -0
  243. keboola_agent_cli/services/_semantic_layer_cascade.py +147 -0
  244. keboola_agent_cli/services/_semantic_layer_crud.py +382 -0
  245. keboola_agent_cli/services/_semantic_layer_internals.py +1078 -0
  246. keboola_agent_cli/services/_semantic_layer_lookup.py +181 -0
  247. keboola_agent_cli/services/_semantic_layer_reference_data.py +217 -0
  248. keboola_agent_cli/services/_sync_bindings.py +456 -0
  249. keboola_agent_cli/services/_sync_branch.py +191 -0
  250. keboola_agent_cli/services/_sync_bulk.py +228 -0
  251. keboola_agent_cli/services/_sync_clone.py +163 -0
  252. keboola_agent_cli/services/_sync_models.py +97 -0
  253. keboola_agent_cli/services/_sync_push_ops.py +369 -0
  254. keboola_agent_cli/services/_sync_storage.py +376 -0
  255. keboola_agent_cli/services/_sync_writeback.py +167 -0
  256. keboola_agent_cli/services/agent_service.py +458 -0
  257. keboola_agent_cli/services/base.py +175 -0
  258. keboola_agent_cli/services/branch_service.py +588 -0
  259. keboola_agent_cli/services/component_service.py +694 -0
  260. keboola_agent_cli/services/config_service.py +2099 -0
  261. keboola_agent_cli/services/data_app_git_service.py +224 -0
  262. keboola_agent_cli/services/data_app_service.py +2082 -0
  263. keboola_agent_cli/services/deep_lineage_service.py +1322 -0
  264. keboola_agent_cli/services/dev_portal_service.py +345 -0
  265. keboola_agent_cli/services/doctor_service.py +445 -0
  266. keboola_agent_cli/services/encrypt_service.py +87 -0
  267. keboola_agent_cli/services/feature_service.py +268 -0
  268. keboola_agent_cli/services/flow_service.py +769 -0
  269. keboola_agent_cli/services/flow_validation.py +188 -0
  270. keboola_agent_cli/services/http_forwarder_service.py +236 -0
  271. keboola_agent_cli/services/job_idempotency_store.py +285 -0
  272. keboola_agent_cli/services/job_service.py +797 -0
  273. keboola_agent_cli/services/kai_service.py +367 -0
  274. keboola_agent_cli/services/lineage_service.py +274 -0
  275. keboola_agent_cli/services/mcp_service.py +1498 -0
  276. keboola_agent_cli/services/mcp_transport.py +259 -0
  277. keboola_agent_cli/services/member_service.py +593 -0
  278. keboola_agent_cli/services/org_service.py +619 -0
  279. keboola_agent_cli/services/project_service.py +947 -0
  280. keboola_agent_cli/services/repo_validate_service.py +767 -0
  281. keboola_agent_cli/services/schedule_service.py +731 -0
  282. keboola_agent_cli/services/search_service.py +331 -0
  283. keboola_agent_cli/services/semantic_layer_service.py +1497 -0
  284. keboola_agent_cli/services/sharing_service.py +307 -0
  285. keboola_agent_cli/services/storage_service.py +2524 -0
  286. keboola_agent_cli/services/stream_service.py +395 -0
  287. keboola_agent_cli/services/sync_service.py +2244 -0
  288. keboola_agent_cli/services/variables_service.py +447 -0
  289. keboola_agent_cli/services/version_service.py +1038 -0
  290. keboola_agent_cli/services/workspace_service.py +1103 -0
  291. keboola_agent_cli/stream_client.py +217 -0
  292. keboola_agent_cli/sync/__init__.py +1 -0
  293. keboola_agent_cli/sync/branch_mapping.py +174 -0
  294. keboola_agent_cli/sync/clone.py +211 -0
  295. keboola_agent_cli/sync/code_extraction.py +655 -0
  296. keboola_agent_cli/sync/config_format.py +290 -0
  297. keboola_agent_cli/sync/diff_engine.py +566 -0
  298. keboola_agent_cli/sync/git_utils.py +93 -0
  299. keboola_agent_cli/sync/manifest.py +162 -0
  300. keboola_agent_cli/sync/naming.py +90 -0
  301. keboola_agent_cli/sync/secrets.py +62 -0
  302. keboola_agent_cli/sync/sql_split.py +134 -0
  303. keboola_cli-0.63.4.dist-info/METADATA +308 -0
  304. keboola_cli-0.63.4.dist-info/RECORD +306 -0
  305. keboola_cli-0.63.4.dist-info/WHEEL +4 -0
  306. 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> &gt; </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", []))