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,588 @@
1
+ """Branch service - business logic for branch lifecycle management.
2
+
3
+ Orchestrates multi-project branch retrieval in parallel, annotates with
4
+ project alias, and aggregates results. Provides create, activate, reset,
5
+ delete, and merge URL generation for development branches. Also provides
6
+ branch metadata CRUD and a "project description" convenience wrapper over
7
+ the ``KBC.projectDescription`` metadata key on the default branch.
8
+ """
9
+
10
+ from typing import Any
11
+
12
+ from ..constants import METADATA_NOT_FOUND
13
+ from ..errors import ConfigError, ErrorCode, KeboolaApiError
14
+ from ..models import ProjectConfig
15
+ from .base import BaseService
16
+
17
+ PROJECT_DESCRIPTION_KEY = "KBC.projectDescription"
18
+
19
+
20
+ class BranchService(BaseService):
21
+ """Business logic for managing Keboola development branches.
22
+
23
+ Supports multi-project aggregation: queries multiple projects in parallel
24
+ using ThreadPoolExecutor, collects results, and reports per-project errors
25
+ without stopping others.
26
+
27
+ Provides branch lifecycle operations: create, activate (use), reset,
28
+ delete, and merge URL generation.
29
+
30
+ Uses dependency injection for config_store and client_factory.
31
+ """
32
+
33
+ def _fetch_project_branches(
34
+ self,
35
+ alias: str,
36
+ project: ProjectConfig,
37
+ ) -> tuple[str, list[dict[str, Any]], bool] | tuple[str, dict[str, str]]:
38
+ """Fetch development branches for a single project (runs in a worker thread).
39
+
40
+ Returns either (alias, branches_list, True) on success
41
+ or (alias, error_dict) on failure.
42
+ """
43
+ client = self._client_factory(project.stack_url, project.token)
44
+ try:
45
+ raw_branches = client.list_dev_branches()
46
+ branches: list[dict[str, Any]] = []
47
+ for branch in raw_branches:
48
+ branches.append(
49
+ {
50
+ "project_alias": alias,
51
+ "id": branch.get("id"),
52
+ "name": branch.get("name", ""),
53
+ "isDefault": branch.get("isDefault", False),
54
+ "created": branch.get("created", ""),
55
+ "description": branch.get("description", ""),
56
+ }
57
+ )
58
+ return (alias, branches, True)
59
+ except KeboolaApiError as exc:
60
+ return (
61
+ alias,
62
+ {
63
+ "project_alias": alias,
64
+ "error_code": exc.error_code,
65
+ "message": exc.message,
66
+ },
67
+ )
68
+ except Exception as exc:
69
+ return (
70
+ alias,
71
+ {
72
+ "project_alias": alias,
73
+ "error_code": "UNEXPECTED_ERROR",
74
+ "message": str(exc),
75
+ },
76
+ )
77
+ finally:
78
+ client.close()
79
+
80
+ def list_branches(
81
+ self,
82
+ aliases: list[str] | None = None,
83
+ ) -> dict[str, Any]:
84
+ """List development branches across one or multiple projects.
85
+
86
+ Queries each resolved project for development branches in parallel,
87
+ flattens them into a unified list. Per-project errors are collected
88
+ but do not stop other projects from being queried.
89
+
90
+ Includes active_branches dict mapping alias -> active_branch_id for
91
+ display purposes.
92
+
93
+ Args:
94
+ aliases: Project aliases to query. None means all projects.
95
+
96
+ Returns:
97
+ Dict with keys:
98
+ - "branches": list of branch dicts with project_alias,
99
+ id, name, isDefault, created, description
100
+ - "errors": list of error dicts with project_alias,
101
+ error_code, message
102
+ - "active_branches": dict mapping alias -> active_branch_id
103
+
104
+ Raises:
105
+ ConfigError: If a specified alias is not found (before querying).
106
+ """
107
+ projects = self.resolve_projects(aliases)
108
+
109
+ # Collect active branch IDs for display
110
+ active_branches: dict[str, int | None] = {}
111
+ for alias, project in projects.items():
112
+ active_branches[alias] = project.active_branch_id
113
+
114
+ def worker(alias: str, project: ProjectConfig) -> tuple[Any, ...]:
115
+ return self._fetch_project_branches(alias, project)
116
+
117
+ successes, errors = self._run_parallel(projects, worker)
118
+
119
+ # Flatten branches from all successful projects
120
+ all_branches: list[dict[str, Any]] = []
121
+ for _alias, branches, _ok in successes:
122
+ all_branches.extend(branches)
123
+
124
+ # Sort for deterministic output
125
+ all_branches.sort(key=lambda b: (b["project_alias"], b.get("id", 0)))
126
+ errors.sort(key=lambda e: e.get("project_alias", ""))
127
+
128
+ return {
129
+ "branches": all_branches,
130
+ "errors": errors,
131
+ "active_branches": active_branches,
132
+ }
133
+
134
+ def create_branch(
135
+ self,
136
+ alias: str,
137
+ name: str,
138
+ description: str = "",
139
+ ) -> dict[str, Any]:
140
+ """Create a new development branch and auto-activate it.
141
+
142
+ Args:
143
+ alias: Project alias.
144
+ name: Branch name.
145
+ description: Optional branch description.
146
+
147
+ Returns:
148
+ Dict with branch details and activation info.
149
+
150
+ Raises:
151
+ ConfigError: If the project alias is not found.
152
+ KeboolaApiError: If the API call fails.
153
+ """
154
+ projects = self.resolve_projects([alias])
155
+ project = projects[alias]
156
+
157
+ client = self._client_factory(project.stack_url, project.token)
158
+ try:
159
+ # create_dev_branch waits for the async job and returns branch data
160
+ # from job.results (contains the real branch ID)
161
+ branch_data = client.create_dev_branch(name=name, description=description)
162
+ finally:
163
+ client.close()
164
+
165
+ branch_id = int(branch_data["id"])
166
+
167
+ # Auto-activate the created branch
168
+ self._config_store.set_project_branch(alias, branch_id)
169
+
170
+ return {
171
+ "project_alias": alias,
172
+ "branch_id": branch_id,
173
+ "branch_name": branch_data.get("name", name),
174
+ "description": branch_data.get("description", description),
175
+ "created": branch_data.get("created", ""),
176
+ "activated": True,
177
+ "message": (
178
+ f"Branch '{name}' (ID: {branch_id}) created and activated for project '{alias}'."
179
+ ),
180
+ }
181
+
182
+ def set_active_branch(self, alias: str, branch_id: int) -> dict[str, Any]:
183
+ """Validate and set an existing branch as active.
184
+
185
+ Calls the API to verify the branch exists before setting it.
186
+
187
+ Args:
188
+ alias: Project alias.
189
+ branch_id: Branch ID to activate.
190
+
191
+ Returns:
192
+ Dict with activation details.
193
+
194
+ Raises:
195
+ ConfigError: If the project alias is not found or branch does not exist.
196
+ KeboolaApiError: If the API call fails.
197
+ """
198
+ projects = self.resolve_projects([alias])
199
+ project = projects[alias]
200
+
201
+ client = self._client_factory(project.stack_url, project.token)
202
+ try:
203
+ raw_branches = client.list_dev_branches()
204
+ finally:
205
+ client.close()
206
+
207
+ # Find the branch by ID
208
+ target_branch = None
209
+ for branch in raw_branches:
210
+ if branch.get("id") == branch_id:
211
+ target_branch = branch
212
+ break
213
+
214
+ if target_branch is None:
215
+ raise ConfigError(
216
+ f"Branch ID {branch_id} not found in project '{alias}'. "
217
+ f"Use 'kbagent branch list --project {alias}' to see available branches."
218
+ )
219
+
220
+ self._config_store.set_project_branch(alias, branch_id)
221
+
222
+ branch_name = target_branch.get("name", "")
223
+ return {
224
+ "project_alias": alias,
225
+ "branch_id": branch_id,
226
+ "branch_name": branch_name,
227
+ "message": (
228
+ f"Active branch set to '{branch_name}' (ID: {branch_id}) for project '{alias}'."
229
+ ),
230
+ }
231
+
232
+ def reset_branch(self, alias: str) -> dict[str, Any]:
233
+ """Clear the active branch, reverting to the main/production branch.
234
+
235
+ Args:
236
+ alias: Project alias.
237
+
238
+ Returns:
239
+ Dict confirming the reset.
240
+
241
+ Raises:
242
+ ConfigError: If the project alias is not found.
243
+ """
244
+ projects = self.resolve_projects([alias])
245
+ previous_branch = projects[alias].active_branch_id
246
+
247
+ self._config_store.set_project_branch(alias, None)
248
+
249
+ return {
250
+ "project_alias": alias,
251
+ "previous_branch_id": previous_branch,
252
+ "message": (f"Active branch reset to main for project '{alias}'."),
253
+ }
254
+
255
+ def delete_branch(self, alias: str, branch_id: int) -> dict[str, Any]:
256
+ """Delete a development branch via API. Auto-resets if it was active.
257
+
258
+ Also cleans up any matching entry in the nearest enclosing sync
259
+ workspace's ``branch-mapping.json`` (issue #267, Bug D), so the
260
+ user is not left with a stale mapping pointing at a deleted
261
+ branch.
262
+
263
+ Args:
264
+ alias: Project alias.
265
+ branch_id: Branch ID to delete.
266
+
267
+ Returns:
268
+ Dict confirming the deletion.
269
+
270
+ Raises:
271
+ ConfigError: If the project alias is not found.
272
+ KeboolaApiError: If the API call fails.
273
+ """
274
+ from ..sync.branch_mapping import cleanup_branch_id_from_mapping
275
+
276
+ projects = self.resolve_projects([alias])
277
+ project = projects[alias]
278
+
279
+ client = self._client_factory(project.stack_url, project.token)
280
+ try:
281
+ client.delete_dev_branch(branch_id)
282
+ finally:
283
+ client.close()
284
+
285
+ # Auto-reset if the deleted branch was active
286
+ was_active = project.active_branch_id == branch_id
287
+ if was_active:
288
+ self._config_store.set_project_branch(alias, None)
289
+
290
+ cleanup = cleanup_branch_id_from_mapping(branch_id)
291
+
292
+ message_parts = [f"Branch ID {branch_id} deleted from project '{alias}'."]
293
+ if was_active:
294
+ message_parts.append("Active branch reset to main.")
295
+ if cleanup:
296
+ unlinked = ", ".join(cleanup["git_branches_unlinked"])
297
+ message_parts.append(f"Unlinked git branch(es): {unlinked}.")
298
+
299
+ result: dict[str, Any] = {
300
+ "project_alias": alias,
301
+ "branch_id": branch_id,
302
+ "was_active": was_active,
303
+ "message": " ".join(message_parts),
304
+ }
305
+ if cleanup:
306
+ result["mapping_cleanup"] = cleanup
307
+ return result
308
+
309
+ def get_merge_url(self, alias: str, branch_id: int | None = None) -> dict[str, Any]:
310
+ """Generate KBC UI merge URL for a development branch.
311
+
312
+ Does not call any API. Constructs the URL from stored project config.
313
+ If no branch_id is provided, uses the active branch from config.
314
+ After generating the URL, resets the active branch to main.
315
+
316
+ Args:
317
+ alias: Project alias.
318
+ branch_id: Branch ID. If None, uses active_branch_id from config.
319
+
320
+ Returns:
321
+ Dict with merge URL and instructions.
322
+
323
+ Raises:
324
+ ConfigError: If project not found or no branch ID available.
325
+ """
326
+ projects = self.resolve_projects([alias])
327
+ project = projects[alias]
328
+
329
+ effective_branch_id = branch_id if branch_id is not None else project.active_branch_id
330
+ if effective_branch_id is None:
331
+ raise ConfigError(
332
+ f"No branch specified and no active branch set for project '{alias}'. "
333
+ f"Use --branch ID or set an active branch with 'kbagent branch use'."
334
+ )
335
+
336
+ if project.project_id is None:
337
+ raise ConfigError(
338
+ f"Project '{alias}' has no project_id stored. "
339
+ "Re-add the project with 'kbagent project edit' to populate it."
340
+ )
341
+
342
+ stack_url = project.stack_url.rstrip("/")
343
+ merge_url = (
344
+ f"{stack_url}/admin/projects/{project.project_id}"
345
+ f"/branch/{effective_branch_id}/development-overview"
346
+ )
347
+
348
+ # Reset active branch to main after generating merge URL
349
+ self._config_store.set_project_branch(alias, None)
350
+
351
+ # Best-effort cleanup of any matching sync-workspace mapping so the
352
+ # user is not left referencing a soon-to-be-merged branch (Bug D).
353
+ from ..sync.branch_mapping import cleanup_branch_id_from_mapping
354
+
355
+ cleanup = cleanup_branch_id_from_mapping(effective_branch_id)
356
+
357
+ result: dict[str, Any] = {
358
+ "project_alias": alias,
359
+ "branch_id": effective_branch_id,
360
+ "url": merge_url,
361
+ "message": (
362
+ f"Open this URL to review and merge branch {effective_branch_id} "
363
+ f"in project '{alias}'. Active branch has been reset to main."
364
+ ),
365
+ }
366
+ if cleanup:
367
+ unlinked = ", ".join(cleanup["git_branches_unlinked"])
368
+ result["message"] += f" Unlinked git branch(es): {unlinked}."
369
+ result["mapping_cleanup"] = cleanup
370
+ return result
371
+
372
+ # ── Branch metadata ────────────────────────────────────────────────
373
+
374
+ def list_branch_metadata(
375
+ self,
376
+ alias: str,
377
+ branch_id: int | str = "default",
378
+ ) -> dict[str, Any]:
379
+ """List all metadata entries on a branch for a single project.
380
+
381
+ Args:
382
+ alias: Project alias.
383
+ branch_id: Branch ID or "default" for the main branch.
384
+
385
+ Returns:
386
+ Dict with project_alias, branch_id, and a key-sorted metadata list.
387
+
388
+ Raises:
389
+ ConfigError: If the project alias is not found.
390
+ KeboolaApiError: If the API call fails.
391
+ """
392
+ projects = self.resolve_projects([alias])
393
+ project = projects[alias]
394
+ client = self._client_factory(project.stack_url, project.token)
395
+ try:
396
+ entries = client.list_branch_metadata(branch_id=branch_id)
397
+ finally:
398
+ client.close()
399
+ return {
400
+ "project_alias": alias,
401
+ "branch_id": branch_id,
402
+ "metadata": sorted(entries, key=lambda e: e.get("key", "")),
403
+ }
404
+
405
+ def get_branch_metadata(
406
+ self,
407
+ alias: str,
408
+ key: str,
409
+ branch_id: int | str = "default",
410
+ ) -> dict[str, Any]:
411
+ """Get a single metadata value by key.
412
+
413
+ Args:
414
+ alias: Project alias.
415
+ key: Metadata key (e.g. "KBC.projectDescription").
416
+ branch_id: Branch ID or "default" for the main branch.
417
+
418
+ Returns:
419
+ Dict with project_alias, branch_id, key, value.
420
+
421
+ Raises:
422
+ ConfigError: If the project alias is not found.
423
+ KeboolaApiError: If the key is not present or the API call fails.
424
+ """
425
+ projects = self.resolve_projects([alias])
426
+ project = projects[alias]
427
+ client = self._client_factory(project.stack_url, project.token)
428
+ try:
429
+ value = client.get_branch_metadata_value(key=key, branch_id=branch_id)
430
+ finally:
431
+ client.close()
432
+ if value is METADATA_NOT_FOUND:
433
+ raise KeboolaApiError(
434
+ message=(
435
+ f"Metadata key '{key}' not found on branch '{branch_id}' of project '{alias}'."
436
+ ),
437
+ status_code=404,
438
+ error_code=ErrorCode.NOT_FOUND,
439
+ retryable=False,
440
+ )
441
+ return {
442
+ "project_alias": alias,
443
+ "branch_id": branch_id,
444
+ "key": key,
445
+ "value": value,
446
+ }
447
+
448
+ def set_branch_metadata(
449
+ self,
450
+ alias: str,
451
+ key: str,
452
+ value: str,
453
+ branch_id: int | str = "default",
454
+ ) -> dict[str, Any]:
455
+ """Set a single metadata key/value on a branch.
456
+
457
+ Args:
458
+ alias: Project alias.
459
+ key: Metadata key to set.
460
+ value: Metadata value (plain string; e.g. markdown).
461
+ branch_id: Branch ID or "default" for the main branch.
462
+
463
+ Returns:
464
+ Dict with project_alias, branch_id, key, value, result, message.
465
+
466
+ Raises:
467
+ ConfigError: If the project alias is not found.
468
+ KeboolaApiError: If the API call fails.
469
+ """
470
+ projects = self.resolve_projects([alias])
471
+ project = projects[alias]
472
+ client = self._client_factory(project.stack_url, project.token)
473
+ try:
474
+ result = client.set_branch_metadata(entries=[(key, value)], branch_id=branch_id)
475
+ finally:
476
+ client.close()
477
+ return {
478
+ "project_alias": alias,
479
+ "branch_id": branch_id,
480
+ "key": key,
481
+ "value": value,
482
+ "result": result,
483
+ "message": (f"Metadata '{key}' set on branch '{branch_id}' of project '{alias}'."),
484
+ }
485
+
486
+ def delete_branch_metadata(
487
+ self,
488
+ alias: str,
489
+ metadata_id: int | str,
490
+ branch_id: int | str = "default",
491
+ ) -> dict[str, Any]:
492
+ """Delete a metadata entry by its numeric ID.
493
+
494
+ Args:
495
+ alias: Project alias.
496
+ metadata_id: Metadata entry ID (from ``list_branch_metadata``).
497
+ branch_id: Branch ID or "default" for the main branch.
498
+
499
+ Returns:
500
+ Dict confirming the deletion.
501
+
502
+ Raises:
503
+ ConfigError: If the project alias is not found.
504
+ KeboolaApiError: If the API call fails.
505
+ """
506
+ projects = self.resolve_projects([alias])
507
+ project = projects[alias]
508
+ client = self._client_factory(project.stack_url, project.token)
509
+ try:
510
+ client.delete_branch_metadata(metadata_id=metadata_id, branch_id=branch_id)
511
+ finally:
512
+ client.close()
513
+ return {
514
+ "project_alias": alias,
515
+ "branch_id": branch_id,
516
+ "metadata_id": metadata_id,
517
+ "message": (
518
+ f"Metadata ID {metadata_id} deleted from branch '{branch_id}' of project '{alias}'."
519
+ ),
520
+ }
521
+
522
+ # ── Project description (convenience wrappers) ─────────────────────
523
+
524
+ def get_project_description(self, alias: str) -> dict[str, Any]:
525
+ """Get the dashboard project description for a project.
526
+
527
+ Reads the ``KBC.projectDescription`` metadata value from the default
528
+ branch -- this is what the Keboola UI displays on the project
529
+ dashboard.
530
+
531
+ Returns an empty description (not an error) if the key is unset, so
532
+ callers don't need to special-case freshly-provisioned projects.
533
+
534
+ Args:
535
+ alias: Project alias.
536
+
537
+ Returns:
538
+ Dict with project_alias, key, description (str, possibly empty).
539
+
540
+ Raises:
541
+ ConfigError: If the project alias is not found.
542
+ KeboolaApiError: If the API call fails.
543
+ """
544
+ projects = self.resolve_projects([alias])
545
+ project = projects[alias]
546
+ client = self._client_factory(project.stack_url, project.token)
547
+ try:
548
+ value = client.get_branch_metadata_value(
549
+ key=PROJECT_DESCRIPTION_KEY, branch_id="default"
550
+ )
551
+ finally:
552
+ client.close()
553
+ return {
554
+ "project_alias": alias,
555
+ "key": PROJECT_DESCRIPTION_KEY,
556
+ "description": "" if value is METADATA_NOT_FOUND else (value or ""),
557
+ }
558
+
559
+ def set_project_description(self, alias: str, description: str) -> dict[str, Any]:
560
+ """Set the dashboard project description for a project.
561
+
562
+ Writes to ``KBC.projectDescription`` on the default branch, which is
563
+ what the Keboola UI reads for the project dashboard description.
564
+
565
+ Args:
566
+ alias: Project alias.
567
+ description: Markdown content for the description.
568
+
569
+ Returns:
570
+ Dict with project_alias, key, description, result, message.
571
+
572
+ Raises:
573
+ ConfigError: If the project alias is not found.
574
+ KeboolaApiError: If the API call fails.
575
+ """
576
+ raw = self.set_branch_metadata(
577
+ alias=alias,
578
+ key=PROJECT_DESCRIPTION_KEY,
579
+ value=description,
580
+ branch_id="default",
581
+ )
582
+ return {
583
+ "project_alias": alias,
584
+ "key": PROJECT_DESCRIPTION_KEY,
585
+ "description": description,
586
+ "result": raw["result"],
587
+ "message": f"Project description updated for '{alias}' ({len(description)} chars).",
588
+ }