scalekit-sdk-python 2.17.0__tar.gz → 2.18.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 (278) hide show
  1. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/PKG-INFO +1 -1
  2. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/_version.py +1 -1
  3. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/client.py +32 -9
  4. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/core.py +90 -9
  5. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit_sdk_python.egg-info/PKG-INFO +1 -1
  6. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit_sdk_python.egg-info/SOURCES.txt +2 -0
  7. scalekit_sdk_python-2.18.0/tests/test_http_timeout.py +59 -0
  8. scalekit_sdk_python-2.18.0/tests/test_keepalive_channel_options.py +87 -0
  9. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/LICENSE +0 -0
  10. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/README.md +0 -0
  11. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/buf/__init__.py +0 -0
  12. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/buf/validate/__init__.py +0 -0
  13. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/buf/validate/validate_pb2.py +0 -0
  14. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/buf/validate/validate_pb2.pyi +0 -0
  15. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/buf/validate/validate_pb2_grpc.py +0 -0
  16. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/pyproject.toml +0 -0
  17. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/__init__.py +0 -0
  18. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/__init__.py +0 -0
  19. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/actions.py +0 -0
  20. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/frameworks/__init__.py +0 -0
  21. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/frameworks/google_adk.py +0 -0
  22. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/frameworks/langchain.py +0 -0
  23. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/frameworks/types/__init__.py +0 -0
  24. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/frameworks/types/google_adk_tool.py +0 -0
  25. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/frameworks/util.py +0 -0
  26. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/__init__.py +0 -0
  27. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/custom_provider.py +0 -0
  28. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/mcp_config.py +0 -0
  29. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/mcp_connection_auth_state.py +0 -0
  30. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/mcp_instance.py +0 -0
  31. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/requests/__init__.py +0 -0
  32. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/requests/create_connected_account_request.py +0 -0
  33. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/requests/create_custom_provider_request.py +0 -0
  34. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/requests/delete_custom_provider_request.py +0 -0
  35. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/requests/list_providers_request.py +0 -0
  36. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/requests/mcp_request.py +0 -0
  37. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/requests/tool_request.py +0 -0
  38. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/requests/update_connected_account_request.py +0 -0
  39. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/requests/update_custom_provider_request.py +0 -0
  40. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/__init__.py +0 -0
  41. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/create_connected_account_response.py +0 -0
  42. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/create_custom_provider_response.py +0 -0
  43. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/create_mcp_config_response.py +0 -0
  44. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/create_mcp_response.py +0 -0
  45. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/create_mcp_session_token_response.py +0 -0
  46. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/delete_connected_account_response.py +0 -0
  47. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/delete_custom_provider_response.py +0 -0
  48. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/delete_mcp_config_response.py +0 -0
  49. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/delete_mcp_instance_response.py +0 -0
  50. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/ensure_mcp_instance_response.py +0 -0
  51. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/execute_tool_response.py +0 -0
  52. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/get_connected_account_auth_response.py +0 -0
  53. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/get_mcp_instance_auth_state_response.py +0 -0
  54. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/get_mcp_instance_response.py +0 -0
  55. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/get_mcp_response.py +0 -0
  56. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/list_connected_accounts_response.py +0 -0
  57. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/list_mcp_configs_response.py +0 -0
  58. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/list_mcp_connected_accounts_response.py +0 -0
  59. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/list_mcp_instances_response.py +0 -0
  60. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/list_providers_response.py +0 -0
  61. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/magic_link_response.py +0 -0
  62. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/update_connected_account_response.py +0 -0
  63. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/update_custom_provider_response.py +0 -0
  64. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/update_mcp_config_response.py +0 -0
  65. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/update_mcp_instance_response.py +0 -0
  66. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/responses/verify_connected_account_user_response.py +0 -0
  67. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/tool_input_output.py +0 -0
  68. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/models/tool_mapping.py +0 -0
  69. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/modifier.py +0 -0
  70. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/actions/types.py +0 -0
  71. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/auth.py +0 -0
  72. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/common/__init__.py +0 -0
  73. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/common/exceptions.py +0 -0
  74. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/common/scalekit.py +0 -0
  75. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/common/user.py +0 -0
  76. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/connected_accounts.py +0 -0
  77. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/connection.py +0 -0
  78. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/constants/__init__.py +0 -0
  79. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/constants/user.py +0 -0
  80. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/directory.py +0 -0
  81. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/domain.py +0 -0
  82. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/events.py +0 -0
  83. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/frameworks/__init__.py +0 -0
  84. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/frameworks/django.py +0 -0
  85. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/frameworks/fastapi.py +0 -0
  86. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/frameworks/flask.py +0 -0
  87. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/m2m_client.py +0 -0
  88. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/mcp.py +0 -0
  89. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/middleware/__init__.py +0 -0
  90. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/middleware/csrf_state.py +0 -0
  91. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/middleware/protocol.py +0 -0
  92. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/middleware/session_crypto.py +0 -0
  93. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/middleware/session_manager.py +0 -0
  94. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/organization.py +0 -0
  95. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/passwordless.py +0 -0
  96. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/permissions.py +0 -0
  97. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/providers.py +0 -0
  98. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/role.py +0 -0
  99. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/sessions.py +0 -0
  100. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/token.py +0 -0
  101. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/tools.py +0 -0
  102. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/users.py +0 -0
  103. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/utils/__init__.py +0 -0
  104. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/utils/directory.py +0 -0
  105. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/__init__.py +0 -0
  106. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/agentkit_logs/__init__.py +0 -0
  107. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/agentkit_logs/agentkit_analytics_pb2.py +0 -0
  108. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/agentkit_logs/agentkit_analytics_pb2.pyi +0 -0
  109. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/agentkit_logs/agentkit_analytics_pb2_grpc.py +0 -0
  110. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/agentkit_logs/agentkit_logs_pb2.py +0 -0
  111. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/agentkit_logs/agentkit_logs_pb2.pyi +0 -0
  112. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/agentkit_logs/agentkit_logs_pb2_grpc.py +0 -0
  113. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/auditlogs/__init__.py +0 -0
  114. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/auditlogs/auditlogs_pb2.py +0 -0
  115. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/auditlogs/auditlogs_pb2.pyi +0 -0
  116. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/auditlogs/auditlogs_pb2_grpc.py +0 -0
  117. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/auth/__init__.py +0 -0
  118. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/auth/auth_pb2.py +0 -0
  119. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/auth/auth_pb2.pyi +0 -0
  120. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/auth/auth_pb2_grpc.py +0 -0
  121. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/auth/passwordless_pb2.py +0 -0
  122. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/auth/passwordless_pb2.pyi +0 -0
  123. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/auth/passwordless_pb2_grpc.py +0 -0
  124. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/auth/totp_pb2.py +0 -0
  125. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/auth/totp_pb2.pyi +0 -0
  126. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/auth/totp_pb2_grpc.py +0 -0
  127. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/auth/webauthn_pb2.py +0 -0
  128. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/auth/webauthn_pb2.pyi +0 -0
  129. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/auth/webauthn_pb2_grpc.py +0 -0
  130. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/clients/__init__.py +0 -0
  131. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/clients/clients_pb2.py +0 -0
  132. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/clients/clients_pb2.pyi +0 -0
  133. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/clients/clients_pb2_grpc.py +0 -0
  134. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/commons/__init__.py +0 -0
  135. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/commons/commons_pb2.py +0 -0
  136. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/commons/commons_pb2.pyi +0 -0
  137. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/commons/commons_pb2_grpc.py +0 -0
  138. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/connected_accounts/__init__.py +0 -0
  139. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/connected_accounts/connected_accounts_pb2.py +0 -0
  140. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/connected_accounts/connected_accounts_pb2.pyi +0 -0
  141. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/connected_accounts/connected_accounts_pb2_grpc.py +0 -0
  142. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/connections/__init__.py +0 -0
  143. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/connections/connections_pb2.py +0 -0
  144. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/connections/connections_pb2.pyi +0 -0
  145. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/connections/connections_pb2_grpc.py +0 -0
  146. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/directories/__init__.py +0 -0
  147. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/directories/directories_pb2.py +0 -0
  148. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/directories/directories_pb2.pyi +0 -0
  149. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/directories/directories_pb2_grpc.py +0 -0
  150. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/domains/__init__.py +0 -0
  151. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/domains/domains_pb2.py +0 -0
  152. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/domains/domains_pb2.pyi +0 -0
  153. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/domains/domains_pb2_grpc.py +0 -0
  154. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/emails/__init__.py +0 -0
  155. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/emails/emails_pb2.py +0 -0
  156. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/emails/emails_pb2.pyi +0 -0
  157. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/emails/emails_pb2_grpc.py +0 -0
  158. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/environments/__init__.py +0 -0
  159. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/environments/environments_pb2.py +0 -0
  160. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/environments/environments_pb2.pyi +0 -0
  161. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/environments/environments_pb2_grpc.py +0 -0
  162. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/errdetails/__init__.py +0 -0
  163. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/errdetails/errdetails_pb2.py +0 -0
  164. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/errdetails/errdetails_pb2.pyi +0 -0
  165. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/errdetails/errdetails_pb2_grpc.py +0 -0
  166. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/events/__init__.py +0 -0
  167. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/events/events_pb2.py +0 -0
  168. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/events/events_pb2.pyi +0 -0
  169. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/events/events_pb2_grpc.py +0 -0
  170. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/gateway/__init__.py +0 -0
  171. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/gateway/gateway_pb2.py +0 -0
  172. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/gateway/gateway_pb2.pyi +0 -0
  173. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/gateway/gateway_pb2_grpc.py +0 -0
  174. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/interceptors/__init__.py +0 -0
  175. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/interceptors/interceptors_pb2.py +0 -0
  176. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/interceptors/interceptors_pb2.pyi +0 -0
  177. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/interceptors/interceptors_pb2_grpc.py +0 -0
  178. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/keys/__init__.py +0 -0
  179. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/keys/keys_pb2.py +0 -0
  180. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/keys/keys_pb2.pyi +0 -0
  181. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/keys/keys_pb2_grpc.py +0 -0
  182. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/mcp/__init__.py +0 -0
  183. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/mcp/mcp_pb2.py +0 -0
  184. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/mcp/mcp_pb2.pyi +0 -0
  185. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/mcp/mcp_pb2_grpc.py +0 -0
  186. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/members/__init__.py +0 -0
  187. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/members/members_pb2.py +0 -0
  188. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/members/members_pb2.pyi +0 -0
  189. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/members/members_pb2_grpc.py +0 -0
  190. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/migrations/__init__.py +0 -0
  191. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/migrations/migrations_pb2.py +0 -0
  192. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/migrations/migrations_pb2.pyi +0 -0
  193. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/migrations/migrations_pb2_grpc.py +0 -0
  194. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/options/__init__.py +0 -0
  195. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/options/options_pb2.py +0 -0
  196. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/options/options_pb2.pyi +0 -0
  197. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/options/options_pb2_grpc.py +0 -0
  198. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/organizations/__init__.py +0 -0
  199. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/organizations/organizations_pb2.py +0 -0
  200. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/organizations/organizations_pb2.pyi +0 -0
  201. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/organizations/organizations_pb2_grpc.py +0 -0
  202. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/providers/__init__.py +0 -0
  203. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/providers/providers_pb2.py +0 -0
  204. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/providers/providers_pb2.pyi +0 -0
  205. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/providers/providers_pb2_grpc.py +0 -0
  206. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/roles/__init__.py +0 -0
  207. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/roles/roles_pb2.py +0 -0
  208. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/roles/roles_pb2.pyi +0 -0
  209. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/roles/roles_pb2_grpc.py +0 -0
  210. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/secrets/__init__.py +0 -0
  211. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/secrets/secrets_pb2.py +0 -0
  212. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/secrets/secrets_pb2.pyi +0 -0
  213. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/secrets/secrets_pb2_grpc.py +0 -0
  214. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/sessions/__init__.py +0 -0
  215. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/sessions/sessions_pb2.py +0 -0
  216. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/sessions/sessions_pb2.pyi +0 -0
  217. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/sessions/sessions_pb2_grpc.py +0 -0
  218. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/tokens/__init__.py +0 -0
  219. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/tokens/tokens_pb2.py +0 -0
  220. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/tokens/tokens_pb2.pyi +0 -0
  221. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/tokens/tokens_pb2_grpc.py +0 -0
  222. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/tools/__init__.py +0 -0
  223. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/tools/tools_pb2.py +0 -0
  224. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/tools/tools_pb2.pyi +0 -0
  225. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/tools/tools_pb2_grpc.py +0 -0
  226. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/user_attributes/__init__.py +0 -0
  227. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/user_attributes/user_attributes_pb2.py +0 -0
  228. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/user_attributes/user_attributes_pb2.pyi +0 -0
  229. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/user_attributes/user_attributes_pb2_grpc.py +0 -0
  230. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/users/__init__.py +0 -0
  231. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/users/users_pb2.py +0 -0
  232. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/users/users_pb2.pyi +0 -0
  233. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/users/users_pb2_grpc.py +0 -0
  234. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/webhooks/__init__.py +0 -0
  235. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/webhooks/webhooks_pb2.py +0 -0
  236. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/webhooks/webhooks_pb2.pyi +0 -0
  237. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/webhooks/webhooks_pb2_grpc.py +0 -0
  238. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/workspaces/__init__.py +0 -0
  239. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/workspaces/workspaces_pb2.py +0 -0
  240. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/workspaces/workspaces_pb2.pyi +0 -0
  241. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/v1/workspaces/workspaces_pb2_grpc.py +0 -0
  242. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit/webauthn.py +0 -0
  243. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit_sdk_python.egg-info/dependency_links.txt +0 -0
  244. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit_sdk_python.egg-info/requires.txt +0 -0
  245. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/scalekit_sdk_python.egg-info/top_level.txt +0 -0
  246. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/setup.cfg +0 -0
  247. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/setup.py +0 -0
  248. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_actions.py +0 -0
  249. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_connected_accounts.py +0 -0
  250. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_connection.py +0 -0
  251. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_directory.py +0 -0
  252. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_domain.py +0 -0
  253. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_events.py +0 -0
  254. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_login.py +0 -0
  255. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_m2m_client.py +0 -0
  256. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_mcp.py +0 -0
  257. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_middleware_csrf_state.py +0 -0
  258. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_middleware_django.py +0 -0
  259. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_middleware_fastapi.py +0 -0
  260. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_middleware_flask.py +0 -0
  261. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_middleware_session_crypto.py +0 -0
  262. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_middleware_session_manager.py +0 -0
  263. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_org_roles.py +0 -0
  264. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_organization.py +0 -0
  265. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_organization_session_policy.py +0 -0
  266. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_organization_slug_logo.py +0 -0
  267. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_passwordless.py +0 -0
  268. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_permissions.py +0 -0
  269. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_providers.py +0 -0
  270. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_role_defaults_dependent.py +0 -0
  271. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_roles.py +0 -0
  272. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_sessions.py +0 -0
  273. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_sk819_retry_behavior.py +0 -0
  274. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_tokens.py +0 -0
  275. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_tools.py +0 -0
  276. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_user_roles_permissions.py +0 -0
  277. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_users.py +0 -0
  278. {scalekit_sdk_python-2.17.0 → scalekit_sdk_python-2.18.0}/tests/test_webauthn_credentials.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scalekit-sdk-python
3
- Version: 2.17.0
3
+ Version: 2.18.0
4
4
  Summary: Scalekit official Python SDK
5
5
  Home-page: https://github.com/scalekit-inc/scalekit-sdk-python
6
6
  Author: Team Scalekit
@@ -1,3 +1,3 @@
1
1
  # Single source of truth for the SDK version.
2
2
  # Import this in setup.py and scalekit/core.py — never hardcode the version elsewhere.
3
- __version__ = "2.17.0"
3
+ __version__ = "2.18.0"
@@ -9,7 +9,7 @@ import hmac
9
9
  import hashlib
10
10
  import base64
11
11
  from datetime import datetime, timedelta, timezone
12
- from scalekit.core import CoreClient
12
+ from scalekit.core import CoreClient, DEFAULT_KEEPALIVE_TIME_MS, DEFAULT_KEEPALIVE_TIMEOUT_MS
13
13
  from scalekit.domain import DomainClient
14
14
  from scalekit.connection import ConnectionClient
15
15
  from scalekit.m2m_client import M2MClient
@@ -50,23 +50,46 @@ webhook_signature_version = "v1"
50
50
  class ScalekitClient:
51
51
  """ Class definition for scalekit client """
52
52
 
53
- def __init__(self, env_url: str, client_id: str, client_secret: str):
53
+ def __init__(
54
+ self,
55
+ env_url: str,
56
+ client_id: str,
57
+ client_secret: str,
58
+ keepalive_time_ms: int = DEFAULT_KEEPALIVE_TIME_MS,
59
+ keepalive_timeout_ms: int = DEFAULT_KEEPALIVE_TIMEOUT_MS,
60
+ ):
54
61
  """
55
62
  Initializer for Scalekit base class
56
63
 
57
- :param env_url : Environment URL
58
- :type : ``` str ```
59
- :param client_id : Client ID
60
- :type : ``` str ```
61
- :param client_secret : Client Secret
62
- :type : ``` str ```
64
+ :param env_url : Environment URL
65
+ :type : ``` str ```
66
+ :param client_id : Client ID
67
+ :type : ``` str ```
68
+ :param client_secret : Client Secret
69
+ :type : ``` str ```
70
+ :param keepalive_time_ms : How often, in milliseconds, an idle gRPC
71
+ connection is verified before reuse.
72
+ Defaults to 60000; most callers never
73
+ need to set this. Set to 0 to disable
74
+ keepalive entirely.
75
+ :type : ``` int ```
76
+ :param keepalive_timeout_ms : How long, in milliseconds, to wait for a
77
+ keepalive response before treating an
78
+ idle connection as dead. Defaults to
79
+ 10000.
80
+ :type : ``` int ```
63
81
 
64
82
  :returns:
65
83
  None
66
84
  """
67
85
  try:
68
86
  self.core_client = CoreClient(
69
- env_url=env_url, client_id=client_id, client_secret=client_secret)
87
+ env_url=env_url,
88
+ client_id=client_id,
89
+ client_secret=client_secret,
90
+ keepalive_time_ms=keepalive_time_ms,
91
+ keepalive_timeout_ms=keepalive_timeout_ms,
92
+ )
70
93
  self.domain = DomainClient(self.core_client)
71
94
  self.connection = ConnectionClient(self.core_client)
72
95
  self.organization = OrganizationClient(self.core_client)
@@ -19,6 +19,33 @@ TMetadata = TypeVar("TMetadata")
19
19
  TOKEN_ENDPOINT = "/oauth/token"
20
20
  JWKS_ENDPOINT = "/keys"
21
21
 
22
+ # Must clear the backend's EnforcementPolicy.MinTime (30s, scalekit's
23
+ # cmd/grpc.go) with real margin, not just match it: grpc-core pings on this
24
+ # exact interval for as long as the channel is open (keepalive_permit_without_calls
25
+ # below), so a value equal to MinTime leaves zero room for jitter between the
26
+ # client's timer and the server's strike window — one early ping is a strike,
27
+ # three and the server GOAWAYs the connection, recreating the bug this fixes.
28
+ # 60s matches the Java SDK's default for the same reason.
29
+ DEFAULT_KEEPALIVE_TIME_MS = 60_000
30
+ DEFAULT_KEEPALIVE_TIMEOUT_MS = 10_000
31
+
32
+ # The floor tracks DEFAULT_KEEPALIVE_TIME_MS for the reason spelled out above:
33
+ # the backend's EnforcementPolicy.MinTime is 30s, and a value at exactly 30s has
34
+ # zero jitter headroom — grpc's ping timer drifts slightly early, so an early
35
+ # ping counts as a strike and enough strikes make the server GOAWAY. 60s gives
36
+ # 2x margin over the 30s MinTime. Since there is no legitimate reason to go below
37
+ # the default, the only sensible choices are 0 (disabled) or >= 60s. (gRPC also
38
+ # silently clamps sub-10s values up to 10s per grpc/proposal A8, so a small value
39
+ # never even reaches the wire as requested.) Reject 1..59999.
40
+ MIN_KEEPALIVE_TIME_MS = 60_000
41
+
42
+ # requests defaults to no timeout, so a black-holed connection blocks the
43
+ # calling thread until the OS abandons the socket. Bound the connect and read
44
+ # phases separately with a (connect, read) tuple.
45
+ DEFAULT_HTTP_CONNECT_TIMEOUT_S = 10
46
+ DEFAULT_HTTP_READ_TIMEOUT_S = 30
47
+ DEFAULT_HTTP_TIMEOUT = (DEFAULT_HTTP_CONNECT_TIMEOUT_S, DEFAULT_HTTP_READ_TIMEOUT_S)
48
+
22
49
 
23
50
  class WithCall(Protocol):
24
51
  def __call__(self, request: TRequest, metadata: TMetadata) -> TResponse: ...
@@ -32,16 +59,36 @@ class CoreClient:
32
59
  api_version = "20260727"
33
60
  user_agent = f"{sdk_version} Python/{platform.python_version()} ({platform.system()}; {platform.architecture()}"
34
61
 
35
- def __init__(self, env_url, client_id, client_secret):
62
+ def __init__(
63
+ self,
64
+ env_url,
65
+ client_id,
66
+ client_secret,
67
+ keepalive_time_ms: int = DEFAULT_KEEPALIVE_TIME_MS,
68
+ keepalive_timeout_ms: int = DEFAULT_KEEPALIVE_TIMEOUT_MS,
69
+ ):
36
70
  """
37
71
  Initializer for Core client
38
72
 
39
- :param env_url : Environment URL
40
- :type : ``` str ```
41
- :param client_id : Client ID
42
- :type : ``` str ```
43
- :param client_secret : Client Secret
44
- :type : ``` str ```
73
+ :param env_url : Environment URL
74
+ :type : ``` str ```
75
+ :param client_id : Client ID
76
+ :type : ``` str ```
77
+ :param client_secret : Client Secret
78
+ :type : ``` str ```
79
+ :param keepalive_time_ms : How often, in milliseconds, an idle gRPC
80
+ connection is verified before reuse.
81
+ Must stay above the backend's keepalive
82
+ MinTime (30s) with real margin, or the
83
+ server treats this ping as abuse. Defaults
84
+ to 60000. Set to 0 to disable keepalive
85
+ entirely.
86
+ :type : ``` int ```
87
+ :param keepalive_timeout_ms : How long, in milliseconds, to wait for a
88
+ keepalive response before treating an
89
+ idle connection as dead. Defaults to
90
+ 10000.
91
+ :type : ``` int ```
45
92
  :returns
46
93
  None
47
94
  """
@@ -50,6 +97,20 @@ class CoreClient:
50
97
  self.env_url = env_url
51
98
  self.client_id = client_id
52
99
  self.client_secret = client_secret
100
+ # 0 means "disabled" and is allowed through deliberately; only 1..59999
101
+ # is rejected. A value below the 60s default leaves too little margin
102
+ # over the Scalekit server's 30s MinTime — an early ping gets struck as
103
+ # abusive and the server GOAWAYs — and gRPC silently raises sub-10s
104
+ # values to 10s, so a small value is never what the caller asked for.
105
+ if keepalive_time_ms and keepalive_time_ms < MIN_KEEPALIVE_TIME_MS:
106
+ raise ValueError(
107
+ f"keepalive_time_ms must be 0 (disabled) or >= {MIN_KEEPALIVE_TIME_MS}; "
108
+ f"got {keepalive_time_ms}. A value below the default leaves too little "
109
+ "margin over the Scalekit server's 30s MinTime (early pings are struck "
110
+ "as abusive), and gRPC silently raises sub-10s values to 10s."
111
+ )
112
+ self.keepalive_time_ms = keepalive_time_ms
113
+ self.keepalive_timeout_ms = keepalive_timeout_ms
53
114
  self.keys = {}
54
115
  self.access_token = None
55
116
  self.grpc_secure_channel = None
@@ -70,7 +131,24 @@ class CoreClient:
70
131
  channel_credentials,
71
132
  call_credentials,
72
133
  )
73
- self.grpc_secure_channel = grpc.secure_channel(self.host, composite_credentials)
134
+ # keepalive_time_ms == 0 disables keepalive entirely: no options are
135
+ # passed, so grpc-core falls back to its own defaults (no idle pings).
136
+ channel_options = []
137
+ if self.keepalive_time_ms:
138
+ # keepalive_permit_without_calls=1 so an idle channel is still
139
+ # periodically verified: without it, grpc-core only sends HTTP/2
140
+ # keepalive PINGs while there are active calls, so a connection
141
+ # silently dropped by a network intermediary while idle isn't
142
+ # detected until the next real call is written to it.
143
+ channel_options = [
144
+ ('grpc.keepalive_time_ms', self.keepalive_time_ms),
145
+ ('grpc.keepalive_timeout_ms', self.keepalive_timeout_ms),
146
+ ('grpc.keepalive_permit_without_calls', 1),
147
+ ('grpc.http2.max_pings_without_data', 0),
148
+ ]
149
+ self.grpc_secure_channel = grpc.secure_channel(
150
+ self.host, composite_credentials, options=channel_options
151
+ )
74
152
 
75
153
  def __authenticate_client(self):
76
154
  """
@@ -104,6 +182,7 @@ class CoreClient:
104
182
  headers=self.get_headers(headers=headers),
105
183
  data=data,
106
184
  verify=True,
185
+ timeout=DEFAULT_HTTP_TIMEOUT,
107
186
  )
108
187
  if response.status_code != 200:
109
188
  raise ScalekitServerException.promote(response)
@@ -114,7 +193,9 @@ class CoreClient:
114
193
  if self.keys and len(self.keys) > 0:
115
194
  return
116
195
  response = requests.get(
117
- self.env_url + JWKS_ENDPOINT, headers=self.get_headers()
196
+ self.env_url + JWKS_ENDPOINT,
197
+ headers=self.get_headers(),
198
+ timeout=DEFAULT_HTTP_TIMEOUT,
118
199
  )
119
200
  response = json.loads(response.content)
120
201
  keys = response["keys"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scalekit-sdk-python
3
- Version: 2.17.0
3
+ Version: 2.18.0
4
4
  Summary: Scalekit official Python SDK
5
5
  Home-page: https://github.com/scalekit-inc/scalekit-sdk-python
6
6
  Author: Team Scalekit
@@ -247,6 +247,8 @@ tests/test_connection.py
247
247
  tests/test_directory.py
248
248
  tests/test_domain.py
249
249
  tests/test_events.py
250
+ tests/test_http_timeout.py
251
+ tests/test_keepalive_channel_options.py
250
252
  tests/test_login.py
251
253
  tests/test_m2m_client.py
252
254
  tests/test_mcp.py
@@ -0,0 +1,59 @@
1
+ """
2
+ Tests that the HTTP calls in CoreClient pass a bounded timeout.
3
+
4
+ requests has no default timeout, so a black-holed connection would block the
5
+ calling thread indefinitely. authenticate() and get_jwks() must pass a
6
+ non-None timeout to requests.post / requests.get.
7
+ """
8
+ import json
9
+ import unittest
10
+ from unittest.mock import MagicMock, patch
11
+
12
+
13
+ def _make_core_client():
14
+ """Return a CoreClient instance with __init__ bypassed (no real network calls)."""
15
+ from scalekit.core import CoreClient
16
+ client = CoreClient.__new__(CoreClient)
17
+ client.access_token = "test-token"
18
+ client.host = "example.com"
19
+ client.env_url = "https://example.com"
20
+ client.client_id = "cid"
21
+ client.client_secret = "csec"
22
+ client.keys = {}
23
+ client.grpc_secure_channel = None
24
+ return client
25
+
26
+
27
+ class TestHttpTimeout(unittest.TestCase):
28
+ def setUp(self):
29
+ self.client = _make_core_client()
30
+
31
+ def test_authenticate_passes_timeout_to_requests_post(self):
32
+ """authenticate() must pass a non-None timeout to requests.post."""
33
+ mock_response = MagicMock()
34
+ mock_response.status_code = 200
35
+
36
+ with patch("scalekit.core.requests.post", return_value=mock_response) as mock_post:
37
+ self.client.authenticate(data={"grant_type": "client_credentials"})
38
+
39
+ mock_post.assert_called_once()
40
+ _, kwargs = mock_post.call_args
41
+ self.assertIn("timeout", kwargs)
42
+ self.assertIsNotNone(kwargs["timeout"])
43
+
44
+ def test_get_jwks_passes_timeout_to_requests_get(self):
45
+ """get_jwks() must pass a non-None timeout to requests.get."""
46
+ mock_response = MagicMock()
47
+ mock_response.content = json.dumps({"keys": []}).encode("utf-8")
48
+
49
+ with patch("scalekit.core.requests.get", return_value=mock_response) as mock_get:
50
+ self.client.get_jwks()
51
+
52
+ mock_get.assert_called_once()
53
+ _, kwargs = mock_get.call_args
54
+ self.assertIn("timeout", kwargs)
55
+ self.assertIsNotNone(kwargs["timeout"])
56
+
57
+
58
+ if __name__ == "__main__":
59
+ unittest.main()
@@ -0,0 +1,87 @@
1
+ """
2
+ Tests for gRPC keepalive channel options in CoreClient.
3
+
4
+ Covers the runtime escape hatch (keepalive_time_ms=0 disables keepalive) and
5
+ the sub-10s validation guard (gRPC clamps values below 10s up to 10s, which
6
+ the Scalekit server rejects).
7
+
8
+ These tests never touch the network: __authenticate_client is mocked and
9
+ grpc.secure_channel is mocked, so only the channel-option wiring is exercised.
10
+ """
11
+ import unittest
12
+ from unittest.mock import patch
13
+
14
+ from scalekit.core import (
15
+ CoreClient,
16
+ DEFAULT_KEEPALIVE_TIME_MS,
17
+ DEFAULT_KEEPALIVE_TIMEOUT_MS,
18
+ )
19
+
20
+
21
+ def _build_client(**kwargs):
22
+ """Construct a CoreClient with auth and channel creation stubbed.
23
+
24
+ Returns (client, mock_secure_channel) so tests can assert on the options
25
+ passed to grpc.secure_channel.
26
+ """
27
+ with patch.object(CoreClient, "_CoreClient__authenticate_client"), \
28
+ patch("scalekit.core.grpc.secure_channel") as mock_secure_channel, \
29
+ patch("scalekit.core.grpc.ssl_channel_credentials"), \
30
+ patch("scalekit.core.grpc.access_token_call_credentials"), \
31
+ patch("scalekit.core.grpc.composite_channel_credentials"):
32
+ client = CoreClient(
33
+ env_url="https://example.com",
34
+ client_id="cid",
35
+ client_secret="csec",
36
+ **kwargs,
37
+ )
38
+ return client, mock_secure_channel
39
+
40
+
41
+ class TestKeepaliveChannelOptions(unittest.TestCase):
42
+ def _options_from(self, mock_secure_channel):
43
+ """Extract the options kwarg from the secure_channel call."""
44
+ mock_secure_channel.assert_called_once()
45
+ _, kwargs = mock_secure_channel.call_args
46
+ return kwargs["options"]
47
+
48
+ def test_disabled_passes_empty_options(self):
49
+ """keepalive_time_ms=0 must call secure_channel with options=[]."""
50
+ _, mock_secure_channel = _build_client(keepalive_time_ms=0)
51
+ self.assertEqual(self._options_from(mock_secure_channel), [])
52
+
53
+ def test_default_construction_includes_all_four_options(self):
54
+ """Default construction must include all four expected option tuples."""
55
+ _, mock_secure_channel = _build_client()
56
+ options = self._options_from(mock_secure_channel)
57
+ self.assertEqual(
58
+ options,
59
+ [
60
+ ('grpc.keepalive_time_ms', DEFAULT_KEEPALIVE_TIME_MS),
61
+ ('grpc.keepalive_timeout_ms', DEFAULT_KEEPALIVE_TIMEOUT_MS),
62
+ ('grpc.keepalive_permit_without_calls', 1),
63
+ ('grpc.http2.max_pings_without_data', 0),
64
+ ],
65
+ )
66
+ # Explicit check on the documented default value.
67
+ self.assertIn(('grpc.keepalive_time_ms', 60000), options)
68
+
69
+ def test_below_minimum_raises_value_error(self):
70
+ """keepalive_time_ms below the 60s minimum must raise ValueError."""
71
+ with self.assertRaises(ValueError):
72
+ _build_client(keepalive_time_ms=5000)
73
+
74
+ def test_thirty_seconds_now_raises_value_error(self):
75
+ """keepalive_time_ms=30000 is below the 60s minimum and must raise ValueError."""
76
+ with self.assertRaises(ValueError):
77
+ _build_client(keepalive_time_ms=30000)
78
+
79
+ def test_sixty_seconds_boundary_accepted(self):
80
+ """keepalive_time_ms=60000 is the boundary and must be accepted."""
81
+ _, mock_secure_channel = _build_client(keepalive_time_ms=60000)
82
+ options = self._options_from(mock_secure_channel)
83
+ self.assertIn(('grpc.keepalive_time_ms', 60000), options)
84
+
85
+
86
+ if __name__ == "__main__":
87
+ unittest.main()