kubiya-control-plane-api 0.9.15__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- control_plane_api/LICENSE +676 -0
- control_plane_api/README.md +350 -0
- control_plane_api/__init__.py +4 -0
- control_plane_api/__version__.py +8 -0
- control_plane_api/alembic/README +1 -0
- control_plane_api/alembic/env.py +121 -0
- control_plane_api/alembic/script.py.mako +28 -0
- control_plane_api/alembic/versions/2613c65c3dbe_initial_database_setup.py +32 -0
- control_plane_api/alembic/versions/2df520d4927d_merge_heads.py +28 -0
- control_plane_api/alembic/versions/43abf98d6a01_add_paused_status_to_executions.py +73 -0
- control_plane_api/alembic/versions/6289854264cb_merge_multiple_heads.py +28 -0
- control_plane_api/alembic/versions/6a4d4dc3d8dc_generate_execution_transitions.py +50 -0
- control_plane_api/alembic/versions/87d11cf0a783_add_disconnected_status_to_worker_.py +44 -0
- control_plane_api/alembic/versions/add_ephemeral_queue_support.py +85 -0
- control_plane_api/alembic/versions/add_model_type_to_llm_models.py +31 -0
- control_plane_api/alembic/versions/add_plan_executions_table.py +114 -0
- control_plane_api/alembic/versions/add_trace_span_tables.py +154 -0
- control_plane_api/alembic/versions/add_user_info_to_traces.py +36 -0
- control_plane_api/alembic/versions/adjusting_foreign_keys.py +32 -0
- control_plane_api/alembic/versions/b4983d976db2_initial_tables.py +1128 -0
- control_plane_api/alembic/versions/d181a3b40e71_rename_custom_metadata_to_metadata_in_.py +50 -0
- control_plane_api/alembic/versions/df9117888e82_add_missing_columns.py +82 -0
- control_plane_api/alembic/versions/f25de6ad895a_missing_migrations.py +34 -0
- control_plane_api/alembic/versions/f71305fb69b9_fix_ephemeral_queue_deletion_foreign_key.py +54 -0
- control_plane_api/alembic/versions/mark_local_exec_queues_as_ephemeral.py +68 -0
- control_plane_api/alembic.ini +148 -0
- control_plane_api/api/index.py +12 -0
- control_plane_api/app/__init__.py +11 -0
- control_plane_api/app/activities/__init__.py +20 -0
- control_plane_api/app/activities/agent_activities.py +384 -0
- control_plane_api/app/activities/plan_generation_activities.py +499 -0
- control_plane_api/app/activities/team_activities.py +424 -0
- control_plane_api/app/activities/temporal_cloud_activities.py +588 -0
- control_plane_api/app/config/__init__.py +35 -0
- control_plane_api/app/config/api_config.py +469 -0
- control_plane_api/app/config/config_loader.py +224 -0
- control_plane_api/app/config/model_pricing.py +323 -0
- control_plane_api/app/config/storage_config.py +159 -0
- control_plane_api/app/config.py +115 -0
- control_plane_api/app/controllers/__init__.py +0 -0
- control_plane_api/app/controllers/execution_environment_controller.py +1315 -0
- control_plane_api/app/database.py +135 -0
- control_plane_api/app/exceptions.py +408 -0
- control_plane_api/app/lib/__init__.py +11 -0
- control_plane_api/app/lib/environment.py +65 -0
- control_plane_api/app/lib/event_bus/__init__.py +17 -0
- control_plane_api/app/lib/event_bus/base.py +136 -0
- control_plane_api/app/lib/event_bus/manager.py +335 -0
- control_plane_api/app/lib/event_bus/providers/__init__.py +6 -0
- control_plane_api/app/lib/event_bus/providers/http_provider.py +166 -0
- control_plane_api/app/lib/event_bus/providers/nats_provider.py +324 -0
- control_plane_api/app/lib/event_bus/providers/redis_provider.py +233 -0
- control_plane_api/app/lib/event_bus/providers/websocket_provider.py +497 -0
- control_plane_api/app/lib/job_executor.py +330 -0
- control_plane_api/app/lib/kubiya_client.py +293 -0
- control_plane_api/app/lib/litellm_pricing.py +166 -0
- control_plane_api/app/lib/mcp_validation.py +163 -0
- control_plane_api/app/lib/nats/__init__.py +13 -0
- control_plane_api/app/lib/nats/credentials_manager.py +288 -0
- control_plane_api/app/lib/nats/listener.py +374 -0
- control_plane_api/app/lib/planning_prompt_builder.py +153 -0
- control_plane_api/app/lib/planning_tools/__init__.py +41 -0
- control_plane_api/app/lib/planning_tools/agents.py +409 -0
- control_plane_api/app/lib/planning_tools/agno_toolkit.py +836 -0
- control_plane_api/app/lib/planning_tools/base.py +119 -0
- control_plane_api/app/lib/planning_tools/cognitive_memory_tools.py +403 -0
- control_plane_api/app/lib/planning_tools/context_graph_tools.py +545 -0
- control_plane_api/app/lib/planning_tools/environments.py +218 -0
- control_plane_api/app/lib/planning_tools/knowledge.py +204 -0
- control_plane_api/app/lib/planning_tools/models.py +93 -0
- control_plane_api/app/lib/planning_tools/planning_service.py +646 -0
- control_plane_api/app/lib/planning_tools/resources.py +242 -0
- control_plane_api/app/lib/planning_tools/teams.py +334 -0
- control_plane_api/app/lib/policy_enforcer_client.py +1016 -0
- control_plane_api/app/lib/redis_client.py +803 -0
- control_plane_api/app/lib/sqlalchemy_utils.py +486 -0
- control_plane_api/app/lib/state_transition_tools/__init__.py +7 -0
- control_plane_api/app/lib/state_transition_tools/execution_context.py +388 -0
- control_plane_api/app/lib/storage/__init__.py +20 -0
- control_plane_api/app/lib/storage/base_provider.py +274 -0
- control_plane_api/app/lib/storage/provider_factory.py +157 -0
- control_plane_api/app/lib/storage/vercel_blob_provider.py +468 -0
- control_plane_api/app/lib/supabase.py +71 -0
- control_plane_api/app/lib/supabase_utils.py +138 -0
- control_plane_api/app/lib/task_planning/__init__.py +138 -0
- control_plane_api/app/lib/task_planning/agent_factory.py +308 -0
- control_plane_api/app/lib/task_planning/agents.py +389 -0
- control_plane_api/app/lib/task_planning/cache.py +218 -0
- control_plane_api/app/lib/task_planning/entity_resolver.py +273 -0
- control_plane_api/app/lib/task_planning/helpers.py +293 -0
- control_plane_api/app/lib/task_planning/hooks.py +474 -0
- control_plane_api/app/lib/task_planning/models.py +503 -0
- control_plane_api/app/lib/task_planning/plan_validator.py +166 -0
- control_plane_api/app/lib/task_planning/planning_workflow.py +2911 -0
- control_plane_api/app/lib/task_planning/runner.py +656 -0
- control_plane_api/app/lib/task_planning/streaming_hook.py +213 -0
- control_plane_api/app/lib/task_planning/workflow.py +424 -0
- control_plane_api/app/lib/templating/__init__.py +88 -0
- control_plane_api/app/lib/templating/compiler.py +278 -0
- control_plane_api/app/lib/templating/engine.py +178 -0
- control_plane_api/app/lib/templating/parsers/__init__.py +29 -0
- control_plane_api/app/lib/templating/parsers/base.py +96 -0
- control_plane_api/app/lib/templating/parsers/env.py +85 -0
- control_plane_api/app/lib/templating/parsers/graph.py +112 -0
- control_plane_api/app/lib/templating/parsers/secret.py +87 -0
- control_plane_api/app/lib/templating/parsers/simple.py +81 -0
- control_plane_api/app/lib/templating/resolver.py +366 -0
- control_plane_api/app/lib/templating/types.py +214 -0
- control_plane_api/app/lib/templating/validator.py +201 -0
- control_plane_api/app/lib/temporal_client.py +232 -0
- control_plane_api/app/lib/temporal_credentials_cache.py +178 -0
- control_plane_api/app/lib/temporal_credentials_service.py +203 -0
- control_plane_api/app/lib/validation/__init__.py +24 -0
- control_plane_api/app/lib/validation/runtime_validation.py +388 -0
- control_plane_api/app/main.py +531 -0
- control_plane_api/app/middleware/__init__.py +10 -0
- control_plane_api/app/middleware/auth.py +645 -0
- control_plane_api/app/middleware/exception_handler.py +267 -0
- control_plane_api/app/middleware/prometheus_middleware.py +173 -0
- control_plane_api/app/middleware/rate_limiting.py +384 -0
- control_plane_api/app/middleware/request_id.py +202 -0
- control_plane_api/app/models/__init__.py +40 -0
- control_plane_api/app/models/agent.py +90 -0
- control_plane_api/app/models/analytics.py +206 -0
- control_plane_api/app/models/associations.py +107 -0
- control_plane_api/app/models/auth_user.py +73 -0
- control_plane_api/app/models/context.py +161 -0
- control_plane_api/app/models/custom_integration.py +99 -0
- control_plane_api/app/models/environment.py +64 -0
- control_plane_api/app/models/execution.py +125 -0
- control_plane_api/app/models/execution_transition.py +50 -0
- control_plane_api/app/models/job.py +159 -0
- control_plane_api/app/models/llm_model.py +78 -0
- control_plane_api/app/models/orchestration.py +66 -0
- control_plane_api/app/models/plan_execution.py +102 -0
- control_plane_api/app/models/presence.py +49 -0
- control_plane_api/app/models/project.py +61 -0
- control_plane_api/app/models/project_management.py +85 -0
- control_plane_api/app/models/session.py +29 -0
- control_plane_api/app/models/skill.py +155 -0
- control_plane_api/app/models/system_tables.py +43 -0
- control_plane_api/app/models/task_planning.py +372 -0
- control_plane_api/app/models/team.py +86 -0
- control_plane_api/app/models/trace.py +257 -0
- control_plane_api/app/models/user_profile.py +54 -0
- control_plane_api/app/models/worker.py +221 -0
- control_plane_api/app/models/workflow.py +161 -0
- control_plane_api/app/models/workspace.py +50 -0
- control_plane_api/app/observability/__init__.py +177 -0
- control_plane_api/app/observability/context_logging.py +475 -0
- control_plane_api/app/observability/decorators.py +337 -0
- control_plane_api/app/observability/local_span_processor.py +702 -0
- control_plane_api/app/observability/metrics.py +303 -0
- control_plane_api/app/observability/middleware.py +246 -0
- control_plane_api/app/observability/optional.py +115 -0
- control_plane_api/app/observability/tracing.py +382 -0
- control_plane_api/app/policies/README.md +149 -0
- control_plane_api/app/policies/approved_users.rego +62 -0
- control_plane_api/app/policies/business_hours.rego +51 -0
- control_plane_api/app/policies/rate_limiting.rego +100 -0
- control_plane_api/app/policies/tool_enforcement/README.md +336 -0
- control_plane_api/app/policies/tool_enforcement/bash_command_validation.rego +71 -0
- control_plane_api/app/policies/tool_enforcement/business_hours_enforcement.rego +82 -0
- control_plane_api/app/policies/tool_enforcement/mcp_tool_allowlist.rego +58 -0
- control_plane_api/app/policies/tool_enforcement/production_safeguards.rego +80 -0
- control_plane_api/app/policies/tool_enforcement/role_based_tool_access.rego +44 -0
- control_plane_api/app/policies/tool_restrictions.rego +86 -0
- control_plane_api/app/routers/__init__.py +4 -0
- control_plane_api/app/routers/agents.py +382 -0
- control_plane_api/app/routers/agents_v2.py +1598 -0
- control_plane_api/app/routers/analytics.py +1310 -0
- control_plane_api/app/routers/auth.py +59 -0
- control_plane_api/app/routers/client_config.py +57 -0
- control_plane_api/app/routers/context_graph.py +561 -0
- control_plane_api/app/routers/context_manager.py +577 -0
- control_plane_api/app/routers/custom_integrations.py +490 -0
- control_plane_api/app/routers/enforcer.py +132 -0
- control_plane_api/app/routers/environment_context.py +252 -0
- control_plane_api/app/routers/environments.py +761 -0
- control_plane_api/app/routers/execution_environment.py +847 -0
- control_plane_api/app/routers/executions/__init__.py +28 -0
- control_plane_api/app/routers/executions/router.py +286 -0
- control_plane_api/app/routers/executions/services/__init__.py +22 -0
- control_plane_api/app/routers/executions/services/demo_worker_health.py +156 -0
- control_plane_api/app/routers/executions/services/status_service.py +420 -0
- control_plane_api/app/routers/executions/services/test_worker_health.py +480 -0
- control_plane_api/app/routers/executions/services/worker_health.py +514 -0
- control_plane_api/app/routers/executions/streaming/__init__.py +22 -0
- control_plane_api/app/routers/executions/streaming/deduplication.py +352 -0
- control_plane_api/app/routers/executions/streaming/event_buffer.py +353 -0
- control_plane_api/app/routers/executions/streaming/event_formatter.py +964 -0
- control_plane_api/app/routers/executions/streaming/history_loader.py +588 -0
- control_plane_api/app/routers/executions/streaming/live_source.py +693 -0
- control_plane_api/app/routers/executions/streaming/streamer.py +849 -0
- control_plane_api/app/routers/executions.py +4888 -0
- control_plane_api/app/routers/health.py +165 -0
- control_plane_api/app/routers/health_v2.py +394 -0
- control_plane_api/app/routers/integration_templates.py +496 -0
- control_plane_api/app/routers/integrations.py +287 -0
- control_plane_api/app/routers/jobs.py +1809 -0
- control_plane_api/app/routers/metrics.py +517 -0
- control_plane_api/app/routers/models.py +82 -0
- control_plane_api/app/routers/models_v2.py +628 -0
- control_plane_api/app/routers/plan_executions.py +1481 -0
- control_plane_api/app/routers/plan_generation_async.py +304 -0
- control_plane_api/app/routers/policies.py +669 -0
- control_plane_api/app/routers/presence.py +234 -0
- control_plane_api/app/routers/projects.py +987 -0
- control_plane_api/app/routers/runners.py +379 -0
- control_plane_api/app/routers/runtimes.py +172 -0
- control_plane_api/app/routers/secrets.py +171 -0
- control_plane_api/app/routers/skills.py +1010 -0
- control_plane_api/app/routers/skills_definitions.py +140 -0
- control_plane_api/app/routers/storage.py +456 -0
- control_plane_api/app/routers/task_planning.py +611 -0
- control_plane_api/app/routers/task_queues.py +650 -0
- control_plane_api/app/routers/team_context.py +274 -0
- control_plane_api/app/routers/teams.py +1747 -0
- control_plane_api/app/routers/templates.py +248 -0
- control_plane_api/app/routers/traces.py +571 -0
- control_plane_api/app/routers/websocket_client.py +479 -0
- control_plane_api/app/routers/websocket_executions_status.py +437 -0
- control_plane_api/app/routers/websocket_gateway.py +323 -0
- control_plane_api/app/routers/websocket_traces.py +576 -0
- control_plane_api/app/routers/worker_queues.py +2555 -0
- control_plane_api/app/routers/worker_websocket.py +419 -0
- control_plane_api/app/routers/workers.py +1004 -0
- control_plane_api/app/routers/workflows.py +204 -0
- control_plane_api/app/runtimes/__init__.py +6 -0
- control_plane_api/app/runtimes/validation.py +344 -0
- control_plane_api/app/schemas/__init__.py +1 -0
- control_plane_api/app/schemas/job_schemas.py +302 -0
- control_plane_api/app/schemas/mcp_schemas.py +311 -0
- control_plane_api/app/schemas/template_schemas.py +133 -0
- control_plane_api/app/schemas/trace_schemas.py +168 -0
- control_plane_api/app/schemas/worker_queue_observability_schemas.py +165 -0
- control_plane_api/app/services/__init__.py +1 -0
- control_plane_api/app/services/agno_planning_strategy.py +233 -0
- control_plane_api/app/services/agno_service.py +838 -0
- control_plane_api/app/services/claude_code_planning_service.py +203 -0
- control_plane_api/app/services/context_graph_client.py +224 -0
- control_plane_api/app/services/custom_integration_service.py +415 -0
- control_plane_api/app/services/integration_resolution_service.py +345 -0
- control_plane_api/app/services/litellm_service.py +394 -0
- control_plane_api/app/services/plan_generator.py +79 -0
- control_plane_api/app/services/planning_strategy.py +66 -0
- control_plane_api/app/services/planning_strategy_factory.py +118 -0
- control_plane_api/app/services/policy_service.py +615 -0
- control_plane_api/app/services/state_transition_service.py +755 -0
- control_plane_api/app/services/storage_service.py +593 -0
- control_plane_api/app/services/temporal_cloud_provisioning.py +150 -0
- control_plane_api/app/services/toolsets/context_graph_skill.py +432 -0
- control_plane_api/app/services/trace_retention.py +354 -0
- control_plane_api/app/services/worker_queue_metrics_service.py +190 -0
- control_plane_api/app/services/workflow_cancellation_manager.py +135 -0
- control_plane_api/app/services/workflow_operations_service.py +611 -0
- control_plane_api/app/skills/__init__.py +100 -0
- control_plane_api/app/skills/base.py +239 -0
- control_plane_api/app/skills/builtin/__init__.py +37 -0
- control_plane_api/app/skills/builtin/agent_communication/__init__.py +8 -0
- control_plane_api/app/skills/builtin/agent_communication/skill.py +246 -0
- control_plane_api/app/skills/builtin/code_ingestion/__init__.py +4 -0
- control_plane_api/app/skills/builtin/code_ingestion/skill.py +267 -0
- control_plane_api/app/skills/builtin/cognitive_memory/__init__.py +4 -0
- control_plane_api/app/skills/builtin/cognitive_memory/skill.py +174 -0
- control_plane_api/app/skills/builtin/contextual_awareness/__init__.py +4 -0
- control_plane_api/app/skills/builtin/contextual_awareness/skill.py +387 -0
- control_plane_api/app/skills/builtin/data_visualization/__init__.py +4 -0
- control_plane_api/app/skills/builtin/data_visualization/skill.py +154 -0
- control_plane_api/app/skills/builtin/docker/__init__.py +4 -0
- control_plane_api/app/skills/builtin/docker/skill.py +104 -0
- control_plane_api/app/skills/builtin/file_generation/__init__.py +4 -0
- control_plane_api/app/skills/builtin/file_generation/skill.py +94 -0
- control_plane_api/app/skills/builtin/file_system/__init__.py +4 -0
- control_plane_api/app/skills/builtin/file_system/skill.py +110 -0
- control_plane_api/app/skills/builtin/knowledge_api/__init__.py +5 -0
- control_plane_api/app/skills/builtin/knowledge_api/skill.py +124 -0
- control_plane_api/app/skills/builtin/python/__init__.py +4 -0
- control_plane_api/app/skills/builtin/python/skill.py +92 -0
- control_plane_api/app/skills/builtin/remote_filesystem/__init__.py +5 -0
- control_plane_api/app/skills/builtin/remote_filesystem/skill.py +170 -0
- control_plane_api/app/skills/builtin/shell/__init__.py +4 -0
- control_plane_api/app/skills/builtin/shell/skill.py +161 -0
- control_plane_api/app/skills/builtin/slack/__init__.py +3 -0
- control_plane_api/app/skills/builtin/slack/skill.py +302 -0
- control_plane_api/app/skills/builtin/workflow_executor/__init__.py +4 -0
- control_plane_api/app/skills/builtin/workflow_executor/skill.py +469 -0
- control_plane_api/app/skills/business_intelligence.py +189 -0
- control_plane_api/app/skills/config.py +63 -0
- control_plane_api/app/skills/loaders/__init__.py +14 -0
- control_plane_api/app/skills/loaders/base.py +73 -0
- control_plane_api/app/skills/loaders/filesystem_loader.py +199 -0
- control_plane_api/app/skills/registry.py +125 -0
- control_plane_api/app/utils/helpers.py +12 -0
- control_plane_api/app/utils/workflow_executor.py +354 -0
- control_plane_api/app/workflows/__init__.py +11 -0
- control_plane_api/app/workflows/agent_execution.py +520 -0
- control_plane_api/app/workflows/agent_execution_with_skills.py +223 -0
- control_plane_api/app/workflows/namespace_provisioning.py +326 -0
- control_plane_api/app/workflows/plan_generation.py +254 -0
- control_plane_api/app/workflows/team_execution.py +442 -0
- control_plane_api/scripts/seed_models.py +240 -0
- control_plane_api/scripts/validate_existing_tool_names.py +492 -0
- control_plane_api/shared/__init__.py +8 -0
- control_plane_api/shared/version.py +17 -0
- control_plane_api/test_deduplication.py +274 -0
- control_plane_api/test_executor_deduplication_e2e.py +309 -0
- control_plane_api/test_job_execution_e2e.py +283 -0
- control_plane_api/test_real_integration.py +193 -0
- control_plane_api/version.py +38 -0
- control_plane_api/worker/__init__.py +0 -0
- control_plane_api/worker/activities/__init__.py +0 -0
- control_plane_api/worker/activities/agent_activities.py +1585 -0
- control_plane_api/worker/activities/approval_activities.py +234 -0
- control_plane_api/worker/activities/job_activities.py +199 -0
- control_plane_api/worker/activities/runtime_activities.py +1167 -0
- control_plane_api/worker/activities/skill_activities.py +282 -0
- control_plane_api/worker/activities/team_activities.py +479 -0
- control_plane_api/worker/agent_runtime_server.py +370 -0
- control_plane_api/worker/binary_manager.py +333 -0
- control_plane_api/worker/config/__init__.py +31 -0
- control_plane_api/worker/config/worker_config.py +273 -0
- control_plane_api/worker/control_plane_client.py +1491 -0
- control_plane_api/worker/examples/analytics_integration_example.py +362 -0
- control_plane_api/worker/health_monitor.py +159 -0
- control_plane_api/worker/metrics.py +237 -0
- control_plane_api/worker/models/__init__.py +1 -0
- control_plane_api/worker/models/error_events.py +105 -0
- control_plane_api/worker/models/inputs.py +89 -0
- control_plane_api/worker/runtimes/__init__.py +35 -0
- control_plane_api/worker/runtimes/agent_runtime/runtime.py +485 -0
- control_plane_api/worker/runtimes/agno/__init__.py +34 -0
- control_plane_api/worker/runtimes/agno/config.py +248 -0
- control_plane_api/worker/runtimes/agno/hooks.py +385 -0
- control_plane_api/worker/runtimes/agno/mcp_builder.py +195 -0
- control_plane_api/worker/runtimes/agno/runtime.py +1063 -0
- control_plane_api/worker/runtimes/agno/utils.py +163 -0
- control_plane_api/worker/runtimes/base.py +979 -0
- control_plane_api/worker/runtimes/claude_code/__init__.py +38 -0
- control_plane_api/worker/runtimes/claude_code/cleanup.py +184 -0
- control_plane_api/worker/runtimes/claude_code/client_pool.py +529 -0
- control_plane_api/worker/runtimes/claude_code/config.py +829 -0
- control_plane_api/worker/runtimes/claude_code/hooks.py +482 -0
- control_plane_api/worker/runtimes/claude_code/litellm_proxy.py +1702 -0
- control_plane_api/worker/runtimes/claude_code/mcp_builder.py +467 -0
- control_plane_api/worker/runtimes/claude_code/mcp_discovery.py +558 -0
- control_plane_api/worker/runtimes/claude_code/runtime.py +1546 -0
- control_plane_api/worker/runtimes/claude_code/tool_mapper.py +403 -0
- control_plane_api/worker/runtimes/claude_code/utils.py +149 -0
- control_plane_api/worker/runtimes/factory.py +173 -0
- control_plane_api/worker/runtimes/model_utils.py +107 -0
- control_plane_api/worker/runtimes/validation.py +93 -0
- control_plane_api/worker/services/__init__.py +1 -0
- control_plane_api/worker/services/agent_communication_tools.py +908 -0
- control_plane_api/worker/services/agent_executor.py +485 -0
- control_plane_api/worker/services/agent_executor_v2.py +793 -0
- control_plane_api/worker/services/analytics_collector.py +457 -0
- control_plane_api/worker/services/analytics_service.py +464 -0
- control_plane_api/worker/services/approval_tools.py +310 -0
- control_plane_api/worker/services/approval_tools_agno.py +207 -0
- control_plane_api/worker/services/cancellation_manager.py +177 -0
- control_plane_api/worker/services/code_ingestion_tools.py +465 -0
- control_plane_api/worker/services/contextual_awareness_tools.py +405 -0
- control_plane_api/worker/services/data_visualization.py +834 -0
- control_plane_api/worker/services/event_publisher.py +531 -0
- control_plane_api/worker/services/jira_tools.py +257 -0
- control_plane_api/worker/services/remote_filesystem_tools.py +498 -0
- control_plane_api/worker/services/runtime_analytics.py +328 -0
- control_plane_api/worker/services/session_service.py +365 -0
- control_plane_api/worker/services/skill_context_enhancement.py +181 -0
- control_plane_api/worker/services/skill_factory.py +471 -0
- control_plane_api/worker/services/system_prompt_enhancement.py +410 -0
- control_plane_api/worker/services/team_executor.py +715 -0
- control_plane_api/worker/services/team_executor_v2.py +1866 -0
- control_plane_api/worker/services/tool_enforcement.py +254 -0
- control_plane_api/worker/services/workflow_executor/__init__.py +52 -0
- control_plane_api/worker/services/workflow_executor/event_processor.py +287 -0
- control_plane_api/worker/services/workflow_executor/event_publisher.py +210 -0
- control_plane_api/worker/services/workflow_executor/executors/__init__.py +15 -0
- control_plane_api/worker/services/workflow_executor/executors/base.py +270 -0
- control_plane_api/worker/services/workflow_executor/executors/json_executor.py +50 -0
- control_plane_api/worker/services/workflow_executor/executors/python_executor.py +50 -0
- control_plane_api/worker/services/workflow_executor/models.py +142 -0
- control_plane_api/worker/services/workflow_executor_tools.py +1748 -0
- control_plane_api/worker/skills/__init__.py +12 -0
- control_plane_api/worker/skills/builtin/context_graph_search/README.md +213 -0
- control_plane_api/worker/skills/builtin/context_graph_search/__init__.py +5 -0
- control_plane_api/worker/skills/builtin/context_graph_search/agno_impl.py +808 -0
- control_plane_api/worker/skills/builtin/context_graph_search/skill.yaml +67 -0
- control_plane_api/worker/skills/builtin/contextual_awareness/__init__.py +4 -0
- control_plane_api/worker/skills/builtin/contextual_awareness/agno_impl.py +62 -0
- control_plane_api/worker/skills/builtin/data_visualization/agno_impl.py +18 -0
- control_plane_api/worker/skills/builtin/data_visualization/skill.yaml +84 -0
- control_plane_api/worker/skills/builtin/docker/agno_impl.py +65 -0
- control_plane_api/worker/skills/builtin/docker/skill.yaml +60 -0
- control_plane_api/worker/skills/builtin/file_generation/agno_impl.py +47 -0
- control_plane_api/worker/skills/builtin/file_generation/skill.yaml +64 -0
- control_plane_api/worker/skills/builtin/file_system/agno_impl.py +32 -0
- control_plane_api/worker/skills/builtin/file_system/skill.yaml +54 -0
- control_plane_api/worker/skills/builtin/knowledge_api/__init__.py +4 -0
- control_plane_api/worker/skills/builtin/knowledge_api/agno_impl.py +50 -0
- control_plane_api/worker/skills/builtin/knowledge_api/skill.yaml +66 -0
- control_plane_api/worker/skills/builtin/python/agno_impl.py +25 -0
- control_plane_api/worker/skills/builtin/python/skill.yaml +60 -0
- control_plane_api/worker/skills/builtin/schema_fix_mixin.py +260 -0
- control_plane_api/worker/skills/builtin/shell/agno_impl.py +31 -0
- control_plane_api/worker/skills/builtin/shell/skill.yaml +60 -0
- control_plane_api/worker/skills/builtin/slack/__init__.py +3 -0
- control_plane_api/worker/skills/builtin/slack/agno_impl.py +1282 -0
- control_plane_api/worker/skills/builtin/slack/skill.yaml +276 -0
- control_plane_api/worker/skills/builtin/workflow_executor/agno_impl.py +62 -0
- control_plane_api/worker/skills/builtin/workflow_executor/skill.yaml +79 -0
- control_plane_api/worker/skills/loaders/__init__.py +5 -0
- control_plane_api/worker/skills/loaders/base.py +23 -0
- control_plane_api/worker/skills/loaders/filesystem_loader.py +357 -0
- control_plane_api/worker/skills/registry.py +208 -0
- control_plane_api/worker/tests/__init__.py +1 -0
- control_plane_api/worker/tests/conftest.py +12 -0
- control_plane_api/worker/tests/e2e/__init__.py +0 -0
- control_plane_api/worker/tests/e2e/test_context_graph_real_api.py +338 -0
- control_plane_api/worker/tests/e2e/test_context_graph_templates_e2e.py +523 -0
- control_plane_api/worker/tests/e2e/test_enforcement_e2e.py +344 -0
- control_plane_api/worker/tests/e2e/test_execution_flow.py +571 -0
- control_plane_api/worker/tests/e2e/test_single_execution_mode.py +656 -0
- control_plane_api/worker/tests/integration/__init__.py +0 -0
- control_plane_api/worker/tests/integration/test_builtin_skills_fixes.py +245 -0
- control_plane_api/worker/tests/integration/test_context_graph_search_integration.py +365 -0
- control_plane_api/worker/tests/integration/test_control_plane_integration.py +308 -0
- control_plane_api/worker/tests/integration/test_hook_enforcement_integration.py +579 -0
- control_plane_api/worker/tests/integration/test_scheduled_job_workflow.py +237 -0
- control_plane_api/worker/tests/integration/test_system_prompt_enhancement_integration.py +343 -0
- control_plane_api/worker/tests/unit/__init__.py +0 -0
- control_plane_api/worker/tests/unit/test_builtin_skill_autoload.py +396 -0
- control_plane_api/worker/tests/unit/test_context_graph_search.py +450 -0
- control_plane_api/worker/tests/unit/test_context_graph_templates.py +403 -0
- control_plane_api/worker/tests/unit/test_control_plane_client.py +401 -0
- control_plane_api/worker/tests/unit/test_control_plane_client_jobs.py +345 -0
- control_plane_api/worker/tests/unit/test_job_activities.py +353 -0
- control_plane_api/worker/tests/unit/test_skill_context_enhancement.py +321 -0
- control_plane_api/worker/tests/unit/test_system_prompt_enhancement.py +415 -0
- control_plane_api/worker/tests/unit/test_tool_enforcement.py +324 -0
- control_plane_api/worker/utils/__init__.py +1 -0
- control_plane_api/worker/utils/chunk_batcher.py +330 -0
- control_plane_api/worker/utils/environment.py +65 -0
- control_plane_api/worker/utils/error_publisher.py +260 -0
- control_plane_api/worker/utils/event_batcher.py +256 -0
- control_plane_api/worker/utils/logging_config.py +335 -0
- control_plane_api/worker/utils/logging_helper.py +326 -0
- control_plane_api/worker/utils/parameter_validator.py +120 -0
- control_plane_api/worker/utils/retry_utils.py +60 -0
- control_plane_api/worker/utils/streaming_utils.py +665 -0
- control_plane_api/worker/utils/tool_validation.py +332 -0
- control_plane_api/worker/utils/workspace_manager.py +163 -0
- control_plane_api/worker/websocket_client.py +393 -0
- control_plane_api/worker/worker.py +1297 -0
- control_plane_api/worker/workflows/__init__.py +0 -0
- control_plane_api/worker/workflows/agent_execution.py +909 -0
- control_plane_api/worker/workflows/scheduled_job_wrapper.py +332 -0
- control_plane_api/worker/workflows/team_execution.py +611 -0
- kubiya_control_plane_api-0.9.15.dist-info/METADATA +354 -0
- kubiya_control_plane_api-0.9.15.dist-info/RECORD +479 -0
- kubiya_control_plane_api-0.9.15.dist-info/WHEEL +5 -0
- kubiya_control_plane_api-0.9.15.dist-info/entry_points.txt +5 -0
- kubiya_control_plane_api-0.9.15.dist-info/licenses/LICENSE +676 -0
- kubiya_control_plane_api-0.9.15.dist-info/top_level.txt +3 -0
- scripts/__init__.py +1 -0
- scripts/migrations.py +39 -0
- scripts/seed_worker_queues.py +128 -0
- scripts/setup_agent_runtime.py +142 -0
- worker_internal/__init__.py +1 -0
- worker_internal/planner/__init__.py +1 -0
- worker_internal/planner/activities.py +1499 -0
- worker_internal/planner/agent_tools.py +197 -0
- worker_internal/planner/event_models.py +148 -0
- worker_internal/planner/event_publisher.py +67 -0
- worker_internal/planner/models.py +199 -0
- worker_internal/planner/retry_logic.py +134 -0
- worker_internal/planner/worker.py +300 -0
- worker_internal/planner/workflows.py +970 -0
|
@@ -0,0 +1,847 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Execution Environment Router - Resolve execution environment for agents/teams
|
|
3
|
+
|
|
4
|
+
This router provides workers with resolved execution environment configuration:
|
|
5
|
+
- Fetches agent/team execution_environment from database
|
|
6
|
+
- Resolves secret names to actual values from Kubiya API
|
|
7
|
+
- Resolves integration IDs to actual tokens from Kubiya API
|
|
8
|
+
- Maps integration tokens to specific env var names (GH_TOKEN, JIRA_TOKEN, etc.)
|
|
9
|
+
- Returns complete env var dict ready for worker to inject into execution
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import httpx
|
|
13
|
+
from fastapi import APIRouter, Depends, HTTPException, Request
|
|
14
|
+
from typing import Dict, Any, Optional
|
|
15
|
+
import structlog
|
|
16
|
+
from sqlalchemy.orm import Session
|
|
17
|
+
|
|
18
|
+
from control_plane_api.app.middleware.auth import get_current_organization
|
|
19
|
+
from control_plane_api.app.database import get_db
|
|
20
|
+
from control_plane_api.app.models import Environment, AgentEnvironment, TeamEnvironment
|
|
21
|
+
from control_plane_api.app.lib.sqlalchemy_utils import model_to_dict
|
|
22
|
+
from control_plane_api.app.lib.kubiya_client import KUBIYA_API_BASE
|
|
23
|
+
from control_plane_api.app.lib.templating import TemplateContext, resolve_templates
|
|
24
|
+
|
|
25
|
+
logger = structlog.get_logger()
|
|
26
|
+
|
|
27
|
+
router = APIRouter(prefix="/execution-environment", tags=["execution-environment"])
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# Integration type to environment variable name mapping
|
|
31
|
+
INTEGRATION_ENV_VAR_MAP = {
|
|
32
|
+
"github": "GH_TOKEN",
|
|
33
|
+
"github_app": "GITHUB_TOKEN",
|
|
34
|
+
"jira": "JIRA_TOKEN",
|
|
35
|
+
"slack": "SLACK_TOKEN",
|
|
36
|
+
"aws": "AWS_ACCESS_KEY_ID", # Note: AWS might need multiple vars
|
|
37
|
+
"aws-serviceaccount": "AWS_ROLE_ARN",
|
|
38
|
+
"kubernetes": "KUBECONFIG",
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
async def resolve_secret_value(
|
|
43
|
+
secret_name: str,
|
|
44
|
+
token: str,
|
|
45
|
+
org_id: str,
|
|
46
|
+
auth_type: str = "UserKey",
|
|
47
|
+
) -> str:
|
|
48
|
+
"""
|
|
49
|
+
Resolve a secret name to its actual value from Kubiya API.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
secret_name: Name of the secret to resolve
|
|
53
|
+
token: Kubiya API token
|
|
54
|
+
org_id: Organization ID
|
|
55
|
+
auth_type: Authorization type ("UserKey" for API keys, "Bearer" for JWT tokens)
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Secret value as string
|
|
59
|
+
"""
|
|
60
|
+
# Map auth types to what Kubiya API accepts
|
|
61
|
+
# "JWT" is used internally for Kubiya API keys that are JWTs - Kubiya API expects "UserKey" for these
|
|
62
|
+
kubiya_auth_type = "UserKey" if auth_type in ("JWT", "UserKey") else auth_type
|
|
63
|
+
headers = {
|
|
64
|
+
"Authorization": f"{kubiya_auth_type} {token}",
|
|
65
|
+
"Accept": "application/json",
|
|
66
|
+
"Content-Type": "application/json",
|
|
67
|
+
"X-Kubiya-Client": "agent-control-plane",
|
|
68
|
+
"X-Organization-ID": org_id,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
72
|
+
response = await client.get(
|
|
73
|
+
f"{KUBIYA_API_BASE}/api/v2/secrets/get_value/{secret_name}",
|
|
74
|
+
headers=headers,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
if response.status_code == 200:
|
|
78
|
+
return response.text
|
|
79
|
+
else:
|
|
80
|
+
logger.warning(
|
|
81
|
+
"secret_resolution_failed",
|
|
82
|
+
secret_name=secret_name[:20],
|
|
83
|
+
status=response.status_code,
|
|
84
|
+
)
|
|
85
|
+
raise HTTPException(
|
|
86
|
+
status_code=response.status_code,
|
|
87
|
+
detail=f"Failed to resolve secret '{secret_name}': {response.text[:200]}",
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
async def resolve_integration_token(
|
|
92
|
+
integration_id: str,
|
|
93
|
+
integration_type: str,
|
|
94
|
+
token: str,
|
|
95
|
+
org_id: str,
|
|
96
|
+
auth_type: str = "UserKey",
|
|
97
|
+
) -> Dict[str, str]:
|
|
98
|
+
"""
|
|
99
|
+
Resolve an integration ID to its actual token from Kubiya API.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
integration_id: Integration UUID
|
|
103
|
+
integration_type: Type of integration (github, jira, etc.)
|
|
104
|
+
token: Kubiya API token
|
|
105
|
+
org_id: Organization ID
|
|
106
|
+
auth_type: Authorization type ("UserKey" for API keys, "Bearer" for JWT tokens)
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Dict with env_var_name and token value
|
|
110
|
+
"""
|
|
111
|
+
# Map auth types to what Kubiya API accepts
|
|
112
|
+
kubiya_auth_type = "UserKey" if auth_type in ("JWT", "UserKey") else auth_type
|
|
113
|
+
headers = {
|
|
114
|
+
"Authorization": f"{kubiya_auth_type} {token}",
|
|
115
|
+
"Accept": "application/json",
|
|
116
|
+
"Content-Type": "application/json",
|
|
117
|
+
"X-Kubiya-Client": "agent-control-plane",
|
|
118
|
+
"X-Organization-ID": org_id,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# Build token URL based on integration type
|
|
122
|
+
integration_type_lower = integration_type.lower()
|
|
123
|
+
|
|
124
|
+
if integration_type_lower == "github":
|
|
125
|
+
token_url = f"{KUBIYA_API_BASE}/api/v1/integration/github/token/{integration_id}"
|
|
126
|
+
elif integration_type_lower == "github_app":
|
|
127
|
+
token_url = f"{KUBIYA_API_BASE}/api/v1/integration/github_app/token/{integration_id}"
|
|
128
|
+
elif integration_type_lower == "jira":
|
|
129
|
+
token_url = f"{KUBIYA_API_BASE}/api/v1/integration/jira/token/{integration_id}"
|
|
130
|
+
else:
|
|
131
|
+
logger.warning(
|
|
132
|
+
"unsupported_integration_type",
|
|
133
|
+
integration_type=integration_type,
|
|
134
|
+
integration_id=integration_id[:8],
|
|
135
|
+
)
|
|
136
|
+
# For unsupported types, skip
|
|
137
|
+
return {}
|
|
138
|
+
|
|
139
|
+
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
140
|
+
response = await client.get(token_url, headers=headers)
|
|
141
|
+
|
|
142
|
+
if response.status_code == 200:
|
|
143
|
+
# Try to parse as JSON first
|
|
144
|
+
try:
|
|
145
|
+
token_data = response.json()
|
|
146
|
+
token_value = token_data.get("token", response.text)
|
|
147
|
+
except:
|
|
148
|
+
# If not JSON, use plain text
|
|
149
|
+
token_value = response.text
|
|
150
|
+
|
|
151
|
+
# Map to env var name
|
|
152
|
+
env_var_name = INTEGRATION_ENV_VAR_MAP.get(integration_type_lower, f"{integration_type.upper()}_TOKEN")
|
|
153
|
+
|
|
154
|
+
return {env_var_name: token_value}
|
|
155
|
+
else:
|
|
156
|
+
logger.warning(
|
|
157
|
+
"integration_token_resolution_failed",
|
|
158
|
+
integration_id=integration_id[:8],
|
|
159
|
+
integration_type=integration_type,
|
|
160
|
+
status=response.status_code,
|
|
161
|
+
)
|
|
162
|
+
# Don't fail the entire request for one integration
|
|
163
|
+
return {}
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
async def resolve_environment_configs(
|
|
167
|
+
environment_ids: list[str],
|
|
168
|
+
org_id: str,
|
|
169
|
+
db: Session,
|
|
170
|
+
) -> Dict[str, Any]:
|
|
171
|
+
"""
|
|
172
|
+
Resolve execution environment configs from a list of environment IDs.
|
|
173
|
+
Merges configs from all environments.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
environment_ids: List of environment IDs
|
|
177
|
+
org_id: Organization ID
|
|
178
|
+
db: Database session
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
Merged execution environment dict with env_vars, secrets, integration_ids, mcp_servers
|
|
182
|
+
"""
|
|
183
|
+
if not environment_ids:
|
|
184
|
+
return {"env_vars": {}, "secrets": [], "integration_ids": [], "mcp_servers": {}}
|
|
185
|
+
|
|
186
|
+
# Fetch all environments
|
|
187
|
+
environments = (
|
|
188
|
+
db.query(Environment)
|
|
189
|
+
.filter(
|
|
190
|
+
Environment.id.in_(environment_ids),
|
|
191
|
+
Environment.organization_id == org_id
|
|
192
|
+
)
|
|
193
|
+
.all()
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
# Merge all environment configs
|
|
197
|
+
merged_env_vars = {}
|
|
198
|
+
merged_secrets = set()
|
|
199
|
+
merged_integration_ids = set()
|
|
200
|
+
merged_mcp_servers = {}
|
|
201
|
+
|
|
202
|
+
for env in environments:
|
|
203
|
+
env_config = env.execution_environment or {}
|
|
204
|
+
|
|
205
|
+
# Merge env vars (later environments override earlier ones)
|
|
206
|
+
merged_env_vars.update(env_config.get("env_vars", {}))
|
|
207
|
+
|
|
208
|
+
# Collect secrets (union)
|
|
209
|
+
merged_secrets.update(env_config.get("secrets", []))
|
|
210
|
+
|
|
211
|
+
# Collect integration IDs (union)
|
|
212
|
+
merged_integration_ids.update(env_config.get("integration_ids", []))
|
|
213
|
+
|
|
214
|
+
# Merge MCP servers (later environments override earlier ones)
|
|
215
|
+
merged_mcp_servers.update(env_config.get("mcp_servers", {}))
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
"env_vars": merged_env_vars,
|
|
219
|
+
"secrets": list(merged_secrets),
|
|
220
|
+
"integration_ids": list(merged_integration_ids),
|
|
221
|
+
"mcp_servers": merged_mcp_servers,
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def apply_template_resolution(
|
|
226
|
+
config: Dict[str, Any],
|
|
227
|
+
resolved_secrets: Dict[str, str],
|
|
228
|
+
resolved_env_vars: Dict[str, str],
|
|
229
|
+
) -> Dict[str, Any]:
|
|
230
|
+
"""
|
|
231
|
+
Apply template resolution to a configuration object.
|
|
232
|
+
|
|
233
|
+
Resolves all templates in the config using resolved secrets and env vars.
|
|
234
|
+
Templates are resolved recursively in all string fields, including:
|
|
235
|
+
- system_prompt
|
|
236
|
+
- description
|
|
237
|
+
- mcp_servers (all fields)
|
|
238
|
+
- runtime_config (all fields)
|
|
239
|
+
- Any other text-based fields
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
config: Configuration dict with potential templates
|
|
243
|
+
resolved_secrets: Map of secret names to resolved values
|
|
244
|
+
resolved_env_vars: Map of env var names to values
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
Configuration with all templates resolved
|
|
248
|
+
"""
|
|
249
|
+
try:
|
|
250
|
+
# Build template context
|
|
251
|
+
context = TemplateContext(
|
|
252
|
+
variables={}, # Simple variables not used in execution environment
|
|
253
|
+
secrets=resolved_secrets,
|
|
254
|
+
env_vars=resolved_env_vars
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# Apply template resolution recursively to entire config
|
|
258
|
+
resolved_config = resolve_templates(config, context, skip_on_error=True)
|
|
259
|
+
|
|
260
|
+
logger.info(
|
|
261
|
+
"template_resolution_applied",
|
|
262
|
+
config_keys=list(config.keys()),
|
|
263
|
+
secrets_count=len(resolved_secrets),
|
|
264
|
+
env_vars_count=len(resolved_env_vars)
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
return resolved_config
|
|
268
|
+
|
|
269
|
+
except Exception as e:
|
|
270
|
+
logger.error(
|
|
271
|
+
"template_resolution_failed",
|
|
272
|
+
error=str(e),
|
|
273
|
+
config_keys=list(config.keys())
|
|
274
|
+
)
|
|
275
|
+
# Return original config on error to avoid breaking execution
|
|
276
|
+
return config
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
@router.get("/agents/{agent_id}/resolved")
|
|
280
|
+
async def get_agent_execution_environment(
|
|
281
|
+
agent_id: str,
|
|
282
|
+
request: Request,
|
|
283
|
+
organization: dict = Depends(get_current_organization),
|
|
284
|
+
db: Session = Depends(get_db),
|
|
285
|
+
) -> Dict[str, str]:
|
|
286
|
+
"""
|
|
287
|
+
Get resolved execution environment for an agent.
|
|
288
|
+
|
|
289
|
+
This endpoint:
|
|
290
|
+
1. Fetches agent's execution_environment and environment_ids from database
|
|
291
|
+
2. Fetches and merges execution configs from all associated environments
|
|
292
|
+
3. Merges agent's own execution_environment (agent config overrides environment)
|
|
293
|
+
4. Resolves all secret names to actual values
|
|
294
|
+
5. Resolves all integration IDs to actual tokens
|
|
295
|
+
6. Maps integration tokens to specific env var names
|
|
296
|
+
7. Returns merged env var dict
|
|
297
|
+
|
|
298
|
+
Inheritance order (later overrides earlier):
|
|
299
|
+
- Environment 1 execution_environment
|
|
300
|
+
- Environment 2 execution_environment
|
|
301
|
+
- ...
|
|
302
|
+
- Agent execution_environment
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
Dict of environment variables ready to inject into agent execution
|
|
306
|
+
"""
|
|
307
|
+
try:
|
|
308
|
+
token = request.state.kubiya_token
|
|
309
|
+
auth_type = getattr(request.state, "kubiya_auth_type", "UserKey")
|
|
310
|
+
org_id = organization["id"]
|
|
311
|
+
|
|
312
|
+
# Import Agent model locally to avoid circular dependency
|
|
313
|
+
from control_plane_api.app.models import Agent
|
|
314
|
+
|
|
315
|
+
# Fetch agent with execution environment
|
|
316
|
+
agent = (
|
|
317
|
+
db.query(Agent)
|
|
318
|
+
.filter(Agent.id == agent_id, Agent.organization_id == org_id)
|
|
319
|
+
.first()
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
if not agent:
|
|
323
|
+
raise HTTPException(status_code=404, detail=f"Agent {agent_id} not found")
|
|
324
|
+
|
|
325
|
+
# Get environment associations from join table
|
|
326
|
+
env_associations = (
|
|
327
|
+
db.query(AgentEnvironment)
|
|
328
|
+
.filter(AgentEnvironment.agent_id == agent_id)
|
|
329
|
+
.all()
|
|
330
|
+
)
|
|
331
|
+
environment_ids = [str(assoc.environment_id) for assoc in env_associations]
|
|
332
|
+
env_config = await resolve_environment_configs(environment_ids, org_id, db)
|
|
333
|
+
|
|
334
|
+
# Get agent-level config
|
|
335
|
+
agent_config = agent.execution_environment or {}
|
|
336
|
+
|
|
337
|
+
# Merge: environment config + agent config (agent overrides environment)
|
|
338
|
+
execution_environment = {
|
|
339
|
+
"env_vars": {**env_config.get("env_vars", {}), **agent_config.get("env_vars", {})},
|
|
340
|
+
"secrets": list(set(env_config.get("secrets", []) + agent_config.get("secrets", []))),
|
|
341
|
+
"integration_ids": list(set(env_config.get("integration_ids", []) + agent_config.get("integration_ids", []))),
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
# Start with custom env vars
|
|
345
|
+
resolved_env_vars = dict(execution_environment.get("env_vars", {}))
|
|
346
|
+
|
|
347
|
+
# Resolve secrets
|
|
348
|
+
secrets = execution_environment.get("secrets", [])
|
|
349
|
+
for secret_name in secrets:
|
|
350
|
+
try:
|
|
351
|
+
secret_value = await resolve_secret_value(secret_name, token, org_id, auth_type)
|
|
352
|
+
resolved_env_vars[secret_name] = secret_value
|
|
353
|
+
logger.info(
|
|
354
|
+
"secret_resolved",
|
|
355
|
+
agent_id=agent_id[:8],
|
|
356
|
+
secret_name=secret_name[:20],
|
|
357
|
+
)
|
|
358
|
+
except Exception as e:
|
|
359
|
+
logger.error(
|
|
360
|
+
"secret_resolution_error",
|
|
361
|
+
agent_id=agent_id[:8],
|
|
362
|
+
secret_name=secret_name[:20],
|
|
363
|
+
error=str(e),
|
|
364
|
+
)
|
|
365
|
+
# Continue with other secrets even if one fails
|
|
366
|
+
|
|
367
|
+
# Resolve integrations
|
|
368
|
+
integration_ids = execution_environment.get("integration_ids", [])
|
|
369
|
+
if integration_ids:
|
|
370
|
+
# First, fetch integration details to get types
|
|
371
|
+
# Map auth types to what Kubiya API accepts
|
|
372
|
+
kubiya_auth_type = "UserKey" if auth_type in ("JWT", "UserKey") else auth_type
|
|
373
|
+
headers = {
|
|
374
|
+
"Authorization": f"{kubiya_auth_type} {token}",
|
|
375
|
+
"Accept": "application/json",
|
|
376
|
+
"Content-Type": "application/json",
|
|
377
|
+
"X-Kubiya-Client": "agent-control-plane",
|
|
378
|
+
"X-Organization-ID": org_id,
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
382
|
+
response = await client.get(
|
|
383
|
+
f"{KUBIYA_API_BASE}/api/v2/integrations?full=true",
|
|
384
|
+
headers=headers,
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
if response.status_code == 200:
|
|
388
|
+
all_integrations = response.json()
|
|
389
|
+
|
|
390
|
+
for integration_id in integration_ids:
|
|
391
|
+
# Find integration by UUID
|
|
392
|
+
integration = next(
|
|
393
|
+
(i for i in all_integrations if i.get("uuid") == integration_id),
|
|
394
|
+
None
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
if integration:
|
|
398
|
+
integration_type = integration.get("integration_type", "")
|
|
399
|
+
try:
|
|
400
|
+
token_env_vars = await resolve_integration_token(
|
|
401
|
+
integration_id,
|
|
402
|
+
integration_type,
|
|
403
|
+
token,
|
|
404
|
+
org_id,
|
|
405
|
+
auth_type,
|
|
406
|
+
)
|
|
407
|
+
resolved_env_vars.update(token_env_vars)
|
|
408
|
+
logger.info(
|
|
409
|
+
"integration_resolved",
|
|
410
|
+
agent_id=agent_id[:8],
|
|
411
|
+
integration_id=integration_id[:8],
|
|
412
|
+
integration_type=integration_type,
|
|
413
|
+
env_vars=list(token_env_vars.keys()),
|
|
414
|
+
)
|
|
415
|
+
except Exception as e:
|
|
416
|
+
logger.error(
|
|
417
|
+
"integration_resolution_error",
|
|
418
|
+
agent_id=agent_id[:8],
|
|
419
|
+
integration_id=integration_id[:8],
|
|
420
|
+
error=str(e),
|
|
421
|
+
)
|
|
422
|
+
else:
|
|
423
|
+
logger.warning(
|
|
424
|
+
"integration_not_found",
|
|
425
|
+
agent_id=agent_id[:8],
|
|
426
|
+
integration_id=integration_id[:8],
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
logger.info(
|
|
430
|
+
"execution_environment_resolved",
|
|
431
|
+
agent_id=agent_id[:8],
|
|
432
|
+
env_var_count=len(resolved_env_vars),
|
|
433
|
+
env_var_keys=list(resolved_env_vars.keys()),
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
return resolved_env_vars
|
|
437
|
+
|
|
438
|
+
except HTTPException:
|
|
439
|
+
raise
|
|
440
|
+
except Exception as e:
|
|
441
|
+
logger.error(
|
|
442
|
+
"execution_environment_resolution_error",
|
|
443
|
+
agent_id=agent_id[:8],
|
|
444
|
+
error=str(e),
|
|
445
|
+
error_type=type(e).__name__,
|
|
446
|
+
)
|
|
447
|
+
raise HTTPException(status_code=500, detail=f"Failed to resolve execution environment: {str(e)}")
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
@router.get("/teams/{team_id}/resolved")
|
|
451
|
+
async def get_team_execution_environment(
|
|
452
|
+
team_id: str,
|
|
453
|
+
request: Request,
|
|
454
|
+
organization: dict = Depends(get_current_organization),
|
|
455
|
+
db: Session = Depends(get_db),
|
|
456
|
+
) -> Dict[str, str]:
|
|
457
|
+
"""
|
|
458
|
+
Get resolved execution environment for a team.
|
|
459
|
+
|
|
460
|
+
This endpoint:
|
|
461
|
+
1. Fetches team's execution_environment and environment_ids from database
|
|
462
|
+
2. Fetches and merges execution configs from all associated environments
|
|
463
|
+
3. Merges team's own execution_environment (team config overrides environment)
|
|
464
|
+
4. Resolves all secret names to actual values
|
|
465
|
+
5. Resolves all integration IDs to actual tokens
|
|
466
|
+
6. Maps integration tokens to specific env var names
|
|
467
|
+
7. Returns merged env var dict
|
|
468
|
+
|
|
469
|
+
Inheritance order (later overrides earlier):
|
|
470
|
+
- Environment 1 execution_environment
|
|
471
|
+
- Environment 2 execution_environment
|
|
472
|
+
- ...
|
|
473
|
+
- Team execution_environment
|
|
474
|
+
|
|
475
|
+
Returns:
|
|
476
|
+
Dict of environment variables ready to inject into team execution
|
|
477
|
+
"""
|
|
478
|
+
try:
|
|
479
|
+
token = request.state.kubiya_token
|
|
480
|
+
auth_type = getattr(request.state, "kubiya_auth_type", "UserKey")
|
|
481
|
+
org_id = organization["id"]
|
|
482
|
+
|
|
483
|
+
# Import Team model locally to avoid circular dependency
|
|
484
|
+
from control_plane_api.app.models import Team
|
|
485
|
+
|
|
486
|
+
# Fetch team with environment associations
|
|
487
|
+
team = (
|
|
488
|
+
db.query(Team)
|
|
489
|
+
.filter(Team.id == team_id, Team.organization_id == org_id)
|
|
490
|
+
.first()
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
if not team:
|
|
494
|
+
raise HTTPException(status_code=404, detail=f"Team {team_id} not found")
|
|
495
|
+
|
|
496
|
+
# Get environment-level configs first
|
|
497
|
+
environment_ids = team.environment_ids or []
|
|
498
|
+
env_config = await resolve_environment_configs(environment_ids, org_id, db)
|
|
499
|
+
|
|
500
|
+
# Get team-level config
|
|
501
|
+
team_config = team.execution_environment or {}
|
|
502
|
+
|
|
503
|
+
# Merge: environment config + team config (team overrides environment)
|
|
504
|
+
execution_environment = {
|
|
505
|
+
"env_vars": {**env_config.get("env_vars", {}), **team_config.get("env_vars", {})},
|
|
506
|
+
"secrets": list(set(env_config.get("secrets", []) + team_config.get("secrets", []))),
|
|
507
|
+
"integration_ids": list(set(env_config.get("integration_ids", []) + team_config.get("integration_ids", []))),
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
# Start with custom env vars
|
|
511
|
+
resolved_env_vars = dict(execution_environment.get("env_vars", {}))
|
|
512
|
+
|
|
513
|
+
# Resolve secrets
|
|
514
|
+
secrets = execution_environment.get("secrets", [])
|
|
515
|
+
for secret_name in secrets:
|
|
516
|
+
try:
|
|
517
|
+
secret_value = await resolve_secret_value(secret_name, token, org_id, auth_type)
|
|
518
|
+
resolved_env_vars[secret_name] = secret_value
|
|
519
|
+
logger.info(
|
|
520
|
+
"secret_resolved",
|
|
521
|
+
team_id=team_id[:8],
|
|
522
|
+
secret_name=secret_name[:20],
|
|
523
|
+
)
|
|
524
|
+
except Exception as e:
|
|
525
|
+
logger.error(
|
|
526
|
+
"secret_resolution_error",
|
|
527
|
+
team_id=team_id[:8],
|
|
528
|
+
secret_name=secret_name[:20],
|
|
529
|
+
error=str(e),
|
|
530
|
+
)
|
|
531
|
+
# Continue with other secrets even if one fails
|
|
532
|
+
|
|
533
|
+
# Resolve integrations
|
|
534
|
+
integration_ids = execution_environment.get("integration_ids", [])
|
|
535
|
+
if integration_ids:
|
|
536
|
+
# First, fetch integration details to get types
|
|
537
|
+
# Map auth types to what Kubiya API accepts
|
|
538
|
+
kubiya_auth_type = "UserKey" if auth_type in ("JWT", "UserKey") else auth_type
|
|
539
|
+
headers = {
|
|
540
|
+
"Authorization": f"{kubiya_auth_type} {token}",
|
|
541
|
+
"Accept": "application/json",
|
|
542
|
+
"Content-Type": "application/json",
|
|
543
|
+
"X-Kubiya-Client": "agent-control-plane",
|
|
544
|
+
"X-Organization-ID": org_id,
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
548
|
+
response = await client.get(
|
|
549
|
+
f"{KUBIYA_API_BASE}/api/v2/integrations?full=true",
|
|
550
|
+
headers=headers,
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
if response.status_code == 200:
|
|
554
|
+
all_integrations = response.json()
|
|
555
|
+
|
|
556
|
+
for integration_id in integration_ids:
|
|
557
|
+
# Find integration by UUID
|
|
558
|
+
integration = next(
|
|
559
|
+
(i for i in all_integrations if i.get("uuid") == integration_id),
|
|
560
|
+
None
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
if integration:
|
|
564
|
+
integration_type = integration.get("integration_type", "")
|
|
565
|
+
try:
|
|
566
|
+
token_env_vars = await resolve_integration_token(
|
|
567
|
+
integration_id,
|
|
568
|
+
integration_type,
|
|
569
|
+
token,
|
|
570
|
+
org_id,
|
|
571
|
+
auth_type,
|
|
572
|
+
)
|
|
573
|
+
resolved_env_vars.update(token_env_vars)
|
|
574
|
+
logger.info(
|
|
575
|
+
"integration_resolved",
|
|
576
|
+
team_id=team_id[:8],
|
|
577
|
+
integration_id=integration_id[:8],
|
|
578
|
+
integration_type=integration_type,
|
|
579
|
+
env_vars=list(token_env_vars.keys()),
|
|
580
|
+
)
|
|
581
|
+
except Exception as e:
|
|
582
|
+
logger.error(
|
|
583
|
+
"integration_resolution_error",
|
|
584
|
+
team_id=team_id[:8],
|
|
585
|
+
integration_id=integration_id[:8],
|
|
586
|
+
error=str(e),
|
|
587
|
+
)
|
|
588
|
+
else:
|
|
589
|
+
logger.warning(
|
|
590
|
+
"integration_not_found",
|
|
591
|
+
team_id=team_id[:8],
|
|
592
|
+
integration_id=integration_id[:8],
|
|
593
|
+
)
|
|
594
|
+
|
|
595
|
+
logger.info(
|
|
596
|
+
"execution_environment_resolved",
|
|
597
|
+
team_id=team_id[:8],
|
|
598
|
+
env_var_count=len(resolved_env_vars),
|
|
599
|
+
env_var_keys=list(resolved_env_vars.keys()),
|
|
600
|
+
)
|
|
601
|
+
|
|
602
|
+
return resolved_env_vars
|
|
603
|
+
|
|
604
|
+
except HTTPException:
|
|
605
|
+
raise
|
|
606
|
+
except Exception as e:
|
|
607
|
+
logger.error(
|
|
608
|
+
"execution_environment_resolution_error",
|
|
609
|
+
team_id=team_id[:8],
|
|
610
|
+
error=str(e),
|
|
611
|
+
error_type=type(e).__name__,
|
|
612
|
+
)
|
|
613
|
+
raise HTTPException(status_code=500, detail=f"Failed to resolve execution environment: {str(e)}")
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
async def resolve_agent_execution_environment_internal(
|
|
617
|
+
agent_id: str,
|
|
618
|
+
org_id: str,
|
|
619
|
+
db: Session,
|
|
620
|
+
token: Optional[str] = None,
|
|
621
|
+
auth_type: str = "UserKey",
|
|
622
|
+
) -> Dict[str, Any]:
|
|
623
|
+
"""
|
|
624
|
+
Internal function to resolve execution environment (can be called directly).
|
|
625
|
+
|
|
626
|
+
This bypasses HTTP/auth and can be called from other endpoints directly.
|
|
627
|
+
Token is optional - when None, secrets and integrations won't be resolved from Kubiya API.
|
|
628
|
+
Auth_type specifies the authorization type ("UserKey" for API keys, "Bearer" for JWT tokens).
|
|
629
|
+
"""
|
|
630
|
+
try:
|
|
631
|
+
# Import Agent model locally to avoid circular dependency
|
|
632
|
+
from control_plane_api.app.models import Agent
|
|
633
|
+
|
|
634
|
+
# Fetch agent with configuration fields
|
|
635
|
+
agent = (
|
|
636
|
+
db.query(Agent)
|
|
637
|
+
.filter(Agent.id == agent_id, Agent.organization_id == org_id)
|
|
638
|
+
.first()
|
|
639
|
+
)
|
|
640
|
+
|
|
641
|
+
if not agent:
|
|
642
|
+
raise HTTPException(status_code=404, detail=f"Agent {agent_id} not found")
|
|
643
|
+
|
|
644
|
+
# Get environment associations from join table
|
|
645
|
+
env_associations = (
|
|
646
|
+
db.query(AgentEnvironment)
|
|
647
|
+
.filter(AgentEnvironment.agent_id == agent_id)
|
|
648
|
+
.all()
|
|
649
|
+
)
|
|
650
|
+
environment_ids = [str(assoc.environment_id) for assoc in env_associations]
|
|
651
|
+
env_config = await resolve_environment_configs(environment_ids, org_id, db)
|
|
652
|
+
|
|
653
|
+
# Get agent-level config
|
|
654
|
+
agent_exec_env = agent.execution_environment or {}
|
|
655
|
+
|
|
656
|
+
# Get system_prompt from configuration, description from agent column
|
|
657
|
+
agent_configuration = agent.configuration or {}
|
|
658
|
+
|
|
659
|
+
# Merge: environment config + agent config (agent overrides environment)
|
|
660
|
+
execution_environment = {
|
|
661
|
+
"env_vars": {**env_config.get("env_vars", {}), **agent_exec_env.get("env_vars", {})},
|
|
662
|
+
"secrets": list(set(env_config.get("secrets", []) + agent_exec_env.get("secrets", []))),
|
|
663
|
+
"integration_ids": list(set(env_config.get("integration_ids", []) + agent_exec_env.get("integration_ids", []))),
|
|
664
|
+
"mcp_servers": {**env_config.get("mcp_servers", {}), **agent_exec_env.get("mcp_servers", {})},
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
# Start with custom env vars
|
|
668
|
+
resolved_env_vars = dict(execution_environment.get("env_vars", {}))
|
|
669
|
+
resolved_secrets = {}
|
|
670
|
+
|
|
671
|
+
# Resolve secrets (only if token is provided)
|
|
672
|
+
secrets = execution_environment.get("secrets", [])
|
|
673
|
+
if token:
|
|
674
|
+
for secret_name in secrets:
|
|
675
|
+
try:
|
|
676
|
+
secret_value = await resolve_secret_value(secret_name, token, org_id, auth_type)
|
|
677
|
+
resolved_env_vars[secret_name] = secret_value
|
|
678
|
+
resolved_secrets[secret_name] = secret_value # Store for template context
|
|
679
|
+
logger.debug("secret_resolved", agent_id=agent_id[:8], secret_name=secret_name[:20])
|
|
680
|
+
except Exception as e:
|
|
681
|
+
logger.error("secret_resolution_error", agent_id=agent_id[:8], secret_name=secret_name[:20], error=str(e))
|
|
682
|
+
|
|
683
|
+
# Resolve integrations (only if token is provided)
|
|
684
|
+
integration_ids = execution_environment.get("integration_ids", [])
|
|
685
|
+
if integration_ids and token:
|
|
686
|
+
# Map auth types to what Kubiya API accepts
|
|
687
|
+
kubiya_auth_type = "UserKey" if auth_type in ("JWT", "UserKey") else auth_type
|
|
688
|
+
headers = {
|
|
689
|
+
"Authorization": f"{kubiya_auth_type} {token}",
|
|
690
|
+
"Accept": "application/json",
|
|
691
|
+
"Content-Type": "application/json",
|
|
692
|
+
"X-Kubiya-Client": "agent-control-plane",
|
|
693
|
+
"X-Organization-ID": org_id,
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
697
|
+
response = await client.get(f"{KUBIYA_API_BASE}/api/v2/integrations?full=true", headers=headers)
|
|
698
|
+
|
|
699
|
+
if response.status_code == 200:
|
|
700
|
+
all_integrations = response.json()
|
|
701
|
+
|
|
702
|
+
for integration_id in integration_ids:
|
|
703
|
+
integration = next((i for i in all_integrations if i.get("uuid") == integration_id), None)
|
|
704
|
+
|
|
705
|
+
if integration:
|
|
706
|
+
integration_type = integration.get("integration_type", "")
|
|
707
|
+
try:
|
|
708
|
+
token_env_vars = await resolve_integration_token(integration_id, integration_type, token, org_id, auth_type)
|
|
709
|
+
resolved_env_vars.update(token_env_vars)
|
|
710
|
+
logger.debug("integration_resolved", agent_id=agent_id[:8], integration_id=integration_id[:8])
|
|
711
|
+
except Exception as e:
|
|
712
|
+
logger.error("integration_resolution_error", agent_id=agent_id[:8], integration_id=integration_id[:8], error=str(e))
|
|
713
|
+
|
|
714
|
+
# Build complete config to resolve templates
|
|
715
|
+
complete_config = {
|
|
716
|
+
"system_prompt": agent_configuration.get("system_prompt"),
|
|
717
|
+
"description": agent.description, # From agents table column
|
|
718
|
+
"configuration": agent_configuration,
|
|
719
|
+
"mcp_servers": execution_environment.get("mcp_servers", {}),
|
|
720
|
+
"env_vars": execution_environment.get("env_vars", {}),
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
# Apply template resolution to ENTIRE config
|
|
724
|
+
resolved_config = apply_template_resolution(
|
|
725
|
+
complete_config,
|
|
726
|
+
resolved_secrets,
|
|
727
|
+
resolved_env_vars
|
|
728
|
+
)
|
|
729
|
+
|
|
730
|
+
mcp_servers_resolved = resolved_config.get("mcp_servers", {})
|
|
731
|
+
|
|
732
|
+
logger.info(
|
|
733
|
+
"full_execution_environment_resolved",
|
|
734
|
+
agent_id=agent_id[:8],
|
|
735
|
+
env_var_count=len(resolved_env_vars),
|
|
736
|
+
mcp_server_count=len(mcp_servers_resolved),
|
|
737
|
+
mcp_server_names=list(mcp_servers_resolved.keys()),
|
|
738
|
+
secrets_count=len(resolved_secrets)
|
|
739
|
+
)
|
|
740
|
+
|
|
741
|
+
# Debug log each MCP server after resolution
|
|
742
|
+
for server_name, server_config in mcp_servers_resolved.items():
|
|
743
|
+
logger.debug(
|
|
744
|
+
"mcp_server_after_template_resolution",
|
|
745
|
+
server_name=server_name,
|
|
746
|
+
config=server_config,
|
|
747
|
+
)
|
|
748
|
+
|
|
749
|
+
return {
|
|
750
|
+
"env_vars": resolved_env_vars,
|
|
751
|
+
"mcp_servers": mcp_servers_resolved,
|
|
752
|
+
"system_prompt": resolved_config.get("system_prompt"),
|
|
753
|
+
"description": resolved_config.get("description"),
|
|
754
|
+
"configuration": resolved_config.get("configuration", {}),
|
|
755
|
+
"secrets_resolved": resolved_secrets, # For debugging/logging only
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
except HTTPException:
|
|
759
|
+
raise
|
|
760
|
+
except Exception as e:
|
|
761
|
+
logger.error("full_execution_environment_resolution_error", agent_id=agent_id[:8], error=str(e), exc_info=True)
|
|
762
|
+
raise HTTPException(status_code=500, detail=f"Failed to resolve full execution environment: {str(e)}")
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
@router.get("/agents/{agent_id}/resolved/full")
|
|
766
|
+
async def get_agent_execution_environment_full(
|
|
767
|
+
agent_id: str,
|
|
768
|
+
request: Request,
|
|
769
|
+
organization: dict = Depends(get_current_organization),
|
|
770
|
+
db: Session = Depends(get_db),
|
|
771
|
+
) -> Dict[str, Any]:
|
|
772
|
+
"""
|
|
773
|
+
Get FULL resolved execution environment for an agent with template resolution.
|
|
774
|
+
|
|
775
|
+
This endpoint extends the basic /resolved endpoint by:
|
|
776
|
+
1. Returning the complete execution environment (not just env vars)
|
|
777
|
+
2. Including MCP servers with templates resolved
|
|
778
|
+
3. Resolving templates in ALL text fields (system_prompt, description, etc.)
|
|
779
|
+
4. Providing resolved secrets dict (for template context)
|
|
780
|
+
|
|
781
|
+
Returns:
|
|
782
|
+
Complete execution environment dict with:
|
|
783
|
+
- env_vars: Resolved environment variables
|
|
784
|
+
- mcp_servers: MCP server configs with templates resolved
|
|
785
|
+
- secrets_resolved: Map of secret names to values (for templates)
|
|
786
|
+
- raw_config: Original config before template resolution
|
|
787
|
+
"""
|
|
788
|
+
from control_plane_api.app.controllers.execution_environment_controller import (
|
|
789
|
+
resolve_agent_execution_environment,
|
|
790
|
+
ExecutionEnvironmentResolutionError,
|
|
791
|
+
)
|
|
792
|
+
import os
|
|
793
|
+
|
|
794
|
+
try:
|
|
795
|
+
org_id = organization["id"]
|
|
796
|
+
|
|
797
|
+
# Determine which token to use for Kubiya API calls
|
|
798
|
+
# If authenticated with UserKey or JWT (Kubiya API keys), use that token from the request
|
|
799
|
+
# If authenticated with Bearer (Auth0 user JWT), fall back to environment KUBIYA_API_KEY
|
|
800
|
+
auth_type = getattr(request.state, "kubiya_auth_type", "Bearer")
|
|
801
|
+
if auth_type in ("UserKey", "JWT", "ManagementKey"):
|
|
802
|
+
# Authenticated with Kubiya API key - use it directly for Kubiya API calls
|
|
803
|
+
kubiya_api_key = request.state.kubiya_token
|
|
804
|
+
else:
|
|
805
|
+
# Authenticated with Auth0 user JWT - fall back to environment API key
|
|
806
|
+
kubiya_api_key = os.environ.get("KUBIYA_API_KEY")
|
|
807
|
+
|
|
808
|
+
return await resolve_agent_execution_environment(agent_id, org_id, db, kubiya_api_key)
|
|
809
|
+
except ExecutionEnvironmentResolutionError as e:
|
|
810
|
+
raise HTTPException(status_code=404 if "not found" in str(e).lower() else 500, detail=str(e))
|
|
811
|
+
|
|
812
|
+
|
|
813
|
+
@router.get("/teams/{team_id}/resolved/full")
|
|
814
|
+
async def get_team_execution_environment_full(
|
|
815
|
+
team_id: str,
|
|
816
|
+
request: Request,
|
|
817
|
+
organization: dict = Depends(get_current_organization),
|
|
818
|
+
db: Session = Depends(get_db),
|
|
819
|
+
) -> Dict[str, Any]:
|
|
820
|
+
"""
|
|
821
|
+
Get FULL resolved execution environment for a team with template resolution.
|
|
822
|
+
|
|
823
|
+
Similar to agent endpoint but for teams.
|
|
824
|
+
"""
|
|
825
|
+
from control_plane_api.app.controllers.execution_environment_controller import (
|
|
826
|
+
resolve_team_execution_environment,
|
|
827
|
+
ExecutionEnvironmentResolutionError,
|
|
828
|
+
)
|
|
829
|
+
import os
|
|
830
|
+
|
|
831
|
+
try:
|
|
832
|
+
org_id = organization["id"]
|
|
833
|
+
|
|
834
|
+
# Determine which token to use for Kubiya API calls
|
|
835
|
+
# If authenticated with UserKey or JWT (Kubiya API keys), use that token from the request
|
|
836
|
+
# If authenticated with Bearer (Auth0 user JWT), fall back to environment KUBIYA_API_KEY
|
|
837
|
+
auth_type = getattr(request.state, "kubiya_auth_type", "Bearer")
|
|
838
|
+
if auth_type in ("UserKey", "JWT", "ManagementKey"):
|
|
839
|
+
# Authenticated with Kubiya API key - use it directly for Kubiya API calls
|
|
840
|
+
kubiya_api_key = request.state.kubiya_token
|
|
841
|
+
else:
|
|
842
|
+
# Authenticated with Auth0 user JWT - fall back to environment API key
|
|
843
|
+
kubiya_api_key = os.environ.get("KUBIYA_API_KEY")
|
|
844
|
+
|
|
845
|
+
return await resolve_team_execution_environment(team_id, org_id, db, kubiya_api_key)
|
|
846
|
+
except ExecutionEnvironmentResolutionError as e:
|
|
847
|
+
raise HTTPException(status_code=404 if "not found" in str(e).lower() else 500, detail=str(e))
|