aegis-stack 0.2.0rc2__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.
- aegis/__init__.py +5 -0
- aegis/__main__.py +51 -0
- aegis/cli/__init__.py +6 -0
- aegis/cli/callbacks.py +114 -0
- aegis/cli/interactive.py +611 -0
- aegis/cli/utils.py +70 -0
- aegis/cli/validators.py +34 -0
- aegis/commands/__init__.py +6 -0
- aegis/commands/add.py +353 -0
- aegis/commands/add_service.py +332 -0
- aegis/commands/components.py +35 -0
- aegis/commands/init.py +370 -0
- aegis/commands/remove.py +227 -0
- aegis/commands/services.py +52 -0
- aegis/commands/update.py +252 -0
- aegis/commands/version.py +12 -0
- aegis/config/__init__.py +1 -0
- aegis/config/shared_files.py +136 -0
- aegis/core/CLAUDE.md +377 -0
- aegis/core/__init__.py +6 -0
- aegis/core/component_files.py +228 -0
- aegis/core/component_utils.py +220 -0
- aegis/core/components.py +127 -0
- aegis/core/copier_manager.py +315 -0
- aegis/core/copier_updater.py +475 -0
- aegis/core/dependency_resolver.py +119 -0
- aegis/core/manual_updater.py +554 -0
- aegis/core/post_gen_tasks.py +547 -0
- aegis/core/service_resolver.py +261 -0
- aegis/core/services.py +157 -0
- aegis/core/template_generator.py +266 -0
- aegis/core/version_compatibility.py +259 -0
- aegis/templates/CLAUDE.md +591 -0
- aegis/templates/cookiecutter-aegis-project/cookiecutter.json +39 -0
- aegis/templates/cookiecutter-aegis-project/hooks/post_gen_project.py +214 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.dockerignore +71 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.env.example.j2 +130 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.gitignore +131 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/Dockerfile +53 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/Makefile +236 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/README.md.j2 +196 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/alembic.ini.j2 +111 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/env.py.j2 +91 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/script.py.mako +25 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/versions/001_initial_auth.py.j2 +51 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/__init__.py +5 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/__init__.py +6 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/ai.py.j2 +700 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/ai_rendering.py +361 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/auth.py.j2 +253 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/health.py.j2 +419 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/load_test.py.j2 +656 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/main.py.j2 +65 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/marko_terminal_renderer.py +489 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/tasks.py.j2 +328 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/{% if cookiecutter.include_scheduler == /"yes/" %}tasks.py{% endif %}" +340 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/__init__.py +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/__init__.py +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/ai/__init__.py +8 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/ai/router.py +329 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/auth/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/auth/router.py +64 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/deps.py +58 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/health.py +163 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/models.py.j2 +280 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/routing.py.j2 +32 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/scheduler.py.j2 +121 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/worker.py.j2 +478 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/hooks.py +144 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/main.py +31 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/middleware/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/middleware/cors.py +20 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/shutdown/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/shutdown/cleanup.py +14 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/component_health.py.j2 +418 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/database_init.py.j2 +83 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/__init__.py +5 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/__init__.py +27 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/table.py +78 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/text.py +142 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/__init__.py.j2 +47 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/ai_card.py +287 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/auth_card.py +198 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/base_card.py +256 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/card_factory.py +227 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/card_utils.py +333 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/database_card.py +420 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/fastapi_card.py +328 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/flet_card.py +267 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/redis_card.py +322 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/scheduler_card.py +352 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/services_card.py +233 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/worker_card.py +684 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/main.py.j2 +653 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/theme.py +48 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/scheduler/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/scheduler/main.py.j2 +156 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/CLAUDE.md.j2 +213 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/__init__.py +6 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/constants.py.j2 +30 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/pools.py +97 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/load_test.py +55 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/media.py +49 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/system.py +44 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/registry.py +139 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/__init__.py +120 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/load_tasks.py +507 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/simple_system_tasks.py +33 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/system_tasks.py +281 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/config.py.j2 +178 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/constants.py +58 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/db.py.j2 +176 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/log.py +92 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/security.py +62 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/webserver.py +40 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/{% if cookiecutter.include_scheduler == /"yes/" %}scheduler.py{% endif %}" +21 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/integrations/__init__.py +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/integrations/main.py +62 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/models/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/models/user.py +44 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/py.typed +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/__init__.py +8 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/config.py +130 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/conversation.py +213 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/health.py +96 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/models.py +229 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/providers.py +370 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/service.py +388 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/auth_service.py +41 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/health.py +164 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/user_service.py +83 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/middleware_inspector.py.j2 +223 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/models.py.j2 +70 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/route_inspector.py.j2 +155 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/load_test.py +679 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/load_test_models.py +266 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/__init__.py.j2 +21 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/models.py.j2 +119 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/scheduled_task_manager.py.j2 +273 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/task_monitor.py.j2 +189 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/shared/__init__.py +15 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/shared/models.py +26 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/__init__.py +52 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/alerts.py +94 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/backup.py.j2 +119 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/health.py.j2 +1333 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/models.py +243 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/ui.py +52 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto-dark.png +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto-square-backup.png +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto.png +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.dockerignore +71 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.env.example.j2 +64 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.gitignore +131 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/Dockerfile +53 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/Makefile +211 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/README.md.j2 +172 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/docker-compose.yml.j2 +78 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/mkdocs.yml.j2 +62 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/pyproject.toml.j2 +120 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/uv.lock +1673 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docker-compose.yml.j2 +200 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/api.md +191 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/components/scheduler.md +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/components/scheduler.md.j2 +621 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/development.md +215 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/health.md +240 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/javascripts/mermaid-config.js +62 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/stylesheets/mermaid.css +95 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/mkdocs.yml.j2 +62 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/pyproject.toml.j2 +131 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/entrypoint.sh +87 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/entrypoint.sh.j2 +93 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/gen_docs.py +16 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_auth_endpoints.py.j2 +307 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_health_endpoints.py.j2 +262 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_scheduler_endpoints.py.j2 +214 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_worker_endpoints.py.j2 +165 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/cli/test_ai_rendering.py +427 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/cli/test_conversation_memory.py +465 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/components/test_scheduler.py +43 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/conftest.py.j2 +195 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/conftest.py +78 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_health.py +157 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_models.py +164 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_service.py +198 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_auth_integration.py.j2 +528 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_component_integration.py.j2 +387 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_conversation_persistence.py +342 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_health_logic.py.j2 +663 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_load_test_models.py +619 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_load_test_service.py +603 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_middleware_inspector.py.j2 +248 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_scheduled_task_manager.py.j2 +292 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_system_service.py +98 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_worker_health_registration.py.j2 +257 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/test_core.py +49 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/uv.lock +1673 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/.copier-answers.yml.jinja +21 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/.dockerignore +71 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/.env.example.jinja +130 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/.gitignore +131 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/Dockerfile +53 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/Makefile.jinja +236 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/README.md.jinja +196 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/alembic.ini.jinja +111 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/env.py.jinja +91 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/script.py.mako +25 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/versions/001_initial_auth.py.jinja +51 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/__init__.py.jinja +5 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/__init__.py.jinja +6 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/ai.py.jinja +700 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/ai_rendering.py +360 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/auth.py.jinja +253 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/health.py.jinja +419 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/load_test.py.jinja +656 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/main.py.jinja +65 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/marko_terminal_renderer.py +489 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/tasks.py.jinja +328 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/__init__.py +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/__init__.py +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/ai/__init__.py +8 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/ai/router.py +329 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/auth/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/auth/router.py +64 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/deps.py +58 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/health.py.jinja +163 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/models.py.jinja +280 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/routing.py.jinja +32 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/scheduler.py.jinja +121 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/worker.py.jinja +478 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/hooks.py +144 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/main.py +31 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/middleware/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/middleware/cors.py +20 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/shutdown/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/shutdown/cleanup.py +14 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/component_health.py.jinja +418 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/database_init.py.jinja +83 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/__init__.py +5 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/__init__.py +27 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/table.py +78 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/text.py +142 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/__init__.py.jinja +47 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/ai_card.py +287 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/auth_card.py +198 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/base_card.py +256 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/card_factory.py +227 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/card_utils.py +333 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/database_card.py +420 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/fastapi_card.py +328 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/flet_card.py +267 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/redis_card.py +322 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/scheduler_card.py +352 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/services_card.py +233 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/worker_card.py +684 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/main.py.jinja +653 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/theme.py +48 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/scheduler/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/scheduler/main.py.jinja +156 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/CLAUDE.md.jinja +213 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/__init__.py +6 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/constants.py.jinja +30 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/pools.py +97 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/load_test.py +55 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/media.py +49 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/system.py +44 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/registry.py +139 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/__init__.py +120 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/load_tasks.py +507 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/simple_system_tasks.py +33 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/system_tasks.py +281 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/config.py.jinja +178 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/constants.py +58 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/db.py.jinja +176 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/log.py +92 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/security.py +62 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/scheduler.py.jinja +21 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/webserver.py +39 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/integrations/__init__.py +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/integrations/main.py +61 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/models/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/models/user.py +44 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/py.typed +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/__init__.py +8 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/config.py +130 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/conversation.py +213 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/health.py +96 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/models.py +229 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/providers.py.jinja +370 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/service.py +387 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/auth_service.py +40 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/health.py +162 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/user_service.py +82 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/middleware_inspector.py.jinja +223 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/models.py.jinja +70 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/route_inspector.py.jinja +155 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/load_test.py +678 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/load_test_models.py +265 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/__init__.py.jinja +21 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/models.py.jinja +119 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/scheduled_task_manager.py.jinja +273 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/task_monitor.py.jinja +189 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/shared/__init__.py +15 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/shared/models.py +26 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/__init__.py +52 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/alerts.py +94 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/backup.py.jinja +119 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/health.py.jinja +1333 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/models.py +243 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/ui.py +52 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57223!aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57224!aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57225!aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57533!aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57534!aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57538!aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57897!aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57898!aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57904!aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58315!aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58316!aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58324!aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58837!aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58838!aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58849!aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/.env.example.jinja +64 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/README.md.jinja +172 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/docker-compose.yml.jinja +78 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/mkdocs.yml.jinja +62 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/pyproject.toml.jinja +120 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docker-compose.yml.jinja +200 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/api.md.jinja +191 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/components/scheduler.md +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/components/scheduler.md.jinja +621 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/development.md.jinja +215 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/health.md.jinja +240 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/javascripts/mermaid-config.js +62 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/stylesheets/mermaid.css +95 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/mkdocs.yml.jinja +62 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/pyproject.toml.jinja +131 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/entrypoint.sh +87 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/entrypoint.sh.jinja +93 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/gen_docs.py +16 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_auth_endpoints.py.jinja +307 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_health_endpoints.py.jinja +262 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_scheduler_endpoints.py.jinja +214 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_worker_endpoints.py.jinja +165 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/cli/test_ai_rendering.py +427 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/cli/test_conversation_memory.py +465 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/components/test_scheduler.py +43 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/conftest.py.jinja +195 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/conftest.py +78 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_health.py +157 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_models.py +164 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_service.py +198 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_auth_integration.py.jinja +528 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_component_integration.py.jinja +387 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_conversation_persistence.py +342 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_health_logic.py.jinja +663 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_load_test_models.py +619 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_load_test_service.py +603 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_middleware_inspector.py.jinja +248 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_scheduled_task_manager.py.jinja +292 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_system_service.py +98 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_worker_health_registration.py.jinja +257 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/test_core.py +49 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/uv.lock +1673 -0
- aegis_stack-0.2.0rc2.dist-info/METADATA +165 -0
- aegis_stack-0.2.0rc2.dist-info/RECORD +392 -0
- aegis_stack-0.2.0rc2.dist-info/WHEEL +4 -0
- aegis_stack-0.2.0rc2.dist-info/entry_points.txt +3 -0
- aegis_stack-0.2.0rc2.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Service dependency resolution for Aegis Stack.
|
|
3
|
+
|
|
4
|
+
This module handles service dependency resolution, converting service selections
|
|
5
|
+
to their required components and validating service-to-component compatibility.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .dependency_resolver import DependencyResolver
|
|
9
|
+
from .services import SERVICES, get_service_dependencies
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ServiceResolver:
|
|
13
|
+
"""Handles service dependency resolution and validation."""
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
def resolve_service_dependencies(
|
|
17
|
+
selected_services: list[str],
|
|
18
|
+
) -> tuple[list[str], list[str]]:
|
|
19
|
+
"""
|
|
20
|
+
Resolve services to their required components.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
selected_services: List of service names selected by user
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Tuple of (resolved_components, service_added_components)
|
|
27
|
+
- resolved_components: All components needed (including auto-added)
|
|
28
|
+
- service_added_components: Components that were added due to services
|
|
29
|
+
|
|
30
|
+
Raises:
|
|
31
|
+
ValueError: If any selected services are invalid
|
|
32
|
+
"""
|
|
33
|
+
# Validate services first
|
|
34
|
+
errors = ServiceResolver.validate_services(selected_services)
|
|
35
|
+
if errors:
|
|
36
|
+
raise ValueError(f"Invalid services: {'; '.join(errors)}")
|
|
37
|
+
|
|
38
|
+
# Collect all components required by services
|
|
39
|
+
service_required_components = set()
|
|
40
|
+
for service_name in selected_services:
|
|
41
|
+
service_deps = get_service_dependencies(service_name)
|
|
42
|
+
service_required_components.update(service_deps)
|
|
43
|
+
|
|
44
|
+
# Convert to list and resolve component-to-component dependencies
|
|
45
|
+
component_list = list(service_required_components)
|
|
46
|
+
resolved_components = DependencyResolver.resolve_dependencies(component_list)
|
|
47
|
+
|
|
48
|
+
# The service_added_components are what services directly require
|
|
49
|
+
# (This represents what gets auto-added when services are selected)
|
|
50
|
+
service_added_components = sorted(service_required_components)
|
|
51
|
+
|
|
52
|
+
return resolved_components, service_added_components
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def validate_services(services: list[str]) -> list[str]:
|
|
56
|
+
"""
|
|
57
|
+
Validate service selection and return errors.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
services: List of service names to validate
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
List of error messages (empty if valid)
|
|
64
|
+
"""
|
|
65
|
+
errors = []
|
|
66
|
+
|
|
67
|
+
for service in services:
|
|
68
|
+
if service not in SERVICES:
|
|
69
|
+
errors.append(f"Unknown service: {service}")
|
|
70
|
+
continue
|
|
71
|
+
|
|
72
|
+
spec = SERVICES[service]
|
|
73
|
+
|
|
74
|
+
# Check service conflicts
|
|
75
|
+
if spec.conflicts:
|
|
76
|
+
for conflict in spec.conflicts:
|
|
77
|
+
if conflict in services:
|
|
78
|
+
errors.append(
|
|
79
|
+
f"Service '{service}' conflicts with service '{conflict}'"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# Check for service-to-service dependencies
|
|
83
|
+
for service in services:
|
|
84
|
+
if service not in SERVICES:
|
|
85
|
+
continue # Already reported above
|
|
86
|
+
|
|
87
|
+
spec = SERVICES[service]
|
|
88
|
+
if spec.required_services:
|
|
89
|
+
for required_service in spec.required_services:
|
|
90
|
+
if required_service not in services:
|
|
91
|
+
errors.append(
|
|
92
|
+
f"Service '{service}' requires service '{required_service}'"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
return errors
|
|
96
|
+
|
|
97
|
+
@staticmethod
|
|
98
|
+
def get_missing_components_for_services(
|
|
99
|
+
selected_services: list[str], available_components: list[str]
|
|
100
|
+
) -> list[str]:
|
|
101
|
+
"""
|
|
102
|
+
Get components that need to be added for the selected services.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
selected_services: List of service names
|
|
106
|
+
available_components: List of already available components
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
List of component names that need to be added
|
|
110
|
+
"""
|
|
111
|
+
if not selected_services:
|
|
112
|
+
return []
|
|
113
|
+
|
|
114
|
+
try:
|
|
115
|
+
resolved_components, _ = ServiceResolver.resolve_service_dependencies(
|
|
116
|
+
selected_services
|
|
117
|
+
)
|
|
118
|
+
missing = set(resolved_components) - set(available_components)
|
|
119
|
+
return sorted(missing)
|
|
120
|
+
except ValueError:
|
|
121
|
+
# If services are invalid, return empty list
|
|
122
|
+
return []
|
|
123
|
+
|
|
124
|
+
@staticmethod
|
|
125
|
+
def validate_service_component_compatibility(
|
|
126
|
+
selected_services: list[str], available_components: list[str]
|
|
127
|
+
) -> list[str]:
|
|
128
|
+
"""
|
|
129
|
+
Validate that all required components are available for selected services.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
selected_services: List of service names
|
|
133
|
+
available_components: List of available component names
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
List of error messages (empty if compatible)
|
|
137
|
+
"""
|
|
138
|
+
errors = []
|
|
139
|
+
|
|
140
|
+
# First validate the services themselves
|
|
141
|
+
service_errors = ServiceResolver.validate_services(selected_services)
|
|
142
|
+
errors.extend(service_errors)
|
|
143
|
+
|
|
144
|
+
# Then check component dependencies
|
|
145
|
+
for service_name in selected_services:
|
|
146
|
+
if service_name not in SERVICES:
|
|
147
|
+
continue # Already reported in service validation
|
|
148
|
+
|
|
149
|
+
service_deps = get_service_dependencies(service_name)
|
|
150
|
+
for required_comp in service_deps:
|
|
151
|
+
if required_comp not in available_components:
|
|
152
|
+
errors.append(
|
|
153
|
+
f"Service '{service_name}' requires component '{required_comp}'"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
return errors
|
|
157
|
+
|
|
158
|
+
@staticmethod
|
|
159
|
+
def get_service_component_summary(
|
|
160
|
+
selected_services: list[str],
|
|
161
|
+
) -> dict[str, list[str]]:
|
|
162
|
+
"""
|
|
163
|
+
Get a summary of what components each service requires.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
selected_services: List of service names
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Dictionary mapping service names to their required components
|
|
170
|
+
"""
|
|
171
|
+
summary = {}
|
|
172
|
+
|
|
173
|
+
for service_name in selected_services:
|
|
174
|
+
if service_name in SERVICES:
|
|
175
|
+
summary[service_name] = get_service_dependencies(service_name)
|
|
176
|
+
else:
|
|
177
|
+
summary[service_name] = [] # Unknown service
|
|
178
|
+
|
|
179
|
+
return summary
|
|
180
|
+
|
|
181
|
+
@staticmethod
|
|
182
|
+
def recommend_components_for_services(selected_services: list[str]) -> list[str]:
|
|
183
|
+
"""
|
|
184
|
+
Get recommended (but not required) components for selected services.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
selected_services: List of service names
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
List of recommended component names
|
|
191
|
+
"""
|
|
192
|
+
recommendations = set()
|
|
193
|
+
|
|
194
|
+
for service_name in selected_services:
|
|
195
|
+
if service_name not in SERVICES:
|
|
196
|
+
continue
|
|
197
|
+
|
|
198
|
+
spec = SERVICES[service_name]
|
|
199
|
+
if spec.recommended_components:
|
|
200
|
+
recommendations.update(spec.recommended_components)
|
|
201
|
+
|
|
202
|
+
return sorted(recommendations)
|
|
203
|
+
|
|
204
|
+
@staticmethod
|
|
205
|
+
def get_services_requiring_component(component_name: str) -> list[str]:
|
|
206
|
+
"""
|
|
207
|
+
Get list of services that require a specific component.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
component_name: Name of the component
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
List of service names that require this component
|
|
214
|
+
"""
|
|
215
|
+
requiring_services = []
|
|
216
|
+
|
|
217
|
+
for service_name, spec in SERVICES.items():
|
|
218
|
+
if component_name in spec.required_components:
|
|
219
|
+
requiring_services.append(service_name)
|
|
220
|
+
|
|
221
|
+
return requiring_services
|
|
222
|
+
|
|
223
|
+
@staticmethod
|
|
224
|
+
def merge_service_and_component_selections(
|
|
225
|
+
selected_services: list[str], selected_components: list[str]
|
|
226
|
+
) -> tuple[list[str], list[str], list[str]]:
|
|
227
|
+
"""
|
|
228
|
+
Merge service and component selections, resolving all dependencies.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
selected_services: List of service names
|
|
232
|
+
selected_components: List of component names
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
Tuple of (final_components, auto_added_from_services, auto_added_from_components)
|
|
236
|
+
- final_components: Final list of all components
|
|
237
|
+
- auto_added_from_services: Components added due to service requirements
|
|
238
|
+
- auto_added_from_components: Components added due to component dependencies
|
|
239
|
+
|
|
240
|
+
Raises:
|
|
241
|
+
ValueError: If services or components are invalid
|
|
242
|
+
"""
|
|
243
|
+
# Resolve service dependencies first
|
|
244
|
+
service_components, service_auto_added = (
|
|
245
|
+
ServiceResolver.resolve_service_dependencies(selected_services)
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
# Combine with explicitly selected components
|
|
249
|
+
all_components = list(set(selected_components + service_components))
|
|
250
|
+
|
|
251
|
+
# Resolve final component dependencies
|
|
252
|
+
final_components = DependencyResolver.resolve_dependencies(all_components)
|
|
253
|
+
|
|
254
|
+
# Calculate what was auto-added from component dependencies
|
|
255
|
+
component_auto_added = set(final_components) - set(all_components)
|
|
256
|
+
|
|
257
|
+
return (
|
|
258
|
+
final_components,
|
|
259
|
+
sorted(service_auto_added),
|
|
260
|
+
sorted(component_auto_added),
|
|
261
|
+
)
|
aegis/core/services.py
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Service registry and specifications for Aegis Stack.
|
|
3
|
+
|
|
4
|
+
This module defines all available services (auth, payment, AI, etc.), their dependencies,
|
|
5
|
+
and metadata used for project generation and validation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from enum import Enum
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ServiceType(Enum):
|
|
13
|
+
"""Service type classifications."""
|
|
14
|
+
|
|
15
|
+
AUTH = "auth" # Authentication and authorization
|
|
16
|
+
PAYMENT = "payment" # Payment processing
|
|
17
|
+
AI = "ai" # AI and ML integrations
|
|
18
|
+
NOTIFICATION = "notification" # Email, SMS, push notifications
|
|
19
|
+
ANALYTICS = "analytics" # Usage analytics and metrics
|
|
20
|
+
STORAGE = "storage" # File storage and CDN
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class ServiceSpec:
|
|
25
|
+
"""Specification for a single service."""
|
|
26
|
+
|
|
27
|
+
name: str
|
|
28
|
+
type: ServiceType
|
|
29
|
+
description: str
|
|
30
|
+
required_components: list[str] = field(
|
|
31
|
+
default_factory=list
|
|
32
|
+
) # Components this service needs
|
|
33
|
+
recommended_components: list[str] = field(
|
|
34
|
+
default_factory=list
|
|
35
|
+
) # Soft component dependencies
|
|
36
|
+
required_services: list[str] = field(
|
|
37
|
+
default_factory=list
|
|
38
|
+
) # Other services this service needs
|
|
39
|
+
conflicts: list[str] = field(default_factory=list) # Mutual exclusions
|
|
40
|
+
pyproject_deps: list[str] = field(
|
|
41
|
+
default_factory=list
|
|
42
|
+
) # Python packages for this service
|
|
43
|
+
template_files: list[str] = field(default_factory=list) # Template files to include
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# Service registry - single source of truth for all available services
|
|
47
|
+
SERVICES: dict[str, ServiceSpec] = {
|
|
48
|
+
"auth": ServiceSpec(
|
|
49
|
+
name="auth",
|
|
50
|
+
type=ServiceType.AUTH,
|
|
51
|
+
description="User authentication and authorization with JWT tokens",
|
|
52
|
+
required_components=["backend", "database"],
|
|
53
|
+
pyproject_deps=[
|
|
54
|
+
"python-jose[cryptography]==3.3.0",
|
|
55
|
+
"passlib[bcrypt]==1.7.4",
|
|
56
|
+
"python-multipart==0.0.9", # For form data parsing
|
|
57
|
+
],
|
|
58
|
+
template_files=[
|
|
59
|
+
"app/components/backend/api/auth/",
|
|
60
|
+
"app/models/user.py",
|
|
61
|
+
"app/services/auth/",
|
|
62
|
+
"app/core/security.py",
|
|
63
|
+
],
|
|
64
|
+
),
|
|
65
|
+
"ai": ServiceSpec(
|
|
66
|
+
name="ai",
|
|
67
|
+
type=ServiceType.AI,
|
|
68
|
+
description="AI chatbot service with PydanticAI engine",
|
|
69
|
+
required_components=["backend"],
|
|
70
|
+
pyproject_deps=[
|
|
71
|
+
"pydantic-ai-slim[{AI_PROVIDERS}]==1.0.10", # Dynamic providers
|
|
72
|
+
"httpx>=0.27.0", # For API providers
|
|
73
|
+
],
|
|
74
|
+
template_files=[
|
|
75
|
+
"app/services/ai/",
|
|
76
|
+
"app/cli/ai.py",
|
|
77
|
+
"app/components/backend/api/ai/",
|
|
78
|
+
],
|
|
79
|
+
),
|
|
80
|
+
# Future services will be added here:
|
|
81
|
+
# "payment_stripe": ServiceSpec(...),
|
|
82
|
+
# "notification_email": ServiceSpec(...),
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def get_service(name: str) -> ServiceSpec:
|
|
87
|
+
"""Get service specification by name."""
|
|
88
|
+
if name not in SERVICES:
|
|
89
|
+
raise ValueError(f"Unknown service: {name}")
|
|
90
|
+
return SERVICES[name]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def get_services_by_type(service_type: ServiceType) -> dict[str, ServiceSpec]:
|
|
94
|
+
"""Get all services of a specific type."""
|
|
95
|
+
return {name: spec for name, spec in SERVICES.items() if spec.type == service_type}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def list_available_services() -> list[str]:
|
|
99
|
+
"""Get list of all available service names."""
|
|
100
|
+
return list(SERVICES.keys())
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def get_service_dependencies(service_name: str) -> list[str]:
|
|
104
|
+
"""
|
|
105
|
+
Get all required components for a service.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
service_name: Name of the service
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
List of component names required by this service
|
|
112
|
+
"""
|
|
113
|
+
if service_name not in SERVICES:
|
|
114
|
+
return []
|
|
115
|
+
|
|
116
|
+
service = SERVICES[service_name]
|
|
117
|
+
return service.required_components.copy()
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def validate_service_dependencies(
|
|
121
|
+
selected_services: list[str], available_components: list[str]
|
|
122
|
+
) -> list[str]:
|
|
123
|
+
"""
|
|
124
|
+
Validate that all required components are available for selected services.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
selected_services: List of service names to validate
|
|
128
|
+
available_components: List of available component names
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
List of error messages (empty if valid)
|
|
132
|
+
"""
|
|
133
|
+
errors = []
|
|
134
|
+
|
|
135
|
+
for service_name in selected_services:
|
|
136
|
+
if service_name not in SERVICES:
|
|
137
|
+
errors.append(f"Unknown service: {service_name}")
|
|
138
|
+
continue
|
|
139
|
+
|
|
140
|
+
service = SERVICES[service_name]
|
|
141
|
+
|
|
142
|
+
# Check required components
|
|
143
|
+
for required_comp in service.required_components:
|
|
144
|
+
if required_comp not in available_components:
|
|
145
|
+
errors.append(
|
|
146
|
+
f"Service '{service_name}' requires component '{required_comp}'"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# Check service conflicts
|
|
150
|
+
if service.conflicts:
|
|
151
|
+
for conflict in service.conflicts:
|
|
152
|
+
if conflict in selected_services:
|
|
153
|
+
errors.append(
|
|
154
|
+
f"Service '{service_name}' conflicts with service '{conflict}'"
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
return errors
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Template generation and context building for Aegis Stack projects.
|
|
3
|
+
|
|
4
|
+
This module handles the generation of cookiecutter context and manages
|
|
5
|
+
the template rendering process based on selected components.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from .component_utils import extract_base_component_name, extract_engine_info
|
|
12
|
+
from .components import COMPONENTS, CORE_COMPONENTS, SchedulerBackend
|
|
13
|
+
from .services import SERVICES
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TemplateGenerator:
|
|
17
|
+
"""Handles template context generation for cookiecutter."""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
project_name: str,
|
|
22
|
+
selected_components: list[str],
|
|
23
|
+
scheduler_backend: str = SchedulerBackend.MEMORY.value,
|
|
24
|
+
selected_services: list[str] | None = None,
|
|
25
|
+
):
|
|
26
|
+
"""
|
|
27
|
+
Initialize template generator.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
project_name: Name of the project being generated
|
|
31
|
+
selected_components: List of component names to include
|
|
32
|
+
scheduler_backend: Scheduler backend: memory, sqlite, or postgres
|
|
33
|
+
selected_services: List of service names to include
|
|
34
|
+
"""
|
|
35
|
+
self.project_name = project_name
|
|
36
|
+
self.project_slug = project_name.lower().replace(" ", "-").replace("_", "-")
|
|
37
|
+
self.scheduler_backend = scheduler_backend
|
|
38
|
+
self.selected_services = selected_services or []
|
|
39
|
+
|
|
40
|
+
# Always include core components
|
|
41
|
+
all_components = CORE_COMPONENTS + selected_components
|
|
42
|
+
|
|
43
|
+
# Add required components from selected services
|
|
44
|
+
for service_name in self.selected_services:
|
|
45
|
+
if service_name in SERVICES:
|
|
46
|
+
service_spec = SERVICES[service_name]
|
|
47
|
+
all_components.extend(service_spec.required_components)
|
|
48
|
+
|
|
49
|
+
# Remove duplicates, preserve order
|
|
50
|
+
self.components = list(dict.fromkeys(all_components))
|
|
51
|
+
|
|
52
|
+
# Extract database engine from database[engine] format for template context
|
|
53
|
+
self.database_engine = None
|
|
54
|
+
for component in self.components:
|
|
55
|
+
if extract_base_component_name(component) == "database":
|
|
56
|
+
self.database_engine = extract_engine_info(component)
|
|
57
|
+
if self.database_engine:
|
|
58
|
+
break
|
|
59
|
+
|
|
60
|
+
# Extract scheduler backend from scheduler[backend] format or use passed param
|
|
61
|
+
# If scheduler[backend] syntax is used, it overrides the passed parameter
|
|
62
|
+
for component in self.components:
|
|
63
|
+
if extract_base_component_name(component) == "scheduler":
|
|
64
|
+
backend = extract_engine_info(component)
|
|
65
|
+
if backend:
|
|
66
|
+
self.scheduler_backend = backend
|
|
67
|
+
break
|
|
68
|
+
|
|
69
|
+
# Build component specs using base names
|
|
70
|
+
self.component_specs = {}
|
|
71
|
+
for name in self.components:
|
|
72
|
+
base_name = extract_base_component_name(name)
|
|
73
|
+
if base_name in COMPONENTS:
|
|
74
|
+
self.component_specs[base_name] = COMPONENTS[base_name]
|
|
75
|
+
|
|
76
|
+
def get_template_context(self) -> dict[str, Any]:
|
|
77
|
+
"""
|
|
78
|
+
Generate cookiecutter context from components.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Dictionary containing all template variables
|
|
82
|
+
"""
|
|
83
|
+
# Store the originally selected components (without core)
|
|
84
|
+
selected_only = [c for c in self.components if c not in CORE_COMPONENTS]
|
|
85
|
+
|
|
86
|
+
# Check for components using base names
|
|
87
|
+
has_database = any(c.startswith("database") for c in self.components)
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
"project_name": self.project_name,
|
|
91
|
+
"project_slug": self.project_slug,
|
|
92
|
+
# Component flags for template conditionals - cookiecutter needs yes/no
|
|
93
|
+
"include_redis": "yes" if "redis" in self.components else "no",
|
|
94
|
+
"include_worker": "yes" if "worker" in self.components else "no",
|
|
95
|
+
"include_scheduler": "yes"
|
|
96
|
+
if any(c.startswith("scheduler") for c in self.components)
|
|
97
|
+
else "no",
|
|
98
|
+
"include_database": "yes" if has_database else "no",
|
|
99
|
+
# Database engine selection
|
|
100
|
+
"database_engine": self.database_engine or "sqlite",
|
|
101
|
+
# Scheduler backend selection
|
|
102
|
+
"scheduler_backend": self.scheduler_backend,
|
|
103
|
+
# Legacy scheduler persistence flag for backwards compatibility
|
|
104
|
+
"scheduler_with_persistence": (
|
|
105
|
+
"yes"
|
|
106
|
+
if self.scheduler_backend != SchedulerBackend.MEMORY.value
|
|
107
|
+
else "no"
|
|
108
|
+
),
|
|
109
|
+
# Derived flags for template logic
|
|
110
|
+
"has_background_infrastructure": any(
|
|
111
|
+
name in self.components for name in ["worker", "scheduler"]
|
|
112
|
+
),
|
|
113
|
+
"needs_redis": "redis" in self.components,
|
|
114
|
+
# Service flags for template conditionals
|
|
115
|
+
"include_auth": "yes" if "auth" in self.selected_services else "no",
|
|
116
|
+
"include_ai": "yes" if "ai" in self.selected_services else "no",
|
|
117
|
+
# AI provider selection for dynamic dependency generation
|
|
118
|
+
"ai_providers": self._get_ai_providers_string(),
|
|
119
|
+
# Dependency lists for templates
|
|
120
|
+
"selected_components": selected_only, # Original selection for context
|
|
121
|
+
"docker_services": self._get_docker_services(),
|
|
122
|
+
"pyproject_dependencies": self._get_pyproject_deps(),
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
def _get_docker_services(self) -> list[str]:
|
|
126
|
+
"""
|
|
127
|
+
Collect all docker services needed.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
List of docker service names
|
|
131
|
+
"""
|
|
132
|
+
services = []
|
|
133
|
+
for component_name in self.components:
|
|
134
|
+
if component_name in self.component_specs:
|
|
135
|
+
spec = self.component_specs[component_name]
|
|
136
|
+
if spec.docker_services:
|
|
137
|
+
services.extend(spec.docker_services)
|
|
138
|
+
return list(dict.fromkeys(services)) # Preserve order, remove duplicates
|
|
139
|
+
|
|
140
|
+
def _get_pyproject_deps(self) -> list[str]:
|
|
141
|
+
"""
|
|
142
|
+
Collect all Python dependencies.
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Sorted list of Python package dependencies
|
|
146
|
+
"""
|
|
147
|
+
deps = []
|
|
148
|
+
# Collect component dependencies
|
|
149
|
+
for component_name in self.components:
|
|
150
|
+
if component_name in self.component_specs:
|
|
151
|
+
spec = self.component_specs[component_name]
|
|
152
|
+
if spec.pyproject_deps:
|
|
153
|
+
deps.extend(spec.pyproject_deps)
|
|
154
|
+
|
|
155
|
+
# Collect service dependencies
|
|
156
|
+
for service_name in self.selected_services:
|
|
157
|
+
if service_name in SERVICES:
|
|
158
|
+
service_spec = SERVICES[service_name]
|
|
159
|
+
if service_spec.pyproject_deps:
|
|
160
|
+
# Process service dependencies with dynamic substitution
|
|
161
|
+
for dep in service_spec.pyproject_deps:
|
|
162
|
+
if service_name == "ai" and "{AI_PROVIDERS}" in dep:
|
|
163
|
+
# Substitute AI providers dynamically
|
|
164
|
+
providers = self._get_ai_providers_string()
|
|
165
|
+
dep = dep.replace("{AI_PROVIDERS}", providers)
|
|
166
|
+
deps.append(dep)
|
|
167
|
+
|
|
168
|
+
return sorted(set(deps)) # Sort and deduplicate
|
|
169
|
+
|
|
170
|
+
def get_template_files(self) -> list[str]:
|
|
171
|
+
"""
|
|
172
|
+
Get list of template files that should be included.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
List of template file paths
|
|
176
|
+
"""
|
|
177
|
+
files = []
|
|
178
|
+
# Collect component template files
|
|
179
|
+
for component_name in self.components:
|
|
180
|
+
base_name = extract_base_component_name(component_name)
|
|
181
|
+
if base_name in self.component_specs:
|
|
182
|
+
spec = self.component_specs[base_name]
|
|
183
|
+
if spec.template_files:
|
|
184
|
+
files.extend(spec.template_files)
|
|
185
|
+
|
|
186
|
+
# Collect service template files
|
|
187
|
+
for service_name in self.selected_services:
|
|
188
|
+
if service_name in SERVICES:
|
|
189
|
+
service_spec = SERVICES[service_name]
|
|
190
|
+
if service_spec.template_files:
|
|
191
|
+
files.extend(service_spec.template_files)
|
|
192
|
+
|
|
193
|
+
return list(dict.fromkeys(files)) # Preserve order, remove duplicates
|
|
194
|
+
|
|
195
|
+
def _get_ai_providers_string(self) -> str:
|
|
196
|
+
"""
|
|
197
|
+
Get AI providers as comma-separated string for pydantic-ai-slim dependency.
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
Comma-separated string of provider names (e.g., "openai,anthropic,google")
|
|
201
|
+
"""
|
|
202
|
+
if "ai" not in self.selected_services:
|
|
203
|
+
return "openai" # Default for PUBLIC provider
|
|
204
|
+
|
|
205
|
+
# Import here to avoid circular imports
|
|
206
|
+
from ..cli.interactive import get_ai_provider_selection
|
|
207
|
+
|
|
208
|
+
providers = get_ai_provider_selection("ai")
|
|
209
|
+
return ",".join(providers)
|
|
210
|
+
|
|
211
|
+
def get_entrypoints(self) -> list[str]:
|
|
212
|
+
"""
|
|
213
|
+
Get list of entrypoints that will be created.
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
List of entrypoint file paths
|
|
217
|
+
"""
|
|
218
|
+
entrypoints = ["app/entrypoints/webserver.py"] # Always included
|
|
219
|
+
|
|
220
|
+
# Check component specs for actual entrypoint files
|
|
221
|
+
for component_name in self.components:
|
|
222
|
+
base_name = extract_base_component_name(component_name)
|
|
223
|
+
if base_name in self.component_specs:
|
|
224
|
+
spec = self.component_specs[base_name]
|
|
225
|
+
if spec.template_files:
|
|
226
|
+
for template_file in spec.template_files:
|
|
227
|
+
if (
|
|
228
|
+
template_file.startswith("app/entrypoints/")
|
|
229
|
+
and template_file not in entrypoints
|
|
230
|
+
):
|
|
231
|
+
entrypoints.append(template_file)
|
|
232
|
+
|
|
233
|
+
return entrypoints
|
|
234
|
+
|
|
235
|
+
def get_worker_queues(self) -> list[str]:
|
|
236
|
+
"""
|
|
237
|
+
Get list of worker queue files that will be created.
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
List of worker queue file paths
|
|
241
|
+
"""
|
|
242
|
+
queues: list[str] = []
|
|
243
|
+
|
|
244
|
+
# Only check if worker component is included
|
|
245
|
+
if not any(c.startswith("worker") for c in self.components):
|
|
246
|
+
return queues
|
|
247
|
+
|
|
248
|
+
# Discover queue files from the template directory
|
|
249
|
+
template_root = (
|
|
250
|
+
Path(__file__).parent.parent / "templates" / "cookiecutter-aegis-project"
|
|
251
|
+
)
|
|
252
|
+
worker_queues_dir = (
|
|
253
|
+
template_root
|
|
254
|
+
/ "{{cookiecutter.project_slug}}"
|
|
255
|
+
/ "app"
|
|
256
|
+
/ "components"
|
|
257
|
+
/ "worker"
|
|
258
|
+
/ "queues"
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
if worker_queues_dir.exists():
|
|
262
|
+
for queue_file in worker_queues_dir.glob("*.py"):
|
|
263
|
+
if queue_file.stem != "__init__":
|
|
264
|
+
queues.append(f"app/components/worker/queues/{queue_file.name}")
|
|
265
|
+
|
|
266
|
+
return sorted(queues)
|