scalekit-sdk-python 2.19.0__tar.gz → 2.19.1__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.
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/PKG-INFO +1 -1
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/_version.py +1 -1
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/client.py +13 -1
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/core.py +62 -5
- scalekit_sdk_python-2.19.1/scalekit/resource.py +96 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit_sdk_python.egg-info/PKG-INFO +1 -1
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit_sdk_python.egg-info/SOURCES.txt +2 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_keepalive_channel_options.py +57 -3
- scalekit_sdk_python-2.19.1/tests/test_resource.py +81 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/LICENSE +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/README.md +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/buf/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/buf/validate/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/buf/validate/validate_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/buf/validate/validate_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/buf/validate/validate_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/pyproject.toml +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/actions.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/frameworks/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/frameworks/google_adk.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/frameworks/langchain.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/frameworks/types/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/frameworks/types/google_adk_tool.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/frameworks/util.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/custom_provider.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/mcp_config.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/mcp_connection_auth_state.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/mcp_instance.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/requests/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/requests/create_connected_account_request.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/requests/create_custom_provider_request.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/requests/delete_custom_provider_request.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/requests/list_providers_request.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/requests/mcp_request.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/requests/tool_request.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/requests/update_connected_account_request.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/requests/update_custom_provider_request.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/create_connected_account_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/create_custom_provider_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/create_mcp_config_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/create_mcp_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/create_mcp_session_token_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/delete_connected_account_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/delete_custom_provider_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/delete_mcp_config_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/delete_mcp_instance_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/ensure_mcp_instance_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/execute_tool_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/get_connected_account_auth_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/get_mcp_instance_auth_state_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/get_mcp_instance_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/get_mcp_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/list_connected_accounts_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/list_mcp_configs_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/list_mcp_connected_accounts_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/list_mcp_instances_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/list_providers_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/list_tools_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/magic_link_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/update_connected_account_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/update_custom_provider_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/update_mcp_config_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/update_mcp_instance_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/responses/verify_connected_account_user_response.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/tool_input_output.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/tool_mapping.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/modifier.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/types.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/auth.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/common/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/common/exceptions.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/common/scalekit.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/common/user.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/connected_accounts.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/connection.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/constants/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/constants/user.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/directory.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/domain.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/events.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/frameworks/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/frameworks/django.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/frameworks/fastapi.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/frameworks/flask.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/m2m_client.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/mcp.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/middleware/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/middleware/csrf_state.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/middleware/protocol.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/middleware/session_crypto.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/middleware/session_manager.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/organization.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/passwordless.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/permissions.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/providers.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/role.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/sessions.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/token.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/tools.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/users.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/utils/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/utils/directory.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/agentkit_logs/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/agentkit_logs/agentkit_analytics_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/agentkit_logs/agentkit_analytics_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/agentkit_logs/agentkit_analytics_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/agentkit_logs/agentkit_logs_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/agentkit_logs/agentkit_logs_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/agentkit_logs/agentkit_logs_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/auditlogs/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/auditlogs/auditlogs_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/auditlogs/auditlogs_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/auditlogs/auditlogs_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/auth/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/auth/auth_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/auth/auth_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/auth/auth_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/auth/passwordless_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/auth/passwordless_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/auth/passwordless_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/auth/totp_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/auth/totp_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/auth/totp_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/auth/webauthn_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/auth/webauthn_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/auth/webauthn_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/billing/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/billing/billing_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/billing/billing_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/billing/billing_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/clients/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/clients/clients_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/clients/clients_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/clients/clients_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/commons/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/commons/commons_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/commons/commons_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/commons/commons_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/connected_accounts/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/connected_accounts/connected_accounts_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/connected_accounts/connected_accounts_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/connected_accounts/connected_accounts_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/connections/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/connections/connections_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/connections/connections_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/connections/connections_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/directories/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/directories/directories_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/directories/directories_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/directories/directories_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/domains/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/domains/domains_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/domains/domains_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/domains/domains_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/emails/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/emails/emails_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/emails/emails_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/emails/emails_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/environments/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/environments/environments_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/environments/environments_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/environments/environments_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/errdetails/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/errdetails/errdetails_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/errdetails/errdetails_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/errdetails/errdetails_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/events/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/events/events_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/events/events_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/events/events_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/gateway/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/gateway/gateway_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/gateway/gateway_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/gateway/gateway_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/interceptors/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/interceptors/interceptors_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/interceptors/interceptors_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/interceptors/interceptors_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/keys/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/keys/keys_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/keys/keys_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/keys/keys_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/logstreams/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/logstreams/logstreams_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/logstreams/logstreams_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/logstreams/logstreams_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/mcp/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/mcp/mcp_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/mcp/mcp_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/mcp/mcp_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/members/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/members/members_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/members/members_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/members/members_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/migrations/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/migrations/migrations_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/migrations/migrations_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/migrations/migrations_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/options/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/options/options_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/options/options_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/options/options_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/organizations/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/organizations/organizations_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/organizations/organizations_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/organizations/organizations_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/providers/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/providers/providers_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/providers/providers_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/providers/providers_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/roles/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/roles/roles_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/roles/roles_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/roles/roles_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/secrets/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/secrets/secrets_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/secrets/secrets_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/secrets/secrets_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/sessions/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/sessions/sessions_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/sessions/sessions_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/sessions/sessions_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/tokens/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/tokens/tokens_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/tokens/tokens_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/tokens/tokens_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/tools/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/tools/tools_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/tools/tools_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/tools/tools_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/user_attributes/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/user_attributes/user_attributes_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/user_attributes/user_attributes_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/user_attributes/user_attributes_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/users/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/users/users_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/users/users_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/users/users_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/webhooks/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/webhooks/webhooks_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/webhooks/webhooks_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/webhooks/webhooks_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/workspaces/__init__.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/workspaces/workspaces_pb2.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/workspaces/workspaces_pb2.pyi +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/v1/workspaces/workspaces_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/webauthn.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit_sdk_python.egg-info/dependency_links.txt +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit_sdk_python.egg-info/requires.txt +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit_sdk_python.egg-info/top_level.txt +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/setup.cfg +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/setup.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_actions.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_call_timeout.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_connected_accounts.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_connection.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_directory.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_domain.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_events.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_http_timeout.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_login.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_m2m_client.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_mcp.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_middleware_csrf_state.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_middleware_django.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_middleware_fastapi.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_middleware_flask.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_middleware_session_crypto.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_middleware_session_manager.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_org_roles.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_organization.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_organization_session_policy.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_organization_slug_logo.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_passwordless.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_permissions.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_providers.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_role_defaults_dependent.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_roles.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_sessions.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_sk819_retry_behavior.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_tokens.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_tools.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_transient_error_no_retry.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_user_roles_permissions.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_users.py +0 -0
- {scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_webauthn_credentials.py +0 -0
|
@@ -19,6 +19,7 @@ from scalekit.core import (
|
|
|
19
19
|
from scalekit.domain import DomainClient
|
|
20
20
|
from scalekit.connection import ConnectionClient
|
|
21
21
|
from scalekit.m2m_client import M2MClient
|
|
22
|
+
from scalekit.resource import ResourceClient
|
|
22
23
|
from scalekit.organization import OrganizationClient
|
|
23
24
|
from scalekit.directory import DirectoryClient
|
|
24
25
|
from scalekit.users import UserClient
|
|
@@ -77,9 +78,19 @@ class ScalekitClient:
|
|
|
77
78
|
:type : ``` str ```
|
|
78
79
|
:param keepalive_time_ms : How often, in milliseconds, an idle gRPC
|
|
79
80
|
connection is verified before reuse.
|
|
81
|
+
Also derives grpc.client_idle_timeout_ms
|
|
82
|
+
(see CLIENT_IDLE_TIMEOUT_CEILING_MS),
|
|
83
|
+
which proactively recycles a connection
|
|
84
|
+
with zero active calls before the
|
|
85
|
+
backend's own MaxConnectionIdle would.
|
|
80
86
|
Defaults to 60000; most callers never
|
|
81
87
|
need to set this. Set to 0 to disable
|
|
82
|
-
|
|
88
|
+
both of this SDK's own settings for
|
|
89
|
+
these — grpc-core still applies its own
|
|
90
|
+
(much larger) default idle behavior
|
|
91
|
+
when no options are passed at all, so
|
|
92
|
+
this isn't "no idle handling," just no
|
|
93
|
+
SDK-configured one.
|
|
83
94
|
:type : ``` int ```
|
|
84
95
|
:param keepalive_timeout_ms : How long, in milliseconds, to wait for a
|
|
85
96
|
keepalive response before treating an
|
|
@@ -122,6 +133,7 @@ class ScalekitClient:
|
|
|
122
133
|
self.organization = OrganizationClient(self.core_client)
|
|
123
134
|
self.directory = DirectoryClient(self.core_client)
|
|
124
135
|
self.m2m_client = M2MClient(self.core_client)
|
|
136
|
+
self.resources = ResourceClient(self.core_client)
|
|
125
137
|
self.users = UserClient(self.core_client)
|
|
126
138
|
self.roles = RoleClient(self.core_client)
|
|
127
139
|
self.permissions = PermissionClient(self.core_client)
|
|
@@ -45,6 +45,32 @@ DEFAULT_KEEPALIVE_TIMEOUT_MS = 10_000
|
|
|
45
45
|
# never even reaches the wire as requested.) Reject 1..59999.
|
|
46
46
|
MIN_KEEPALIVE_TIME_MS = 60_000
|
|
47
47
|
|
|
48
|
+
# grpc-core exposes 'grpc.client_idle_timeout_ms' (gRFC A9) as the client-side
|
|
49
|
+
# equivalent of what the Go and Node SDKs implement by hand on their
|
|
50
|
+
# self-managed HTTP/2 transports (grpcIdleConnTimeout / idleConnectionTimeoutMs):
|
|
51
|
+
# proactively transition a fully-idle channel so its connection is torn down
|
|
52
|
+
# and lazily recreated on the next call, instead of waiting to discover it's
|
|
53
|
+
# dead (or waiting for the backend to GOAWAY it) only when a real request is
|
|
54
|
+
# written to it. This SDK previously configured keepalive pings but never this
|
|
55
|
+
# option — the gap this constant and CLIENT_IDLE_TIMEOUT_PING_CYCLES close.
|
|
56
|
+
#
|
|
57
|
+
# Must stay strictly BELOW the backend's own grpcKeepaliveMaxConnectionIdle
|
|
58
|
+
# (5 min, scalekit's cmd/grpc.go), not equal to it: landing exactly on the
|
|
59
|
+
# backend's bound is a race — whichever side's timer fires first wins, and the
|
|
60
|
+
# loser is a request written into a socket the other side just closed. Mirrors
|
|
61
|
+
# the Go SDK's grpcIdleConnCeiling and the Node SDK's
|
|
62
|
+
# IDLE_CONNECTION_TIMEOUT_CEILING_MS exactly (both 4 minutes), for the same
|
|
63
|
+
# reason.
|
|
64
|
+
CLIENT_IDLE_TIMEOUT_CEILING_MS = 240_000
|
|
65
|
+
|
|
66
|
+
# Multiplier applied to keepalive_time_ms before clamping to the ceiling above
|
|
67
|
+
# — mirrors the Go SDK's grpcIdleConnPingCycles / Node SDK's
|
|
68
|
+
# IDLE_PING_CYCLES_BEFORE_CLOSE (both 5) exactly, so this SDK scales down
|
|
69
|
+
# correctly alongside the other two should MIN_KEEPALIVE_TIME_MS itself ever
|
|
70
|
+
# be lowered. Superseded by the ceiling for every currently-valid non-zero
|
|
71
|
+
# keepalive_time_ms (60s x 5 = 300s already exceeds the 4-minute ceiling).
|
|
72
|
+
CLIENT_IDLE_TIMEOUT_PING_CYCLES = 5
|
|
73
|
+
|
|
48
74
|
# requests defaults to no timeout, so a black-holed connection blocks the
|
|
49
75
|
# calling thread until the OS abandons the socket. Bound the connect and read
|
|
50
76
|
# phases separately with a (connect, read) tuple.
|
|
@@ -113,6 +139,22 @@ def _assert_valid_timeout(name: str, value) -> None:
|
|
|
113
139
|
)
|
|
114
140
|
|
|
115
141
|
|
|
142
|
+
def _client_idle_timeout_ms_for(keepalive_time_ms: int) -> int:
|
|
143
|
+
"""Derive grpc.client_idle_timeout_ms from keepalive_time_ms — mirrors the
|
|
144
|
+
Go SDK's idleConnTimeoutFor / Node SDK's idleConnectionTimeoutMsFor exactly
|
|
145
|
+
(same formula, same constants), so the three SDKs land on the same idle-close
|
|
146
|
+
behavior. keepalive_time_ms == 0 (disabled) returns 0, meaning "not set" to
|
|
147
|
+
the caller (see __grpc_secure_channel — no option is passed, so grpc-core's
|
|
148
|
+
own much larger default applies), consistent with keepalive_time_ms == 0
|
|
149
|
+
disabling the ping-based idle detection above too."""
|
|
150
|
+
if not keepalive_time_ms:
|
|
151
|
+
return 0
|
|
152
|
+
scaled = keepalive_time_ms * CLIENT_IDLE_TIMEOUT_PING_CYCLES
|
|
153
|
+
if scaled <= 0 or scaled > CLIENT_IDLE_TIMEOUT_CEILING_MS:
|
|
154
|
+
return CLIENT_IDLE_TIMEOUT_CEILING_MS
|
|
155
|
+
return scaled
|
|
156
|
+
|
|
157
|
+
|
|
116
158
|
def _is_success_status(status_code: int) -> bool:
|
|
117
159
|
"""200-only would be a footgun: the token/JWKS endpoints are only ever
|
|
118
160
|
expected to reply 200, but a strict != 200 check would misclassify any
|
|
@@ -171,9 +213,18 @@ class CoreClient:
|
|
|
171
213
|
connection is verified before reuse.
|
|
172
214
|
Must stay above the backend's keepalive
|
|
173
215
|
MinTime (30s) with real margin, or the
|
|
174
|
-
server treats this ping as abuse.
|
|
175
|
-
|
|
176
|
-
|
|
216
|
+
server treats this ping as abuse. Also
|
|
217
|
+
derives grpc.client_idle_timeout_ms (see
|
|
218
|
+
CLIENT_IDLE_TIMEOUT_CEILING_MS), which
|
|
219
|
+
proactively recycles a connection with
|
|
220
|
+
zero active calls before the backend's
|
|
221
|
+
own MaxConnectionIdle would. Defaults to
|
|
222
|
+
60000. Set to 0 to disable both of this
|
|
223
|
+
SDK's own settings for these — grpc-core
|
|
224
|
+
still applies its own (much larger)
|
|
225
|
+
default idle behavior when no options
|
|
226
|
+
are passed at all, so this isn't "no
|
|
227
|
+
idle handling," just no SDK-configured one.
|
|
177
228
|
:type : ``` int ```
|
|
178
229
|
:param keepalive_timeout_ms : How long, in milliseconds, to wait for a
|
|
179
230
|
keepalive response before treating an
|
|
@@ -242,8 +293,10 @@ class CoreClient:
|
|
|
242
293
|
channel_credentials,
|
|
243
294
|
call_credentials,
|
|
244
295
|
)
|
|
245
|
-
# keepalive_time_ms == 0 disables keepalive
|
|
246
|
-
#
|
|
296
|
+
# keepalive_time_ms == 0 disables keepalive (and, below, the derived
|
|
297
|
+
# idle-close timeout) entirely: no options are passed, so grpc-core
|
|
298
|
+
# falls back to its own defaults (no idle pings, ~30 min idle timeout)
|
|
299
|
+
# — an escape hatch for a network path that rejects this pattern.
|
|
247
300
|
channel_options = []
|
|
248
301
|
if self.keepalive_time_ms:
|
|
249
302
|
# keepalive_permit_without_calls=1 so an idle channel is still
|
|
@@ -256,6 +309,10 @@ class CoreClient:
|
|
|
256
309
|
('grpc.keepalive_timeout_ms', self.keepalive_timeout_ms),
|
|
257
310
|
('grpc.keepalive_permit_without_calls', 1),
|
|
258
311
|
('grpc.http2.max_pings_without_data', 0),
|
|
312
|
+
# Proactively idle out (and lazily recreate on next use) a
|
|
313
|
+
# connection with zero active calls before the backend's own
|
|
314
|
+
# MaxConnectionIdle would — see CLIENT_IDLE_TIMEOUT_CEILING_MS.
|
|
315
|
+
('grpc.client_idle_timeout_ms', _client_idle_timeout_ms_for(self.keepalive_time_ms)),
|
|
259
316
|
]
|
|
260
317
|
self.grpc_secure_channel = grpc.secure_channel(
|
|
261
318
|
self.host, composite_credentials, options=channel_options
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from typing import Optional, List
|
|
2
|
+
|
|
3
|
+
from scalekit.core import CoreClient
|
|
4
|
+
from scalekit.v1.clients.clients_pb2 import *
|
|
5
|
+
from scalekit.v1.clients.clients_pb2_grpc import ClientServiceStub
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ResourceClient:
|
|
9
|
+
"""API to read and revoke end-user consents granted against a resource"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, core_client: CoreClient):
|
|
12
|
+
"""
|
|
13
|
+
Initializer for Resource Client
|
|
14
|
+
|
|
15
|
+
:param core_client : CoreClient Object
|
|
16
|
+
:type : ``` obj ```
|
|
17
|
+
:returns
|
|
18
|
+
None
|
|
19
|
+
"""
|
|
20
|
+
self.core_client = core_client
|
|
21
|
+
self.client_service = ClientServiceStub(self.core_client.grpc_secure_channel)
|
|
22
|
+
|
|
23
|
+
def list_user_consents(
|
|
24
|
+
self,
|
|
25
|
+
resource_id: str,
|
|
26
|
+
search: Optional[str] = None,
|
|
27
|
+
page_size: Optional[int] = None,
|
|
28
|
+
page_token: Optional[str] = None,
|
|
29
|
+
user_ids: Optional[List[str]] = None,
|
|
30
|
+
) -> ListResourceUserConsentsResponse:
|
|
31
|
+
"""
|
|
32
|
+
Method to list the end-user consents granted against a resource
|
|
33
|
+
|
|
34
|
+
Each returned consent carries id, external_user_id, client_id, client_name,
|
|
35
|
+
scopes and granted_at. The response also carries total_size plus
|
|
36
|
+
next_page_token / prev_page_token cursors.
|
|
37
|
+
|
|
38
|
+
Pass user_ids to match specific external user IDs exactly and
|
|
39
|
+
case-sensitively. Pass search for a case-insensitive substring match.
|
|
40
|
+
When both are given, user_ids wins and search is ignored.
|
|
41
|
+
|
|
42
|
+
:param resource_id : Resource id to list consents for (format: res_xxxxx)
|
|
43
|
+
:type : ``` str ```
|
|
44
|
+
:param search : Case-insensitive substring match on external user IDs
|
|
45
|
+
:type : ``` str ```
|
|
46
|
+
:param page_size : Page size for pagination (max 30)
|
|
47
|
+
:type : ``` int ```
|
|
48
|
+
:param page_token : Page token for pagination
|
|
49
|
+
:type : ``` str ```
|
|
50
|
+
:param user_ids : Exact match on external user IDs (max 25)
|
|
51
|
+
:type : ``` list ```
|
|
52
|
+
:returns:
|
|
53
|
+
List Resource User Consents Response
|
|
54
|
+
"""
|
|
55
|
+
if not resource_id:
|
|
56
|
+
raise ValueError("resource_id is required")
|
|
57
|
+
|
|
58
|
+
return self.core_client.grpc_exec(
|
|
59
|
+
self.client_service.ListResourceUserConsents.with_call,
|
|
60
|
+
ListResourceUserConsentsRequest(
|
|
61
|
+
resource_id=resource_id,
|
|
62
|
+
search=search,
|
|
63
|
+
page_size=page_size,
|
|
64
|
+
page_token=page_token,
|
|
65
|
+
filter=ResourceUserConsentFilter(external_user_id=user_ids) if user_ids else None,
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def revoke_user_consent(self, client_id: str, consent_id: str) -> RevokeUserConsentResponse:
|
|
70
|
+
"""
|
|
71
|
+
Method to revoke a single end-user consent held by an API client
|
|
72
|
+
|
|
73
|
+
Deletes the consent, so the client is prompted for consent again on its next
|
|
74
|
+
authorization attempt, and revokes every active refresh token issued to that
|
|
75
|
+
client for the same user. Access tokens already issued stay valid until they
|
|
76
|
+
expire.
|
|
77
|
+
|
|
78
|
+
Note that client_id is the API client that holds the consent, not the
|
|
79
|
+
resource id.
|
|
80
|
+
|
|
81
|
+
:param client_id : Client holding the consent (format: m2m_xxxxx)
|
|
82
|
+
:type : ``` str ```
|
|
83
|
+
:param consent_id : Consent to revoke (format: usrcnst_xxxxx)
|
|
84
|
+
:type : ``` str ```
|
|
85
|
+
:returns:
|
|
86
|
+
Revoke User Consent Response
|
|
87
|
+
"""
|
|
88
|
+
if not client_id:
|
|
89
|
+
raise ValueError("client_id is required")
|
|
90
|
+
if not consent_id:
|
|
91
|
+
raise ValueError("consent_id is required")
|
|
92
|
+
|
|
93
|
+
return self.core_client.grpc_exec(
|
|
94
|
+
self.client_service.RevokeUserConsent.with_call,
|
|
95
|
+
RevokeUserConsentRequest(client_id=client_id, consent_id=consent_id)
|
|
96
|
+
)
|
{scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit_sdk_python.egg-info/SOURCES.txt
RENAMED
|
@@ -23,6 +23,7 @@ scalekit/organization.py
|
|
|
23
23
|
scalekit/passwordless.py
|
|
24
24
|
scalekit/permissions.py
|
|
25
25
|
scalekit/providers.py
|
|
26
|
+
scalekit/resource.py
|
|
26
27
|
scalekit/role.py
|
|
27
28
|
scalekit/sessions.py
|
|
28
29
|
scalekit/token.py
|
|
@@ -275,6 +276,7 @@ tests/test_organization_slug_logo.py
|
|
|
275
276
|
tests/test_passwordless.py
|
|
276
277
|
tests/test_permissions.py
|
|
277
278
|
tests/test_providers.py
|
|
279
|
+
tests/test_resource.py
|
|
278
280
|
tests/test_role_defaults_dependent.py
|
|
279
281
|
tests/test_roles.py
|
|
280
282
|
tests/test_sessions.py
|
{scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/tests/test_keepalive_channel_options.py
RENAMED
|
@@ -15,6 +15,9 @@ from scalekit.core import (
|
|
|
15
15
|
CoreClient,
|
|
16
16
|
DEFAULT_KEEPALIVE_TIME_MS,
|
|
17
17
|
DEFAULT_KEEPALIVE_TIMEOUT_MS,
|
|
18
|
+
CLIENT_IDLE_TIMEOUT_CEILING_MS,
|
|
19
|
+
CLIENT_IDLE_TIMEOUT_PING_CYCLES,
|
|
20
|
+
_client_idle_timeout_ms_for,
|
|
18
21
|
)
|
|
19
22
|
|
|
20
23
|
|
|
@@ -50,8 +53,8 @@ class TestKeepaliveChannelOptions(unittest.TestCase):
|
|
|
50
53
|
_, mock_secure_channel = _build_client(keepalive_time_ms=0)
|
|
51
54
|
self.assertEqual(self._options_from(mock_secure_channel), [])
|
|
52
55
|
|
|
53
|
-
def
|
|
54
|
-
"""Default construction must include all
|
|
56
|
+
def test_default_construction_includes_all_five_options(self):
|
|
57
|
+
"""Default construction must include all five expected option tuples."""
|
|
55
58
|
_, mock_secure_channel = _build_client()
|
|
56
59
|
options = self._options_from(mock_secure_channel)
|
|
57
60
|
self.assertEqual(
|
|
@@ -61,10 +64,12 @@ class TestKeepaliveChannelOptions(unittest.TestCase):
|
|
|
61
64
|
('grpc.keepalive_timeout_ms', DEFAULT_KEEPALIVE_TIMEOUT_MS),
|
|
62
65
|
('grpc.keepalive_permit_without_calls', 1),
|
|
63
66
|
('grpc.http2.max_pings_without_data', 0),
|
|
67
|
+
('grpc.client_idle_timeout_ms', CLIENT_IDLE_TIMEOUT_CEILING_MS),
|
|
64
68
|
],
|
|
65
69
|
)
|
|
66
|
-
# Explicit check on the documented default
|
|
70
|
+
# Explicit check on the documented default values.
|
|
67
71
|
self.assertIn(('grpc.keepalive_time_ms', 60000), options)
|
|
72
|
+
self.assertIn(('grpc.client_idle_timeout_ms', 240000), options)
|
|
68
73
|
|
|
69
74
|
def test_below_minimum_raises_value_error(self):
|
|
70
75
|
"""keepalive_time_ms below the 60s minimum must raise ValueError."""
|
|
@@ -83,5 +88,54 @@ class TestKeepaliveChannelOptions(unittest.TestCase):
|
|
|
83
88
|
self.assertIn(('grpc.keepalive_time_ms', 60000), options)
|
|
84
89
|
|
|
85
90
|
|
|
91
|
+
class TestClientIdleTimeout(unittest.TestCase):
|
|
92
|
+
"""Regression tests for the grpc.client_idle_timeout_ms option — the
|
|
93
|
+
client-side idle-close equivalent of the Go SDK's grpcIdleConnTimeout /
|
|
94
|
+
Node SDK's idleConnectionTimeoutMs, which this SDK previously omitted."""
|
|
95
|
+
|
|
96
|
+
# The backend's own grpc.max_connection_idle enforcement (scalekit's
|
|
97
|
+
# cmd/grpc.go, grpcKeepaliveMaxConnectionIdle) — kept here as a literal,
|
|
98
|
+
# not imported, so this test independently pins the actual production
|
|
99
|
+
# value rather than trusting whatever CLIENT_IDLE_TIMEOUT_CEILING_MS
|
|
100
|
+
# happens to be set to.
|
|
101
|
+
_BACKEND_MAX_CONNECTION_IDLE_MS = 5 * 60 * 1000
|
|
102
|
+
|
|
103
|
+
def test_ceiling_is_strictly_below_backend_max_connection_idle(self):
|
|
104
|
+
"""Landing exactly on (or above) the backend's own bound is a race —
|
|
105
|
+
whichever side's timer fires first wins, and the loser is a request
|
|
106
|
+
written into a socket the other side just closed."""
|
|
107
|
+
self.assertLess(CLIENT_IDLE_TIMEOUT_CEILING_MS, self._BACKEND_MAX_CONNECTION_IDLE_MS)
|
|
108
|
+
|
|
109
|
+
def test_derived_value_scales_with_keepalive_time_when_below_ceiling(self):
|
|
110
|
+
small = 1000 # hypothetically below the real MIN_KEEPALIVE_TIME_MS floor, to exercise scaling
|
|
111
|
+
self.assertEqual(
|
|
112
|
+
_client_idle_timeout_ms_for(small),
|
|
113
|
+
small * CLIENT_IDLE_TIMEOUT_PING_CYCLES,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
def test_derived_value_clamps_to_ceiling_at_default_keepalive(self):
|
|
117
|
+
self.assertEqual(
|
|
118
|
+
_client_idle_timeout_ms_for(DEFAULT_KEEPALIVE_TIME_MS),
|
|
119
|
+
CLIENT_IDLE_TIMEOUT_CEILING_MS,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
def test_disabled_keepalive_derives_to_zero(self):
|
|
123
|
+
self.assertEqual(_client_idle_timeout_ms_for(0), 0)
|
|
124
|
+
|
|
125
|
+
def test_disabling_keepalive_omits_idle_timeout_option_too(self):
|
|
126
|
+
"""keepalive_time_ms=0 must disable BOTH the ping-based and the
|
|
127
|
+
idle-close detection — a partial disable would be a surprising,
|
|
128
|
+
undocumented middle state."""
|
|
129
|
+
_, mock_secure_channel = _build_client(keepalive_time_ms=0)
|
|
130
|
+
mock_secure_channel.assert_called_once()
|
|
131
|
+
_, kwargs = mock_secure_channel.call_args
|
|
132
|
+
options = kwargs["options"]
|
|
133
|
+
self.assertEqual(options, [])
|
|
134
|
+
self.assertNotIn(
|
|
135
|
+
"grpc.client_idle_timeout_ms",
|
|
136
|
+
[name for name, _ in options],
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
|
|
86
140
|
if __name__ == "__main__":
|
|
87
141
|
unittest.main()
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from basetest import BaseTest
|
|
2
|
+
|
|
3
|
+
# A real MCP server resource in the test environment. These tests assert the
|
|
4
|
+
# call shape and the pagination envelope, never the consent contents, so they
|
|
5
|
+
# hold whether or not the resource currently has consents.
|
|
6
|
+
TEST_RESOURCE_ID = "res_90805895235109156"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestResourceClient(BaseTest):
|
|
10
|
+
""" Class definition for TestResourceClient Class """
|
|
11
|
+
|
|
12
|
+
def test_list_user_consents(self):
|
|
13
|
+
""" Method to test list user consents for a resource """
|
|
14
|
+
response = self.scalekit_client.resources.list_user_consents(
|
|
15
|
+
resource_id=TEST_RESOURCE_ID
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
self.assertEqual(response[1].code().name, "OK")
|
|
19
|
+
self.assertIsNotNone(response[0].consents)
|
|
20
|
+
self.assertIsInstance(response[0].total_size, int)
|
|
21
|
+
|
|
22
|
+
def test_list_user_consents_with_page_size(self):
|
|
23
|
+
""" Method to test list user consents with a page size """
|
|
24
|
+
response = self.scalekit_client.resources.list_user_consents(
|
|
25
|
+
resource_id=TEST_RESOURCE_ID, page_size=10
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
self.assertEqual(response[1].code().name, "OK")
|
|
29
|
+
self.assertTrue(len(response[0].consents) <= 10)
|
|
30
|
+
|
|
31
|
+
def test_list_user_consents_with_search(self):
|
|
32
|
+
""" Method to test list user consents with a search term """
|
|
33
|
+
response = self.scalekit_client.resources.list_user_consents(
|
|
34
|
+
resource_id=TEST_RESOURCE_ID, search="usr_"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
self.assertEqual(response[1].code().name, "OK")
|
|
38
|
+
self.assertIsNotNone(response[0].consents)
|
|
39
|
+
|
|
40
|
+
def test_list_user_consents_with_user_ids(self):
|
|
41
|
+
""" Method to test list user consents with an exact user_ids filter """
|
|
42
|
+
response = self.scalekit_client.resources.list_user_consents(
|
|
43
|
+
resource_id=TEST_RESOURCE_ID, user_ids=["usr_does_not_exist"]
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
self.assertEqual(response[1].code().name, "OK")
|
|
47
|
+
self.assertEqual(len(response[0].consents), 0)
|
|
48
|
+
|
|
49
|
+
def test_list_user_consents_user_ids_takes_precedence_over_search(self):
|
|
50
|
+
""" Method to test that user_ids is honoured alongside search """
|
|
51
|
+
response = self.scalekit_client.resources.list_user_consents(
|
|
52
|
+
resource_id=TEST_RESOURCE_ID, user_ids=["usr_does_not_exist"], search="usr_"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
self.assertEqual(response[1].code().name, "OK")
|
|
56
|
+
self.assertEqual(len(response[0].consents), 0)
|
|
57
|
+
|
|
58
|
+
def test_list_user_consents_without_resource_id(self):
|
|
59
|
+
""" Method to test list user consents without a resource id """
|
|
60
|
+
with self.assertRaises(ValueError) as context:
|
|
61
|
+
self.scalekit_client.resources.list_user_consents(resource_id="")
|
|
62
|
+
|
|
63
|
+
self.assertEqual(str(context.exception), "resource_id is required")
|
|
64
|
+
|
|
65
|
+
def test_revoke_user_consent_without_client_id(self):
|
|
66
|
+
""" Method to test revoke user consent without a client id """
|
|
67
|
+
with self.assertRaises(ValueError) as context:
|
|
68
|
+
self.scalekit_client.resources.revoke_user_consent(
|
|
69
|
+
client_id="", consent_id="usrcnst_1234567890"
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
self.assertEqual(str(context.exception), "client_id is required")
|
|
73
|
+
|
|
74
|
+
def test_revoke_user_consent_without_consent_id(self):
|
|
75
|
+
""" Method to test revoke user consent without a consent id """
|
|
76
|
+
with self.assertRaises(ValueError) as context:
|
|
77
|
+
self.scalekit_client.resources.revoke_user_consent(
|
|
78
|
+
client_id="m2m_1234567890", consent_id=""
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
self.assertEqual(str(context.exception), "consent_id is required")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/frameworks/__init__.py
RENAMED
|
File without changes
|
{scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/frameworks/google_adk.py
RENAMED
|
File without changes
|
{scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/frameworks/langchain.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/frameworks/util.py
RENAMED
|
File without changes
|
{scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/__init__.py
RENAMED
|
File without changes
|
{scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/custom_provider.py
RENAMED
|
File without changes
|
{scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/mcp_config.py
RENAMED
|
File without changes
|
|
File without changes
|
{scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/mcp_instance.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scalekit_sdk_python-2.19.0 → scalekit_sdk_python-2.19.1}/scalekit/actions/models/tool_mapping.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|