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,694 @@
1
+ """Component discovery and scaffold generation service.
2
+
3
+ Provides component search (via AI Service suggestions or Storage API listing),
4
+ detailed component inspection, and configuration scaffold generation for
5
+ local-first development workflows.
6
+ """
7
+
8
+ import logging
9
+ from collections.abc import Callable
10
+ from typing import Any
11
+
12
+ import yaml
13
+
14
+ from ..ai_client import AiServiceClient
15
+ from ..config_store import ConfigStore
16
+ from ..constants import SECRET_PLACEHOLDER
17
+ from ..errors import KeboolaApiError
18
+ from ..models import ComponentDetail, ComponentSuggestion, ProjectConfig
19
+ from .base import BaseService, ClientFactory
20
+ from .org_service import slugify
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+ AiClientFactory = Callable[[str, str], AiServiceClient]
25
+
26
+
27
+ def default_ai_client_factory(stack_url: str, token: str) -> AiServiceClient:
28
+ """Create an AiServiceClient with the given stack URL and token."""
29
+ return AiServiceClient(stack_url=stack_url, token=token)
30
+
31
+
32
+ # --- Component type detection ---
33
+
34
+ _SQL_TRANSFORMATION_FRAGMENTS = (
35
+ "snowflake-transformation",
36
+ "synapse-transformation",
37
+ "redshift-transformation",
38
+ "bigquery-transformation",
39
+ )
40
+
41
+ _PYTHON_TRANSFORMATION_FRAGMENT = "python-transformation"
42
+ _CUSTOM_PYTHON_APP_ID = "kds-team.app-custom-python"
43
+ _FLOW_COMPONENT_IDS = ("keboola.flow",)
44
+
45
+
46
+ def _detect_component_category(component_id: str) -> str:
47
+ """Determine scaffold category from component_id.
48
+
49
+ Returns one of: sql_transformation, python_transformation,
50
+ custom_python, flow, generic.
51
+ """
52
+ for fragment in _SQL_TRANSFORMATION_FRAGMENTS:
53
+ if fragment in component_id:
54
+ return "sql_transformation"
55
+ if _PYTHON_TRANSFORMATION_FRAGMENT in component_id:
56
+ return "python_transformation"
57
+ if component_id == _CUSTOM_PYTHON_APP_ID:
58
+ return "custom_python"
59
+ if component_id in _FLOW_COMPONENT_IDS:
60
+ return "flow"
61
+ return "generic"
62
+
63
+
64
+ # --- Scaffold file builders ---
65
+
66
+
67
+ def _build_config_yml(detail: ComponentDetail, name: str) -> str:
68
+ """Generate _config.yml content with inline comments.
69
+
70
+ Priority for parameters section:
71
+ 1. First rootConfigurationExample's parameters key
72
+ 2. Schema-derived placeholders from configurationSchema
73
+ 3. Empty parameters dict
74
+ """
75
+ lines: list[str] = []
76
+
77
+ # Header comments
78
+ lines.append(f"# Component: {detail.component_name} ({detail.component_id})")
79
+ lines.append(f"# Type: {detail.component_type}")
80
+ if detail.documentation_url:
81
+ lines.append(f"# Documentation: {detail.documentation_url}")
82
+ lines.append("#")
83
+ lines.append("# NOTE: config_id will be assigned by Keboola on first push")
84
+
85
+ # Version and name
86
+ lines.append("version: 2")
87
+ lines.append(f'name: "{name}"')
88
+ lines.append("description: |")
89
+ lines.append(" TODO: describe this configuration")
90
+ lines.append("")
91
+
92
+ # Parameters section
93
+ params = _resolve_parameters(detail)
94
+ if params:
95
+ params_yaml = yaml.dump({"parameters": params}, default_flow_style=False, sort_keys=False)
96
+ # Post-process secret placeholders with inline comments
97
+ processed_lines: list[str] = []
98
+ for line in params_yaml.splitlines():
99
+ if SECRET_PLACEHOLDER in line and "# encrypted by Keboola on push" not in line:
100
+ line = f"{line} # encrypted by Keboola on push"
101
+ processed_lines.append(line)
102
+ lines.extend(processed_lines)
103
+ else:
104
+ lines.append("parameters: {}")
105
+
106
+ # Storage mappings based on component flags
107
+ storage_lines = _build_storage_section(detail)
108
+ if storage_lines:
109
+ lines.append("")
110
+ lines.extend(storage_lines)
111
+
112
+ # Configuration rows hint
113
+ if detail.configuration_row_schema:
114
+ lines.append("")
115
+ lines.append("# This component uses configuration rows. Add rows via 'rows/' subdirectory.")
116
+
117
+ # _keboola metadata (component_id required for sync push, config_id assigned on first push)
118
+ lines.append("")
119
+ lines.append("_keboola:")
120
+ lines.append(f" component_id: {detail.component_id}")
121
+
122
+ lines.append("")
123
+ return "\n".join(lines)
124
+
125
+
126
+ def _resolve_parameters(detail: ComponentDetail) -> dict[str, Any]:
127
+ """Extract parameters from examples or schema, applying secret masking."""
128
+ # Priority 1: examples
129
+ if detail.root_configuration_examples:
130
+ first_example = detail.root_configuration_examples[0]
131
+ raw_params = first_example.get("parameters", {})
132
+ if raw_params:
133
+ return _mask_secrets(raw_params)
134
+
135
+ # Priority 2: schema
136
+ schema = detail.configuration_schema
137
+ if schema and schema.get("properties"):
138
+ params_schema = schema.get("properties", {}).get("parameters", {})
139
+ if params_schema and params_schema.get("properties"):
140
+ return _generate_from_schema(params_schema)
141
+ # If parameters is not nested, try top-level properties
142
+ return _generate_from_schema(schema)
143
+
144
+ # Priority 3: empty
145
+ return {}
146
+
147
+
148
+ def _mask_secrets(obj: Any) -> Any:
149
+ """Recursively replace secret values with SECRET_PLACEHOLDER."""
150
+ if isinstance(obj, dict):
151
+ result = {}
152
+ for key, value in obj.items():
153
+ if key.startswith("#") or (isinstance(value, str) and value == "<secret>"):
154
+ result[key] = SECRET_PLACEHOLDER
155
+ else:
156
+ result[key] = _mask_secrets(value)
157
+ return result
158
+ if isinstance(obj, list):
159
+ return [_mask_secrets(item) for item in obj]
160
+ return obj
161
+
162
+
163
+ def _generate_from_schema(schema: dict[str, Any]) -> dict[str, Any]:
164
+ """Generate placeholder values from a JSON schema."""
165
+ properties = schema.get("properties", {})
166
+ result: dict[str, Any] = {}
167
+
168
+ for prop_name, prop_schema in properties.items():
169
+ prop_type = prop_schema.get("type", "string")
170
+
171
+ if prop_name.startswith("#"):
172
+ result[prop_name] = SECRET_PLACEHOLDER
173
+ elif prop_type == "string":
174
+ result[prop_name] = prop_schema.get("default", "")
175
+ elif prop_type == "integer" or prop_type == "number":
176
+ result[prop_name] = prop_schema.get("default", 0)
177
+ elif prop_type == "boolean":
178
+ result[prop_name] = prop_schema.get("default", False)
179
+ elif prop_type == "array":
180
+ result[prop_name] = prop_schema.get("default", [])
181
+ elif prop_type == "object":
182
+ nested = prop_schema.get("properties")
183
+ if nested:
184
+ result[prop_name] = _generate_from_schema(prop_schema)
185
+ else:
186
+ result[prop_name] = prop_schema.get("default", {})
187
+ else:
188
+ result[prop_name] = ""
189
+
190
+ return result
191
+
192
+
193
+ def _build_storage_section(detail: ComponentDetail) -> list[str]:
194
+ """Generate storage input/output mapping skeleton based on component flags."""
195
+ flags = detail.component_flags
196
+ lines: list[str] = []
197
+ has_input = "genericDockerUI-tableInput" in flags
198
+ has_output = "genericDockerUI-tableOutput" in flags
199
+
200
+ if not has_input and not has_output:
201
+ return lines
202
+
203
+ lines.append("storage:")
204
+
205
+ if has_input:
206
+ lines.append(" input:")
207
+ lines.append(" tables:")
208
+ lines.append(' - source: "in.c-bucket.table"')
209
+ lines.append(' destination: "input.csv"')
210
+
211
+ if has_output:
212
+ lines.append(" output:")
213
+ lines.append(" tables:")
214
+ lines.append(' - source: "output.csv"')
215
+ lines.append(' destination: "out.c-bucket.table"')
216
+
217
+ return lines
218
+
219
+
220
+ def _build_transform_sql(name: str) -> str:
221
+ """Generate SQL transformation boilerplate."""
222
+ return (
223
+ "/* ===== BLOCK: 001-main ===== */\n"
224
+ "/* ===== CODE: 001-query ===== */\n"
225
+ "\n"
226
+ "-- TODO: write your SQL transformation here\n"
227
+ "-- Input tables are available as temporary tables\n"
228
+ "-- Output tables will be created from SELECT results\n"
229
+ "\n"
230
+ "SELECT 1;\n"
231
+ )
232
+
233
+
234
+ def _build_transform_py(name: str) -> str:
235
+ """Generate Python transformation boilerplate."""
236
+ return (
237
+ "# ===== BLOCK: 001-main =====\n"
238
+ "# ===== CODE: 001-script =====\n"
239
+ "\n"
240
+ "from keboola.component import CommonInterface\n"
241
+ "\n"
242
+ "ci = CommonInterface()\n"
243
+ "\n"
244
+ "# Read input tables\n"
245
+ '# input_table = ci.get_input_table_definition_by_name("input.csv")\n'
246
+ "# df = pd.read_csv(input_table.full_path)\n"
247
+ "\n"
248
+ "# Write output tables\n"
249
+ '# output_table = ci.create_out_table_definition("output.csv")\n'
250
+ "# df.to_csv(output_table.full_path, index=False)\n"
251
+ "\n"
252
+ 'print("Transformation complete")\n'
253
+ )
254
+
255
+
256
+ def _build_code_py() -> str:
257
+ """Generate custom Python application boilerplate."""
258
+ return (
259
+ "import logging\n"
260
+ "from keboola.component import CommonInterface\n"
261
+ "\n"
262
+ "logging.basicConfig(level=logging.INFO)\n"
263
+ "\n"
264
+ "ci = CommonInterface()\n"
265
+ "params = ci.configuration.parameters\n"
266
+ "\n"
267
+ "# TODO: implement your application logic here\n"
268
+ "\n"
269
+ 'logging.info("Application complete")\n'
270
+ )
271
+
272
+
273
+ def _build_pyproject_toml(component_id: str, name: str, packages: list[str] | None = None) -> str:
274
+ """Generate pyproject.toml for custom Python apps."""
275
+ slugified_name = slugify(name)
276
+ deps_lines = ""
277
+ if packages:
278
+ formatted = ",\n".join(f' "{pkg}"' for pkg in packages)
279
+ deps_lines = f"\ndependencies = [\n{formatted},\n]\n"
280
+ else:
281
+ deps_lines = "\ndependencies = [\n # Add your dependencies here\n]\n"
282
+
283
+ return (
284
+ "[project]\n"
285
+ f'name = "{slugified_name}"\n'
286
+ 'version = "1.0.0"\n'
287
+ 'requires-python = ">=3.11"\n'
288
+ f"{deps_lines}"
289
+ )
290
+
291
+
292
+ def _build_flow_config_yml(name: str, component_id: str = "keboola.flow") -> str:
293
+ """Generate a conditional-flow (keboola.flow) configuration YAML skeleton.
294
+
295
+ IDs are strings; phases carry next[].goto transitions (a phase id or null)
296
+ and tasks are typed (job/notification/variable).
297
+ """
298
+ lines = [
299
+ f'name: "{name}"',
300
+ "description: |",
301
+ " TODO: describe this flow",
302
+ "phases:",
303
+ ' - id: "phase-1"',
304
+ ' name: "Phase 1"',
305
+ " next:",
306
+ ' - id: "default"',
307
+ " goto: null",
308
+ "tasks:",
309
+ ' - id: "task-1"',
310
+ ' name: "Task 1"',
311
+ ' phase: "phase-1"',
312
+ " enabled: true",
313
+ " task:",
314
+ " type: job",
315
+ ' componentId: "keboola.ex-http"',
316
+ ' configId: "TODO"',
317
+ " mode: run",
318
+ ]
319
+ return "\n".join(lines) + "\n"
320
+
321
+
322
+ class ComponentService(BaseService):
323
+ """Business logic for component discovery and scaffold generation.
324
+
325
+ Supports two discovery modes:
326
+ - AI-powered search via AiServiceClient (natural language query)
327
+ - Storage API listing via KeboolaClient (component type filter)
328
+
329
+ Scaffold generation creates ready-to-use configuration files based on
330
+ component schema, examples, and type-specific templates.
331
+ """
332
+
333
+ def __init__(
334
+ self,
335
+ config_store: ConfigStore,
336
+ client_factory: ClientFactory | None = None,
337
+ ai_client_factory: AiClientFactory | None = None,
338
+ ) -> None:
339
+ super().__init__(config_store, client_factory)
340
+ self._ai_client_factory = ai_client_factory or default_ai_client_factory
341
+
342
+ def list_components(
343
+ self,
344
+ aliases: list[str] | None = None,
345
+ component_type: str | None = None,
346
+ query: str | None = None,
347
+ ) -> dict[str, Any]:
348
+ """List or search components across projects.
349
+
350
+ Two modes of operation:
351
+ - With ``query``: Uses AI Service to suggest components matching a
352
+ natural language description. Enriches each suggestion with detail
353
+ from get_component_detail(). Runs against first/default project.
354
+ - Without ``query``: Uses Storage API list_components() across all
355
+ resolved projects in parallel, returning unique components.
356
+
357
+ Args:
358
+ aliases: Project aliases to query. None means all projects.
359
+ component_type: Optional filter by component type
360
+ (extractor, writer, transformation, application).
361
+ query: Natural language search query for AI-powered discovery.
362
+
363
+ Returns:
364
+ Dict with keys:
365
+ - "components": list of component dicts
366
+ - "errors": list of error dicts
367
+ """
368
+ if query:
369
+ return self._list_via_ai(aliases, component_type, query)
370
+ return self._list_via_storage(aliases, component_type)
371
+
372
+ def get_component_detail(self, alias: str, component_id: str) -> dict[str, Any]:
373
+ """Fetch detailed component documentation via AI Service.
374
+
375
+ Args:
376
+ alias: Project alias (used to derive stack URL and token).
377
+ component_id: The component identifier (e.g. 'keboola.ex-aws-s3').
378
+
379
+ Returns:
380
+ Dict with component detail including schema summary,
381
+ examples count, and full documentation.
382
+
383
+ Raises:
384
+ ConfigError: If the alias is not found.
385
+ KeboolaApiError: If the AI Service call fails.
386
+ """
387
+ projects = self.resolve_projects([alias])
388
+ project = projects[alias]
389
+
390
+ ai_client = self._ai_client_factory(project.stack_url, project.token)
391
+ try:
392
+ raw = ai_client.get_component_detail(component_id)
393
+ finally:
394
+ ai_client.close()
395
+
396
+ detail = ComponentDetail(**raw)
397
+
398
+ # Build schema summary
399
+ schema = detail.configuration_schema
400
+ schema_properties = schema.get("properties", {}) if schema else {}
401
+ schema_required = schema.get("required", []) if schema else []
402
+
403
+ return {
404
+ "component_id": detail.component_id,
405
+ "component_name": detail.component_name,
406
+ "component_type": detail.component_type,
407
+ "categories": detail.component_categories,
408
+ "flags": detail.component_flags,
409
+ "description": detail.description,
410
+ "long_description": detail.long_description,
411
+ "documentation_url": detail.documentation_url,
412
+ "schema_summary": {
413
+ "property_count": len(schema_properties),
414
+ "required_count": len(schema_required),
415
+ "has_row_schema": bool(detail.configuration_row_schema),
416
+ },
417
+ "examples_count": len(detail.root_configuration_examples),
418
+ "row_examples_count": len(detail.row_configuration_examples),
419
+ "project_alias": alias,
420
+ }
421
+
422
+ def generate_scaffold(
423
+ self,
424
+ alias: str,
425
+ component_id: str,
426
+ name: str | None = None,
427
+ ) -> dict[str, Any]:
428
+ """Generate configuration scaffold files for a component.
429
+
430
+ Fetches component detail from AI Service, then generates appropriate
431
+ configuration files based on component type and schema.
432
+
433
+ Args:
434
+ alias: Project alias (used to derive stack URL and token).
435
+ component_id: The component identifier.
436
+ name: Configuration name. If None, defaults to
437
+ "{component_name} Configuration".
438
+
439
+ Returns:
440
+ Dict with scaffold metadata and generated files list.
441
+
442
+ Raises:
443
+ ConfigError: If the alias is not found.
444
+ KeboolaApiError: If the AI Service call fails.
445
+ """
446
+ projects = self.resolve_projects([alias])
447
+ project = projects[alias]
448
+
449
+ ai_client = self._ai_client_factory(project.stack_url, project.token)
450
+ try:
451
+ raw = ai_client.get_component_detail(component_id)
452
+ finally:
453
+ ai_client.close()
454
+
455
+ detail = ComponentDetail(**raw)
456
+
457
+ config_name = name or f"{detail.component_name} Configuration"
458
+ category = _detect_component_category(component_id)
459
+
460
+ # Build directory path
461
+ dir_name = slugify(config_name)
462
+ directory = f"{detail.component_type}/{component_id}/{dir_name}"
463
+
464
+ # Generate files based on category
465
+ files = self._generate_files(detail, config_name, category)
466
+
467
+ return {
468
+ "component_id": component_id,
469
+ "component_name": detail.component_name,
470
+ "component_type": detail.component_type,
471
+ "config_name": config_name,
472
+ "directory": directory,
473
+ "documentation_url": detail.documentation_url,
474
+ "files": files,
475
+ }
476
+
477
+ # --- Private helpers ---
478
+
479
+ def _list_via_ai(
480
+ self,
481
+ aliases: list[str] | None,
482
+ component_type: str | None,
483
+ query: str,
484
+ ) -> dict[str, Any]:
485
+ """Search components via AI Service suggestions.
486
+
487
+ Uses first/default project for AI queries, then enriches each
488
+ suggestion with component detail.
489
+ """
490
+ projects = self.resolve_projects(aliases)
491
+ # Use first project for AI queries
492
+ first_alias = next(iter(projects))
493
+ project = projects[first_alias]
494
+
495
+ ai_client = self._ai_client_factory(project.stack_url, project.token)
496
+ components: list[dict[str, Any]] = []
497
+ errors: list[dict[str, str]] = []
498
+
499
+ try:
500
+ suggestions_raw = ai_client.suggest_components(query)
501
+ suggestions = [ComponentSuggestion(**s) for s in suggestions_raw]
502
+
503
+ for suggestion in suggestions:
504
+ try:
505
+ raw_detail = ai_client.get_component_detail(suggestion.component_id)
506
+ detail = ComponentDetail(**raw_detail)
507
+
508
+ # Apply component_type filter if provided
509
+ if component_type and detail.component_type != component_type:
510
+ continue
511
+
512
+ components.append(
513
+ {
514
+ "component_id": detail.component_id,
515
+ "component_name": detail.component_name,
516
+ "component_type": detail.component_type,
517
+ "categories": detail.component_categories,
518
+ "description": detail.description,
519
+ "score": suggestion.score,
520
+ }
521
+ )
522
+ except KeboolaApiError as exc:
523
+ logger.debug(
524
+ "Failed to fetch detail for %s: %s",
525
+ suggestion.component_id,
526
+ exc.message,
527
+ )
528
+ errors.append(
529
+ {
530
+ "component_id": suggestion.component_id,
531
+ "error_code": exc.error_code,
532
+ "message": exc.message,
533
+ }
534
+ )
535
+ except Exception as exc:
536
+ logger.debug(
537
+ "Unexpected error fetching detail for %s: %s",
538
+ suggestion.component_id,
539
+ exc,
540
+ )
541
+ errors.append(
542
+ {
543
+ "component_id": suggestion.component_id,
544
+ "error_code": "UNEXPECTED_ERROR",
545
+ "message": str(exc),
546
+ }
547
+ )
548
+ except KeboolaApiError as exc:
549
+ errors.append(
550
+ {
551
+ "project_alias": first_alias,
552
+ "error_code": exc.error_code,
553
+ "message": exc.message,
554
+ }
555
+ )
556
+ except Exception as exc:
557
+ errors.append(
558
+ {
559
+ "project_alias": first_alias,
560
+ "error_code": "UNEXPECTED_ERROR",
561
+ "message": str(exc),
562
+ }
563
+ )
564
+ finally:
565
+ ai_client.close()
566
+
567
+ return {"components": components, "errors": errors}
568
+
569
+ def _list_via_storage(
570
+ self,
571
+ aliases: list[str] | None,
572
+ component_type: str | None,
573
+ ) -> dict[str, Any]:
574
+ """List components via Storage API across projects in parallel."""
575
+ projects = self.resolve_projects(aliases)
576
+
577
+ def worker(
578
+ alias: str, project: ProjectConfig
579
+ ) -> tuple[str, list[dict[str, Any]], bool] | tuple[str, dict[str, str]]:
580
+ client = self._client_factory(project.stack_url, project.token)
581
+ try:
582
+ raw_components = client.list_components(component_type=component_type)
583
+ result: list[dict[str, Any]] = []
584
+ for comp in raw_components:
585
+ result.append(
586
+ {
587
+ "component_id": comp.get("id", ""),
588
+ "component_name": comp.get("name", ""),
589
+ "component_type": comp.get("type", ""),
590
+ "categories": comp.get("categories", []),
591
+ "description": comp.get("description", ""),
592
+ }
593
+ )
594
+ return (alias, result, True)
595
+ except KeboolaApiError as exc:
596
+ return (
597
+ alias,
598
+ {
599
+ "project_alias": alias,
600
+ "error_code": exc.error_code,
601
+ "message": exc.message,
602
+ },
603
+ )
604
+ except Exception as exc:
605
+ return (
606
+ alias,
607
+ {
608
+ "project_alias": alias,
609
+ "error_code": "UNEXPECTED_ERROR",
610
+ "message": str(exc),
611
+ },
612
+ )
613
+ finally:
614
+ client.close()
615
+
616
+ successes, errors = self._run_parallel(projects, worker)
617
+
618
+ # Deduplicate components across projects by component_id
619
+ seen: dict[str, dict[str, Any]] = {}
620
+ for _alias, components, _ok in successes:
621
+ for comp in components:
622
+ comp_id = comp["component_id"]
623
+ if comp_id not in seen:
624
+ seen[comp_id] = comp
625
+
626
+ unique_components = sorted(seen.values(), key=lambda c: c["component_id"])
627
+ errors.sort(key=lambda e: e.get("project_alias", ""))
628
+
629
+ return {"components": unique_components, "errors": errors}
630
+
631
+ def _generate_files(
632
+ self,
633
+ detail: ComponentDetail,
634
+ config_name: str,
635
+ category: str,
636
+ ) -> list[dict[str, str]]:
637
+ """Generate scaffold files based on component category.
638
+
639
+ Returns a list of file dicts with path, content, and description.
640
+ """
641
+ files: list[dict[str, str]] = []
642
+
643
+ if category == "flow":
644
+ files.append(
645
+ {
646
+ "path": "_config.yml",
647
+ "content": _build_flow_config_yml(config_name, detail.component_id),
648
+ "description": "Conditional flow (keboola.flow) configuration",
649
+ }
650
+ )
651
+ return files
652
+
653
+ # All other categories get a _config.yml
654
+ files.append(
655
+ {
656
+ "path": "_config.yml",
657
+ "content": _build_config_yml(detail, config_name),
658
+ "description": "Configuration file",
659
+ }
660
+ )
661
+
662
+ if category == "sql_transformation":
663
+ files.append(
664
+ {
665
+ "path": "transform.sql",
666
+ "content": _build_transform_sql(config_name),
667
+ "description": "SQL transformation code",
668
+ }
669
+ )
670
+ elif category == "python_transformation":
671
+ files.append(
672
+ {
673
+ "path": "transform.py",
674
+ "content": _build_transform_py(config_name),
675
+ "description": "Python transformation code",
676
+ }
677
+ )
678
+ elif category == "custom_python":
679
+ files.append(
680
+ {
681
+ "path": "code.py",
682
+ "content": _build_code_py(),
683
+ "description": "Custom Python application code",
684
+ }
685
+ )
686
+ files.append(
687
+ {
688
+ "path": "pyproject.toml",
689
+ "content": _build_pyproject_toml(detail.component_id, config_name),
690
+ "description": "Python project configuration",
691
+ }
692
+ )
693
+
694
+ return files