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,117 @@
1
+ """Bearer token auth middleware for kbagent serve.
2
+
3
+ Generates a single token at startup; clients must send it as
4
+ ``Authorization: Bearer <token>`` on every request. Public paths
5
+ (``/docs``, ``/openapi.json``, ``/health/auth-info``) are allowed
6
+ without auth so the UI can bootstrap.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import hmac
12
+ import logging
13
+ from dataclasses import dataclass
14
+
15
+ from fastapi import FastAPI, Request
16
+ from fastapi.responses import JSONResponse
17
+ from starlette.middleware.base import BaseHTTPMiddleware
18
+ from starlette.types import ASGIApp
19
+
20
+ from ..errors import ErrorCode
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+ PUBLIC_PATHS: frozenset[str] = frozenset(
25
+ {
26
+ "/docs",
27
+ "/redoc",
28
+ "/openapi.json",
29
+ "/health/ping",
30
+ }
31
+ )
32
+
33
+
34
+ @dataclass(frozen=True, slots=True)
35
+ class AuthSettings:
36
+ """Bearer-auth configuration for the FastAPI app."""
37
+
38
+ token: str
39
+ header_name: str = "authorization"
40
+
41
+
42
+ class BearerAuthMiddleware(BaseHTTPMiddleware):
43
+ """Reject requests without a matching ``Authorization: Bearer <token>`` header.
44
+
45
+ Token comparison uses :func:`hmac.compare_digest` to mitigate timing attacks.
46
+ Public paths (docs, openapi, ping) are unauthenticated so the UI can
47
+ discover the server without first knowing the token.
48
+ """
49
+
50
+ def __init__(self, app: ASGIApp, settings: AuthSettings) -> None:
51
+ super().__init__(app)
52
+ self._token = settings.token
53
+ self._header = settings.header_name
54
+
55
+ async def dispatch(self, request: Request, call_next): # type: ignore[override]
56
+ if request.method == "OPTIONS":
57
+ return await call_next(request)
58
+
59
+ path = request.url.path
60
+ if path in PUBLIC_PATHS or path.startswith("/docs") or path.startswith("/redoc"):
61
+ return await call_next(request)
62
+
63
+ # Single-process UI mode (`kbagent serve --ui`): the SPA shell
64
+ # (index.html, /assets/*, favicons, client-side route URLs) is
65
+ # served unauthenticated so the browser can boot. The HttpOnly
66
+ # ``kbagent_session`` cookie set on ``GET /`` then carries auth
67
+ # on every same-origin API call (see the cookie branch below).
68
+ # ``is_ui_public`` is set on app.state by ``_install_ui`` only when
69
+ # ``--ui`` is enabled; in pure-API mode it is absent and this skip
70
+ # never fires.
71
+ is_ui_public = getattr(request.app.state, "is_ui_public", None)
72
+ if callable(is_ui_public) and is_ui_public(request.method, path):
73
+ return await call_next(request)
74
+
75
+ header = request.headers.get(self._header, "")
76
+ scheme, _, value = header.partition(" ")
77
+ # Browser fallback: in single-process UI mode the SPA reaches the
78
+ # API on the same origin via ``credentials: "include"``, so the
79
+ # browser attaches a HttpOnly ``kbagent_session`` cookie set by
80
+ # ``GET /`` (see ``server.__init__._install_ui``). We accept it
81
+ # only when no Authorization header was present, so scripted callers
82
+ # (``kbagent http``, curl, BFF) keep the header path -- the token
83
+ # therefore never lands in URLs / access logs and never in any
84
+ # JS-readable surface.
85
+ if not value:
86
+ cookie_token = request.cookies.get("kbagent_session", "")
87
+ if cookie_token:
88
+ value = cookie_token
89
+ scheme = "bearer"
90
+ if scheme.lower() != "bearer" or not value:
91
+ return JSONResponse(
92
+ status_code=401,
93
+ content={
94
+ "status": "error",
95
+ "error": {
96
+ "code": str(ErrorCode.UNAUTHORIZED),
97
+ "message": "Missing Bearer token. Set Authorization header.",
98
+ },
99
+ },
100
+ )
101
+ if not hmac.compare_digest(value, self._token):
102
+ return JSONResponse(
103
+ status_code=401,
104
+ content={
105
+ "status": "error",
106
+ "error": {
107
+ "code": str(ErrorCode.UNAUTHORIZED),
108
+ "message": "Invalid Bearer token.",
109
+ },
110
+ },
111
+ )
112
+ return await call_next(request)
113
+
114
+
115
+ def install_auth(app: FastAPI, settings: AuthSettings) -> None:
116
+ """Attach the auth middleware to a FastAPI app."""
117
+ app.add_middleware(BearerAuthMiddleware, settings=settings)
@@ -0,0 +1,149 @@
1
+ """Service registry and FastAPI dependency providers.
2
+
3
+ A single :class:`ServiceRegistry` holds long-lived service instances bound
4
+ to one :class:`ConfigStore`. Endpoints declare what they need via FastAPI
5
+ ``Depends(get_<service>)`` helpers — clean DI without rebuilding services
6
+ per request.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from dataclasses import dataclass, field
12
+ from typing import TYPE_CHECKING
13
+
14
+ from fastapi import FastAPI, Request
15
+
16
+ from ..config_store import ConfigStore
17
+ from ..dev_portal_client import DeveloperPortalClient
18
+ from ..services.branch_service import BranchService
19
+ from ..services.component_service import ComponentService
20
+ from ..services.config_service import ConfigService
21
+ from ..services.data_app_git_service import DataAppGitService
22
+ from ..services.data_app_service import DataAppService
23
+ from ..services.deep_lineage_service import DeepLineageService
24
+ from ..services.dev_portal_service import DeveloperPortalService
25
+ from ..services.doctor_service import DoctorService
26
+ from ..services.encrypt_service import EncryptService
27
+ from ..services.feature_service import FeatureService
28
+ from ..services.flow_service import FlowService
29
+ from ..services.job_service import JobService
30
+ from ..services.kai_service import KaiService
31
+ from ..services.lineage_service import LineageService
32
+ from ..services.mcp_service import McpService
33
+ from ..services.member_service import MemberService
34
+ from ..services.org_service import OrgService
35
+ from ..services.project_service import ProjectService
36
+ from ..services.repo_validate_service import RepoValidateService
37
+ from ..services.schedule_service import ScheduleService
38
+ from ..services.search_service import SearchService
39
+ from ..services.semantic_layer_service import SemanticLayerService
40
+ from ..services.sharing_service import SharingService
41
+ from ..services.storage_service import StorageService
42
+ from ..services.stream_service import StreamService
43
+ from ..services.sync_service import SyncService
44
+ from ..services.variables_service import VariablesService
45
+ from ..services.version_service import VersionService
46
+ from ..services.workspace_service import WorkspaceService
47
+
48
+ if TYPE_CHECKING:
49
+ pass
50
+
51
+
52
+ @dataclass
53
+ class ServiceRegistry:
54
+ """Container of long-lived services for the FastAPI app."""
55
+
56
+ config_store: ConfigStore
57
+ # Self-contact info -- the URL + bearer token of the running serve.
58
+ # Injected into AI-agent / CLI subprocess env so `kbagent http` (and
59
+ # any HTTP-aware tool) can call this very server instead of forking
60
+ # a fresh process tree against potentially stale local config.
61
+ # Populated by ``create_app`` once uvicorn binding is known.
62
+ serve_url: str | None = None
63
+ serve_token: str | None = None
64
+ project: ProjectService = field(init=False)
65
+ config: ConfigService = field(init=False)
66
+ component: ComponentService = field(init=False)
67
+ storage: StorageService = field(init=False)
68
+ stream: StreamService = field(init=False)
69
+ job: JobService = field(init=False)
70
+ branch: BranchService = field(init=False)
71
+ workspace: WorkspaceService = field(init=False)
72
+ flow: FlowService = field(init=False)
73
+ schedule: ScheduleService = field(init=False)
74
+ lineage: LineageService = field(init=False)
75
+ deep_lineage: DeepLineageService = field(init=False)
76
+ sharing: SharingService = field(init=False)
77
+ data_app: DataAppService = field(init=False)
78
+ data_app_git: DataAppGitService = field(init=False)
79
+ dev_portal: DeveloperPortalService = field(init=False)
80
+ semantic_layer: SemanticLayerService = field(init=False)
81
+ repo_validate: RepoValidateService = field(init=False)
82
+ mcp: McpService = field(init=False)
83
+ kai: KaiService = field(init=False)
84
+ encrypt: EncryptService = field(init=False)
85
+ search: SearchService = field(init=False)
86
+ org: OrgService = field(init=False)
87
+ member: MemberService = field(init=False)
88
+ feature: FeatureService = field(init=False)
89
+ sync: SyncService = field(init=False)
90
+ variables: VariablesService = field(init=False)
91
+ doctor: DoctorService = field(init=False)
92
+ version: VersionService = field(init=False)
93
+
94
+ def __post_init__(self) -> None:
95
+ cs = self.config_store
96
+ self.project = ProjectService(config_store=cs)
97
+ self.config = ConfigService(config_store=cs)
98
+ self.component = ComponentService(config_store=cs)
99
+ self.storage = StorageService(config_store=cs)
100
+ self.stream = StreamService(config_store=cs)
101
+ self.job = JobService(config_store=cs)
102
+ self.branch = BranchService(config_store=cs)
103
+ self.workspace = WorkspaceService(config_store=cs)
104
+ self.flow = FlowService(config_store=cs)
105
+ self.schedule = ScheduleService(config_store=cs)
106
+ self.lineage = LineageService(config_store=cs)
107
+ self.deep_lineage = DeepLineageService(config_store=cs)
108
+ self.sharing = SharingService(config_store=cs)
109
+ self.data_app = DataAppService(config_store=cs)
110
+ self.data_app_git = DataAppGitService(config_store=cs)
111
+ self.dev_portal = DeveloperPortalService(
112
+ config_store=cs,
113
+ client_factory=lambda identity: DeveloperPortalClient(identity),
114
+ )
115
+ # SemanticLayerService takes both a storage client_factory (for
116
+ # validate --deep + add dataset --deep-fields + build) and an
117
+ # optional metastore_client_factory; the defaults work for both.
118
+ self.semantic_layer = SemanticLayerService(config_store=cs)
119
+ self.repo_validate = RepoValidateService(config_store=cs)
120
+ self.mcp = McpService(config_store=cs)
121
+ self.kai = KaiService(config_store=cs)
122
+ self.encrypt = EncryptService(config_store=cs)
123
+ self.search = SearchService(config_store=cs)
124
+ self.org = OrgService(config_store=cs)
125
+ self.member = MemberService(config_store=cs)
126
+ self.feature = FeatureService(config_store=cs)
127
+ self.sync = SyncService(config_store=cs)
128
+ self.variables = VariablesService(config_store=cs)
129
+ self.doctor = DoctorService(config_store=cs, mcp_service=self.mcp)
130
+ self.version = VersionService()
131
+
132
+
133
+ def install_registry(app: FastAPI, registry: ServiceRegistry) -> None:
134
+ """Attach the registry to the FastAPI app state."""
135
+ app.state.registry = registry
136
+
137
+
138
+ def get_registry(request: Request) -> ServiceRegistry:
139
+ """FastAPI dependency: return the registry from app state."""
140
+ return request.app.state.registry # type: ignore[no-any-return]
141
+
142
+
143
+ def get_manage_token(request: Request) -> str | None:
144
+ """Return the per-request manage token from the X-Manage-Token header.
145
+
146
+ Returns None if not provided. Endpoints that require it should validate.
147
+ Never logged, never persisted.
148
+ """
149
+ return request.headers.get("x-manage-token")
@@ -0,0 +1,303 @@
1
+ """Per-model pricing tables and cost calculation for AI agent runs.
2
+
3
+ Used by the agent-runs persistence path to attach a ``$`` figure and a
4
+ token breakdown to every persisted run, so the UI can show users the
5
+ cost of each scheduled-agent invocation without having to dig into the
6
+ raw event stream.
7
+
8
+ Pricing rates are quoted in **USD per 1,000,000 tokens** (USD/MTok).
9
+ Numbers reflect Anthropic's published pricing as of January 2026 and
10
+ follow the four-bucket structure that ``stream-json`` reports per
11
+ assistant turn:
12
+
13
+ - ``input_tokens`` -- regular prompt tokens this turn.
14
+ - ``output_tokens`` -- assistant-generated tokens this turn.
15
+ - ``cache_creation_input_tokens`` -- prompt tokens that *populated* the
16
+ prompt cache (5-min TTL); billed
17
+ at ~1.25x the input rate.
18
+ - ``cache_read_input_tokens`` -- prompt tokens served from a warm
19
+ cache; billed at ~0.10x the input
20
+ rate. This is where caching pays off.
21
+
22
+ When the live event stream surfaces ``total_cost_usd`` (claude does, in
23
+ the ``result`` event), prefer that authoritative figure; the manual
24
+ calculation here is the fallback for streams that do not.
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ from typing import Any
30
+
31
+ # Per-model rates in USD per 1M tokens. Buckets:
32
+ # input | output | cache_create | cache_read
33
+ # Source: anthropic.com/pricing as of 2026-01.
34
+ PRICING_USD_PER_MTOK: dict[str, dict[str, float]] = {
35
+ # Claude Opus 4.7 (incl. 1M ctx variant) -- premium tier.
36
+ "claude-opus-4-7": {
37
+ "input": 15.00,
38
+ "output": 75.00,
39
+ "cache_create": 18.75,
40
+ "cache_read": 1.50,
41
+ },
42
+ "claude-opus-4-7[1m]": {
43
+ "input": 15.00,
44
+ "output": 75.00,
45
+ "cache_create": 18.75,
46
+ "cache_read": 1.50,
47
+ },
48
+ # Earlier Opus revisions kept on the same rate (Anthropic has not
49
+ # split historical Opus pricing to date).
50
+ "claude-opus-4-6": {
51
+ "input": 15.00,
52
+ "output": 75.00,
53
+ "cache_create": 18.75,
54
+ "cache_read": 1.50,
55
+ },
56
+ "claude-opus-4-5": {
57
+ "input": 15.00,
58
+ "output": 75.00,
59
+ "cache_create": 18.75,
60
+ "cache_read": 1.50,
61
+ },
62
+ # Sonnet 4.6 -- balanced tier.
63
+ "claude-sonnet-4-6": {
64
+ "input": 3.00,
65
+ "output": 15.00,
66
+ "cache_create": 3.75,
67
+ "cache_read": 0.30,
68
+ },
69
+ "claude-sonnet-4-5": {
70
+ "input": 3.00,
71
+ "output": 15.00,
72
+ "cache_create": 3.75,
73
+ "cache_read": 0.30,
74
+ },
75
+ # Haiku 4.5 -- speed tier.
76
+ "claude-haiku-4-5": {
77
+ "input": 1.00,
78
+ "output": 5.00,
79
+ "cache_create": 1.25,
80
+ "cache_read": 0.10,
81
+ },
82
+ "claude-haiku-4-5-20251001": {
83
+ "input": 1.00,
84
+ "output": 5.00,
85
+ "cache_create": 1.25,
86
+ "cache_read": 0.10,
87
+ },
88
+ }
89
+
90
+ # Fallback rate when an unknown model id appears in the stream. We pick
91
+ # Sonnet's rate because it's the median tier; users will see *roughly*
92
+ # the right ballpark even if the model is mis-tagged. The structured
93
+ # response also returns ``model_recognized: false`` so the UI can show
94
+ # a "approximate" badge.
95
+ FALLBACK_RATE: dict[str, float] = {
96
+ "input": 3.00,
97
+ "output": 15.00,
98
+ "cache_create": 3.75,
99
+ "cache_read": 0.30,
100
+ }
101
+
102
+
103
+ def get_rate(model: str | None) -> tuple[dict[str, float], bool]:
104
+ """Resolve a per-MTok rate dict for a model id.
105
+
106
+ Returns ``(rate, recognized)``: ``recognized`` is False when we fell
107
+ back to the median tier, so callers can mark the figure as approximate.
108
+ Matching is exact first, then prefix-based (``claude-opus-4-7-foo``
109
+ matches ``claude-opus-4-7``) so future point-release tags still hit.
110
+ """
111
+ if not model:
112
+ return FALLBACK_RATE, False
113
+ norm = model.strip().lower()
114
+ if norm in PRICING_USD_PER_MTOK:
115
+ return PRICING_USD_PER_MTOK[norm], True
116
+ # Longest-prefix wins so ``claude-opus-4-7-future`` doesn't accidentally
117
+ # match ``claude-opus-4-5``.
118
+ candidates = sorted(
119
+ (k for k in PRICING_USD_PER_MTOK if norm.startswith(k)),
120
+ key=lambda k: len(k),
121
+ reverse=True,
122
+ )
123
+ if candidates:
124
+ return PRICING_USD_PER_MTOK[candidates[0]], True
125
+ return FALLBACK_RATE, False
126
+
127
+
128
+ def compute_cost(
129
+ model: str | None,
130
+ usage: dict[str, Any] | None,
131
+ ) -> dict[str, Any]:
132
+ """Compute cost breakdown for a single turn or aggregated usage.
133
+
134
+ ``usage`` is the dict shape claude emits per assistant message:
135
+
136
+ {"input_tokens": 1234, "output_tokens": 567,
137
+ "cache_creation_input_tokens": 8000, "cache_read_input_tokens": 2000}
138
+
139
+ Returns a self-describing dict with both the rate that was applied
140
+ and the per-bucket cost contribution, so the UI can render a
141
+ breakdown table without re-doing the math:
142
+
143
+ {
144
+ "model": "claude-opus-4-7",
145
+ "model_recognized": true,
146
+ "rate_per_mtok": {"input": 15.0, "output": 75.0, ...},
147
+ "tokens": {input: 1234, output: 567, ...},
148
+ "cost_usd": {input: 0.0185, output: 0.0425, cache_create: 0.15, cache_read: 0.003,
149
+ total: 0.214}
150
+ }
151
+ """
152
+ usage = usage or {}
153
+ rate, recognized = get_rate(model)
154
+ in_tok = int(usage.get("input_tokens") or 0)
155
+ out_tok = int(usage.get("output_tokens") or 0)
156
+ cc_tok = int(usage.get("cache_creation_input_tokens") or 0)
157
+ cr_tok = int(usage.get("cache_read_input_tokens") or 0)
158
+ in_cost = (in_tok / 1_000_000) * rate["input"]
159
+ out_cost = (out_tok / 1_000_000) * rate["output"]
160
+ cc_cost = (cc_tok / 1_000_000) * rate["cache_create"]
161
+ cr_cost = (cr_tok / 1_000_000) * rate["cache_read"]
162
+ total_cost = in_cost + out_cost + cc_cost + cr_cost
163
+ return {
164
+ "model": model,
165
+ "model_recognized": recognized,
166
+ "rate_per_mtok": rate,
167
+ "tokens": {
168
+ "input": in_tok,
169
+ "output": out_tok,
170
+ "cache_create": cc_tok,
171
+ "cache_read": cr_tok,
172
+ "total": in_tok + out_tok + cc_tok + cr_tok,
173
+ },
174
+ "cost_usd": {
175
+ "input": round(in_cost, 6),
176
+ "output": round(out_cost, 6),
177
+ "cache_create": round(cc_cost, 6),
178
+ "cache_read": round(cr_cost, 6),
179
+ "total": round(total_cost, 6),
180
+ },
181
+ }
182
+
183
+
184
+ def aggregate_usage_from_events(events: list[dict[str, Any]]) -> dict[str, Any]:
185
+ """Sum per-assistant-turn ``usage`` blocks into a single dict.
186
+
187
+ Walks the stream-json event list, picks every ``assistant`` event,
188
+ and accumulates the four token buckets. Final ``result`` event also
189
+ carries a usage block (often equal to the last assistant turn) which
190
+ we deliberately *skip* to avoid double-counting -- claude's result
191
+ usage is already reflected in the assistant turns it summarizes.
192
+ """
193
+ totals = {
194
+ "input_tokens": 0,
195
+ "output_tokens": 0,
196
+ "cache_creation_input_tokens": 0,
197
+ "cache_read_input_tokens": 0,
198
+ }
199
+ for evt in events:
200
+ if evt.get("event") not in {"stdout"}:
201
+ continue
202
+ data = evt.get("data") or {}
203
+ if data.get("type") != "assistant":
204
+ continue
205
+ usage = (data.get("message") or {}).get("usage") or {}
206
+ for k in totals:
207
+ v = usage.get(k)
208
+ if isinstance(v, int):
209
+ totals[k] += v
210
+ return totals
211
+
212
+
213
+ def extract_model_from_events(events: list[dict[str, Any]]) -> str | None:
214
+ """Return the model id from the first ``system.init`` event, if present."""
215
+ for evt in events:
216
+ if evt.get("event") != "stdout":
217
+ continue
218
+ data = evt.get("data") or {}
219
+ if data.get("type") == "system" and data.get("subtype") == "init":
220
+ model = data.get("model")
221
+ if isinstance(model, str):
222
+ return model
223
+ return None
224
+
225
+
226
+ def extract_total_cost_from_events(events: list[dict[str, Any]]) -> float | None:
227
+ """Return claude's authoritative ``total_cost_usd`` from the result event.
228
+
229
+ When present this is preferred over the manual ``compute_cost`` figure
230
+ (it accounts for any per-turn pricing nuance Anthropic applies that
231
+ we cannot model from the public rate card).
232
+ """
233
+ for evt in events:
234
+ if evt.get("event") != "stdout":
235
+ continue
236
+ data = evt.get("data") or {}
237
+ if data.get("type") == "result":
238
+ cost = data.get("total_cost_usd")
239
+ if isinstance(cost, int | float):
240
+ return float(cost)
241
+ return None
242
+
243
+
244
+ def extract_tool_summary(events: list[dict[str, Any]]) -> dict[str, Any]:
245
+ """Walk events and produce a per-tool call count + ordered list of tools used.
246
+
247
+ Returns:
248
+ {
249
+ "count": <total tool_use blocks>,
250
+ "by_tool": {"Bash": 4, "Read": 7, ...},
251
+ "errors": <count of tool_results with is_error=true>,
252
+ }
253
+ """
254
+ by_tool: dict[str, int] = {}
255
+ total = 0
256
+ errors = 0
257
+ for evt in events:
258
+ if evt.get("event") != "stdout":
259
+ continue
260
+ data = evt.get("data") or {}
261
+ if data.get("type") == "assistant":
262
+ for block in (data.get("message") or {}).get("content") or []:
263
+ if isinstance(block, dict) and block.get("type") == "tool_use":
264
+ name = str(block.get("name") or "?")
265
+ by_tool[name] = by_tool.get(name, 0) + 1
266
+ total += 1
267
+ elif data.get("type") == "user":
268
+ for block in (data.get("message") or {}).get("content") or []:
269
+ if (
270
+ isinstance(block, dict)
271
+ and block.get("type") == "tool_result"
272
+ and block.get("is_error") is True
273
+ ):
274
+ errors += 1
275
+ return {"count": total, "by_tool": by_tool, "errors": errors}
276
+
277
+
278
+ def build_run_summary(events: list[dict[str, Any]]) -> dict[str, Any]:
279
+ """Single entry point: build the summary blob persisted on a finished run.
280
+
281
+ Combines model, token totals, cost breakdown (preferring claude's
282
+ authoritative number), and tool-call breakdown. Shape is stable across
283
+ runs so the UI can render unconditionally.
284
+ """
285
+ model = extract_model_from_events(events)
286
+ usage = aggregate_usage_from_events(events)
287
+ cost_breakdown = compute_cost(model, usage)
288
+ authoritative = extract_total_cost_from_events(events)
289
+ if authoritative is not None:
290
+ cost_breakdown["cost_usd"]["total"] = round(authoritative, 6)
291
+ cost_breakdown["cost_usd"]["source"] = "claude_result"
292
+ else:
293
+ cost_breakdown["cost_usd"]["source"] = "computed"
294
+ tools = extract_tool_summary(events)
295
+ return {
296
+ "model": model,
297
+ "model_recognized": cost_breakdown["model_recognized"],
298
+ "tokens": cost_breakdown["tokens"],
299
+ "rate_per_mtok": cost_breakdown["rate_per_mtok"],
300
+ "cost_usd": cost_breakdown["cost_usd"],
301
+ "tools": tools,
302
+ "events_count": len(events),
303
+ }
@@ -0,0 +1 @@
1
+ """FastAPI routers for kbagent serve."""