digitalkin 1.0.0.dev20__tar.gz → 1.0.0.dev22__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.
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/PKG-INFO +2 -2
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/pyproject.toml +2 -2
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/__version__.py +1 -1
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/community/agno/hitl.py +1 -1
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/task_manager/module_runner.py +4 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/grpc_servers/exceptions.py +4 -0
- digitalkin-1.0.0.dev22/src/digitalkin/grpc_servers/interceptors/permission.py +103 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/grpc_servers/module_servicer.py +40 -1
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/grpc_servers/utils/grpc_client_wrapper.py +2 -1
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/grpc_servers/utils/grpc_error_handler.py +4 -1
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/grpc_servers/stream_error_codes.py +1 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/module/module_context.py +6 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/modules/_base_module.py +35 -0
- digitalkin-1.0.0.dev22/src/digitalkin/services/secret/__init__.py +13 -0
- digitalkin-1.0.0.dev22/src/digitalkin/services/secret/default_secret.py +45 -0
- digitalkin-1.0.0.dev22/src/digitalkin/services/secret/exceptions.py +5 -0
- digitalkin-1.0.0.dev22/src/digitalkin/services/secret/grpc_secret.py +63 -0
- digitalkin-1.0.0.dev22/src/digitalkin/services/secret/secret_strategy.py +21 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/services_config.py +8 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/services_models.py +2 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/setup/grpc_setup.py +4 -1
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/storage/grpc_storage.py +41 -1
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/user_profile/default_user_profile.py +12 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/user_profile/grpc_user_profile.py +22 -1
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/user_profile/user_profile_strategy.py +15 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin.egg-info/PKG-INFO +2 -2
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin.egg-info/SOURCES.txt +6 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin.egg-info/requires.txt +1 -1
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/LICENSE +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/README.md +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/base_server/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/base_server/mock/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/base_server/mock/mock_pb2.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/base_server/mock/mock_pb2_grpc.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/base_server/server_async_insecure.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/base_server/server_async_secure.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/base_server/server_sync_insecure.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/base_server/server_sync_secure.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/bench_module/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/bench_module/echo_module.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/bench_module/models/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/bench_module/models/input.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/bench_module/models/output.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/bench_module/models/secret.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/bench_module/models/setup.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/bench_module/server.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/bench_module/triggers/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/bench_module/triggers/message_trigger.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/modules/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/modules/archetype_with_tools_module.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/modules/cpu_intensive_module.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/modules/dynamic_setup_module.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/modules/minimal_llm_module.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/modules/text_transform_module.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/monitoring/digitalkin_observability/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/monitoring/digitalkin_observability/http_server.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/monitoring/digitalkin_observability/interceptors.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/monitoring/digitalkin_observability/metrics.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/monitoring/digitalkin_observability/prometheus.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/monitoring/tests/test_metrics.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/redis_demo/client.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/redis_demo/echo_module.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/redis_demo/models/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/redis_demo/models/input.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/redis_demo/models/output.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/redis_demo/models/secret.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/redis_demo/models/setup.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/redis_demo/server.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/redis_demo/triggers/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/redis_demo/triggers/message_trigger.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/services/filesystem_module.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/examples/services/storage_module.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/setup.cfg +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/community/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/community/agno/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/community/agno/agno_adapter.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/community/agno/agui_tools.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/community/agno/models.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/common/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/common/factories.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/exceptions.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/job_manager/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/job_manager/base_job_manager.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/job_manager/single_job_manager.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/profiling/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/profiling/step_timer.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/profiling/task_profiler.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/resilience/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/resilience/bulkhead.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/resilience/task_supervisor.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/task_manager/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/task_manager/base_task_manager.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/task_manager/local_task_manager.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/task_manager/redis/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/task_manager/redis/proto_streams.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/task_manager/redis/redis_client.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/task_manager/redis/redis_idempotency.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/task_manager/redis/redis_signal.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/task_manager/redis/redis_state.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/task_manager/remote_task_manager.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/task_manager/task_executor.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/task_manager/task_session.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/exceptions.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/grpc_servers/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/grpc_servers/_base_server.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/grpc_servers/gateway_servicer.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/grpc_servers/interceptors/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/grpc_servers/interceptors/request_ids.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/grpc_servers/m2m_call_registry.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/grpc_servers/module_server.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/grpc_servers/stream_registry.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/grpc_servers/stream_session.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/grpc_servers/utils/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/grpc_servers/utils/circuit_breaker.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/grpc_servers/utils/utility_schema_extender.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/grpc_servers/utils/validators.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/logger.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/mixins/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/mixins/agui_mixin.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/mixins/base_mixin.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/mixins/cost_mixin.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/mixins/file_history_mixin.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/mixins/filesystem_mixin.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/mixins/logger_mixin.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/mixins/storage_mixin.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/core/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/core/job_manager_models.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/core/redis.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/core/task_monitor.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/events/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/events/agent_events.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/grpc_servers/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/grpc_servers/circuit_breaker.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/grpc_servers/m2m.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/grpc_servers/models.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/grpc_servers/types.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/module/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/module/ag_ui.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/module/base_types.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/module/module.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/module/module_types.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/module/request_metadata.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/module/select_schema.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/module/setup_types.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/module/tool_cache.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/module/tool_reference.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/module/utility.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/services/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/services/cost.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/services/registry.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/services/services.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/services/storage.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/settings/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/settings/gateway.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/settings/grpc_client.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/settings/log.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/settings/module.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/settings/profiling.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/settings/queue.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/settings/redis.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/settings/resilience.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/settings/server/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/settings/server/channel.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/settings/server/grpc.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/settings/server/server.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/settings/server/servicer.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/settings/task_manager.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/settings/utils/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/settings/utils/channel.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/utils/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/utils/dynamic_schema.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/modules/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/modules/archetype_module.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/modules/tool_module.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/modules/trigger_handler.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/modules/triggers/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/modules/triggers/healthcheck_ping_trigger.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/modules/triggers/healthcheck_services_trigger.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/modules/triggers/healthcheck_status_trigger.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/py.typed +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/base_strategy.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/communication/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/communication/communication_strategy.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/communication/default_communication.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/communication/exceptions.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/communication/grpc_communication.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/cost/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/cost/cost_strategy.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/cost/default_cost.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/cost/exceptions.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/cost/grpc_cost.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/filesystem/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/filesystem/default_filesystem.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/filesystem/exceptions.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/filesystem/filesystem_strategy.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/filesystem/grpc_filesystem.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/identity/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/identity/default_identity.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/identity/identity_strategy.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/registry/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/registry/default_registry.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/registry/exceptions.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/registry/grpc_registry.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/registry/registry_models.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/registry/registry_strategy.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/setup/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/setup/default_setup.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/setup/exceptions.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/setup/setup_strategy.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/storage/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/storage/default_storage.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/storage/exceptions.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/storage/storage_strategy.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/task_manager/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/task_manager/default_task_manager.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/task_manager/exceptions.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/task_manager/redis_task_manager.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/task_manager/task_manager_strategy.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/user_profile/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/user_profile/exceptions.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/utils/__init__.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/utils/arg_parser.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/utils/conditional_schema.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/utils/development_mode_action.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/utils/dynamic_schema.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/utils/exceptions.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/utils/llm_ready_schema.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/utils/package_discover.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/utils/proto_utils.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/utils/schema_splitter.py +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin.egg-info/dependency_links.txt +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin.egg-info/top_level.txt +0 -0
- {digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/tests/test_exceptions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: digitalkin
|
|
3
|
-
Version: 1.0.0.
|
|
3
|
+
Version: 1.0.0.dev22
|
|
4
4
|
Summary: SDK to build kin used in DigitalKin
|
|
5
5
|
Author-email: "DigitalKin.ai" <contact@digitalkin.ai>
|
|
6
6
|
License: Attribution-NonCommercial-ShareAlike 4.0 International
|
|
@@ -453,7 +453,7 @@ Requires-Python: >=3.10
|
|
|
453
453
|
Description-Content-Type: text/markdown
|
|
454
454
|
License-File: LICENSE
|
|
455
455
|
Requires-Dist: ag-ui-protocol>=0.1.18
|
|
456
|
-
Requires-Dist: agentic-mesh-protocol==1.0.
|
|
456
|
+
Requires-Dist: agentic-mesh-protocol==1.0.0a1
|
|
457
457
|
Requires-Dist: anyio>=4.13.0
|
|
458
458
|
Requires-Dist: grpcio-health-checking==1.81.0
|
|
459
459
|
Requires-Dist: grpcio-reflection==1.81.0
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
dependencies = [
|
|
30
30
|
"ag-ui-protocol>=0.1.18",
|
|
31
|
-
"agentic-mesh-protocol==1.0.
|
|
31
|
+
"agentic-mesh-protocol==1.0.0a1",
|
|
32
32
|
"anyio>=4.13.0",
|
|
33
33
|
"grpcio-health-checking==1.81.0",
|
|
34
34
|
"grpcio-reflection==1.81.0",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"pydantic-settings>=2.14.1",
|
|
38
38
|
"redis[hiredis]>=7.4.0,<9",
|
|
39
39
|
]
|
|
40
|
-
version = "1.0.0.
|
|
40
|
+
version = "1.0.0.dev22"
|
|
41
41
|
|
|
42
42
|
[project.optional-dependencies]
|
|
43
43
|
performance = [ "uvloop>=0.21" ]
|
|
@@ -611,7 +611,7 @@ class AgnoHitlRunner:
|
|
|
611
611
|
stream: Any,
|
|
612
612
|
send: Callable[[BaseAgentRunEvent], Coroutine[Any, Any, None]],
|
|
613
613
|
thread_id: str,
|
|
614
|
-
run_output_cls: type,
|
|
614
|
+
run_output_cls: type[RunOutput],
|
|
615
615
|
) -> PauseInfo | None:
|
|
616
616
|
"""Drain an Agno stream, forward events, persist on pause.
|
|
617
617
|
|
{digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/core/task_manager/module_runner.py
RENAMED
|
@@ -13,6 +13,7 @@ from redis.exceptions import RedisError
|
|
|
13
13
|
from digitalkin.core.exceptions import BackpressureTimeoutError
|
|
14
14
|
from digitalkin.core.profiling.step_timer import StepTimer
|
|
15
15
|
from digitalkin.core.profiling.task_profiler import TaskProfiler
|
|
16
|
+
from digitalkin.grpc_servers.exceptions import PermissionDeniedError
|
|
16
17
|
from digitalkin.grpc_servers.interceptors.request_ids import RequestContext
|
|
17
18
|
from digitalkin.logger import logger
|
|
18
19
|
from digitalkin.models.grpc_servers.stream_error_codes import StreamErrorCode
|
|
@@ -226,6 +227,9 @@ class ModuleRunner:
|
|
|
226
227
|
StreamErrorCode.REDIS_UNAVAILABLE.value,
|
|
227
228
|
f"redis unavailable: {type(exc).__name__}: {exc}",
|
|
228
229
|
)
|
|
230
|
+
except PermissionDeniedError as exc:
|
|
231
|
+
logger.warning("ModuleRunner: setup access denied: %s", exc, extra=log_extra)
|
|
232
|
+
await on_fatal(StreamErrorCode.SETUP_ACCESS_DENIED.value, str(exc))
|
|
229
233
|
except Exception as exc:
|
|
230
234
|
logger.exception("ModuleRunner: module job failed", extra=log_extra)
|
|
231
235
|
await on_fatal(
|
|
@@ -19,6 +19,10 @@ class SecurityError(ServerError):
|
|
|
19
19
|
"""Error related to security configuration."""
|
|
20
20
|
|
|
21
21
|
|
|
22
|
+
class PermissionDeniedError(ServerError):
|
|
23
|
+
"""Remote service rejected the call with gRPC PERMISSION_DENIED."""
|
|
24
|
+
|
|
25
|
+
|
|
22
26
|
class ServerStateError(ServerError):
|
|
23
27
|
"""Error related to server state (e.g., already started, not started)."""
|
|
24
28
|
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Client-side permission middleware for gRPC service-data access.
|
|
2
|
+
|
|
3
|
+
A single cross-cutting interceptor: any unary call a backend rejects with
|
|
4
|
+
``PERMISSION_DENIED`` surfaces as :class:`PermissionDeniedError`, so callers
|
|
5
|
+
never handle permission per service. Attached on every channel, it covers all
|
|
6
|
+
data services and module-to-module calls uniformly.
|
|
7
|
+
|
|
8
|
+
The interceptor *returns* a terminal denied call rather than raising: raising a
|
|
9
|
+
non-``AioRpcError`` from an aio interceptor leaks into the intercepted call's
|
|
10
|
+
``__del__`` (grpc only swallows ``AioRpcError``/``CancelledError`` there), so we
|
|
11
|
+
mirror grpc's own ``UnaryUnaryCallResponse`` pattern and raise from ``__await__``.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from typing import TYPE_CHECKING, Any
|
|
17
|
+
|
|
18
|
+
import grpc
|
|
19
|
+
import grpc.aio
|
|
20
|
+
|
|
21
|
+
from digitalkin.grpc_servers.exceptions import PermissionDeniedError
|
|
22
|
+
from digitalkin.logger import logger
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from collections.abc import Awaitable, Callable
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class _DeniedUnaryUnaryCall(grpc.aio.UnaryUnaryCall):
|
|
29
|
+
"""A finished unary call that raises ``PermissionDeniedError`` when awaited."""
|
|
30
|
+
|
|
31
|
+
def __init__(self, method: str, details: str) -> None:
|
|
32
|
+
self._message = f"[{method}] {details or 'permission denied'}"
|
|
33
|
+
self._details = details
|
|
34
|
+
|
|
35
|
+
def cancel(self) -> bool: # noqa: PLR6301
|
|
36
|
+
return False
|
|
37
|
+
|
|
38
|
+
def cancelled(self) -> bool: # noqa: PLR6301
|
|
39
|
+
return False
|
|
40
|
+
|
|
41
|
+
def done(self) -> bool: # noqa: PLR6301
|
|
42
|
+
return True
|
|
43
|
+
|
|
44
|
+
def add_done_callback(self, unused_callback: Any) -> None:
|
|
45
|
+
"""No-op: the call is already finished.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
unused_callback: Ignored; present to satisfy the call interface.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def time_remaining(self) -> float | None: # noqa: PLR6301
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
async def initial_metadata(self) -> grpc.aio.Metadata: # noqa: PLR6301
|
|
55
|
+
return grpc.aio.Metadata()
|
|
56
|
+
|
|
57
|
+
async def trailing_metadata(self) -> grpc.aio.Metadata: # noqa: PLR6301
|
|
58
|
+
return grpc.aio.Metadata()
|
|
59
|
+
|
|
60
|
+
async def code(self) -> grpc.StatusCode: # noqa: PLR6301
|
|
61
|
+
return grpc.StatusCode.PERMISSION_DENIED
|
|
62
|
+
|
|
63
|
+
async def details(self) -> str:
|
|
64
|
+
return self._details
|
|
65
|
+
|
|
66
|
+
async def debug_error_string(self) -> None: # noqa: PLR6301
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
async def wait_for_connection(self) -> None:
|
|
70
|
+
"""No-op: the call already resolved to a permission denial."""
|
|
71
|
+
|
|
72
|
+
def __await__(self) -> Any:
|
|
73
|
+
raise PermissionDeniedError(self._message)
|
|
74
|
+
yield # unreachable; marks this method a generator so it is awaitable
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class PermissionClientInterceptor(grpc.aio.UnaryUnaryClientInterceptor):
|
|
78
|
+
"""Map a ``PERMISSION_DENIED`` unary reply to ``PermissionDeniedError``."""
|
|
79
|
+
|
|
80
|
+
async def intercept_unary_unary( # noqa: PLR6301
|
|
81
|
+
self,
|
|
82
|
+
continuation: Callable[[grpc.aio.ClientCallDetails, Any], Awaitable[Any]],
|
|
83
|
+
client_call_details: grpc.aio.ClientCallDetails,
|
|
84
|
+
request: Any,
|
|
85
|
+
) -> Any:
|
|
86
|
+
"""Return a denied call on PERMISSION_DENIED, else pass the real call through.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
continuation: Downstream call continuation.
|
|
90
|
+
client_call_details: Original call details.
|
|
91
|
+
request: Request message.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
The downstream call, or a terminal call raising PermissionDeniedError on await.
|
|
95
|
+
"""
|
|
96
|
+
call = await continuation(client_call_details, request)
|
|
97
|
+
if await call.code() == grpc.StatusCode.PERMISSION_DENIED:
|
|
98
|
+
method = client_call_details.method
|
|
99
|
+
method_name = method.decode() if isinstance(method, bytes) else method
|
|
100
|
+
details = await call.details()
|
|
101
|
+
logger.warning("permission denied on %s: %s", method_name, details or "")
|
|
102
|
+
return _DeniedUnaryUnaryCall(method_name, details)
|
|
103
|
+
return call
|
{digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/grpc_servers/module_servicer.py
RENAMED
|
@@ -14,12 +14,13 @@ from agentic_mesh_protocol.module.v1 import (
|
|
|
14
14
|
lifecycle_pb2,
|
|
15
15
|
module_service_pb2_grpc,
|
|
16
16
|
)
|
|
17
|
+
from agentic_mesh_protocol.user_profile.v1 import user_profile_pb2
|
|
17
18
|
from google.protobuf import json_format, struct_pb2
|
|
18
19
|
|
|
19
20
|
from digitalkin.core.job_manager.base_job_manager import BaseJobManager
|
|
20
21
|
from digitalkin.core.job_manager.single_job_manager import SingleJobManager
|
|
21
22
|
from digitalkin.core.task_manager.redis.redis_signal import SharedRedisListener
|
|
22
|
-
from digitalkin.grpc_servers.exceptions import ServicerError
|
|
23
|
+
from digitalkin.grpc_servers.exceptions import PermissionDeniedError, ServicerError
|
|
23
24
|
from digitalkin.logger import logger
|
|
24
25
|
from digitalkin.models.module.module import ModuleCodeModel
|
|
25
26
|
from digitalkin.models.module.setup_types import SetupModel
|
|
@@ -31,6 +32,7 @@ from digitalkin.services.registry import GrpcRegistry, RegistryStrategy
|
|
|
31
32
|
from digitalkin.services.setup.default_setup import DefaultSetup
|
|
32
33
|
from digitalkin.services.setup.grpc_setup import GrpcSetup
|
|
33
34
|
from digitalkin.services.setup.setup_strategy import SetupStrategy, SetupVersionData
|
|
35
|
+
from digitalkin.services.user_profile import DefaultUserProfile, GrpcUserProfile, UserProfileStrategy
|
|
34
36
|
from digitalkin.utils.arg_parser import ArgParser
|
|
35
37
|
from digitalkin.utils.development_mode_action import DevelopmentModeMappingAction
|
|
36
38
|
|
|
@@ -40,6 +42,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
|
|
|
40
42
|
|
|
41
43
|
args: Namespace
|
|
42
44
|
setup: SetupStrategy
|
|
45
|
+
user_profile: UserProfileStrategy
|
|
43
46
|
job_manager: BaseJobManager
|
|
44
47
|
_registry_cache: RegistryStrategy | None
|
|
45
48
|
_tool_cache_by_setup: dict[str, tuple[Any, float]]
|
|
@@ -82,6 +85,16 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
|
|
|
82
85
|
|
|
83
86
|
logger.debug("ModuleServicer initialized with SingleJobManager")
|
|
84
87
|
self.setup = GrpcSetup() if self.args.services_mode == ServicesMode.REMOTE else DefaultSetup()
|
|
88
|
+
# Access-control client gating the setup cache. Always built, always called (fail-closed).
|
|
89
|
+
if self.args.services_mode == ServicesMode.REMOTE:
|
|
90
|
+
up_cfg = self.module_class.services_config_params.get("user_profile") or {}
|
|
91
|
+
up_client_config = up_cfg.get("client_config")
|
|
92
|
+
if not up_client_config:
|
|
93
|
+
msg = "user_profile client_config is required for setup access control"
|
|
94
|
+
raise RuntimeError(msg)
|
|
95
|
+
self.user_profile = GrpcUserProfile("", "", "", up_client_config)
|
|
96
|
+
else:
|
|
97
|
+
self.user_profile = DefaultUserProfile("", "", "")
|
|
85
98
|
self._setup_cache: dict[str, tuple[float, SetupVersionData]] = {}
|
|
86
99
|
self._setup_inflight: dict[str, asyncio.Future[SetupVersionData]] = {}
|
|
87
100
|
|
|
@@ -97,6 +110,11 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
|
|
|
97
110
|
await self.setup.close_channel()
|
|
98
111
|
except Exception:
|
|
99
112
|
logger.exception("Error closing GrpcSetup channel")
|
|
113
|
+
if isinstance(self.user_profile, GrpcUserProfile):
|
|
114
|
+
try:
|
|
115
|
+
await self.user_profile.close_channel()
|
|
116
|
+
except Exception:
|
|
117
|
+
logger.exception("Error closing GrpcUserProfile channel")
|
|
100
118
|
# M8: close the Redis connection pools (were leaked on every server stop).
|
|
101
119
|
logger.info("[VALIDATE M8] closing module servicer Redis pools") # TODO(validate): remove after prod validation
|
|
102
120
|
await self._redis_client.close()
|
|
@@ -245,6 +263,19 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
|
|
|
245
263
|
del self._setup_cache[oldest_key]
|
|
246
264
|
self._setup_cache[setup_id] = (time.monotonic(), version_data)
|
|
247
265
|
|
|
266
|
+
async def _check_setup_access(self, setup_id: str) -> None:
|
|
267
|
+
"""Block if the caller may not access the setup (RESOURCE_TYPE_SETUP).
|
|
268
|
+
|
|
269
|
+
Args:
|
|
270
|
+
setup_id: The setup identifier being resolved.
|
|
271
|
+
|
|
272
|
+
Raises:
|
|
273
|
+
PermissionDeniedError: If access to the setup is denied.
|
|
274
|
+
"""
|
|
275
|
+
if not await self.user_profile.check_resource_access(user_profile_pb2.RESOURCE_TYPE_SETUP, setup_id):
|
|
276
|
+
msg = f"access denied to setup {setup_id}"
|
|
277
|
+
raise PermissionDeniedError(msg)
|
|
278
|
+
|
|
248
279
|
async def resolve_setup(self, setup_id: str, mission_id: str) -> SetupVersionData:
|
|
249
280
|
"""Return setup version data from cache or remote service.
|
|
250
281
|
|
|
@@ -257,7 +288,9 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
|
|
|
257
288
|
|
|
258
289
|
Raises:
|
|
259
290
|
LookupError: No setup data found for setup_id.
|
|
291
|
+
PermissionDeniedError: If the caller may not access this setup.
|
|
260
292
|
"""
|
|
293
|
+
await self._check_setup_access(setup_id)
|
|
261
294
|
# Fast path: cache hit within TTL
|
|
262
295
|
if (cached := self._setup_cache.get(setup_id)) is not None:
|
|
263
296
|
if time.monotonic() - cached[0] < get_module_servicer_settings().setup_cache_ttl:
|
|
@@ -326,6 +359,12 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
|
|
|
326
359
|
extra={"mission_id": request.mission_id},
|
|
327
360
|
)
|
|
328
361
|
setup_version = request.setup_version
|
|
362
|
+
if not await self.user_profile.check_resource_access(
|
|
363
|
+
user_profile_pb2.RESOURCE_TYPE_SETUP, setup_version.setup_id
|
|
364
|
+
):
|
|
365
|
+
context.set_code(grpc.StatusCode.PERMISSION_DENIED)
|
|
366
|
+
context.set_details(f"access denied to setup {setup_version.setup_id}")
|
|
367
|
+
return lifecycle_pb2.ConfigSetupModuleResponse(success=False)
|
|
329
368
|
# Invalidate cached setup so concurrent/subsequent starts refetch the reconfigured version
|
|
330
369
|
self._setup_cache.pop(setup_version.setup_id, None)
|
|
331
370
|
config_setup_data = self.module_class.create_config_setup_model(json_format.MessageToDict(request.content))
|
|
@@ -16,6 +16,7 @@ import grpc.aio
|
|
|
16
16
|
|
|
17
17
|
from digitalkin.core.resilience.bulkhead import Bulkhead
|
|
18
18
|
from digitalkin.grpc_servers.exceptions import CircuitOpenError, ServerError
|
|
19
|
+
from digitalkin.grpc_servers.interceptors.permission import PermissionClientInterceptor
|
|
19
20
|
from digitalkin.grpc_servers.interceptors.request_ids import RequestIdClientInterceptor
|
|
20
21
|
from digitalkin.grpc_servers.utils.circuit_breaker import CircuitBreaker
|
|
21
22
|
from digitalkin.logger import logger
|
|
@@ -103,7 +104,7 @@ class GrpcClientWrapper:
|
|
|
103
104
|
|
|
104
105
|
credentials = self._build_channel_credentials(config)
|
|
105
106
|
grpc_compression = config.compression.to_grpc()
|
|
106
|
-
interceptors = [RequestIdClientInterceptor()]
|
|
107
|
+
interceptors = [RequestIdClientInterceptor(), PermissionClientInterceptor()]
|
|
107
108
|
if credentials is not None:
|
|
108
109
|
channel = grpc.aio.secure_channel(
|
|
109
110
|
config.address,
|
|
@@ -4,7 +4,7 @@ from collections.abc import AsyncGenerator
|
|
|
4
4
|
from contextlib import asynccontextmanager
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
|
-
from digitalkin.grpc_servers.exceptions import ServerError
|
|
7
|
+
from digitalkin.grpc_servers.exceptions import PermissionDeniedError, ServerError
|
|
8
8
|
from digitalkin.logger import logger
|
|
9
9
|
|
|
10
10
|
|
|
@@ -28,6 +28,7 @@ class GrpcErrorHandlerMixin:
|
|
|
28
28
|
Context for the operation.
|
|
29
29
|
|
|
30
30
|
Raises:
|
|
31
|
+
PermissionDeniedError: Re-raised as-is so the authz status is never masked.
|
|
31
32
|
ServerError: For gRPC-related errors.
|
|
32
33
|
service_error_class: For service-specific errors if provided.
|
|
33
34
|
"""
|
|
@@ -36,6 +37,8 @@ class GrpcErrorHandlerMixin:
|
|
|
36
37
|
|
|
37
38
|
try:
|
|
38
39
|
yield
|
|
40
|
+
except PermissionDeniedError:
|
|
41
|
+
raise
|
|
39
42
|
except service_error_class as e:
|
|
40
43
|
# Re-raise service-specific errors as-is
|
|
41
44
|
msg = f"{service_error_class.__name__} in {operation}: {e}"
|
{digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/models/module/module_context.py
RENAMED
|
@@ -17,6 +17,7 @@ from digitalkin.services.cost.cost_strategy import CostStrategy
|
|
|
17
17
|
from digitalkin.services.filesystem.filesystem_strategy import FilesystemStrategy
|
|
18
18
|
from digitalkin.services.identity.identity_strategy import IdentityStrategy
|
|
19
19
|
from digitalkin.services.registry.registry_strategy import RegistryStrategy
|
|
20
|
+
from digitalkin.services.secret.secret_strategy import SecretStrategy
|
|
20
21
|
from digitalkin.services.storage.storage_strategy import StorageStrategy
|
|
21
22
|
from digitalkin.services.task_manager.task_manager_strategy import TaskManagerStrategy
|
|
22
23
|
from digitalkin.services.user_profile.user_profile_strategy import UserProfileStrategy
|
|
@@ -95,6 +96,7 @@ class ModuleContext:
|
|
|
95
96
|
filesystem: FilesystemStrategy
|
|
96
97
|
identity: IdentityStrategy
|
|
97
98
|
registry: RegistryStrategy
|
|
99
|
+
secret: SecretStrategy
|
|
98
100
|
storage: StorageStrategy
|
|
99
101
|
task_manager: TaskManagerStrategy | None
|
|
100
102
|
user_profile: UserProfileStrategy
|
|
@@ -115,6 +117,7 @@ class ModuleContext:
|
|
|
115
117
|
filesystem: FilesystemStrategy,
|
|
116
118
|
identity: IdentityStrategy,
|
|
117
119
|
registry: RegistryStrategy,
|
|
120
|
+
secret: SecretStrategy,
|
|
118
121
|
storage: StorageStrategy,
|
|
119
122
|
user_profile: UserProfileStrategy,
|
|
120
123
|
session: dict[str, Any],
|
|
@@ -135,6 +138,7 @@ class ModuleContext:
|
|
|
135
138
|
filesystem: FilesystemStrategy.
|
|
136
139
|
identity: IdentityStrategy.
|
|
137
140
|
registry: RegistryStrategy.
|
|
141
|
+
secret: SecretStrategy.
|
|
138
142
|
storage: StorageStrategy.
|
|
139
143
|
user_profile: UserProfileStrategy.
|
|
140
144
|
task_manager: Optional, injected by SingleJobManager (RedisTaskManager).
|
|
@@ -153,6 +157,7 @@ class ModuleContext:
|
|
|
153
157
|
self.filesystem = filesystem
|
|
154
158
|
self.identity = identity
|
|
155
159
|
self.registry = registry
|
|
160
|
+
self.secret = secret
|
|
156
161
|
self.storage = storage
|
|
157
162
|
self.task_manager = task_manager
|
|
158
163
|
self.user_profile = user_profile
|
|
@@ -374,6 +379,7 @@ class ModuleContext:
|
|
|
374
379
|
("registry", self.registry),
|
|
375
380
|
("filesystem", self.filesystem),
|
|
376
381
|
("user_profile", self.user_profile),
|
|
382
|
+
("secret", self.secret),
|
|
377
383
|
("identity", self.identity),
|
|
378
384
|
)
|
|
379
385
|
for name, service in owned:
|
|
@@ -7,6 +7,7 @@ from abc import ABC, abstractmethod
|
|
|
7
7
|
from collections.abc import Callable, Coroutine
|
|
8
8
|
from typing import Any, ClassVar, Generic
|
|
9
9
|
|
|
10
|
+
from digitalkin.grpc_servers.exceptions import PermissionDeniedError
|
|
10
11
|
from digitalkin.grpc_servers.utils.utility_schema_extender import UtilitySchemaExtender
|
|
11
12
|
from digitalkin.logger import logger
|
|
12
13
|
from digitalkin.models.module.module import ModuleCodeModel, ModuleStatus
|
|
@@ -548,6 +549,10 @@ class BaseModule( # Module SDK base class requires many public methods # noqa:
|
|
|
548
549
|
self._status = ModuleStatus.CANCELLED
|
|
549
550
|
logger.info("Module %s cancelled", self.name, extra=self.context.session.current_ids())
|
|
550
551
|
raise
|
|
552
|
+
except PermissionDeniedError as e:
|
|
553
|
+
self._status = ModuleStatus.FAILED
|
|
554
|
+
logger.warning("Permission denied in module %s: %s", self.name, e, extra=self.context.session.current_ids())
|
|
555
|
+
await self._notify_permission_denied(self.context.callbacks.send_message, e)
|
|
551
556
|
except Exception as e:
|
|
552
557
|
self._status = ModuleStatus.FAILED
|
|
553
558
|
logger.exception("Error inside module %s", self.name, extra=self.context.session.current_ids())
|
|
@@ -564,6 +569,28 @@ class BaseModule( # Module SDK base class requires many public methods # noqa:
|
|
|
564
569
|
else:
|
|
565
570
|
self._status = ModuleStatus.STOPPING
|
|
566
571
|
|
|
572
|
+
async def _notify_permission_denied(
|
|
573
|
+
self,
|
|
574
|
+
callback: Callable[..., Coroutine[Any, Any, None]],
|
|
575
|
+
e: PermissionDeniedError,
|
|
576
|
+
) -> None:
|
|
577
|
+
"""Send a PermissionDenied notification to the user via the callback.
|
|
578
|
+
|
|
579
|
+
Args:
|
|
580
|
+
callback: The output callback installed on the module context.
|
|
581
|
+
e: The permission-denied error raised by a service call.
|
|
582
|
+
"""
|
|
583
|
+
try:
|
|
584
|
+
await callback(
|
|
585
|
+
ModuleCodeModel(
|
|
586
|
+
code="PermissionDenied",
|
|
587
|
+
short_description="Permission denied",
|
|
588
|
+
message=str(e),
|
|
589
|
+
)
|
|
590
|
+
)
|
|
591
|
+
except Exception:
|
|
592
|
+
logger.exception("Failed to send permission-denied callback", extra=self.context.session.current_ids())
|
|
593
|
+
|
|
567
594
|
async def prepare(
|
|
568
595
|
self,
|
|
569
596
|
setup_data: SetupModelT,
|
|
@@ -623,6 +650,14 @@ class BaseModule( # Module SDK base class requires many public methods # noqa:
|
|
|
623
650
|
try:
|
|
624
651
|
await self.prepare(setup_data, callback)
|
|
625
652
|
timer.mark("prepare")
|
|
653
|
+
except PermissionDeniedError as e:
|
|
654
|
+
self._status = ModuleStatus.FAILED
|
|
655
|
+
logger.warning("Permission denied initializing module: %s", e, extra=self.context.session.current_ids())
|
|
656
|
+
await self._notify_permission_denied(callback, e)
|
|
657
|
+
if done_callback is not None:
|
|
658
|
+
await done_callback(None)
|
|
659
|
+
await self.stop()
|
|
660
|
+
return
|
|
626
661
|
except Exception as e:
|
|
627
662
|
self._status = ModuleStatus.FAILED
|
|
628
663
|
short_description = "Error initializing module"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Secret service package."""
|
|
2
|
+
|
|
3
|
+
from digitalkin.services.secret.default_secret import DefaultSecret
|
|
4
|
+
from digitalkin.services.secret.exceptions import SecretServiceError
|
|
5
|
+
from digitalkin.services.secret.grpc_secret import GrpcSecret
|
|
6
|
+
from digitalkin.services.secret.secret_strategy import SecretStrategy
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"DefaultSecret",
|
|
10
|
+
"GrpcSecret",
|
|
11
|
+
"SecretServiceError",
|
|
12
|
+
"SecretStrategy",
|
|
13
|
+
]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Default secret implementation."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from digitalkin.logger import logger
|
|
6
|
+
from digitalkin.services.secret.secret_strategy import SecretStrategy
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DefaultSecret(SecretStrategy):
|
|
10
|
+
"""Default secret strategy with in-memory storage."""
|
|
11
|
+
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
mission_id: str,
|
|
15
|
+
setup_id: str,
|
|
16
|
+
setup_version_id: str,
|
|
17
|
+
) -> None:
|
|
18
|
+
"""Initialize the strategy.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
mission_id: The ID of the mission this strategy is associated with
|
|
22
|
+
setup_id: The ID of the setup
|
|
23
|
+
setup_version_id: The ID of the setup version
|
|
24
|
+
"""
|
|
25
|
+
super().__init__(mission_id=mission_id, setup_id=setup_id, setup_version_id=setup_version_id)
|
|
26
|
+
self.db: dict[str, dict[str, Any]] = {}
|
|
27
|
+
|
|
28
|
+
async def get_secret(self) -> dict[str, Any] | None:
|
|
29
|
+
"""Get the secret for this setup from in-memory storage.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Secret values, or None if not found.
|
|
33
|
+
"""
|
|
34
|
+
if self.setup_id not in self.db:
|
|
35
|
+
logger.warning("No secret found for setup_id: %s", self.setup_id)
|
|
36
|
+
return None
|
|
37
|
+
return self.db[self.setup_id]
|
|
38
|
+
|
|
39
|
+
def add_secret(self, secret_data: dict[str, Any]) -> None:
|
|
40
|
+
"""Add a secret to the in-memory database (helper for testing).
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
secret_data: Dictionary containing secret values.
|
|
44
|
+
"""
|
|
45
|
+
self.db[self.setup_id] = secret_data
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Digital Kin Secret Service gRPC Client (wraps UserProfileService.GetSetupSecret)."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from agentic_mesh_protocol.user_profile.v1 import (
|
|
6
|
+
user_profile_pb2,
|
|
7
|
+
user_profile_service_pb2_grpc,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from digitalkin.grpc_servers.utils.grpc_client_wrapper import GrpcClientWrapper
|
|
11
|
+
from digitalkin.grpc_servers.utils.grpc_error_handler import GrpcErrorHandlerMixin
|
|
12
|
+
from digitalkin.logger import logger
|
|
13
|
+
from digitalkin.models.grpc_servers.models import ClientConfig
|
|
14
|
+
from digitalkin.services.secret.exceptions import SecretServiceError
|
|
15
|
+
from digitalkin.services.secret.secret_strategy import SecretStrategy
|
|
16
|
+
from digitalkin.utils.proto_utils import ProtoUtils
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class GrpcSecret(SecretStrategy, GrpcClientWrapper, GrpcErrorHandlerMixin):
|
|
20
|
+
"""gRPC client for setup secrets (backed by the UserProfileService)."""
|
|
21
|
+
|
|
22
|
+
service_name: str = "UserProfileService"
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
mission_id: str,
|
|
27
|
+
setup_id: str,
|
|
28
|
+
setup_version_id: str,
|
|
29
|
+
client_config: ClientConfig,
|
|
30
|
+
) -> None:
|
|
31
|
+
"""Initialize the secret service.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
mission_id: The ID of the mission this strategy is associated with
|
|
35
|
+
setup_id: The ID of the setup
|
|
36
|
+
setup_version_id: The ID of the setup version
|
|
37
|
+
client_config: Client configuration for gRPC connection
|
|
38
|
+
"""
|
|
39
|
+
super().__init__(mission_id=mission_id, setup_id=setup_id, setup_version_id=setup_version_id)
|
|
40
|
+
self._init_channel(client_config)
|
|
41
|
+
self.stub = self._get_or_create_stub(user_profile_service_pb2_grpc.UserProfileServiceStub)
|
|
42
|
+
logger.debug("Channel client 'Secret' initialized successfully")
|
|
43
|
+
|
|
44
|
+
async def close(self) -> None:
|
|
45
|
+
"""Release this instance's pooled gRPC channel ref."""
|
|
46
|
+
await self.close_channel()
|
|
47
|
+
|
|
48
|
+
async def get_secret(self) -> dict[str, Any] | None:
|
|
49
|
+
"""Resolve the secret object attached to this setup.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
The secret values, or None if not found.
|
|
53
|
+
|
|
54
|
+
Raises:
|
|
55
|
+
SecretServiceError: If the gRPC operation fails.
|
|
56
|
+
"""
|
|
57
|
+
async with self.handle_grpc_errors("GetSetupSecret", SecretServiceError):
|
|
58
|
+
request = user_profile_pb2.GetSetupSecretRequest(setup_id=self.setup_id, mission_id=self.mission_id)
|
|
59
|
+
response = await self.exec_grpc_query("GetSetupSecret", request)
|
|
60
|
+
if not response.success:
|
|
61
|
+
logger.warning("No secret found for setup_id: %s", self.setup_id)
|
|
62
|
+
return None
|
|
63
|
+
return ProtoUtils.proto_to_dict(response.secret, with_defaults=True)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""This module contains the abstract base class for Secret strategies."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from digitalkin.services.base_strategy import BaseStrategy
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SecretStrategy(BaseStrategy, ABC):
|
|
10
|
+
"""Abstract base class for Secret strategies."""
|
|
11
|
+
|
|
12
|
+
@abstractmethod
|
|
13
|
+
async def get_secret(self) -> dict[str, Any] | None:
|
|
14
|
+
"""Resolve the secret object attached to this setup.
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
The secret values (matching the module's secret_schema), or None if not found.
|
|
18
|
+
|
|
19
|
+
Raises:
|
|
20
|
+
SecretServiceError: If the service call fails.
|
|
21
|
+
"""
|
{digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/services_config.py
RENAMED
|
@@ -10,6 +10,7 @@ from digitalkin.services.cost import CostStrategy, DefaultCost, GrpcCost
|
|
|
10
10
|
from digitalkin.services.filesystem import DefaultFilesystem, FilesystemStrategy, GrpcFilesystem
|
|
11
11
|
from digitalkin.services.identity import DefaultIdentity, IdentityStrategy
|
|
12
12
|
from digitalkin.services.registry import DefaultRegistry, GrpcRegistry, RegistryStrategy
|
|
13
|
+
from digitalkin.services.secret import DefaultSecret, GrpcSecret, SecretStrategy
|
|
13
14
|
from digitalkin.services.services_models import ServicesStrategy
|
|
14
15
|
from digitalkin.services.storage import DefaultStorage, GrpcStorage, StorageStrategy
|
|
15
16
|
from digitalkin.services.user_profile import DefaultUserProfile, GrpcUserProfile, UserProfileStrategy
|
|
@@ -36,6 +37,7 @@ class ServicesConfig(BaseModel):
|
|
|
36
37
|
"identity",
|
|
37
38
|
"communication",
|
|
38
39
|
"user_profile",
|
|
40
|
+
"secret",
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
def __init__(
|
|
@@ -67,6 +69,7 @@ class ServicesConfig(BaseModel):
|
|
|
67
69
|
"identity": ServicesStrategy(local=DefaultIdentity, remote=DefaultIdentity),
|
|
68
70
|
"communication": ServicesStrategy(local=DefaultCommunication, remote=GrpcCommunication),
|
|
69
71
|
"user_profile": ServicesStrategy(local=DefaultUserProfile, remote=GrpcUserProfile),
|
|
72
|
+
"secret": ServicesStrategy(local=DefaultSecret, remote=GrpcSecret),
|
|
70
73
|
}
|
|
71
74
|
|
|
72
75
|
# Apply strategy overrides
|
|
@@ -162,6 +165,11 @@ class ServicesConfig(BaseModel):
|
|
|
162
165
|
"""The user_profile service strategy class for the current mode."""
|
|
163
166
|
return self._strategies["user_profile"][self.mode.value]
|
|
164
167
|
|
|
168
|
+
@property
|
|
169
|
+
def secret(self) -> type[SecretStrategy]:
|
|
170
|
+
"""The secret service strategy class for the current mode."""
|
|
171
|
+
return self._strategies["secret"][self.mode.value]
|
|
172
|
+
|
|
165
173
|
def update_mode(self, mode: ServicesMode) -> None:
|
|
166
174
|
"""Update the strategy mode.
|
|
167
175
|
|
{digitalkin-1.0.0.dev20 → digitalkin-1.0.0.dev22}/src/digitalkin/services/services_models.py
RENAMED
|
@@ -11,6 +11,7 @@ from digitalkin.services.cost import CostStrategy
|
|
|
11
11
|
from digitalkin.services.filesystem import FilesystemStrategy
|
|
12
12
|
from digitalkin.services.identity import IdentityStrategy
|
|
13
13
|
from digitalkin.services.registry import RegistryStrategy
|
|
14
|
+
from digitalkin.services.secret import SecretStrategy
|
|
14
15
|
from digitalkin.services.storage import StorageStrategy
|
|
15
16
|
from digitalkin.services.task_manager.task_manager_strategy import TaskManagerStrategy
|
|
16
17
|
from digitalkin.services.user_profile import UserProfileStrategy
|
|
@@ -23,6 +24,7 @@ T = TypeVar(
|
|
|
23
24
|
| FilesystemStrategy
|
|
24
25
|
| IdentityStrategy
|
|
25
26
|
| RegistryStrategy
|
|
27
|
+
| SecretStrategy
|
|
26
28
|
| StorageStrategy
|
|
27
29
|
| UserProfileStrategy
|
|
28
30
|
| TaskManagerStrategy,
|