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,490 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Custom Integrations Router
|
|
3
|
+
|
|
4
|
+
API endpoints for managing custom user-defined integrations with:
|
|
5
|
+
- Environment variables
|
|
6
|
+
- Secrets (vault references)
|
|
7
|
+
- Files (configs, certificates)
|
|
8
|
+
- Contextual prompts
|
|
9
|
+
"""
|
|
10
|
+
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
11
|
+
from typing import List, Optional, Dict, Any
|
|
12
|
+
from sqlalchemy.orm import Session
|
|
13
|
+
from pydantic import BaseModel, Field
|
|
14
|
+
import structlog
|
|
15
|
+
|
|
16
|
+
from control_plane_api.app.database import get_db
|
|
17
|
+
from control_plane_api.app.middleware.auth import get_current_organization
|
|
18
|
+
from control_plane_api.app.models.custom_integration import CustomIntegration, CustomIntegrationStatus
|
|
19
|
+
|
|
20
|
+
logger = structlog.get_logger()
|
|
21
|
+
|
|
22
|
+
router = APIRouter(prefix="/custom-integrations", tags=["custom-integrations"])
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Pydantic Models
|
|
26
|
+
class FileConfig(BaseModel):
|
|
27
|
+
"""Configuration for a file to be written to workspace"""
|
|
28
|
+
path: str = Field(..., description="File path (e.g., ~/.postgresql/client.crt)")
|
|
29
|
+
content: Optional[str] = Field(None, description="File content (direct)")
|
|
30
|
+
secret_ref: Optional[str] = Field(None, description="Secret name to load content from")
|
|
31
|
+
mode: Optional[str] = Field("0644", description="File permissions (octal)")
|
|
32
|
+
description: Optional[str] = Field(None, description="Description of this file")
|
|
33
|
+
|
|
34
|
+
class Config:
|
|
35
|
+
json_schema_extra = {
|
|
36
|
+
"example": {
|
|
37
|
+
"path": "~/.postgresql/client.crt",
|
|
38
|
+
"content": "-----BEGIN CERTIFICATE-----\n...",
|
|
39
|
+
"mode": "0600",
|
|
40
|
+
"description": "PostgreSQL client certificate"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ConnectionTest(BaseModel):
|
|
46
|
+
"""Configuration for testing integration connectivity"""
|
|
47
|
+
enabled: bool = Field(True, description="Whether to test connection")
|
|
48
|
+
command: str = Field(..., description="Command to run for testing")
|
|
49
|
+
timeout: int = Field(5, description="Timeout in seconds")
|
|
50
|
+
|
|
51
|
+
class Config:
|
|
52
|
+
json_schema_extra = {
|
|
53
|
+
"example": {
|
|
54
|
+
"enabled": True,
|
|
55
|
+
"command": "pg_isready -h $DB_HOST -p $DB_PORT",
|
|
56
|
+
"timeout": 5
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class CustomIntegrationConfig(BaseModel):
|
|
62
|
+
"""Custom integration configuration"""
|
|
63
|
+
env_vars: Dict[str, str] = Field(default_factory=dict, description="Environment variables")
|
|
64
|
+
secrets: List[str] = Field(default_factory=list, description="Secret names from vault")
|
|
65
|
+
files: List[FileConfig] = Field(default_factory=list, description="Files to create")
|
|
66
|
+
context_prompt: Optional[str] = Field(None, description="Contextual guidance for agent")
|
|
67
|
+
connection_test: Optional[ConnectionTest] = Field(None, description="Connection test config")
|
|
68
|
+
|
|
69
|
+
class Config:
|
|
70
|
+
json_schema_extra = {
|
|
71
|
+
"example": {
|
|
72
|
+
"env_vars": {
|
|
73
|
+
"DB_HOST": "postgres.prod.example.com",
|
|
74
|
+
"DB_PORT": "5432",
|
|
75
|
+
"DB_NAME": "production"
|
|
76
|
+
},
|
|
77
|
+
"secrets": ["DB_PASSWORD", "DB_SSL_CERT"],
|
|
78
|
+
"files": [
|
|
79
|
+
{
|
|
80
|
+
"path": "~/.postgresql/client.crt",
|
|
81
|
+
"secret_ref": "POSTGRES_CLIENT_CERT",
|
|
82
|
+
"mode": "0600"
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
"context_prompt": "Production PostgreSQL database. Use connection pooling."
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class CreateCustomIntegrationRequest(BaseModel):
|
|
91
|
+
"""Request to create a custom integration"""
|
|
92
|
+
name: str = Field(..., min_length=1, max_length=255, description="Integration name")
|
|
93
|
+
integration_type: str = Field(..., min_length=1, max_length=100, description="Integration type")
|
|
94
|
+
description: Optional[str] = Field(None, description="Description")
|
|
95
|
+
config: CustomIntegrationConfig = Field(..., description="Integration configuration")
|
|
96
|
+
tags: List[str] = Field(default_factory=list, description="Tags")
|
|
97
|
+
|
|
98
|
+
class Config:
|
|
99
|
+
json_schema_extra = {
|
|
100
|
+
"example": {
|
|
101
|
+
"name": "production-database",
|
|
102
|
+
"integration_type": "postgres",
|
|
103
|
+
"description": "Production PostgreSQL database",
|
|
104
|
+
"config": {
|
|
105
|
+
"env_vars": {
|
|
106
|
+
"DB_HOST": "postgres.prod.example.com",
|
|
107
|
+
"DB_PORT": "5432"
|
|
108
|
+
},
|
|
109
|
+
"secrets": ["DB_PASSWORD"],
|
|
110
|
+
"context_prompt": "Production database - handle with care"
|
|
111
|
+
},
|
|
112
|
+
"tags": ["production", "database"]
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class UpdateCustomIntegrationRequest(BaseModel):
|
|
118
|
+
"""Request to update a custom integration"""
|
|
119
|
+
name: Optional[str] = Field(None, min_length=1, max_length=255)
|
|
120
|
+
integration_type: Optional[str] = Field(None, min_length=1, max_length=100)
|
|
121
|
+
description: Optional[str] = None
|
|
122
|
+
config: Optional[CustomIntegrationConfig] = None
|
|
123
|
+
status: Optional[CustomIntegrationStatus] = None
|
|
124
|
+
tags: Optional[List[str]] = None
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class CustomIntegrationResponse(BaseModel):
|
|
128
|
+
"""Custom integration response"""
|
|
129
|
+
id: str
|
|
130
|
+
organization_id: str
|
|
131
|
+
name: str
|
|
132
|
+
integration_type: str
|
|
133
|
+
description: Optional[str]
|
|
134
|
+
status: str
|
|
135
|
+
config: Dict[str, Any]
|
|
136
|
+
tags: List[str]
|
|
137
|
+
created_at: str
|
|
138
|
+
updated_at: str
|
|
139
|
+
created_by: Optional[str]
|
|
140
|
+
|
|
141
|
+
class Config:
|
|
142
|
+
from_attributes = True
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# API Endpoints
|
|
146
|
+
|
|
147
|
+
@router.get(
|
|
148
|
+
"",
|
|
149
|
+
response_model=List[CustomIntegrationResponse],
|
|
150
|
+
summary="List Custom Integrations",
|
|
151
|
+
description="""
|
|
152
|
+
List all custom integrations for the organization.
|
|
153
|
+
|
|
154
|
+
**Filtering Options:**
|
|
155
|
+
- `integration_type`: Filter by type (postgres, mongodb, redis, etc.)
|
|
156
|
+
- `status`: Filter by status (active, inactive, deleted)
|
|
157
|
+
- `tags`: Filter by tags (comma-separated, e.g., "production,database")
|
|
158
|
+
|
|
159
|
+
**Default Behavior:**
|
|
160
|
+
- Excludes deleted integrations unless explicitly filtered
|
|
161
|
+
- Results sorted by creation date (newest first)
|
|
162
|
+
|
|
163
|
+
**Example:**
|
|
164
|
+
```
|
|
165
|
+
GET /api/v1/custom-integrations?integration_type=postgres&tags=production
|
|
166
|
+
```
|
|
167
|
+
""",
|
|
168
|
+
responses={
|
|
169
|
+
200: {
|
|
170
|
+
"description": "List of custom integrations",
|
|
171
|
+
"content": {
|
|
172
|
+
"application/json": {
|
|
173
|
+
"example": [
|
|
174
|
+
{
|
|
175
|
+
"id": "550e8400-e29b-41d4-a716-446655440000",
|
|
176
|
+
"organization_id": "org-123",
|
|
177
|
+
"name": "production-postgres",
|
|
178
|
+
"integration_type": "postgres",
|
|
179
|
+
"description": "Production PostgreSQL database",
|
|
180
|
+
"status": "active",
|
|
181
|
+
"config": {
|
|
182
|
+
"env_vars": {"DB_HOST": "postgres.example.com"},
|
|
183
|
+
"secrets": ["DB_PASSWORD"]
|
|
184
|
+
},
|
|
185
|
+
"tags": ["production", "database"],
|
|
186
|
+
"created_at": "2025-12-16T10:00:00Z",
|
|
187
|
+
"updated_at": "2025-12-16T10:00:00Z",
|
|
188
|
+
"created_by": "user-123"
|
|
189
|
+
}
|
|
190
|
+
]
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
)
|
|
196
|
+
async def list_custom_integrations(
|
|
197
|
+
organization: dict = Depends(get_current_organization),
|
|
198
|
+
db: Session = Depends(get_db),
|
|
199
|
+
integration_type: Optional[str] = Query(None, description="Filter by integration type (e.g., postgres, mongodb, redis)"),
|
|
200
|
+
status: Optional[CustomIntegrationStatus] = Query(None, description="Filter by status (active, inactive, deleted)"),
|
|
201
|
+
tags: Optional[str] = Query(None, description="Filter by tags - comma-separated (e.g., production,database)"),
|
|
202
|
+
):
|
|
203
|
+
"""List all custom integrations for the organization with optional filtering."""
|
|
204
|
+
org_id = organization["id"]
|
|
205
|
+
|
|
206
|
+
query = db.query(CustomIntegration).filter(
|
|
207
|
+
CustomIntegration.organization_id == org_id
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
if integration_type:
|
|
211
|
+
query = query.filter(CustomIntegration.integration_type == integration_type)
|
|
212
|
+
|
|
213
|
+
if status:
|
|
214
|
+
query = query.filter(CustomIntegration.status == status)
|
|
215
|
+
else:
|
|
216
|
+
# By default, exclude deleted integrations
|
|
217
|
+
query = query.filter(CustomIntegration.status != CustomIntegrationStatus.DELETED)
|
|
218
|
+
|
|
219
|
+
if tags:
|
|
220
|
+
tag_list = [t.strip() for t in tags.split(",")]
|
|
221
|
+
# Filter integrations that have ANY of the specified tags
|
|
222
|
+
for tag in tag_list:
|
|
223
|
+
query = query.filter(CustomIntegration.tags.contains([tag]))
|
|
224
|
+
|
|
225
|
+
integrations = query.order_by(CustomIntegration.created_at.desc()).all()
|
|
226
|
+
|
|
227
|
+
logger.info(
|
|
228
|
+
"custom_integrations_listed",
|
|
229
|
+
org_id=org_id,
|
|
230
|
+
count=len(integrations),
|
|
231
|
+
filters={"type": integration_type, "status": status, "tags": tags}
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
return integrations
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
@router.get(
|
|
238
|
+
"/{integration_id}",
|
|
239
|
+
response_model=CustomIntegrationResponse,
|
|
240
|
+
summary="Get Custom Integration",
|
|
241
|
+
description="""
|
|
242
|
+
Retrieve detailed information about a specific custom integration.
|
|
243
|
+
|
|
244
|
+
Returns the complete configuration including:
|
|
245
|
+
- Environment variables
|
|
246
|
+
- Secret references
|
|
247
|
+
- File configurations
|
|
248
|
+
- Context prompt
|
|
249
|
+
- Tags and metadata
|
|
250
|
+
|
|
251
|
+
**Example:**
|
|
252
|
+
```
|
|
253
|
+
GET /api/v1/custom-integrations/550e8400-e29b-41d4-a716-446655440000
|
|
254
|
+
```
|
|
255
|
+
""",
|
|
256
|
+
responses={
|
|
257
|
+
200: {"description": "Custom integration details"},
|
|
258
|
+
404: {"description": "Integration not found"}
|
|
259
|
+
}
|
|
260
|
+
)
|
|
261
|
+
async def get_custom_integration(
|
|
262
|
+
integration_id: str,
|
|
263
|
+
organization: dict = Depends(get_current_organization),
|
|
264
|
+
db: Session = Depends(get_db),
|
|
265
|
+
):
|
|
266
|
+
"""Retrieve a specific custom integration by ID."""
|
|
267
|
+
org_id = organization["id"]
|
|
268
|
+
|
|
269
|
+
integration = db.query(CustomIntegration).filter(
|
|
270
|
+
CustomIntegration.id == integration_id,
|
|
271
|
+
CustomIntegration.organization_id == org_id
|
|
272
|
+
).first()
|
|
273
|
+
|
|
274
|
+
if not integration:
|
|
275
|
+
raise HTTPException(status_code=404, detail="Custom integration not found")
|
|
276
|
+
|
|
277
|
+
logger.info(
|
|
278
|
+
"custom_integration_fetched",
|
|
279
|
+
org_id=org_id,
|
|
280
|
+
integration_id=integration_id[:8],
|
|
281
|
+
integration_name=integration.name
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
return integration
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
@router.post(
|
|
288
|
+
"",
|
|
289
|
+
response_model=CustomIntegrationResponse,
|
|
290
|
+
status_code=201,
|
|
291
|
+
summary="Create Custom Integration",
|
|
292
|
+
description="""
|
|
293
|
+
Create a new custom integration instance.
|
|
294
|
+
|
|
295
|
+
**Configuration Options:**
|
|
296
|
+
- `env_vars`: Key-value pairs for environment variables
|
|
297
|
+
- `secrets`: List of secret names to resolve from vault
|
|
298
|
+
- `files`: List of files to create in workspace
|
|
299
|
+
- `context_prompt`: Contextual guidance for AI agents
|
|
300
|
+
- `connection_test`: Optional command to test connectivity
|
|
301
|
+
|
|
302
|
+
**Name Requirements:**
|
|
303
|
+
- Must be unique within the organization
|
|
304
|
+
- Cannot be empty
|
|
305
|
+
- Alphanumeric and hyphens recommended
|
|
306
|
+
|
|
307
|
+
**Example Request:**
|
|
308
|
+
```json
|
|
309
|
+
{
|
|
310
|
+
"name": "production-postgres",
|
|
311
|
+
"integration_type": "postgres",
|
|
312
|
+
"description": "Production PostgreSQL database",
|
|
313
|
+
"config": {
|
|
314
|
+
"env_vars": {
|
|
315
|
+
"DB_HOST": "postgres.prod.example.com",
|
|
316
|
+
"DB_PORT": "5432",
|
|
317
|
+
"DB_NAME": "production"
|
|
318
|
+
},
|
|
319
|
+
"secrets": ["DB_PASSWORD"],
|
|
320
|
+
"files": [
|
|
321
|
+
{
|
|
322
|
+
"path": "~/.postgresql/client.crt",
|
|
323
|
+
"secret_ref": "POSTGRES_CLIENT_CERT",
|
|
324
|
+
"mode": "0600"
|
|
325
|
+
}
|
|
326
|
+
],
|
|
327
|
+
"context_prompt": "Production database - use connection pooling"
|
|
328
|
+
},
|
|
329
|
+
"tags": ["production", "database"]
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
""",
|
|
333
|
+
responses={
|
|
334
|
+
201: {"description": "Integration created successfully"},
|
|
335
|
+
409: {"description": "Integration with this name already exists"},
|
|
336
|
+
422: {"description": "Validation error"}
|
|
337
|
+
}
|
|
338
|
+
)
|
|
339
|
+
async def create_custom_integration(
|
|
340
|
+
request: CreateCustomIntegrationRequest,
|
|
341
|
+
organization: dict = Depends(get_current_organization),
|
|
342
|
+
db: Session = Depends(get_db),
|
|
343
|
+
):
|
|
344
|
+
"""Create a new custom integration for the organization."""
|
|
345
|
+
org_id = organization["id"]
|
|
346
|
+
user_id = organization.get("user_id")
|
|
347
|
+
|
|
348
|
+
# Check if integration with this name already exists
|
|
349
|
+
existing = db.query(CustomIntegration).filter(
|
|
350
|
+
CustomIntegration.organization_id == org_id,
|
|
351
|
+
CustomIntegration.name == request.name,
|
|
352
|
+
CustomIntegration.status != CustomIntegrationStatus.DELETED
|
|
353
|
+
).first()
|
|
354
|
+
|
|
355
|
+
if existing:
|
|
356
|
+
raise HTTPException(
|
|
357
|
+
status_code=409,
|
|
358
|
+
detail=f"Custom integration with name '{request.name}' already exists"
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
# Create the integration
|
|
362
|
+
integration = CustomIntegration(
|
|
363
|
+
organization_id=org_id,
|
|
364
|
+
name=request.name,
|
|
365
|
+
integration_type=request.integration_type,
|
|
366
|
+
description=request.description,
|
|
367
|
+
config=request.config.model_dump(),
|
|
368
|
+
tags=request.tags,
|
|
369
|
+
status=CustomIntegrationStatus.ACTIVE,
|
|
370
|
+
created_by=user_id
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
db.add(integration)
|
|
374
|
+
db.commit()
|
|
375
|
+
db.refresh(integration)
|
|
376
|
+
|
|
377
|
+
logger.info(
|
|
378
|
+
"custom_integration_created",
|
|
379
|
+
org_id=org_id,
|
|
380
|
+
integration_id=str(integration.id)[:8],
|
|
381
|
+
integration_name=integration.name,
|
|
382
|
+
integration_type=integration.integration_type
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
return integration
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
@router.put("/{integration_id}", response_model=CustomIntegrationResponse)
|
|
389
|
+
async def update_custom_integration(
|
|
390
|
+
integration_id: str,
|
|
391
|
+
request: UpdateCustomIntegrationRequest,
|
|
392
|
+
organization: dict = Depends(get_current_organization),
|
|
393
|
+
db: Session = Depends(get_db),
|
|
394
|
+
):
|
|
395
|
+
"""Update a custom integration."""
|
|
396
|
+
org_id = organization["id"]
|
|
397
|
+
|
|
398
|
+
integration = db.query(CustomIntegration).filter(
|
|
399
|
+
CustomIntegration.id == integration_id,
|
|
400
|
+
CustomIntegration.organization_id == org_id
|
|
401
|
+
).first()
|
|
402
|
+
|
|
403
|
+
if not integration:
|
|
404
|
+
raise HTTPException(status_code=404, detail="Custom integration not found")
|
|
405
|
+
|
|
406
|
+
# Update fields
|
|
407
|
+
if request.name is not None:
|
|
408
|
+
# Check name uniqueness
|
|
409
|
+
existing = db.query(CustomIntegration).filter(
|
|
410
|
+
CustomIntegration.organization_id == org_id,
|
|
411
|
+
CustomIntegration.name == request.name,
|
|
412
|
+
CustomIntegration.id != integration_id,
|
|
413
|
+
CustomIntegration.status != CustomIntegrationStatus.DELETED
|
|
414
|
+
).first()
|
|
415
|
+
if existing:
|
|
416
|
+
raise HTTPException(
|
|
417
|
+
status_code=409,
|
|
418
|
+
detail=f"Custom integration with name '{request.name}' already exists"
|
|
419
|
+
)
|
|
420
|
+
integration.name = request.name
|
|
421
|
+
|
|
422
|
+
if request.integration_type is not None:
|
|
423
|
+
integration.integration_type = request.integration_type
|
|
424
|
+
|
|
425
|
+
if request.description is not None:
|
|
426
|
+
integration.description = request.description
|
|
427
|
+
|
|
428
|
+
if request.config is not None:
|
|
429
|
+
integration.config = request.config.model_dump()
|
|
430
|
+
|
|
431
|
+
if request.status is not None:
|
|
432
|
+
integration.status = request.status
|
|
433
|
+
|
|
434
|
+
if request.tags is not None:
|
|
435
|
+
integration.tags = request.tags
|
|
436
|
+
|
|
437
|
+
db.commit()
|
|
438
|
+
db.refresh(integration)
|
|
439
|
+
|
|
440
|
+
logger.info(
|
|
441
|
+
"custom_integration_updated",
|
|
442
|
+
org_id=org_id,
|
|
443
|
+
integration_id=integration_id[:8],
|
|
444
|
+
integration_name=integration.name
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
return integration
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
@router.delete("/{integration_id}", status_code=204)
|
|
451
|
+
async def delete_custom_integration(
|
|
452
|
+
integration_id: str,
|
|
453
|
+
organization: dict = Depends(get_current_organization),
|
|
454
|
+
db: Session = Depends(get_db),
|
|
455
|
+
hard_delete: bool = Query(False, description="Permanently delete (vs soft delete)"),
|
|
456
|
+
):
|
|
457
|
+
"""
|
|
458
|
+
Delete a custom integration.
|
|
459
|
+
|
|
460
|
+
By default, this is a soft delete (sets status to DELETED).
|
|
461
|
+
Use hard_delete=true to permanently remove from database.
|
|
462
|
+
"""
|
|
463
|
+
org_id = organization["id"]
|
|
464
|
+
|
|
465
|
+
integration = db.query(CustomIntegration).filter(
|
|
466
|
+
CustomIntegration.id == integration_id,
|
|
467
|
+
CustomIntegration.organization_id == org_id
|
|
468
|
+
).first()
|
|
469
|
+
|
|
470
|
+
if not integration:
|
|
471
|
+
raise HTTPException(status_code=404, detail="Custom integration not found")
|
|
472
|
+
|
|
473
|
+
if hard_delete:
|
|
474
|
+
db.delete(integration)
|
|
475
|
+
logger.info(
|
|
476
|
+
"custom_integration_hard_deleted",
|
|
477
|
+
org_id=org_id,
|
|
478
|
+
integration_id=integration_id[:8],
|
|
479
|
+
integration_name=integration.name
|
|
480
|
+
)
|
|
481
|
+
else:
|
|
482
|
+
integration.status = CustomIntegrationStatus.DELETED
|
|
483
|
+
logger.info(
|
|
484
|
+
"custom_integration_soft_deleted",
|
|
485
|
+
org_id=org_id,
|
|
486
|
+
integration_id=integration_id[:8],
|
|
487
|
+
integration_name=integration.name
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
db.commit()
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Enforcer Proxy Router - Proxy to OPA Watchdog Enforcer Service
|
|
3
|
+
|
|
4
|
+
This router provides a transparent proxy to the OPA Watchdog enforcer service,
|
|
5
|
+
allowing workers to call the enforcer through the control plane without knowing
|
|
6
|
+
the actual enforcer service URL.
|
|
7
|
+
|
|
8
|
+
All requests to /api/v1/enforcer/* are forwarded to the enforcer service.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import httpx
|
|
13
|
+
from fastapi import APIRouter, Request, Response, status, HTTPException
|
|
14
|
+
from fastapi.responses import StreamingResponse
|
|
15
|
+
import structlog
|
|
16
|
+
|
|
17
|
+
from control_plane_api.app.middleware.auth import get_current_organization
|
|
18
|
+
from fastapi import Depends
|
|
19
|
+
|
|
20
|
+
logger = structlog.get_logger()
|
|
21
|
+
|
|
22
|
+
router = APIRouter(prefix="/enforcer", tags=["enforcer"])
|
|
23
|
+
|
|
24
|
+
# Get the actual enforcer service URL from environment
|
|
25
|
+
# Default to the hosted enforcer service
|
|
26
|
+
ENFORCER_SERVICE_URL = os.environ.get("ENFORCER_SERVICE_URL", "https://enforcer-psi.vercel.app")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
async def proxy_enforcer_request(
|
|
30
|
+
request: Request,
|
|
31
|
+
path: str = "",
|
|
32
|
+
) -> Response:
|
|
33
|
+
"""
|
|
34
|
+
Generic proxy function for Enforcer API requests.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
request: FastAPI request object
|
|
38
|
+
path: Additional path to append after /enforcer
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Response from the enforcer service
|
|
42
|
+
"""
|
|
43
|
+
# Build target URL
|
|
44
|
+
target_url = f"{ENFORCER_SERVICE_URL.rstrip('/')}/{path.lstrip('/')}"
|
|
45
|
+
|
|
46
|
+
# Get request body if present
|
|
47
|
+
body = None
|
|
48
|
+
if request.method in ["POST", "PUT", "PATCH"]:
|
|
49
|
+
body = await request.body()
|
|
50
|
+
|
|
51
|
+
# Get query parameters
|
|
52
|
+
query_params = dict(request.query_params)
|
|
53
|
+
|
|
54
|
+
# Forward headers (including Authorization)
|
|
55
|
+
headers = dict(request.headers)
|
|
56
|
+
# Remove host header to avoid conflicts
|
|
57
|
+
headers.pop("host", None)
|
|
58
|
+
|
|
59
|
+
logger.debug(
|
|
60
|
+
"proxy_enforcer_request",
|
|
61
|
+
method=request.method,
|
|
62
|
+
target_url=target_url,
|
|
63
|
+
has_body=body is not None,
|
|
64
|
+
query_params=query_params,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
69
|
+
response = await client.request(
|
|
70
|
+
method=request.method,
|
|
71
|
+
url=target_url,
|
|
72
|
+
params=query_params,
|
|
73
|
+
content=body,
|
|
74
|
+
headers=headers,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Return response
|
|
78
|
+
return Response(
|
|
79
|
+
content=response.content,
|
|
80
|
+
status_code=response.status_code,
|
|
81
|
+
headers=dict(response.headers),
|
|
82
|
+
)
|
|
83
|
+
except httpx.TimeoutException:
|
|
84
|
+
logger.error("enforcer_proxy_timeout", target_url=target_url)
|
|
85
|
+
raise HTTPException(
|
|
86
|
+
status_code=status.HTTP_504_GATEWAY_TIMEOUT,
|
|
87
|
+
detail="Enforcer service request timed out"
|
|
88
|
+
)
|
|
89
|
+
except httpx.RequestError as e:
|
|
90
|
+
logger.error("enforcer_proxy_error", error=str(e), target_url=target_url)
|
|
91
|
+
raise HTTPException(
|
|
92
|
+
status_code=status.HTTP_502_BAD_GATEWAY,
|
|
93
|
+
detail=f"Failed to connect to enforcer service: {str(e)}"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@router.get("/health")
|
|
98
|
+
async def health_check():
|
|
99
|
+
"""Health check endpoint for enforcer proxy"""
|
|
100
|
+
return {
|
|
101
|
+
"status": "healthy",
|
|
102
|
+
"enforcer_url": ENFORCER_SERVICE_URL,
|
|
103
|
+
"proxy": "enabled"
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@router.get("/status")
|
|
108
|
+
async def status_check(request: Request):
|
|
109
|
+
"""Proxy status endpoint to enforcer service"""
|
|
110
|
+
return await proxy_enforcer_request(request, "status")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@router.api_route("/api/v1/{path:path}", methods=["GET", "POST", "PUT", "PATCH", "DELETE"])
|
|
114
|
+
async def proxy_api_v1(
|
|
115
|
+
request: Request,
|
|
116
|
+
path: str,
|
|
117
|
+
):
|
|
118
|
+
"""Proxy all /api/v1/* requests to enforcer service"""
|
|
119
|
+
return await proxy_enforcer_request(request, f"api/v1/{path}")
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@router.api_route("/{path:path}", methods=["GET", "POST", "PUT", "PATCH", "DELETE"])
|
|
123
|
+
async def proxy_all(
|
|
124
|
+
request: Request,
|
|
125
|
+
path: str,
|
|
126
|
+
):
|
|
127
|
+
"""Proxy all other requests to enforcer service"""
|
|
128
|
+
# Skip if already handled by other routes
|
|
129
|
+
if path in ["health", "status"] or path.startswith("api/v1/"):
|
|
130
|
+
return
|
|
131
|
+
|
|
132
|
+
return await proxy_enforcer_request(request, path)
|