keboola-cli 0.65.0__tar.gz → 0.66.0__tar.gz

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 (387) hide show
  1. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/CLAUDE.md +13 -8
  2. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/PKG-INFO +1 -1
  3. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/pyproject.toml +1 -1
  4. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/scripts/hatch_build.py +13 -1
  5. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/__init__.py +4 -0
  6. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/changelog.py +46 -1
  7. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/cli.py +5 -0
  8. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/client.py +278 -0
  9. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/_data_app_git.py +0 -67
  10. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/context.py +18 -12
  11. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/serve.py +9 -0
  12. keboola_cli-0.66.0/src/keboola_agent_cli/commands/token.py +197 -0
  13. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/lib.py +100 -1
  14. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/permissions.py +5 -1
  15. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/result_models.py +74 -0
  16. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/app.py +59 -7
  17. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/dependencies.py +3 -0
  18. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/data_apps.py +0 -35
  19. keboola_cli-0.66.0/src/keboola_agent_cli/server/routers/token.py +74 -0
  20. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/_encryption.py +21 -2
  21. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/data_app_service.py +20 -214
  22. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/stream_service.py +6 -30
  23. keboola_cli-0.66.0/src/keboola_agent_cli/services/token_service.py +114 -0
  24. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/version_service.py +24 -8
  25. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/stream_client.py +53 -1
  26. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_build_hook.py +12 -2
  27. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_client.py +44 -0
  28. keboola_cli-0.66.0/tests/test_client_device_enrollment.py +364 -0
  29. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_data_app_cli.py +0 -69
  30. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_data_app_service.py +4 -67
  31. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_e2e.py +241 -23
  32. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_encryption.py +49 -0
  33. keboola_cli-0.66.0/tests/test_lib_device_enrollment.py +255 -0
  34. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_serve_ui.py +60 -0
  35. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_server_router_calls.py +0 -20
  36. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_server_smoke.py +1 -0
  37. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_sync_encrypt.py +6 -3
  38. keboola_cli-0.66.0/tests/test_token_cli.py +190 -0
  39. keboola_cli-0.66.0/tests/test_token_service.py +143 -0
  40. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_version_service.py +43 -0
  41. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/.gitignore +0 -0
  42. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/CONTRIBUTING.md +0 -0
  43. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/Makefile +0 -0
  44. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/README.md +0 -0
  45. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/examples/README.md +0 -0
  46. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/examples/storage_tui/README.md +0 -0
  47. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/plugins/kbagent/.claude-plugin/CLAUDE.md +0 -0
  48. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/scripts/benchmark.py +0 -0
  49. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/scripts/check_command_sync.py +0 -0
  50. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/scripts/check_error_codes.py +0 -0
  51. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/scripts/check_wheel_ui.py +0 -0
  52. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/scripts/debug_linkedby_type.sh +0 -0
  53. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/scripts/generate_changelog.py +0 -0
  54. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/scripts/generate_skill.py +0 -0
  55. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/scripts/lineage_deep.py +0 -0
  56. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/scripts/post-edit-quality.sh +0 -0
  57. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/scripts/pre-commit +0 -0
  58. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/scripts/sync_version.py +0 -0
  59. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/scripts/web-dev.sh +0 -0
  60. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/__main__.py +0 -0
  61. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/ai_client.py +0 -0
  62. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/auto_update.py +0 -0
  63. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/__init__.py +0 -0
  64. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/_data_app_runtime.py +0 -0
  65. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/_helpers.py +0 -0
  66. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/_metadata_input.py +0 -0
  67. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/_semantic_layer_crud.py +0 -0
  68. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/_semantic_layer_helpers.py +0 -0
  69. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/_semantic_layer_reference_data.py +0 -0
  70. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/agent.py +0 -0
  71. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/branch.py +0 -0
  72. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/changelog.py +0 -0
  73. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/component.py +0 -0
  74. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/config.py +0 -0
  75. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/data_app.py +0 -0
  76. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/dev_portal.py +0 -0
  77. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/doctor.py +0 -0
  78. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/encrypt.py +0 -0
  79. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/feature.py +0 -0
  80. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/flow.py +0 -0
  81. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/http_client.py +0 -0
  82. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/init.py +0 -0
  83. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/job.py +0 -0
  84. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/kai.py +0 -0
  85. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/lineage.py +0 -0
  86. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/org.py +0 -0
  87. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/permissions.py +0 -0
  88. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/project.py +0 -0
  89. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/repl.py +0 -0
  90. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/schedule.py +0 -0
  91. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/search.py +0 -0
  92. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/semantic_layer.py +0 -0
  93. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/sharing.py +0 -0
  94. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/storage.py +0 -0
  95. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/stream.py +0 -0
  96. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/sync.py +0 -0
  97. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/tool.py +0 -0
  98. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/version.py +0 -0
  99. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/commands/workspace.py +0 -0
  100. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/config_store.py +0 -0
  101. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/constants.py +0 -0
  102. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/data_science_client.py +0 -0
  103. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/dev_portal_client.py +0 -0
  104. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/errors.py +0 -0
  105. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/http_base.py +0 -0
  106. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/json_utils.py +0 -0
  107. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/manage_client.py +0 -0
  108. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/metastore_client.py +0 -0
  109. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/models.py +0 -0
  110. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/output.py +0 -0
  111. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/py.typed +0 -0
  112. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/__init__.py +0 -0
  113. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/agent_runner.py +0 -0
  114. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/agents_store.py +0 -0
  115. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/auth.py +0 -0
  116. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/pricing.py +0 -0
  117. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/__init__.py +0 -0
  118. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/agents.py +0 -0
  119. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/ai_chat.py +0 -0
  120. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/branches.py +0 -0
  121. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/components.py +0 -0
  122. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/configs.py +0 -0
  123. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/dev_portal.py +0 -0
  124. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/encrypt.py +0 -0
  125. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/feature.py +0 -0
  126. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/flows.py +0 -0
  127. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/health.py +0 -0
  128. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/jobs.py +0 -0
  129. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/kai.py +0 -0
  130. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/lineage.py +0 -0
  131. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/mcp.py +0 -0
  132. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/members.py +0 -0
  133. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/org.py +0 -0
  134. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/projects.py +0 -0
  135. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/schedules.py +0 -0
  136. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/search.py +0 -0
  137. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/semantic_layer.py +0 -0
  138. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/sharing.py +0 -0
  139. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/storage.py +0 -0
  140. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/stream.py +0 -0
  141. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/routers/workspaces.py +0 -0
  142. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/run_broadcaster.py +0 -0
  143. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/server/sse.py +0 -0
  144. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/__init__.py +0 -0
  145. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/_semantic_layer_cascade.py +0 -0
  146. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/_semantic_layer_crud.py +0 -0
  147. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/_semantic_layer_internals.py +0 -0
  148. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/_semantic_layer_lookup.py +0 -0
  149. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/_semantic_layer_reference_data.py +0 -0
  150. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/_sync_bindings.py +0 -0
  151. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/_sync_branch.py +0 -0
  152. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/_sync_bulk.py +0 -0
  153. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/_sync_clone.py +0 -0
  154. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/_sync_models.py +0 -0
  155. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/_sync_push_ops.py +0 -0
  156. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/_sync_storage.py +0 -0
  157. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/_sync_writeback.py +0 -0
  158. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/agent_service.py +0 -0
  159. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/base.py +0 -0
  160. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/branch_service.py +0 -0
  161. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/component_service.py +0 -0
  162. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/config_service.py +0 -0
  163. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/data_app_git_service.py +0 -0
  164. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/deep_lineage_service.py +0 -0
  165. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/dev_portal_service.py +0 -0
  166. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/doctor_service.py +0 -0
  167. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/encrypt_service.py +0 -0
  168. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/feature_service.py +0 -0
  169. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/flow_service.py +0 -0
  170. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/flow_validation.py +0 -0
  171. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/http_forwarder_service.py +0 -0
  172. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/job_idempotency_store.py +0 -0
  173. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/job_service.py +0 -0
  174. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/kai_service.py +0 -0
  175. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/lineage_service.py +0 -0
  176. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/mcp_service.py +0 -0
  177. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/mcp_transport.py +0 -0
  178. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/member_service.py +0 -0
  179. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/org_service.py +0 -0
  180. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/project_service.py +0 -0
  181. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/repo_validate_service.py +0 -0
  182. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/schedule_service.py +0 -0
  183. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/search_service.py +0 -0
  184. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/semantic_layer_service.py +0 -0
  185. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/sharing_service.py +0 -0
  186. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/storage_service.py +0 -0
  187. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/sync_service.py +0 -0
  188. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/variables_service.py +0 -0
  189. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/services/workspace_service.py +0 -0
  190. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/sync/__init__.py +0 -0
  191. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/sync/branch_mapping.py +0 -0
  192. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/sync/clone.py +0 -0
  193. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/sync/code_extraction.py +0 -0
  194. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/sync/config_format.py +0 -0
  195. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/sync/diff_engine.py +0 -0
  196. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/sync/git_utils.py +0 -0
  197. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/sync/manifest.py +0 -0
  198. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/sync/naming.py +0 -0
  199. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/sync/secrets.py +0 -0
  200. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/src/keboola_agent_cli/sync/sql_split.py +0 -0
  201. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/__init__.py +0 -0
  202. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/conftest.py +0 -0
  203. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/helpers.py +0 -0
  204. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_agent_cli.py +0 -0
  205. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_agent_prompt.py +0 -0
  206. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_agent_prompt_helper.py +0 -0
  207. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_agent_runner.py +0 -0
  208. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_agent_service.py +0 -0
  209. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_agents_store_events.py +0 -0
  210. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_ai_client.py +0 -0
  211. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_auto_update.py +0 -0
  212. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_base_service.py +0 -0
  213. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_branch_metadata_cli.py +0 -0
  214. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_branch_metadata_service.py +0 -0
  215. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_branch_service.py +0 -0
  216. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_changelog_check.py +0 -0
  217. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_changelog_render.py +0 -0
  218. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_check_command_sync.py +0 -0
  219. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_choice_enums.py +0 -0
  220. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_cli.py +0 -0
  221. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_component_cli.py +0 -0
  222. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_component_service.py +0 -0
  223. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_config_create_cli.py +0 -0
  224. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_config_create_service.py +0 -0
  225. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_config_encryption.py +0 -0
  226. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_config_metadata.py +0 -0
  227. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_config_rename.py +0 -0
  228. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_config_rename_cli.py +0 -0
  229. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_config_resolution.py +0 -0
  230. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_config_row_cli.py +0 -0
  231. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_config_row_service.py +0 -0
  232. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_config_search.py +0 -0
  233. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_config_search_cli.py +0 -0
  234. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_config_set_default_bucket.py +0 -0
  235. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_config_store.py +0 -0
  236. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_config_update.py +0 -0
  237. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_data_app_git_repo.py +0 -0
  238. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_data_app_plain_env_keys.py +0 -0
  239. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_data_app_secrets_cli.py +0 -0
  240. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_data_app_secrets_service.py +0 -0
  241. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_data_app_validate_repo_service.py +0 -0
  242. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_deep_lineage_service.py +0 -0
  243. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_dev_portal_cli.py +0 -0
  244. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_dev_portal_client.py +0 -0
  245. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_dev_portal_service.py +0 -0
  246. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_doctor_service.py +0 -0
  247. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_e2e_config_dir_mode.py +0 -0
  248. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_e2e_lineage_deep.py +0 -0
  249. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_encrypt_cli.py +0 -0
  250. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_encrypt_service.py +0 -0
  251. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_errors.py +0 -0
  252. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_feature_cli.py +0 -0
  253. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_feature_service.py +0 -0
  254. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_file_locking.py +0 -0
  255. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_flow_cli.py +0 -0
  256. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_flow_service.py +0 -0
  257. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_flow_validation.py +0 -0
  258. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_helpers.py +0 -0
  259. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_http_base.py +0 -0
  260. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_http_client_cmd.py +0 -0
  261. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_http_forwarder_service.py +0 -0
  262. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_integration.py +0 -0
  263. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_job_idempotency_store.py +0 -0
  264. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_json_utils.py +0 -0
  265. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_kai_cli.py +0 -0
  266. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_kai_service.py +0 -0
  267. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_lib.py +0 -0
  268. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_lineage_service.py +0 -0
  269. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_local_ai_chat.py +0 -0
  270. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_manage_client.py +0 -0
  271. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_manage_token_bulk.py +0 -0
  272. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_manage_token_cli.py +0 -0
  273. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_mcp_service.py +0 -0
  274. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_mcp_transport.py +0 -0
  275. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_member_cli.py +0 -0
  276. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_member_service.py +0 -0
  277. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_metastore_client.py +0 -0
  278. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_models.py +0 -0
  279. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_normalize_script.py +0 -0
  280. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_org_service.py +0 -0
  281. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_output.py +0 -0
  282. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_permissions.py +0 -0
  283. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_permissions_cli.py +0 -0
  284. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_pricing.py +0 -0
  285. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_project_edit.py +0 -0
  286. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_project_edit_cli.py +0 -0
  287. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_project_info_cli.py +0 -0
  288. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_project_info_service.py +0 -0
  289. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_repl.py +0 -0
  290. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_result_models.py +0 -0
  291. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_run_broadcaster.py +0 -0
  292. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_schedule_cli.py +0 -0
  293. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_schedule_service.py +0 -0
  294. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_search_cli.py +0 -0
  295. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_search_service.py +0 -0
  296. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_semantic_layer_cli.py +0 -0
  297. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_semantic_layer_service.py +0 -0
  298. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_serve_conversation_id.py +0 -0
  299. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_server_semantic_layer_routes_e2e.py +0 -0
  300. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_services.py +0 -0
  301. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_sharing_service.py +0 -0
  302. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_sql_split.py +0 -0
  303. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_storage_clone.py +0 -0
  304. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_storage_delete.py +0 -0
  305. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_storage_describe_cli.py +0 -0
  306. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_storage_describe_service.py +0 -0
  307. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_storage_empty_definition.py +0 -0
  308. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_storage_export_filters.py +0 -0
  309. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_storage_files.py +0 -0
  310. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_storage_swap.py +0 -0
  311. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_storage_tables.py +0 -0
  312. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_storage_truncate.py +0 -0
  313. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_storage_write.py +0 -0
  314. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_stream_cli.py +0 -0
  315. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_stream_client.py +0 -0
  316. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_stream_service.py +0 -0
  317. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_sync_branch_mapping.py +0 -0
  318. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_sync_cli.py +0 -0
  319. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_sync_clone.py +0 -0
  320. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_sync_code_extraction.py +0 -0
  321. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_sync_config_format.py +0 -0
  322. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_sync_diff_engine.py +0 -0
  323. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_sync_e2e.py +0 -0
  324. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_sync_force_pull_baseline.py +0 -0
  325. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_sync_manifest.py +0 -0
  326. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_sync_naming.py +0 -0
  327. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_sync_plaintext_audit.py +0 -0
  328. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_sync_secrets.py +0 -0
  329. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_sync_service.py +0 -0
  330. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_sync_storage_jobs.py +0 -0
  331. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_sync_version_script.py +0 -0
  332. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_variables_cli.py +0 -0
  333. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_variables_service.py +0 -0
  334. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_workspace_cli.py +0 -0
  335. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_workspace_gc.py +0 -0
  336. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_workspace_service.py +0 -0
  337. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/tests/test_workspace_sql_helper.py +0 -0
  338. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/README.md +0 -0
  339. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/index.html +0 -0
  340. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/package-lock.json +0 -0
  341. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/package.json +0 -0
  342. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/postcss.config.js +0 -0
  343. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/App.tsx +0 -0
  344. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/api/client.ts +0 -0
  345. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/components/AgentRunView.tsx +0 -0
  346. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/components/Drawer.tsx +0 -0
  347. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/components/Empty.tsx +0 -0
  348. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/components/JsonView.tsx +0 -0
  349. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/components/ManageTokenModal.tsx +0 -0
  350. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/components/Table.tsx +0 -0
  351. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/index.css +0 -0
  352. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/layout/Shell.tsx +0 -0
  353. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/layout/Sidebar.tsx +0 -0
  354. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/layout/StatusBar.tsx +0 -0
  355. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/layout/TopBar.tsx +0 -0
  356. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/main.tsx +0 -0
  357. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/Agents.tsx +0 -0
  358. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/Branches.tsx +0 -0
  359. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/Changelog.tsx +0 -0
  360. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/Components.tsx +0 -0
  361. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/Configs.tsx +0 -0
  362. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/Dashboard.tsx +0 -0
  363. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/DataApps.tsx +0 -0
  364. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/Doctor.tsx +0 -0
  365. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/Encrypt.tsx +0 -0
  366. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/Flows.tsx +0 -0
  367. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/Jobs.tsx +0 -0
  368. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/Lineage.tsx +0 -0
  369. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/LocalAi.tsx +0 -0
  370. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/Mcp.tsx +0 -0
  371. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/Members.tsx +0 -0
  372. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/Org.tsx +0 -0
  373. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/Projects.tsx +0 -0
  374. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/Schedules.tsx +0 -0
  375. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/Search.tsx +0 -0
  376. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/SemanticLayer.tsx +0 -0
  377. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/SemanticLayerDialogs.tsx +0 -0
  378. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/Sharing.tsx +0 -0
  379. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/Storage.tsx +0 -0
  380. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/Streams.tsx +0 -0
  381. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/pages/Workspaces.tsx +0 -0
  382. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/state.tsx +0 -0
  383. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/theme.tsx +0 -0
  384. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/src/types.ts +0 -0
  385. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/tailwind.config.ts +0 -0
  386. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/tsconfig.json +0 -0
  387. {keboola_cli-0.65.0 → keboola_cli-0.66.0}/web/frontend/vite.config.ts +0 -0
@@ -385,6 +385,14 @@ kbagent feature user-show --project ALIAS --email EMAIL
385
385
  kbagent feature user-add --project ALIAS --email EMAIL --feature NAME [--dry-run] [--yes]
386
386
  kbagent feature user-remove --project ALIAS --email EMAIL --feature NAME [--dry-run] [--yes]
387
387
 
388
+ # token: scoped Storage tokens (Keboola single-bucket-write pattern; acting token needs canManageTokens; secret shown once).
389
+ kbagent token create --project NAME --description DESC [--bucket-write BUCKET ...] [--bucket-read BUCKET ...] [--component-access ID ...] [--can-read-all-file-uploads] [--expires-in N]
390
+ kbagent token delete --project NAME --token-id ID [--yes]
391
+ kbagent token refresh --project NAME --token-id ID [--yes]
392
+ # SDK (importable Client(url,token)) now exposes create_scoped_token / delete_token / refresh_token /
393
+ # create_stream_source / get_stream_source / list_stream_sources / delete_stream_source: dicts on .raw,
394
+ # typed ScopedTokenResult / StreamSourceResult on the facade. See docs/sdk.md.
395
+
388
396
  # permissions: session write/destructive firewall. The top-level --deny-writes / --deny-destructive
389
397
  # flags are the one-shot form; `permissions set` persists a policy (mode allow|deny + allow/deny patterns
390
398
  # like cli:write, cli:destructive, tool:write). The agent guards rails against mistakes; not a sandbox.
@@ -430,13 +438,11 @@ kbagent data-app create --project ALIAS --name NAME --slug SLUG (--git-repo URL
430
438
  # NO parameters.dataApp.git block, and forces --no-deploy (nothing to run yet). Mutually exclusive with
431
439
  # --git-repo and all --git-*/PAT auth flags. Full flow to a RUNNING managed-repo app (0.65.0+):
432
440
  # (1) create --use-managed-git-repo -> (2) `git-credentials-create --type http_token --permissions
433
- # readWrite` + `git push` code to the managed repo URL (from `git-repo`) -> (3) `data-app
434
- # git-bind-credential` (mints an http_token ON the app, encrypts it under the project KMS, and writes
435
- # parameters.dataApp.git so the runtime's `git clone` can authenticate -- required on stacks that do NOT
436
- # inject managed-repo creds at deploy time) -> (4) `data-app deploy`. deploy pins the LATEST
437
- # configVersion when a git block is present and omits it for a PURE managed repo (deploys from
438
- # managedGitRepoId). Use `data-app runs` to debug a deploy that reverts to stopped (setup-phase
439
- # failures, e.g. git-clone "could not read Username", produce no container logs).
441
+ # readWrite` + `git push` code to the managed repo URL (from `git-repo`) -> (3) `data-app deploy`.
442
+ # The platform injects the clone credentials at deploy time, so no credential wiring is needed.
443
+ # deploy pins the LATEST configVersion when a git block is present and omits it for a PURE managed
444
+ # repo (deploys from managedGitRepoId). Use `data-app runs` to debug a deploy that reverts to
445
+ # stopped (setup-phase failures produce no container logs).
440
446
  kbagent data-app deploy --project NAME --app-id ID [--config-version N] [--wait] [--timeout SECONDS] [--branch ID]
441
447
  kbagent data-app start --project NAME --app-id ID [--wait] [--timeout SECONDS]
442
448
  kbagent data-app stop --project NAME --app-id ID [--wait] [--timeout SECONDS]
@@ -454,7 +460,6 @@ kbagent data-app git-branches --project NAME --app-id ID
454
460
  kbagent data-app git-entrypoints --project NAME --app-id ID
455
461
  kbagent data-app git-credentials --project NAME --app-id ID
456
462
  kbagent data-app git-credentials-create --project NAME --app-id ID --type ssh_key|http_token --permissions readOnly|readWrite [--public-key KEY | --public-key-file PATH] [--name LABEL] [--yes]
457
- kbagent data-app git-bind-credential --project NAME --app-id ID [--branch-name main] [--permissions readOnly|readWrite] [--dry-run]
458
463
  # git-repo/git-branches/git-entrypoints introspect the deployed-from git repo (sandboxes-service /apps/{id}/git-repo/*); they return 409 "no Git repository configured" until the app has been DEPLOYED at least once (git config syncs Storage->DS record at deploy). git-credentials* manage credentials for a MANAGED repo only; apps from `data-app create --git-repo` are external => git-credentials-create returns 409. http_token mints a ONE-TIME secret (shown once); credentials endpoints need an admin storage token.
459
464
 
460
465
  kbagent component list [--project NAME] [--type TYPE] [--query QUERY]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: keboola-cli
3
- Version: 0.65.0
3
+ Version: 0.66.0
4
4
  Summary: AI-friendly CLI for managing Keboola projects
5
5
  Author-email: Keboola <dev@keboola.com>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "keboola-cli"
3
- version = "0.65.0"
3
+ version = "0.66.0"
4
4
  description = "AI-friendly CLI for managing Keboola projects"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -138,7 +138,19 @@ def _bundle_ui(repo_root: Path, log: Callable[[str], None] = print) -> None:
138
138
  log("no prebuilt dist found; running npm build")
139
139
  try:
140
140
  subprocess.check_call(
141
- [npm, "ci", "--prefer-offline", "--no-audit", "--no-fund"],
141
+ # --ignore-scripts: do NOT run npm lifecycle scripts from the
142
+ # (transitive) dependency tree at wheel-build / `git+` install time.
143
+ # Without it, a compromised dependency's postinstall would execute
144
+ # arbitrary code on the user's machine during install (GHSA-pfvh).
145
+ # Verified non-breaking: `tsc -b && vite build` needs no dependency
146
+ # install-scripts -- vite 8 (rolldown) ships prebuilt platform
147
+ # binaries rather than fetching them via a postinstall.
148
+ # NOTE: this also suppresses any *root* web/frontend/package.json
149
+ # prepare/postinstall script (there is none today). If one is ever
150
+ # added AND required for the build, invoke it explicitly here rather
151
+ # than dropping --ignore-scripts, which would re-open the dependency
152
+ # lifecycle-script RCE surface.
153
+ [npm, "ci", "--prefer-offline", "--no-audit", "--no-fund", "--ignore-scripts"],
142
154
  cwd=frontend_dir,
143
155
  )
144
156
  subprocess.check_call([npm, "run", "build"], cwd=frontend_dir)
@@ -9,6 +9,8 @@ from .result_models import (
9
9
  ConfigDetailResult,
10
10
  JobResult,
11
11
  QueryResult,
12
+ ScopedTokenResult,
13
+ StreamSourceResult,
12
14
  SyncPushResult,
13
15
  UploadTableResult,
14
16
  )
@@ -28,6 +30,8 @@ __all__ = [
28
30
  "JobIdempotencyStore",
29
31
  "JobResult",
30
32
  "QueryResult",
33
+ "ScopedTokenResult",
34
+ "StreamSourceResult",
31
35
  "SyncPushResult",
32
36
  "UploadTableResult",
33
37
  "__version__",
@@ -24,6 +24,49 @@ from .constants import CHANGELOG_HEADLINE_MAX_CHARS
24
24
 
25
25
  # Ordered newest-first. Each value is a list of brief one-line descriptions.
26
26
  CHANGELOG: dict[str, list[str]] = {
27
+ "0.66.0": [
28
+ "New: device-enrollment primitives on the importable library -- a hosted Data App can now mint "
29
+ "per-device credentials in-process (no CLI subprocess, no master token on the device). "
30
+ "`Client(url, token)` / `.raw` (`KeboolaClient`) gains `create_scoped_token`, `delete_token`, "
31
+ "`refresh_token`, and per-device Data Streams `create_stream_source` / `get_stream_source` / "
32
+ "`list_stream_sources` / `delete_stream_source`. The facade returns typed `ScopedTokenResult` / "
33
+ "`StreamSourceResult` (exported from the package root); `.raw` returns plain dicts. Built for "
34
+ "keboola/jasnost device enrollment (ADR 0005).",
35
+ "New: `create_scoped_token(description, bucket_permissions, component_access, "
36
+ "can_read_all_file_uploads, expires_in)` generalises `create_short_lived_token` beyond "
37
+ "component scope -- it maps straight to `POST /v2/storage/tokens` so you can mint the narrow "
38
+ "'upload Files + write one sink bucket, expiring' token a capture device needs (the Keboola "
39
+ "single-bucket-write pattern). The acting token must carry `canManageTokens`.",
40
+ "New: `create_stream_source` provisions a per-device OTLP source AND (for `otlp`, default) its "
41
+ "logs/metrics/traces sinks + `in.c-otlp-<sourceId>` bucket, and returns `sink_bucket_id` so the "
42
+ "scoped device token can be granted write on exactly that bucket. Per-device sources are the "
43
+ "unit of isolated event-plane revocation (`delete_stream_source`) -- no shared-secret rotation. "
44
+ "`otlp_url` carries the ingest secret unmasked (revealed to the device once, never persisted).",
45
+ "New: `kbagent token` command group (create | delete | refresh) mirrors the scoped-token "
46
+ "primitives on the CLI (Project Management). `token create` prints the secret once; `token "
47
+ "delete` revokes immediately; `token refresh` rotates and invalidates the old value. These are "
48
+ "Storage-API operations (no manage token).",
49
+ "Note: contrary to earlier assumptions, a Data Streams source create is authenticated with a "
50
+ "normal per-project Storage token (NOT a master token) and there is no `masterTokenRequired` "
51
+ "error code; and a Files upload is NOT gated by `componentAccess` / `canReadAllFileUploads` -- "
52
+ "any valid Storage token can upload its own Files. `canReadAllFileUploads` only widens reading "
53
+ "files uploaded by OTHER tokens.",
54
+ ],
55
+ "0.65.1": [
56
+ "BREAKING: Removed `data-app git-bind-credential` (and its `kbagent serve` endpoint). It shipped in "
57
+ "0.65.0 on a misdiagnosis: managed-repo deploys were failing and we believed the platform "
58
+ "did not inject the git-clone credential, so the command wired an encrypted credential into "
59
+ "`parameters.dataApp.git`. A clean reproduction on `data-science.us-east4.gcp` confirmed the "
60
+ "platform DOES inject the clone credential at deploy time (matching the sandboxes-service "
61
+ "`testManagedGitRepo.sh` contract) -- the command was unnecessary. The real fix was the "
62
+ "0.65.0 `configVersion`-omit change; pinning a managed app's no-git-block config is what made "
63
+ "the runtime demand `dataApp.git.repository` and revert the deploy to stopped.",
64
+ "Change: Corrected the managed-repo guidance everywhere: the canonical flow is `data-app create "
65
+ "--use-managed-git-repo` -> `git-credentials-create --type http_token --permissions readWrite` "
66
+ "+ `git push` your code -> `data-app deploy`. No credential wiring is needed. The earlier "
67
+ "'platform does not inject credentials' / `could not read Username` framing was wrong "
68
+ "(GitHub issue #454 closed as not-a-bug).",
69
+ ],
27
70
  "0.65.0": [
28
71
  "New: deploy a data app from a Keboola-MANAGED git repository end-to-end. "
29
72
  "`data-app create --use-managed-git-repo` provisions an EMPTY Keboola-hosted repo (POST "
@@ -39,7 +82,9 @@ CHANGELOG: dict[str, list[str]] = {
39
82
  "credentials at deploy time -- without it the runtime's `git clone` of the managed repo "
40
83
  "fails `could not read Username` and the deploy reverts to stopped. The token is encrypted "
41
84
  "in place and never printed; `--dry-run` previews what would be wired without minting the "
42
- "one-time credential or touching the config.",
85
+ "one-time credential or touching the config. (Removed in 0.65.1 -- this was based on a "
86
+ "misdiagnosis; the platform does inject the clone credential, so the command was "
87
+ "unnecessary. See the 0.65.1 notes.)",
43
88
  "New: `data-app runs --project P --app-id ID [--limit N]` lists a data app's recent "
44
89
  "deployment attempts with `failure_reason` + `startup_logs`, including setup-phase failures "
45
90
  "(e.g. a git-clone error during `app_setup`) that produce no container logs. It works on "
@@ -35,6 +35,7 @@ from .commands.sharing import sharing_app
35
35
  from .commands.storage import storage_app
36
36
  from .commands.stream import stream_app
37
37
  from .commands.sync import sync_app
38
+ from .commands.token import token_app
38
39
  from .commands.tool import tool_app
39
40
  from .commands.version import update_command, version_command
40
41
  from .commands.workspace import workspace_app
@@ -71,6 +72,7 @@ from .services.sharing_service import SharingService
71
72
  from .services.storage_service import StorageService
72
73
  from .services.stream_service import StreamService
73
74
  from .services.sync_service import SyncService
75
+ from .services.token_service import TokenService
74
76
  from .services.variables_service import VariablesService
75
77
  from .services.version_service import VersionService
76
78
  from .services.workspace_service import WorkspaceService
@@ -98,6 +100,7 @@ _PROJ = "Project Management"
98
100
  app.add_typer(project_app, name="project", rich_help_panel=_PROJ)
99
101
  app.add_typer(org_app, name="org", rich_help_panel=_PROJ)
100
102
  app.add_typer(feature_app, name="feature", rich_help_panel=_PROJ)
103
+ app.add_typer(token_app, name="token", rich_help_panel=_PROJ)
101
104
 
102
105
  # -- Browse & Inspect --
103
106
  _BROWSE = "Browse & Inspect"
@@ -320,6 +323,7 @@ def main(
320
323
  search_service = SearchService(config_store=config_store)
321
324
  storage_service = StorageService(config_store=config_store)
322
325
  stream_service = StreamService(config_store=config_store)
326
+ token_service = TokenService(config_store=config_store)
323
327
  sync_service = SyncService(config_store=config_store)
324
328
  variables_service = VariablesService(config_store=config_store)
325
329
  encrypt_service = EncryptService(config_store=config_store)
@@ -375,6 +379,7 @@ def main(
375
379
  ctx.obj["search_service"] = search_service
376
380
  ctx.obj["storage_service"] = storage_service
377
381
  ctx.obj["stream_service"] = stream_service
382
+ ctx.obj["token_service"] = token_service
378
383
  ctx.obj["sync_service"] = sync_service
379
384
  ctx.obj["variables_service"] = variables_service
380
385
  ctx.obj["encrypt_service"] = encrypt_service
@@ -34,16 +34,19 @@ from .constants import (
34
34
  METADATA_NOT_FOUND,
35
35
  OAUTH_HOST,
36
36
  OAUTH_PATH,
37
+ OTLP_BUCKET_PREFIX,
37
38
  QUERY_JOB_MAX_WAIT,
38
39
  QUERY_JOB_POLL_INTERVAL,
39
40
  QUERY_RESULTS_PAGE_SIZE,
40
41
  STORAGE_JOB_MAX_WAIT,
41
42
  STORAGE_JOB_POLL_INTERVAL,
43
+ STREAM_DEFAULT_BRANCH,
42
44
  VALID_POLL_STRATEGIES,
43
45
  )
44
46
  from .errors import ErrorCode, KeboolaApiError
45
47
  from .http_base import BaseHttpClient
46
48
  from .models import TokenVerifyResponse
49
+ from .stream_client import StreamClient, provision_otlp_sinks, stream_task_source_id
47
50
 
48
51
  logger = logging.getLogger(__name__)
49
52
 
@@ -171,6 +174,9 @@ class KeboolaClient(BaseHttpClient):
171
174
  self._queue_client: httpx.Client | None = None
172
175
  self._query_client: httpx.Client | None = None
173
176
  self._encrypt_client: httpx.Client | None = None
177
+ # Lazily built on first Data Streams call (per-device OTLP sources); the
178
+ # Stream control plane is a sibling host reachable from this stack+token.
179
+ self._stream_client: StreamClient | None = None
174
180
  # Cache of project feature flags. Populated lazily on first
175
181
  # has_feature() / get_project_features() call so we don't pay an
176
182
  # extra verify_token round-trip on every kbagent invocation, and
@@ -199,6 +205,8 @@ class KeboolaClient(BaseHttpClient):
199
205
  self._query_client.close()
200
206
  if self._encrypt_client is not None:
201
207
  self._encrypt_client.close()
208
+ if self._stream_client is not None:
209
+ self._stream_client.close()
202
210
 
203
211
  def __enter__(self) -> "KeboolaClient":
204
212
  return self
@@ -376,6 +384,209 @@ class KeboolaClient(BaseHttpClient):
376
384
  )
377
385
  return response.json()
378
386
 
387
+ def create_scoped_token(
388
+ self,
389
+ *,
390
+ description: str,
391
+ bucket_permissions: dict[str, str] | None = None,
392
+ component_access: list[str] | None = None,
393
+ can_read_all_file_uploads: bool = False,
394
+ expires_in: int | None = None,
395
+ ) -> dict[str, Any]:
396
+ """Create a scoped Storage API token (``POST /v2/storage/tokens``).
397
+
398
+ The general form of :meth:`create_short_lived_token`: instead of only a
399
+ component allow-list it also expresses **bucket** permissions, so a
400
+ caller can mint the narrow "upload Files + write one sink bucket,
401
+ expiring, nothing else" token a capture device needs (Keboola's
402
+ single-bucket-write pattern).
403
+
404
+ Note on Files upload: a Files upload (``POST /v2/storage/files/prepare``)
405
+ is a generic Storage write available to any valid Storage token -- it is
406
+ **not** gated by ``componentAccess`` or ``canReadAllFileUploads``. Grant
407
+ ``bucket_permissions={sink_bucket: "write"}`` for the sink write;
408
+ ``can_read_all_file_uploads`` only widens *reading* files uploaded by
409
+ *other* tokens (a device sees its own uploads regardless).
410
+
411
+ The acting token must carry ``canManageTokens`` (the API rejects the
412
+ create otherwise -- surfaced as an ``ACCESS_DENIED`` :class:`KeboolaApiError`
413
+ with the token masked). The returned dict is the raw API response; its
414
+ ``token`` field is a **one-time** secret reveal -- persist only ``id``
415
+ (for :meth:`delete_token` / :meth:`refresh_token`) and ``expires``.
416
+
417
+ Args:
418
+ description: Human-readable token description (per-device label).
419
+ bucket_permissions: ``{bucketId: "read" | "write"}`` grants.
420
+ component_access: Component IDs the token may run (often empty for a
421
+ capture device that only uploads Files + streams OTLP).
422
+ can_read_all_file_uploads: If True the token may read files uploaded
423
+ by other tokens (default False = only its own uploads).
424
+ expires_in: Lifetime in seconds; ``None`` = never expires.
425
+ """
426
+ data: dict[str, Any] = {"description": description}
427
+ if expires_in is not None:
428
+ data["expiresIn"] = str(expires_in)
429
+ if can_read_all_file_uploads:
430
+ # Storage API reads this form field as truthy; omit it (=> default
431
+ # false) rather than sending "0" so an unset scope stays minimal.
432
+ data["canReadAllFileUploads"] = "1"
433
+ for bucket_id, permission in (bucket_permissions or {}).items():
434
+ data[f"bucketPermissions[{bucket_id}]"] = permission
435
+ if component_access:
436
+ data["componentAccess[]"] = component_access
437
+ response = self._request("POST", "/v2/storage/tokens", data=data)
438
+ return response.json()
439
+
440
+ def delete_token(self, token_id: str) -> None:
441
+ """Revoke a Storage API token immediately (``DELETE /v2/storage/tokens/{id}``).
442
+
443
+ Returns 204 and the token stops authenticating at once. Only a
444
+ **non-master** token can be deleted (the API refuses to delete the master
445
+ token). Use this for active per-device revocation instead of waiting for
446
+ the token to expire.
447
+ """
448
+ self._request("DELETE", f"/v2/storage/tokens/{quote(str(token_id), safe='')}")
449
+
450
+ def refresh_token(self, token_id: str) -> dict[str, Any]:
451
+ """Rotate a Storage API token (``POST /v2/storage/tokens/{id}/refresh``).
452
+
453
+ Generates a **new** token value and returns the updated token dict; the
454
+ **old** token string becomes immediately invalid (rotation, not
455
+ additive), so every place using it must be updated. The token id is
456
+ stable across a refresh.
457
+ """
458
+ response = self._request(
459
+ "POST", f"/v2/storage/tokens/{quote(str(token_id), safe='')}/refresh"
460
+ )
461
+ return response.json()
462
+
463
+ # ------------------------------------------------------------------
464
+ # Data Streams (per-device OTLP sources) -- delegate to StreamClient
465
+ # ------------------------------------------------------------------
466
+
467
+ def _get_stream_client(self) -> StreamClient:
468
+ """Lazily build (and cache) a :class:`StreamClient` over this stack+token.
469
+
470
+ The Stream control plane lives on a sibling host (``stream.<region>``)
471
+ and authenticates with the same Storage token, so it is reachable from
472
+ the same ``(stack_url, token)`` this client already holds.
473
+ """
474
+ if self._stream_client is None:
475
+ self._stream_client = StreamClient(stack_url=self._stack_url, token=self._token)
476
+ return self._stream_client
477
+
478
+ @staticmethod
479
+ def _stream_source_detail(
480
+ source: dict[str, Any], branch_id: str, sink_bucket_id: str | None
481
+ ) -> dict[str, Any]:
482
+ """Normalise a raw Stream ``source`` object into the returned detail dict.
483
+
484
+ Flattens the OTLP block so the caller gets ``otlp_url`` (with the ingest
485
+ secret embedded, **unmasked** -- the lib layer hands it to the device
486
+ once and never persists it) without digging into ``source["otlp"]``. The
487
+ raw ``source`` is echoed under ``source`` so nothing is lost.
488
+ """
489
+ otlp = source.get("otlp") or {}
490
+ source_id = source.get("sourceId", "")
491
+ return {
492
+ "id": source_id,
493
+ "source_id": source_id,
494
+ "name": source.get("name", ""),
495
+ "type": source.get("type", ""),
496
+ "description": source.get("description", ""),
497
+ "branch_id": branch_id,
498
+ "otlp_url": otlp.get("url", ""),
499
+ "otlp_secret": otlp.get("secret", ""),
500
+ "base_endpoint": otlp.get("baseUrl", ""),
501
+ "sink_bucket_id": sink_bucket_id,
502
+ "source": source,
503
+ }
504
+
505
+ def create_stream_source(
506
+ self,
507
+ name: str,
508
+ *,
509
+ source_type: str = "otlp",
510
+ description: str = "",
511
+ branch_id: str = STREAM_DEFAULT_BRANCH,
512
+ provision_sinks: bool = True,
513
+ ) -> dict[str, Any]:
514
+ """Create a per-device OTLP (or HTTP) stream source and return its detail.
515
+
516
+ Async under the hood: the Stream API returns a 202 ``Task`` which is
517
+ polled to completion here (the caller sees one blocking call). For an
518
+ ``otlp`` source with ``provision_sinks`` (default) the logs/metrics/traces
519
+ sinks and the ``in.c-otlp-<sourceId>`` sink bucket are auto-created so
520
+ OTLP data actually lands in Storage -- and so a scoped device token can be
521
+ granted ``write`` on that bucket. Pass ``provision_sinks=False`` for a
522
+ bare source.
523
+
524
+ Per-device sources are the unit of isolated event-plane revocation:
525
+ delete one device's source (:meth:`delete_stream_source`) without
526
+ rotating a secret shared with other devices.
527
+
528
+ Returns a dict with the flattened endpoint: ``id`` / ``source_id``,
529
+ ``otlp_url`` (secret **unmasked**), ``otlp_secret``, ``sink_bucket_id``
530
+ (``None`` when no sinks were provisioned), and the raw ``source``.
531
+ Requires a Storage token privileged to manage Data Streams.
532
+ """
533
+ stream = self._get_stream_client()
534
+ task = stream.create_source(
535
+ branch_id, name=name, source_type=source_type, description=description or None
536
+ )
537
+ finished = stream.wait_for_task(task)
538
+ source_id = stream_task_source_id(finished) or name
539
+ sink_bucket_id: str | None = None
540
+ if provision_sinks and source_type == "otlp":
541
+ provision_otlp_sinks(stream, branch_id, source_id)
542
+ sink_bucket_id = f"{OTLP_BUCKET_PREFIX}{source_id}"
543
+ source = stream.get_source(branch_id, source_id)
544
+ return self._stream_source_detail(source, branch_id, sink_bucket_id)
545
+
546
+ @staticmethod
547
+ def _sink_bucket_from_sinks(sinks_raw: dict[str, Any]) -> str | None:
548
+ """Return the bucket of the source's first table sink, or None if it has none.
549
+
550
+ Derived from the ACTUAL sinks rather than assumed from the source type,
551
+ so a source created with ``provision_sinks=False`` (or outside kbagent)
552
+ truthfully reports ``None`` instead of a bucket that does not exist.
553
+ """
554
+ for sink in sinks_raw.get("sinks", []):
555
+ table_id = (sink.get("table") or {}).get("tableId", "")
556
+ if "." in table_id:
557
+ return table_id.rsplit(".", 1)[0]
558
+ return None
559
+
560
+ def get_stream_source(
561
+ self, source_id: str, *, branch_id: str = STREAM_DEFAULT_BRANCH
562
+ ) -> dict[str, Any]:
563
+ """Fetch one stream source's detail (endpoint + secret + sink bucket).
564
+
565
+ ``sink_bucket_id`` reflects the source's ACTUAL sinks (``None`` when it
566
+ has none -- e.g. created with ``provision_sinks=False`` or outside
567
+ kbagent), so a caller never scopes a device token to a bucket that does
568
+ not exist.
569
+ """
570
+ stream = self._get_stream_client()
571
+ source = stream.get_source(branch_id, source_id)
572
+ sinks = stream.list_sinks(branch_id, source.get("sourceId", source_id))
573
+ return self._stream_source_detail(source, branch_id, self._sink_bucket_from_sinks(sinks))
574
+
575
+ def list_stream_sources(
576
+ self, *, branch_id: str = STREAM_DEFAULT_BRANCH
577
+ ) -> list[dict[str, Any]]:
578
+ """List the project's stream sources (raw source objects; find-or-create by name)."""
579
+ raw = self._get_stream_client().list_sources(branch_id)
580
+ return list(raw.get("sources", []))
581
+
582
+ def delete_stream_source(
583
+ self, source_id: str, *, branch_id: str = STREAM_DEFAULT_BRANCH
584
+ ) -> None:
585
+ """Delete a stream source (per-device revocation) -- async task polled to completion."""
586
+ stream = self._get_stream_client()
587
+ task = stream.delete_source(branch_id, source_id)
588
+ stream.wait_for_task(task)
589
+
379
590
  def global_search(
380
591
  self,
381
592
  query: str,
@@ -2295,6 +2506,7 @@ class KeboolaClient(BaseHttpClient):
2295
2506
  provider = file_detail.get("provider", "")
2296
2507
  downloader = _CloudDownloader.create(file_detail)
2297
2508
 
2509
+ _assert_safe_download_url(file_detail["url"])
2298
2510
  with httpx.Client(timeout=FILE_DOWNLOAD_TIMEOUT) as http:
2299
2511
  resp = http.get(file_detail["url"])
2300
2512
  resp.raise_for_status()
@@ -2393,6 +2605,7 @@ class KeboolaClient(BaseHttpClient):
2393
2605
  import gzip
2394
2606
  import shutil
2395
2607
 
2608
+ _assert_safe_download_url(url)
2396
2609
  out_path = Path(output_path)
2397
2610
  out_path.parent.mkdir(parents=True, exist_ok=True)
2398
2611
  is_gzipped = url.rstrip("?").split("?")[0].endswith(".gz")
@@ -3101,6 +3314,70 @@ def _build_abs_upload_url(abs_params: dict[str, Any]) -> str:
3101
3314
  # ---------------------------------------------------------------------------
3102
3315
 
3103
3316
 
3317
+ def _assert_safe_download_url(url: str) -> None:
3318
+ """Reject a download URL whose host resolves to a non-public address.
3319
+
3320
+ A malicious or compromised Storage API response can return a download URL
3321
+ pointing at the cloud instance-metadata endpoint (169.254.169.254) or
3322
+ localhost; because these fetches carry no Storage token and don't follow
3323
+ redirects, the residual SSRF still writes internal/credential data into the
3324
+ user's download file (GHSA-hjhx-mx7m-8xx2). We resolve the host and allow
3325
+ ONLY globally-routable (public) addresses plus the explicit BYOC private
3326
+ ranges below; everything else -- loopback, link-local (incl. the
3327
+ 169.254.169.254 metadata endpoint), CGNAT 100.64.0.0/10, reserved,
3328
+ multicast, unspecified -- is refused.
3329
+
3330
+ RFC1918 + IPv6-ULA *private* ranges are deliberately ALLOWED: BYOC /
3331
+ private-tenant Keboola deployments legitimately serve storage from private
3332
+ endpoints, and the high-value SSRF target (instance metadata) is link-local,
3333
+ not private. An allow-list (public OR explicit private) rather than a
3334
+ block-list of `ipaddress` predicates avoids gaps like CGNAT, which none of
3335
+ `is_loopback/is_link_local/is_reserved/is_multicast/is_unspecified` catch.
3336
+ """
3337
+ import ipaddress
3338
+ import socket
3339
+ from urllib.parse import urlparse
3340
+
3341
+ byoc_private = (
3342
+ ipaddress.ip_network("10.0.0.0/8"),
3343
+ ipaddress.ip_network("172.16.0.0/12"),
3344
+ ipaddress.ip_network("192.168.0.0/16"),
3345
+ ipaddress.ip_network("fc00::/7"), # IPv6 unique-local
3346
+ )
3347
+
3348
+ host = urlparse(url).hostname
3349
+ if not host:
3350
+ raise KeboolaApiError(
3351
+ message=f"Refusing to download: URL has no host ({url!r}).",
3352
+ status_code=0,
3353
+ error_code=ErrorCode.INVALID_ARGUMENT,
3354
+ retryable=False,
3355
+ )
3356
+ try:
3357
+ resolved = {info[4][0] for info in socket.getaddrinfo(host, None)}
3358
+ except socket.gaierror:
3359
+ # DNS failure is surfaced by the real fetch; don't mask it here.
3360
+ return
3361
+ for addr in resolved:
3362
+ try:
3363
+ ip = ipaddress.ip_address(addr)
3364
+ except ValueError:
3365
+ continue
3366
+ if ip.is_global or any(ip in net for net in byoc_private):
3367
+ continue # public host, or a BYOC private endpoint -- allowed
3368
+ raise KeboolaApiError(
3369
+ message=(
3370
+ f"Refusing to download from {host} -> {addr}: non-public address "
3371
+ f"(not a public host nor a BYOC private range). This indicates a "
3372
+ f"malicious or compromised Storage API response (possible SSRF, "
3373
+ f"e.g. the cloud instance-metadata endpoint)."
3374
+ ),
3375
+ status_code=0,
3376
+ error_code=ErrorCode.INVALID_ARGUMENT,
3377
+ retryable=False,
3378
+ )
3379
+
3380
+
3104
3381
  class _IterBytesReader:
3105
3382
  """Adapt an httpx iter_bytes() iterator to a .read(n) file-like interface.
3106
3383
 
@@ -3299,6 +3576,7 @@ class _CloudDownloader:
3299
3576
  import gzip
3300
3577
  import shutil
3301
3578
 
3579
+ _assert_safe_download_url(url)
3302
3580
  headers = self._request_headers(url)
3303
3581
  dest_path = Path(dest)
3304
3582
  with (
@@ -341,70 +341,3 @@ def register_git_commands(app: typer.Typer) -> None:
341
341
  )
342
342
 
343
343
  formatter.output(result, _human)
344
-
345
- @app.command("git-bind-credential")
346
- def data_app_git_bind_credential(
347
- ctx: typer.Context,
348
- project: str = typer.Option(..., "--project", help="Project alias"),
349
- app_id: str = typer.Option(..., "--app-id", help="Data Science numeric app id"),
350
- branch: str = typer.Option(
351
- "main", "--branch-name", help="Managed-repo branch to deploy from (default main)."
352
- ),
353
- permissions: str = typer.Option(
354
- "readOnly",
355
- "--permissions",
356
- help="Credential access level: readOnly (default) | readWrite.",
357
- ),
358
- dry_run: bool = typer.Option(
359
- False,
360
- "--dry-run",
361
- help="Preview what would be wired (repo/branch/permissions) WITHOUT minting a credential or editing the config.",
362
- ),
363
- ) -> None:
364
- """Make a MANAGED-repo app deployable by wiring a credential into its config.
365
-
366
- A `--use-managed-git-repo` app owns an empty Keboola-hosted repo but its
367
- Storage config has no git block. On stacks that do NOT inject managed-repo
368
- credentials at deploy time, the runtime's `git clone` of the managed repo
369
- fails ("could not read Username") and the deploy reverts to stopped. This
370
- command mints a fresh `http_token` ON the app, encrypts it under the
371
- project KMS, and writes `parameters.dataApp.git` (repository + placeholder
372
- username + encrypted `#password` + branch) so the next `data-app deploy`
373
- can clone. The token is encrypted in-place and never printed.
374
-
375
- `--dry-run` validates the app + resolves the managed repo URL but mints
376
- nothing and leaves the config untouched -- the http_token is one-time, so
377
- a real run that you abort would orphan it.
378
- """
379
- formatter = get_formatter(ctx)
380
- service = get_service(ctx, "data_app_service")
381
- try:
382
- result = service.bind_managed_credential(
383
- project, app_id, branch=branch, permissions=permissions, dry_run=dry_run
384
- )
385
- except KeboolaApiError as exc:
386
- formatter.error(
387
- message=exc.message,
388
- error_code=exc.error_code,
389
- retryable=exc.retryable,
390
- details=exc.details,
391
- )
392
- raise typer.Exit(code=map_error_to_exit_code(exc)) from None
393
- except ConfigError as exc:
394
- formatter.error(message=exc.message, error_code=ErrorCode.CONFIG_ERROR)
395
- raise typer.Exit(code=5) from None
396
-
397
- def _human(c: Console, d: dict) -> None:
398
- if d.get("dry_run"):
399
- c.print("[bold]DRY RUN -- no credential minted, config unchanged.[/bold]")
400
- c.print(f" [bold]Repository:[/bold] {d.get('repository', '')}")
401
- c.print(f" [bold]Branch:[/bold] {d.get('branch', '')}")
402
- c.print(f" [bold]Permissions:[/bold] {d.get('permissions', '')}")
403
- return
404
- c.print(f"[bold green]Success:[/bold green] {d['message']}")
405
- c.print(f" [bold]Repository:[/bold] {d.get('repository', '')}")
406
- c.print(f" [bold]Branch:[/bold] {d.get('branch', '')}")
407
- c.print(f" [bold]Permissions:[/bold] {d.get('permissions', '')}")
408
- c.print(f" [bold]Config version:[/bold] {d.get('config_version', '')}")
409
-
410
- formatter.output(result, _human)