voodoo-framework 2.4.0__tar.gz → 2.5.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {voodoo_framework-2.4.0/src/voodoo_framework.egg-info → voodoo_framework-2.5.1}/PKG-INFO +6 -4
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/README.md +5 -3
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/__init__.py +1 -1
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/__init__.py +2 -0
- voodoo_framework-2.5.1/src/voodoo/cli/create.py +361 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/dev.py +54 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/protocol/schemas.py +81 -15
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/runtime/execution.py +53 -1
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/execution/sqlite.py +5 -1
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1/src/voodoo_framework.egg-info}/PKG-INFO +6 -4
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo_framework.egg-info/SOURCES.txt +2 -0
- voodoo_framework-2.5.1/tests/test_create.py +150 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/LICENSE +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/pyproject.toml +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/setup.cfg +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/adapters/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/adapters/capabilities.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/adapters/registry.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/adapters/tailwind.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/adapters/voodoo_css.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/agent.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/agents/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/agents/models.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/agents/registry.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/ai/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/ai/agent.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/ai/providers/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/ai/providers/anthropic.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/ai/providers/gemini.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/ai/providers/mock.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/ai/providers/ollama.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/ai/providers/openai.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/ai/tools/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/ai/tools/registry.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/api.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/auth/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/auth/cookies.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/auth/guards.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/auth/jwt.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/auth/middleware.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/auth/passwords.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/auth/user.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/__main__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/agents.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/ai.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/approvals.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/auth.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/doctor.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/executions.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/generate.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/inspect.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/new.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/objects.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/protocol.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/recover.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/routes.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/scaffolding.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/schedules.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/status.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/tasks.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/terminal.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/theme.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/version.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/cli/workers_cmd.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/components.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/config.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/core/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/core/app.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/core/errors.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/core/events.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/core/render.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/core/routing.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/core/sitemap.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/core/state.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/data/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/data/base.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/data/model.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/i18n.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/mcp/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/mcp/client.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/memory/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/memory/interfaces.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/memory/sqlite.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/mesh/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/mesh/client.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/mesh/event_bus.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/primitives/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/primitives/capability.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/primitives/compute.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/primitives/constraint.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/primitives/effect.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/primitives/intent.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/primitives/resource.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/primitives/state.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/primitives/time.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/protocol/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/protocol/export.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/py.typed +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/queue.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/routing/api.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/routing/pages.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/runtime/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/runtime/adaptive.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/runtime/capability.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/runtime/constraint.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/runtime/context.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/runtime/engine.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/runtime/errors.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/runtime/graph.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/runtime/human.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/runtime/persistence.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/runtime/planner.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/runtime/scheduler.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/runtime/task.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/runtime/workflow.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/schedule.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/security/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/security/cors.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/security/csrf.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/security/headers.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/security/passwords.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/security/rate_limit.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/security/redaction.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/security/secrets.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/seo.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/static/client.js +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/status.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/cache/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/cache/interfaces.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/cache/memory.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/cache/redis.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/database/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/database/interfaces.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/database/postgres.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/database/sqlite.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/events/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/events/interfaces.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/events/local.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/events/postgres.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/events/sqlite.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/execution/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/execution/migrations.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/execution/postgres.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/manager.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/objects/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/objects/interfaces.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/objects/local.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/objects/s3.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/queue/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/queue/interfaces.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/queue/memory.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/queue/postgres.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/queue/redis.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/queue/sqlite.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/scheduler/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/storage/scheduler/sqlite.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/telemetry/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/telemetry/api.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/telemetry/middleware.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/telemetry/otlp.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/telemetry/store.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/theme.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/tools/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/tools/registry.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/ui/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/ui/component.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/ui/events.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/ui/library.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/ui/markdown.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/ui/rendering.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/ui/state.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/ui/styles/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/ui/styles/presets.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/ui/styles/theme.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/workers/__init__.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo/workers/queue.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo_framework.egg-info/dependency_links.txt +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo_framework.egg-info/entry_points.txt +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo_framework.egg-info/requires.txt +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/src/voodoo_framework.egg-info/top_level.txt +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_agent.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_agent_registry.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_agent_runtime.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_app.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_auth.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_capability_security.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_checkpoint_resume.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_chrome.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_cli.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_cli_inspect.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_components.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_config.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_contract_api.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_data.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_design_system.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_durable_hitl.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_events.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_execution_sqlite_store.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_http_runtime.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_human.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_i18n.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_integration.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_mcp.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_mcp_runtime.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_memory.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_mesh.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_mesh_event_bus.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_model.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_object_store.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_observability.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_persistence.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_planner_adaptive.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_primitives.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_protocol.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_provider_switching.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_providers.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_queue.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_reactive.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_runtime.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_scheduler.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_security.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_seo.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_state.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_theme.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_theme_presets.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_tools.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_ui.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_websocket.py +0 -0
- {voodoo_framework-2.4.0 → voodoo_framework-2.5.1}/tests/test_workers.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: voodoo-framework
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5.1
|
|
4
4
|
Summary: The programmable runtime for adaptive applications and operational systems — web, APIs, agents, workers, human workflows, distributed systems, and physical systems in one runtime.
|
|
5
5
|
Author-email: Helder Perez <contact@helderperez.com>
|
|
6
6
|
Maintainer-email: Helder Perez <contact@helderperez.com>
|
|
@@ -119,14 +119,16 @@ Voodoo is **not** a wrapper around other frameworks. It's a unified runtime wher
|
|
|
119
119
|
|
|
120
120
|
```bash
|
|
121
121
|
pip install voodoo-framework
|
|
122
|
-
voodoo
|
|
122
|
+
voodoo create my_app
|
|
123
123
|
cd my_app
|
|
124
124
|
voodoo dev
|
|
125
125
|
```
|
|
126
126
|
|
|
127
127
|
Open `http://localhost:8000` — that's it. No npm, no bundler, no config files.
|
|
128
128
|
|
|
129
|
-
|
|
129
|
+
`voodoo create` scaffolds a full local runtime app with durable tasks, scheduler, events, and agent runtime — all on zero infrastructure. The generated app includes a crash/restart demo proving durability.
|
|
130
|
+
|
|
131
|
+
> **Want a minimal scaffold?** Use `voodoo new` instead for a bare-bones app with just UI routing.
|
|
130
132
|
|
|
131
133
|
> **Want AI features?** Install with `pip install "voodoo-framework[ai]"` to add OpenAI, Anthropic, Gemini, and Ollama SDKs.
|
|
132
134
|
|
|
@@ -232,7 +234,7 @@ if __name__ == "__main__":
|
|
|
232
234
|
| **Human-in-the-Loop** | `ask_human()` + `approve()`/`deny()` — humans as compute participants, not afterthoughts |
|
|
233
235
|
| **Adaptive execution** | Planner resolves capabilities to compute participants; supervisor steers with retry, fallback, budget control |
|
|
234
236
|
| **Durable by default** | Tasks, executions, schedules, and events survive process restarts — backed by SQLite out of the box |
|
|
235
|
-
| **Zero-config runtime** | `voodoo
|
|
237
|
+
| **Zero-config runtime** | `voodoo create` → `voodoo dev` → working app. No build step. Add `voodoo.toml` when you need configuration |
|
|
236
238
|
| **Local-first, cloud-capable** | SQLite by default; PostgreSQL, Redis, and S3/R2 are optional adapters behind the same contracts |
|
|
237
239
|
|
|
238
240
|
## The Computational Model
|
|
@@ -52,14 +52,16 @@ Voodoo is **not** a wrapper around other frameworks. It's a unified runtime wher
|
|
|
52
52
|
|
|
53
53
|
```bash
|
|
54
54
|
pip install voodoo-framework
|
|
55
|
-
voodoo
|
|
55
|
+
voodoo create my_app
|
|
56
56
|
cd my_app
|
|
57
57
|
voodoo dev
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
Open `http://localhost:8000` — that's it. No npm, no bundler, no config files.
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
`voodoo create` scaffolds a full local runtime app with durable tasks, scheduler, events, and agent runtime — all on zero infrastructure. The generated app includes a crash/restart demo proving durability.
|
|
63
|
+
|
|
64
|
+
> **Want a minimal scaffold?** Use `voodoo new` instead for a bare-bones app with just UI routing.
|
|
63
65
|
|
|
64
66
|
> **Want AI features?** Install with `pip install "voodoo-framework[ai]"` to add OpenAI, Anthropic, Gemini, and Ollama SDKs.
|
|
65
67
|
|
|
@@ -165,7 +167,7 @@ if __name__ == "__main__":
|
|
|
165
167
|
| **Human-in-the-Loop** | `ask_human()` + `approve()`/`deny()` — humans as compute participants, not afterthoughts |
|
|
166
168
|
| **Adaptive execution** | Planner resolves capabilities to compute participants; supervisor steers with retry, fallback, budget control |
|
|
167
169
|
| **Durable by default** | Tasks, executions, schedules, and events survive process restarts — backed by SQLite out of the box |
|
|
168
|
-
| **Zero-config runtime** | `voodoo
|
|
170
|
+
| **Zero-config runtime** | `voodoo create` → `voodoo dev` → working app. No build step. Add `voodoo.toml` when you need configuration |
|
|
169
171
|
| **Local-first, cloud-capable** | SQLite by default; PostgreSQL, Redis, and S3/R2 are optional adapters behind the same contracts |
|
|
170
172
|
|
|
171
173
|
## The Computational Model
|
|
@@ -4,6 +4,7 @@ from voodoo.cli import (
|
|
|
4
4
|
agents,
|
|
5
5
|
ai,
|
|
6
6
|
approvals,
|
|
7
|
+
create,
|
|
7
8
|
dev,
|
|
8
9
|
doctor,
|
|
9
10
|
executions,
|
|
@@ -32,6 +33,7 @@ app = typer.Typer(
|
|
|
32
33
|
)
|
|
33
34
|
|
|
34
35
|
app.command()(new.new)
|
|
36
|
+
app.command()(create.create)
|
|
35
37
|
app.command()(dev.dev)
|
|
36
38
|
app.command()(generate.generate)
|
|
37
39
|
app.add_typer(auth_app, name="auth")
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
"""CLI: ``voodoo create <app>`` — scaffold a full local runtime app.
|
|
2
|
+
|
|
3
|
+
Sprint 22: Local runtime DX. Evolves ``voodoo new`` to wire up the full
|
|
4
|
+
local runtime (durable tasks, scheduler, events, object store, agent runtime)
|
|
5
|
+
with a crash/restart demo proving durability.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import subprocess
|
|
11
|
+
import sys
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
import typer
|
|
15
|
+
|
|
16
|
+
from voodoo.cli import terminal
|
|
17
|
+
|
|
18
|
+
__all__ = ["create"]
|
|
19
|
+
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
# Template: main.py — full local runtime
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
_MAIN_PY = '''"""{name} — a Voodoo autonomous app.
|
|
25
|
+
|
|
26
|
+
This app demonstrates the full local runtime:
|
|
27
|
+
- Durable tasks (queue + workers) that survive restarts
|
|
28
|
+
- Scheduler for recurring jobs
|
|
29
|
+
- Mesh events for real-time communication
|
|
30
|
+
- Agent runtime with mock provider (no network needed)
|
|
31
|
+
|
|
32
|
+
Run: python main.py or voodoo dev
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
from voodoo import (
|
|
36
|
+
Agent,
|
|
37
|
+
App,
|
|
38
|
+
Button,
|
|
39
|
+
Card,
|
|
40
|
+
Container,
|
|
41
|
+
Heading,
|
|
42
|
+
Stack,
|
|
43
|
+
Text,
|
|
44
|
+
event,
|
|
45
|
+
page,
|
|
46
|
+
state,
|
|
47
|
+
tool,
|
|
48
|
+
)
|
|
49
|
+
from voodoo.workers.queue import enqueue, queue
|
|
50
|
+
|
|
51
|
+
app = App()
|
|
52
|
+
|
|
53
|
+
# ---------------------------------------------------------------------------
|
|
54
|
+
# State
|
|
55
|
+
# ---------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
counter = state("counter", 0)
|
|
58
|
+
log_lines = state("log_lines", [])
|
|
59
|
+
agent_result = state("agent_result", "Click to run the agent")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _log(msg: str) -> None:
|
|
63
|
+
"""Append a timestamped line to the on-screen log."""
|
|
64
|
+
from datetime import datetime
|
|
65
|
+
|
|
66
|
+
ts = datetime.now().strftime("%H:%M:%S")
|
|
67
|
+
lines = log_lines.get()
|
|
68
|
+
lines.append(f"[{{ts}}] {{msg}}")
|
|
69
|
+
# Keep last 20 lines
|
|
70
|
+
log_lines.set(lines[-20:])
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# ---------------------------------------------------------------------------
|
|
74
|
+
# Durable task — survives restarts
|
|
75
|
+
# ---------------------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
@queue("increment")
|
|
78
|
+
async def increment_worker(payload: dict) -> None:
|
|
79
|
+
"""Durable worker: increments the counter and logs the result."""
|
|
80
|
+
n = counter.get() + 1
|
|
81
|
+
counter.set(n)
|
|
82
|
+
_log(f"increment task done → counter = {{n}}")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# ---------------------------------------------------------------------------
|
|
86
|
+
# Agent (mock provider — no network needed)
|
|
87
|
+
# ---------------------------------------------------------------------------
|
|
88
|
+
|
|
89
|
+
@tool
|
|
90
|
+
async def get_counter() -> str:
|
|
91
|
+
"""Return the current counter value."""
|
|
92
|
+
return str(counter.get())
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
agent = Agent(
|
|
96
|
+
model="mock:test",
|
|
97
|
+
tools=["get_counter"],
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# ---------------------------------------------------------------------------
|
|
102
|
+
# Routes
|
|
103
|
+
# ---------------------------------------------------------------------------
|
|
104
|
+
|
|
105
|
+
@page("/")
|
|
106
|
+
def home():
|
|
107
|
+
return Container(
|
|
108
|
+
Stack(
|
|
109
|
+
Heading("{name}", level=1),
|
|
110
|
+
Text(
|
|
111
|
+
"A Voodoo autonomous app — durable tasks, scheduler, "
|
|
112
|
+
"events, and agents, all running on zero infrastructure.",
|
|
113
|
+
tone="muted",
|
|
114
|
+
),
|
|
115
|
+
_status_card(),
|
|
116
|
+
_task_card(),
|
|
117
|
+
_agent_card(),
|
|
118
|
+
_log_card(),
|
|
119
|
+
gap="lg",
|
|
120
|
+
)
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _status_card():
|
|
125
|
+
return Card(
|
|
126
|
+
Stack(
|
|
127
|
+
Heading("Runtime", level=2),
|
|
128
|
+
Text(f"Counter: {{counter.get()}}"),
|
|
129
|
+
Text(f"Log entries: {{len(log_lines.get())}}"),
|
|
130
|
+
gap="sm",
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _task_card():
|
|
136
|
+
return Card(
|
|
137
|
+
Stack(
|
|
138
|
+
Heading("Durable Tasks", level=2),
|
|
139
|
+
Text(
|
|
140
|
+
"Enqueue a durable task. It survives restarts — "
|
|
141
|
+
"stop the server, start it again, and watch the counter increment.",
|
|
142
|
+
tone="muted",
|
|
143
|
+
),
|
|
144
|
+
Button(
|
|
145
|
+
"Enqueue increment",
|
|
146
|
+
onclick="vd.event('enqueue_task')",
|
|
147
|
+
variant="primary",
|
|
148
|
+
),
|
|
149
|
+
gap="sm",
|
|
150
|
+
)
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _agent_card():
|
|
155
|
+
return Card(
|
|
156
|
+
Stack(
|
|
157
|
+
Heading("Agent", level=2),
|
|
158
|
+
Text(f"Result: {{agent_result.get()}}"),
|
|
159
|
+
Button(
|
|
160
|
+
"Run agent",
|
|
161
|
+
onclick="vd.event('run_agent')",
|
|
162
|
+
variant="secondary",
|
|
163
|
+
),
|
|
164
|
+
gap="sm",
|
|
165
|
+
)
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _log_card():
|
|
170
|
+
return Card(
|
|
171
|
+
Stack(
|
|
172
|
+
Heading("Event Log", level=2),
|
|
173
|
+
*[Text(line, tone="muted") for line in log_lines.get()],
|
|
174
|
+
gap="xs",
|
|
175
|
+
)
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
# ---------------------------------------------------------------------------
|
|
180
|
+
# Events
|
|
181
|
+
# ---------------------------------------------------------------------------
|
|
182
|
+
|
|
183
|
+
@event
|
|
184
|
+
async def enqueue_task(element_id, value):
|
|
185
|
+
"""Enqueue a durable increment task."""
|
|
186
|
+
await enqueue("increment", {{"source": "ui"}})
|
|
187
|
+
_log("enqueued increment task")
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
@event
|
|
191
|
+
async def run_agent(element_id, value):
|
|
192
|
+
"""Run the agent and display the result."""
|
|
193
|
+
_log("running agent...")
|
|
194
|
+
run = await agent.run("What is the current counter value?")
|
|
195
|
+
agent_result.set(run.output)
|
|
196
|
+
_log(f"agent done: {{run.output}}")
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
# ---------------------------------------------------------------------------
|
|
200
|
+
# Startup — enqueue a demo task on first boot
|
|
201
|
+
# ---------------------------------------------------------------------------
|
|
202
|
+
|
|
203
|
+
@app.on_startup
|
|
204
|
+
async def on_startup():
|
|
205
|
+
"""On first boot, enqueue a demo task to prove durability."""
|
|
206
|
+
import os
|
|
207
|
+
|
|
208
|
+
marker = ".voodoo/state/.booted"
|
|
209
|
+
if not os.path.exists(marker):
|
|
210
|
+
os.makedirs(os.path.dirname(marker), exist_ok=True)
|
|
211
|
+
with open(marker, "w") as f:
|
|
212
|
+
f.write("booted")
|
|
213
|
+
await enqueue("increment", {{"source": "startup"}})
|
|
214
|
+
_log("first boot — enqueued demo task")
|
|
215
|
+
else:
|
|
216
|
+
_log("restart detected — pending tasks will be recovered")
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
if __name__ == "__main__":
|
|
220
|
+
app.run()
|
|
221
|
+
'''
|
|
222
|
+
|
|
223
|
+
# ---------------------------------------------------------------------------
|
|
224
|
+
# Template: voodoo.toml
|
|
225
|
+
# ---------------------------------------------------------------------------
|
|
226
|
+
|
|
227
|
+
_VOODOO_TOML = """[app]
|
|
228
|
+
name = "{name}"
|
|
229
|
+
|
|
230
|
+
# Local-first defaults — zero infrastructure required.
|
|
231
|
+
# The runtime uses SQLite, local filesystem, and in-memory queues by default.
|
|
232
|
+
# Override with env vars or this config for production:
|
|
233
|
+
# VOODOO_DATABASE_URL=postgresql://...
|
|
234
|
+
# VOODOO_QUEUE_PROVIDER=redis
|
|
235
|
+
# VOODOO_OBJECT_STORE_PROVIDER=s3
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
# ---------------------------------------------------------------------------
|
|
239
|
+
# Template: pyproject.toml
|
|
240
|
+
# ---------------------------------------------------------------------------
|
|
241
|
+
|
|
242
|
+
_PYPROJECT_TOML = """[project]
|
|
243
|
+
name = "{name}"
|
|
244
|
+
version = "0.1.0"
|
|
245
|
+
requires-python = ">=3.12"
|
|
246
|
+
dependencies = [
|
|
247
|
+
"voodoo-framework",
|
|
248
|
+
]
|
|
249
|
+
"""
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
# ---------------------------------------------------------------------------
|
|
253
|
+
# Command
|
|
254
|
+
# ---------------------------------------------------------------------------
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def create(
|
|
258
|
+
project_name: str = typer.Argument(..., help="Name of the project to create"),
|
|
259
|
+
) -> None:
|
|
260
|
+
"""Scaffold a full local runtime app with durable tasks, scheduler, events, and agents.
|
|
261
|
+
|
|
262
|
+
The generated app runs on zero infrastructure (SQLite + local filesystem)
|
|
263
|
+
and includes a crash/restart demo proving durability.
|
|
264
|
+
"""
|
|
265
|
+
project_dir = Path(project_name)
|
|
266
|
+
if project_dir.exists():
|
|
267
|
+
terminal.error(f"Directory '{project_name}' already exists")
|
|
268
|
+
raise typer.Exit(1)
|
|
269
|
+
|
|
270
|
+
terminal.wordmark()
|
|
271
|
+
terminal.blank()
|
|
272
|
+
terminal.status("creating", project_name)
|
|
273
|
+
terminal.blank()
|
|
274
|
+
|
|
275
|
+
# Create directory structure
|
|
276
|
+
project_dir.mkdir(parents=True)
|
|
277
|
+
(project_dir / "app").mkdir()
|
|
278
|
+
(project_dir / ".voodoo" / "state").mkdir(parents=True)
|
|
279
|
+
|
|
280
|
+
# Write files
|
|
281
|
+
(project_dir / "main.py").write_text(_MAIN_PY.format(name=project_name))
|
|
282
|
+
(project_dir / "voodoo.toml").write_text(_VOODOO_TOML.format(name=project_name))
|
|
283
|
+
(project_dir / "pyproject.toml").write_text(
|
|
284
|
+
_PYPROJECT_TOML.format(name=project_name)
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
terminal.label_value("created", f"{project_name}/")
|
|
288
|
+
terminal.tree(
|
|
289
|
+
[
|
|
290
|
+
"main.py # App entry point — full runtime demo",
|
|
291
|
+
"voodoo.toml # Runtime configuration",
|
|
292
|
+
"pyproject.toml # Python project metadata",
|
|
293
|
+
"app/ # Folder-based routes (add page.py files here)",
|
|
294
|
+
".voodoo/state/ # Local state (SQLite, queue, schedules)",
|
|
295
|
+
]
|
|
296
|
+
)
|
|
297
|
+
terminal.blank()
|
|
298
|
+
|
|
299
|
+
# Install dependencies
|
|
300
|
+
terminal.status("installing", "dependencies")
|
|
301
|
+
terminal.blank()
|
|
302
|
+
|
|
303
|
+
local_venv = project_dir / ".venv"
|
|
304
|
+
try:
|
|
305
|
+
subprocess.run(
|
|
306
|
+
[sys.executable, "-m", "uv", "venv", str(local_venv)],
|
|
307
|
+
check=True,
|
|
308
|
+
capture_output=True,
|
|
309
|
+
)
|
|
310
|
+
subprocess.run(
|
|
311
|
+
[
|
|
312
|
+
str(local_venv / "bin" / "python"),
|
|
313
|
+
"-m",
|
|
314
|
+
"uv",
|
|
315
|
+
"pip",
|
|
316
|
+
"install",
|
|
317
|
+
"-e",
|
|
318
|
+
".",
|
|
319
|
+
],
|
|
320
|
+
cwd=str(project_dir),
|
|
321
|
+
check=True,
|
|
322
|
+
capture_output=True,
|
|
323
|
+
)
|
|
324
|
+
terminal.muted("installed via uv")
|
|
325
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
326
|
+
# Fallback to plain venv + pip
|
|
327
|
+
try:
|
|
328
|
+
subprocess.run(
|
|
329
|
+
[sys.executable, "-m", "venv", str(local_venv)],
|
|
330
|
+
check=True,
|
|
331
|
+
capture_output=True,
|
|
332
|
+
)
|
|
333
|
+
subprocess.run(
|
|
334
|
+
[
|
|
335
|
+
str(local_venv / "bin" / "pip"),
|
|
336
|
+
"install",
|
|
337
|
+
"-e",
|
|
338
|
+
".",
|
|
339
|
+
],
|
|
340
|
+
cwd=str(project_dir),
|
|
341
|
+
check=True,
|
|
342
|
+
capture_output=True,
|
|
343
|
+
)
|
|
344
|
+
terminal.muted("installed via pip")
|
|
345
|
+
except subprocess.CalledProcessError as exc:
|
|
346
|
+
terminal.warning(f"dependency install failed: {exc}")
|
|
347
|
+
terminal.muted("run 'cd {project_name} && pip install -e .' manually")
|
|
348
|
+
|
|
349
|
+
terminal.blank()
|
|
350
|
+
terminal.success("ready")
|
|
351
|
+
terminal.blank()
|
|
352
|
+
terminal.next_steps(
|
|
353
|
+
[
|
|
354
|
+
f"cd {project_name}",
|
|
355
|
+
"voodoo dev",
|
|
356
|
+
"",
|
|
357
|
+
"Then open http://localhost:8000 and click 'Enqueue increment'.",
|
|
358
|
+
"Stop the server (Ctrl+C), run 'voodoo dev' again — the counter",
|
|
359
|
+
"increments because the task is durable.",
|
|
360
|
+
]
|
|
361
|
+
)
|
|
@@ -70,6 +70,9 @@ def dev(
|
|
|
70
70
|
terminal.label_value("docs", f"{local_url}/docs")
|
|
71
71
|
terminal.blank()
|
|
72
72
|
|
|
73
|
+
# Sprint 22: runtime banner — show providers and runtime state
|
|
74
|
+
_print_runtime_banner()
|
|
75
|
+
|
|
73
76
|
local_venv_python = (
|
|
74
77
|
Path(".venv/bin/python")
|
|
75
78
|
if os.name != "nt"
|
|
@@ -117,3 +120,54 @@ def dev(
|
|
|
117
120
|
except KeyboardInterrupt:
|
|
118
121
|
terminal.blank()
|
|
119
122
|
terminal.status("runtime", "stopped")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _print_runtime_banner() -> None:
|
|
126
|
+
"""Print runtime providers and state (Sprint 22)."""
|
|
127
|
+
from voodoo.config import get_config
|
|
128
|
+
|
|
129
|
+
cfg = get_config()
|
|
130
|
+
|
|
131
|
+
# Providers
|
|
132
|
+
terminal.heading("providers")
|
|
133
|
+
db_provider = cfg.database.provider.lower()
|
|
134
|
+
terminal.status("database", db_provider)
|
|
135
|
+
|
|
136
|
+
queue_provider = cfg.queue.provider.lower()
|
|
137
|
+
terminal.status("queue", queue_provider)
|
|
138
|
+
|
|
139
|
+
# Object store
|
|
140
|
+
terminal.status("objects", cfg.objects.provider.lower())
|
|
141
|
+
|
|
142
|
+
# Agent runtime
|
|
143
|
+
try:
|
|
144
|
+
from voodoo.ai.providers import _PROVIDER_CLASSES
|
|
145
|
+
|
|
146
|
+
providers = list(_PROVIDER_CLASSES.keys())
|
|
147
|
+
terminal.status("agents", ", ".join(providers) if providers else "none")
|
|
148
|
+
except Exception:
|
|
149
|
+
terminal.status("agents", "mock")
|
|
150
|
+
|
|
151
|
+
# MCP endpoint
|
|
152
|
+
terminal.label_value("mcp", "/mcp")
|
|
153
|
+
|
|
154
|
+
# Registered workers
|
|
155
|
+
try:
|
|
156
|
+
from voodoo.workers.queue import _workers
|
|
157
|
+
|
|
158
|
+
worker_names = list(_workers.keys())
|
|
159
|
+
if worker_names:
|
|
160
|
+
terminal.label_value("workers", ", ".join(worker_names))
|
|
161
|
+
else:
|
|
162
|
+
terminal.label_value("workers", "none")
|
|
163
|
+
except Exception:
|
|
164
|
+
terminal.label_value("workers", "none")
|
|
165
|
+
|
|
166
|
+
# Schedules DB
|
|
167
|
+
schedule_path = Path(".voodoo/state/schedules.db")
|
|
168
|
+
if schedule_path.exists():
|
|
169
|
+
terminal.status("scheduler", "ready")
|
|
170
|
+
else:
|
|
171
|
+
terminal.status("scheduler", "will be created")
|
|
172
|
+
|
|
173
|
+
terminal.blank()
|
|
@@ -17,7 +17,7 @@ Compatibility policy (see ``docs/protocol.md``):
|
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
-
from datetime import datetime
|
|
20
|
+
from datetime import UTC, datetime
|
|
21
21
|
from enum import StrEnum
|
|
22
22
|
from typing import Any
|
|
23
23
|
|
|
@@ -150,8 +150,8 @@ class Identity(BaseModel):
|
|
|
150
150
|
id: str = Field(description="Globally unique entity ID (UUID4).")
|
|
151
151
|
kind: str = Field(description="Entity kind (e.g. 'agent', 'execution', 'task').")
|
|
152
152
|
owner: str | None = None
|
|
153
|
-
created_at: datetime = Field(default_factory=datetime.
|
|
154
|
-
updated_at: datetime = Field(default_factory=datetime.
|
|
153
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
154
|
+
updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
155
155
|
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
156
156
|
|
|
157
157
|
|
|
@@ -165,7 +165,7 @@ class Capability(BaseModel):
|
|
|
165
165
|
delegate_to: str | None = None
|
|
166
166
|
expires_at: datetime | None = None
|
|
167
167
|
revoked: bool = False
|
|
168
|
-
issued_at: datetime = Field(default_factory=datetime.
|
|
168
|
+
issued_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
169
169
|
issued_by: str | None = None
|
|
170
170
|
|
|
171
171
|
|
|
@@ -228,8 +228,8 @@ class Intent(BaseModel):
|
|
|
228
228
|
requires: list[str] = Field(default_factory=list)
|
|
229
229
|
constraints: list[Constraint] = Field(default_factory=list)
|
|
230
230
|
effect_ids: list[str] = Field(default_factory=list)
|
|
231
|
-
created_at: datetime = Field(default_factory=datetime.
|
|
232
|
-
updated_at: datetime = Field(default_factory=datetime.
|
|
231
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
232
|
+
updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
233
233
|
result: Any = None
|
|
234
234
|
error: str | None = None
|
|
235
235
|
|
|
@@ -248,7 +248,7 @@ class Effect(BaseModel):
|
|
|
248
248
|
status: EffectStatus = EffectStatus.PENDING
|
|
249
249
|
result: Any = None
|
|
250
250
|
error: str | None = None
|
|
251
|
-
created_at: datetime = Field(default_factory=datetime.
|
|
251
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
252
252
|
executed_at: datetime | None = None
|
|
253
253
|
actor: str | None = None
|
|
254
254
|
principal: str | None = None
|
|
@@ -275,10 +275,76 @@ class Execution(BaseModel):
|
|
|
275
275
|
error: str | None = None
|
|
276
276
|
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
277
277
|
checkpoint: dict[str, Any] | None = None
|
|
278
|
-
created_at: datetime = Field(default_factory=datetime.
|
|
278
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
279
279
|
started_at: datetime | None = None
|
|
280
280
|
completed_at: datetime | None = None
|
|
281
281
|
|
|
282
|
+
# -- runtime conversion ------------------------------------------------
|
|
283
|
+
|
|
284
|
+
@classmethod
|
|
285
|
+
def from_runtime_execution(
|
|
286
|
+
cls,
|
|
287
|
+
exec: Any, # noqa: ANN401 — runtime.Execution
|
|
288
|
+
) -> Execution:
|
|
289
|
+
"""Convert a runtime ``Execution`` to a protocol ``Execution``.
|
|
290
|
+
|
|
291
|
+
Runtime ``State`` objects in ``state_changes`` are converted to dicts
|
|
292
|
+
via ``model_dump()`` so the protocol representation is JSON-safe.
|
|
293
|
+
"""
|
|
294
|
+
return cls(
|
|
295
|
+
id=exec.id,
|
|
296
|
+
trace_id=exec.trace_id,
|
|
297
|
+
parent_execution_id=exec.parent_execution_id,
|
|
298
|
+
status=ExecutionStatus(exec.status.value),
|
|
299
|
+
intent=exec.intent,
|
|
300
|
+
actor=exec.actor,
|
|
301
|
+
compute=exec.compute,
|
|
302
|
+
capabilities=list(exec.capabilities),
|
|
303
|
+
resources=exec.resources,
|
|
304
|
+
effects=list(exec.effects),
|
|
305
|
+
state_changes=[
|
|
306
|
+
s.model_dump() if hasattr(s, "model_dump") else s
|
|
307
|
+
for s in exec.state_changes
|
|
308
|
+
],
|
|
309
|
+
result=exec.result,
|
|
310
|
+
error=exec.error,
|
|
311
|
+
metadata=dict(exec.metadata),
|
|
312
|
+
checkpoint=exec.checkpoint,
|
|
313
|
+
created_at=exec.created_at,
|
|
314
|
+
started_at=exec.started_at,
|
|
315
|
+
completed_at=exec.completed_at,
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
def to_runtime_execution(self) -> Any: # noqa: ANN401
|
|
319
|
+
"""Convert this protocol ``Execution`` back to a runtime ``Execution``.
|
|
320
|
+
|
|
321
|
+
Requires ``voodoo.runtime.execution.Execution`` to be importable.
|
|
322
|
+
Uses a lazy import to avoid circular dependencies.
|
|
323
|
+
"""
|
|
324
|
+
from voodoo.runtime.execution import Execution as RuntimeExecution
|
|
325
|
+
from voodoo.runtime.execution import ExecutionStatus as RuntimeStatus
|
|
326
|
+
|
|
327
|
+
return RuntimeExecution(
|
|
328
|
+
id=self.id,
|
|
329
|
+
trace_id=self.trace_id,
|
|
330
|
+
parent_execution_id=self.parent_execution_id,
|
|
331
|
+
status=RuntimeStatus(self.status.value),
|
|
332
|
+
intent=self.intent,
|
|
333
|
+
actor=self.actor,
|
|
334
|
+
compute=self.compute,
|
|
335
|
+
capabilities=list(self.capabilities),
|
|
336
|
+
resources=self.resources,
|
|
337
|
+
effects=list(self.effects),
|
|
338
|
+
state_changes=self.state_changes,
|
|
339
|
+
result=self.result,
|
|
340
|
+
error=self.error,
|
|
341
|
+
metadata=dict(self.metadata),
|
|
342
|
+
checkpoint=self.checkpoint,
|
|
343
|
+
created_at=self.created_at,
|
|
344
|
+
started_at=self.started_at,
|
|
345
|
+
completed_at=self.completed_at,
|
|
346
|
+
)
|
|
347
|
+
|
|
282
348
|
|
|
283
349
|
class Task(BaseModel):
|
|
284
350
|
"""A queued work item (worker task)."""
|
|
@@ -326,7 +392,7 @@ class ObjectRef(BaseModel):
|
|
|
326
392
|
content_type: str | None = None
|
|
327
393
|
checksum: str | None = None
|
|
328
394
|
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
329
|
-
created_at: datetime = Field(default_factory=datetime.
|
|
395
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
330
396
|
|
|
331
397
|
|
|
332
398
|
class Error(BaseModel):
|
|
@@ -339,7 +405,7 @@ class Error(BaseModel):
|
|
|
339
405
|
trace_id: str | None = None
|
|
340
406
|
details: dict[str, Any] = Field(default_factory=dict)
|
|
341
407
|
cause: str | None = None
|
|
342
|
-
timestamp: datetime = Field(default_factory=datetime.
|
|
408
|
+
timestamp: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
343
409
|
|
|
344
410
|
|
|
345
411
|
class TelemetrySpan(BaseModel):
|
|
@@ -370,7 +436,7 @@ class Approval(BaseModel):
|
|
|
370
436
|
decided_by: str | None = None
|
|
371
437
|
decided_at: datetime | None = None
|
|
372
438
|
reason: str | None = None
|
|
373
|
-
created_at: datetime = Field(default_factory=datetime.
|
|
439
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
374
440
|
participant: str | None = None
|
|
375
441
|
|
|
376
442
|
|
|
@@ -389,8 +455,8 @@ class AgentEntity(BaseModel):
|
|
|
389
455
|
config: dict[str, Any] = Field(default_factory=dict)
|
|
390
456
|
state: str = "active"
|
|
391
457
|
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
392
|
-
created_at: datetime = Field(default_factory=datetime.
|
|
393
|
-
updated_at: datetime = Field(default_factory=datetime.
|
|
458
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
459
|
+
updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
394
460
|
|
|
395
461
|
|
|
396
462
|
class AgentRun(BaseModel):
|
|
@@ -424,8 +490,8 @@ class MemoryEntry(BaseModel):
|
|
|
424
490
|
tags: list[str] = Field(default_factory=list)
|
|
425
491
|
source_execution_id: str | None = None
|
|
426
492
|
importance: float = 0.5
|
|
427
|
-
created_at: datetime = Field(default_factory=datetime.
|
|
428
|
-
updated_at: datetime = Field(default_factory=datetime.
|
|
493
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
494
|
+
updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
429
495
|
expires_at: datetime | None = None
|
|
430
496
|
|
|
431
497
|
|