voodoo-framework 1.2.0__tar.gz → 1.4.0__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-1.2.0 → voodoo_framework-1.4.0}/PKG-INFO +1 -1
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/__init__.py +1 -1
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/cli/__init__.py +2 -1
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/cli/inspect.py +83 -37
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/cli/new.py +0 -1
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/cli/recover.py +12 -4
- voodoo_framework-1.4.0/src/voodoo/cli/tasks.py +114 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/cli/terminal.py +3 -1
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/data/__init__.py +7 -4
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/data/base.py +37 -12
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/data/model.py +5 -7
- voodoo_framework-1.4.0/src/voodoo/queue.py +12 -0
- voodoo_framework-1.4.0/src/voodoo/storage/__init__.py +15 -0
- voodoo_framework-1.4.0/src/voodoo/storage/database/__init__.py +29 -0
- voodoo_framework-1.4.0/src/voodoo/storage/database/interfaces.py +80 -0
- voodoo_framework-1.4.0/src/voodoo/storage/database/sqlite.py +198 -0
- voodoo_framework-1.4.0/src/voodoo/storage/queue/__init__.py +31 -0
- voodoo_framework-1.4.0/src/voodoo/storage/queue/interfaces.py +231 -0
- voodoo_framework-1.4.0/src/voodoo/storage/queue/memory.py +326 -0
- voodoo_framework-1.4.0/src/voodoo/storage/queue/sqlite.py +364 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/workers/__init__.py +2 -5
- voodoo_framework-1.4.0/src/voodoo/workers/queue.py +160 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo_framework.egg-info/PKG-INFO +1 -1
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo_framework.egg-info/SOURCES.txt +10 -1
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_integration.py +16 -11
- voodoo_framework-1.2.0/src/voodoo/queue.py +0 -22
- voodoo_framework-1.2.0/src/voodoo/workers/queue.py +0 -94
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/README.md +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/pyproject.toml +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/setup.cfg +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/adapters/__init__.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/adapters/tailwind.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/adapters/voodoo_css.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/agent.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/ai/__init__.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/ai/agent.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/ai/providers/__init__.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/ai/providers/anthropic.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/ai/providers/gemini.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/ai/providers/mock.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/ai/providers/ollama.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/ai/providers/openai.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/ai/tools/__init__.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/ai/tools/registry.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/api.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/auth/__init__.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/auth/cookies.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/auth/guards.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/auth/jwt.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/auth/middleware.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/auth/passwords.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/auth/user.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/cli/__main__.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/cli/ai.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/cli/auth.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/cli/dev.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/cli/doctor.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/cli/generate.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/cli/routes.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/cli/scaffolding.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/cli/version.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/components.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/config.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/core/__init__.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/core/app.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/core/errors.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/core/events.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/core/render.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/core/routing.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/core/sitemap.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/core/state.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/i18n.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/mcp/__init__.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/mcp/client.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/mesh/__init__.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/mesh/client.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/primitives/__init__.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/primitives/capability.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/primitives/compute.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/primitives/constraint.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/primitives/effect.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/primitives/intent.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/primitives/resource.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/primitives/state.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/primitives/time.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/py.typed +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/routing/api.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/routing/pages.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/runtime/__init__.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/runtime/adaptive.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/runtime/capability.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/runtime/constraint.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/runtime/context.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/runtime/engine.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/runtime/errors.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/runtime/execution.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/runtime/graph.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/runtime/human.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/runtime/persistence.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/runtime/planner.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/runtime/task.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/runtime/workflow.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/security/__init__.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/security/cors.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/security/csrf.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/security/headers.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/security/passwords.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/security/rate_limit.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/seo.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/static/client.js +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/status.py +0 -0
- /voodoo_framework-1.2.0/src/voodoo/storage.py → /voodoo_framework-1.4.0/src/voodoo/storage/manager.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/telemetry/__init__.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/telemetry/api.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/telemetry/middleware.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/telemetry/store.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/theme.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/tools/__init__.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/tools/registry.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/ui/__init__.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/ui/component.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/ui/events.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/ui/library.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/ui/rendering.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/ui/state.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/ui/styles/__init__.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo/ui/styles/theme.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo_framework.egg-info/dependency_links.txt +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo_framework.egg-info/entry_points.txt +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo_framework.egg-info/requires.txt +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/src/voodoo_framework.egg-info/top_level.txt +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_agent.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_agent_runtime.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_app.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_auth.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_cli.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_cli_inspect.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_components.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_contract_api.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_data.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_events.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_http_runtime.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_human.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_i18n.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_mcp.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_mcp_runtime.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_mesh.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_model.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_persistence.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_planner_adaptive.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_primitives.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_providers.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_queue.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_reactive.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_runtime.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_security.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_seo.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_state.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_theme.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_tools.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_ui.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_websocket.py +0 -0
- {voodoo_framework-1.2.0 → voodoo_framework-1.4.0}/tests/test_workers.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import typer
|
|
2
2
|
|
|
3
|
-
from voodoo.cli import ai, dev, doctor, generate, new, recover, routes, version
|
|
3
|
+
from voodoo.cli import ai, dev, doctor, generate, new, recover, routes, tasks, version
|
|
4
4
|
from voodoo.cli.auth import auth_app
|
|
5
5
|
from voodoo.cli.inspect import inspect_app
|
|
6
6
|
|
|
@@ -18,6 +18,7 @@ app.command()(generate.generate)
|
|
|
18
18
|
app.add_typer(auth_app, name="auth")
|
|
19
19
|
app.add_typer(ai.ai_app, name="ai")
|
|
20
20
|
app.add_typer(inspect_app, name="inspect")
|
|
21
|
+
app.add_typer(tasks.tasks_app, name="tasks")
|
|
21
22
|
app.command()(version.version)
|
|
22
23
|
app.command()(doctor.doctor)
|
|
23
24
|
app.command()(routes.routes)
|
|
@@ -52,13 +52,15 @@ def _load_app(app_str: str | None) -> Any:
|
|
|
52
52
|
return getattr(mod, attr, None)
|
|
53
53
|
|
|
54
54
|
|
|
55
|
-
|
|
56
55
|
def _is_json(json_mode: bool) -> bool:
|
|
57
56
|
"""JSON mode: explicit flag or global --json in argv."""
|
|
58
57
|
return bool(json_mode) or terminal.is_json_mode()
|
|
59
58
|
|
|
59
|
+
|
|
60
60
|
def _table(columns: list[str]) -> Table:
|
|
61
|
-
table = Table(
|
|
61
|
+
table = Table(
|
|
62
|
+
show_header=True, header_style="dim", border_style="#262626", pad_edge=False
|
|
63
|
+
)
|
|
62
64
|
for c in columns:
|
|
63
65
|
table.add_column(c)
|
|
64
66
|
return table
|
|
@@ -75,9 +77,13 @@ def _emit(data: Any, json_mode: bool = False) -> None:
|
|
|
75
77
|
|
|
76
78
|
@inspect_app.command("run")
|
|
77
79
|
def inspect_run(
|
|
78
|
-
execution_id: str = typer.Argument(
|
|
80
|
+
execution_id: str = typer.Argument(
|
|
81
|
+
None, help="Execution id (lists recent if omitted)"
|
|
82
|
+
),
|
|
79
83
|
app_str: str = typer.Option(None, "--app", help="App instance (e.g. main:app)"),
|
|
80
|
-
json_mode: bool = typer.Option(
|
|
84
|
+
json_mode: bool = typer.Option(
|
|
85
|
+
False, "--json", help="Output machine-readable JSON"
|
|
86
|
+
),
|
|
81
87
|
):
|
|
82
88
|
"""Show an execution — status, intent, capabilities, effects, cost."""
|
|
83
89
|
from voodoo.runtime import engine
|
|
@@ -121,7 +127,10 @@ def inspect_run(
|
|
|
121
127
|
("intent", d["intent"] or "-"),
|
|
122
128
|
("actor", d["actor"]),
|
|
123
129
|
("cost", f"{d['cost']:.6f}"),
|
|
124
|
-
(
|
|
130
|
+
(
|
|
131
|
+
"duration",
|
|
132
|
+
f"{d['duration_seconds']:.3f}s" if d["duration_seconds"] else "-",
|
|
133
|
+
),
|
|
125
134
|
]
|
|
126
135
|
)
|
|
127
136
|
if d["capabilities"]:
|
|
@@ -136,8 +145,12 @@ def inspect_run(
|
|
|
136
145
|
@inspect_app.command("agent")
|
|
137
146
|
def inspect_agent(
|
|
138
147
|
app_str: str = typer.Option(None, "--app"),
|
|
139
|
-
json_mode: bool = typer.Option(
|
|
140
|
-
|
|
148
|
+
json_mode: bool = typer.Option(
|
|
149
|
+
False, "--json", help="Output machine-readable JSON"
|
|
150
|
+
),
|
|
151
|
+
url: str = typer.Option(
|
|
152
|
+
None, "--url", help="Live server URL (e.g. http://localhost:8000)"
|
|
153
|
+
),
|
|
141
154
|
limit: int = typer.Option(10, "--limit"),
|
|
142
155
|
):
|
|
143
156
|
"""Recent agent runs: model, tokens, cost, tool calls, status."""
|
|
@@ -188,10 +201,16 @@ def inspect_agent(
|
|
|
188
201
|
|
|
189
202
|
@inspect_app.command("plan")
|
|
190
203
|
def inspect_plan(
|
|
191
|
-
intent_name: str = typer.Argument(
|
|
192
|
-
|
|
204
|
+
intent_name: str = typer.Argument(
|
|
205
|
+
..., help="Intent name to plan (e.g. 'notify.customer')"
|
|
206
|
+
),
|
|
207
|
+
requires: str = typer.Option(
|
|
208
|
+
None, "--requires", help="Comma-separated required capabilities"
|
|
209
|
+
),
|
|
193
210
|
app_str: str = typer.Option(None, "--app", help="App instance (e.g. main:app)"),
|
|
194
|
-
json_mode: bool = typer.Option(
|
|
211
|
+
json_mode: bool = typer.Option(
|
|
212
|
+
False, "--json", help="Output machine-readable JSON"
|
|
213
|
+
),
|
|
195
214
|
):
|
|
196
215
|
"""Plan an intent — show the planner's strategy + participant assignment."""
|
|
197
216
|
from voodoo.primitives.intent import Intent
|
|
@@ -215,12 +234,14 @@ def inspect_plan(
|
|
|
215
234
|
terminal.json_output(data)
|
|
216
235
|
return
|
|
217
236
|
|
|
218
|
-
terminal.status_block(
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
237
|
+
terminal.status_block(
|
|
238
|
+
[
|
|
239
|
+
("intent", data["intent"]),
|
|
240
|
+
("strategy", data["strategy"]),
|
|
241
|
+
("steps", str(len(data["steps"]))),
|
|
242
|
+
("unresolved", str(len(data["unresolved"]))),
|
|
243
|
+
]
|
|
244
|
+
)
|
|
224
245
|
if data["decisions"]:
|
|
225
246
|
terminal.blank()
|
|
226
247
|
for d in data["decisions"]:
|
|
@@ -234,7 +255,9 @@ def inspect_plan(
|
|
|
234
255
|
@inspect_app.command("tool")
|
|
235
256
|
def inspect_tool(
|
|
236
257
|
app_str: str = typer.Option(None, "--app"),
|
|
237
|
-
json_mode: bool = typer.Option(
|
|
258
|
+
json_mode: bool = typer.Option(
|
|
259
|
+
False, "--json", help="Output machine-readable JSON"
|
|
260
|
+
),
|
|
238
261
|
limit: int = typer.Option(15, "--limit"),
|
|
239
262
|
):
|
|
240
263
|
"""Recent tool calls: name, latency, error."""
|
|
@@ -263,7 +286,9 @@ def inspect_tool(
|
|
|
263
286
|
@inspect_app.command("task")
|
|
264
287
|
def inspect_task(
|
|
265
288
|
app_str: str = typer.Option(None, "--app"),
|
|
266
|
-
json_mode: bool = typer.Option(
|
|
289
|
+
json_mode: bool = typer.Option(
|
|
290
|
+
False, "--json", help="Output machine-readable JSON"
|
|
291
|
+
),
|
|
267
292
|
limit: int = typer.Option(20, "--limit"),
|
|
268
293
|
):
|
|
269
294
|
"""Executions whose actor is task/agent-driven (Task units)."""
|
|
@@ -294,7 +319,9 @@ def inspect_task(
|
|
|
294
319
|
def inspect_workflow(
|
|
295
320
|
trace_id: str = typer.Argument(None, help="Trace id (groups task executions)"),
|
|
296
321
|
app_str: str = typer.Option(None, "--app"),
|
|
297
|
-
json_mode: bool = typer.Option(
|
|
322
|
+
json_mode: bool = typer.Option(
|
|
323
|
+
False, "--json", help="Output machine-readable JSON"
|
|
324
|
+
),
|
|
298
325
|
):
|
|
299
326
|
"""Executions grouped by trace — the observable workflow structure."""
|
|
300
327
|
from voodoo.runtime import ExecutionGraph, engine
|
|
@@ -313,10 +340,15 @@ def inspect_workflow(
|
|
|
313
340
|
terminal.muted("no executions recorded in this process")
|
|
314
341
|
for root in described:
|
|
315
342
|
terminal.heading(f"trace {root['trace_id'][:8]} · {root.get('intent') or '-'}")
|
|
316
|
-
terminal.tree(
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
343
|
+
terminal.tree(
|
|
344
|
+
[
|
|
345
|
+
f"{root['intent'] or root['id'][:8]} [{root['status']}]",
|
|
346
|
+
*[
|
|
347
|
+
f" {c.get('intent') or c['id'][:8]} [{c['status']}] (actor: {c.get('actor')})"
|
|
348
|
+
for c in root.get("children", [])
|
|
349
|
+
],
|
|
350
|
+
]
|
|
351
|
+
)
|
|
320
352
|
terminal.blank()
|
|
321
353
|
|
|
322
354
|
|
|
@@ -324,7 +356,9 @@ def inspect_workflow(
|
|
|
324
356
|
def inspect_state(
|
|
325
357
|
entity: str = typer.Argument(None, help="Entity kind (e.g. lead, order)"),
|
|
326
358
|
app_str: str = typer.Option(None, "--app"),
|
|
327
|
-
json_mode: bool = typer.Option(
|
|
359
|
+
json_mode: bool = typer.Option(
|
|
360
|
+
False, "--json", help="Output machine-readable JSON"
|
|
361
|
+
),
|
|
328
362
|
):
|
|
329
363
|
"""Observable state changes recorded on executions."""
|
|
330
364
|
from voodoo.runtime import engine
|
|
@@ -365,7 +399,9 @@ def inspect_state(
|
|
|
365
399
|
@inspect_app.command("capabilities")
|
|
366
400
|
def inspect_capabilities(
|
|
367
401
|
app_str: str = typer.Option(None, "--app"),
|
|
368
|
-
json_mode: bool = typer.Option(
|
|
402
|
+
json_mode: bool = typer.Option(
|
|
403
|
+
False, "--json", help="Output machine-readable JSON"
|
|
404
|
+
),
|
|
369
405
|
):
|
|
370
406
|
"""Registered capability templates and tool permission requirements."""
|
|
371
407
|
from voodoo.runtime import engine
|
|
@@ -387,10 +423,12 @@ def inspect_capabilities(
|
|
|
387
423
|
if _is_json(json_mode):
|
|
388
424
|
terminal.json_output({"capabilities": cap_data, "tool_permissions": tool_perms})
|
|
389
425
|
return
|
|
390
|
-
terminal.status_block(
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
426
|
+
terminal.status_block(
|
|
427
|
+
[
|
|
428
|
+
("capabilities", str(len(cap_data["capabilities"]))),
|
|
429
|
+
("approval req.", str(len(cap_data["approval_required"]))),
|
|
430
|
+
]
|
|
431
|
+
)
|
|
394
432
|
if cap_data["capabilities"]:
|
|
395
433
|
terminal.label_value("names", ", ".join(cap_data["capabilities"]))
|
|
396
434
|
if tool_perms:
|
|
@@ -405,7 +443,9 @@ def inspect_capabilities(
|
|
|
405
443
|
@inspect_app.command("mesh")
|
|
406
444
|
def inspect_mesh(
|
|
407
445
|
app_str: str = typer.Option(None, "--app"),
|
|
408
|
-
json_mode: bool = typer.Option(
|
|
446
|
+
json_mode: bool = typer.Option(
|
|
447
|
+
False, "--json", help="Output machine-readable JSON"
|
|
448
|
+
),
|
|
409
449
|
):
|
|
410
450
|
"""Mesh surface: exposed functions, event handlers, active nodes."""
|
|
411
451
|
from voodoo.mesh import mesh
|
|
@@ -420,11 +460,13 @@ def inspect_mesh(
|
|
|
420
460
|
if _is_json(json_mode):
|
|
421
461
|
terminal.json_output(data)
|
|
422
462
|
return
|
|
423
|
-
terminal.status_block(
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
463
|
+
terminal.status_block(
|
|
464
|
+
[
|
|
465
|
+
("exposed", str(len(data["exposed"]))),
|
|
466
|
+
("event types", str(len(data["handlers"]))),
|
|
467
|
+
("active nodes", str(data["active_nodes"])),
|
|
468
|
+
]
|
|
469
|
+
)
|
|
428
470
|
if data["handlers"]:
|
|
429
471
|
terminal.blank()
|
|
430
472
|
table = _table(["event", "handlers"])
|
|
@@ -436,9 +478,13 @@ def inspect_mesh(
|
|
|
436
478
|
|
|
437
479
|
@inspect_app.command("approvals")
|
|
438
480
|
def inspect_approvals(
|
|
439
|
-
pending_only: bool = typer.Option(
|
|
481
|
+
pending_only: bool = typer.Option(
|
|
482
|
+
False, "--pending", help="Only show pending approvals"
|
|
483
|
+
),
|
|
440
484
|
app_str: str = typer.Option(None, "--app"),
|
|
441
|
-
json_mode: bool = typer.Option(
|
|
485
|
+
json_mode: bool = typer.Option(
|
|
486
|
+
False, "--json", help="Output machine-readable JSON"
|
|
487
|
+
),
|
|
442
488
|
):
|
|
443
489
|
"""Human approvals — pending and decided, with capability + requester."""
|
|
444
490
|
from voodoo.runtime import engine
|
|
@@ -9,7 +9,6 @@ from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
|
9
9
|
|
|
10
10
|
from voodoo.cli import terminal
|
|
11
11
|
|
|
12
|
-
|
|
13
12
|
# ---------------------------------------------------------------------------
|
|
14
13
|
# Offline scaffold — used when no template repository is available.
|
|
15
14
|
# Showcases Voodoo CSS (the default adapter), folder-based routing, and the
|
|
@@ -18,15 +18,21 @@ from voodoo.cli import terminal
|
|
|
18
18
|
|
|
19
19
|
def recover(
|
|
20
20
|
app_str: str = typer.Option(None, "--app", help="App instance (e.g. main:app)"),
|
|
21
|
-
store_path: str = typer.Option(
|
|
22
|
-
|
|
21
|
+
store_path: str = typer.Option(
|
|
22
|
+
None, "--store", help="Path to the JSONL execution store"
|
|
23
|
+
),
|
|
24
|
+
json_mode: bool = typer.Option(
|
|
25
|
+
False, "--json", help="Output machine-readable JSON"
|
|
26
|
+
),
|
|
23
27
|
):
|
|
24
28
|
"""Reload unfinished executions from the store into the engine."""
|
|
25
29
|
from voodoo.runtime.engine import engine as runtime_engine
|
|
26
30
|
from voodoo.runtime.persistence import JSONFileExecutionStore
|
|
27
31
|
|
|
28
32
|
if store_path is None:
|
|
29
|
-
store_path = os.environ.get(
|
|
33
|
+
store_path = os.environ.get(
|
|
34
|
+
"VOODOO_EXECUTION_STORE", ".voodoo/executions.jsonl"
|
|
35
|
+
)
|
|
30
36
|
|
|
31
37
|
if app_str is not None:
|
|
32
38
|
# Import the app first: it may attach its own store / register
|
|
@@ -57,7 +63,9 @@ def recover(
|
|
|
57
63
|
terminal.status_block([("store", store_path), ("recovered", str(len(recovered)))])
|
|
58
64
|
from rich.table import Table
|
|
59
65
|
|
|
60
|
-
table = Table(
|
|
66
|
+
table = Table(
|
|
67
|
+
show_header=True, header_style="dim", border_style="#262626", pad_edge=False
|
|
68
|
+
)
|
|
61
69
|
for c in ("id", "intent", "status", "actor"):
|
|
62
70
|
table.add_column(c)
|
|
63
71
|
for ex in recovered:
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""voodoo tasks — inspect and manage durable background tasks."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from voodoo.cli import terminal
|
|
8
|
+
|
|
9
|
+
tasks_app = typer.Typer(
|
|
10
|
+
name="tasks",
|
|
11
|
+
help="Inspect and manage durable background tasks.",
|
|
12
|
+
no_args_is_help=True,
|
|
13
|
+
rich_markup_mode="rich",
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _get_queue():
|
|
18
|
+
from voodoo.workers.queue import _get_queue
|
|
19
|
+
|
|
20
|
+
return _get_queue()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@tasks_app.command("list")
|
|
24
|
+
def list_tasks(
|
|
25
|
+
status: str = typer.Option(None, "--status", help="Filter by status"),
|
|
26
|
+
task_type: str = typer.Option(None, "--type", help="Filter by task type"),
|
|
27
|
+
json_mode: bool = typer.Option(False, "--json", help="Machine-readable JSON"),
|
|
28
|
+
):
|
|
29
|
+
"""List recent tasks."""
|
|
30
|
+
import asyncio
|
|
31
|
+
|
|
32
|
+
from voodoo.storage.queue import TaskStatus
|
|
33
|
+
|
|
34
|
+
async def run():
|
|
35
|
+
q = await _get_queue()
|
|
36
|
+
stat = TaskStatus(status) if status else None
|
|
37
|
+
tasks = await q.list(status=stat, task_type=task_type)
|
|
38
|
+
stats = await q.stats()
|
|
39
|
+
if json_mode or terminal.is_json_mode():
|
|
40
|
+
terminal.json_output(
|
|
41
|
+
{"stats": stats.describe(), "tasks": [t.describe() for t in tasks]}
|
|
42
|
+
)
|
|
43
|
+
return
|
|
44
|
+
terminal.wordmark()
|
|
45
|
+
terminal.blank()
|
|
46
|
+
terminal.status_block(
|
|
47
|
+
[
|
|
48
|
+
("provider", q.capabilities().provider),
|
|
49
|
+
("total", str(stats.total)),
|
|
50
|
+
("pending", str(stats.pending)),
|
|
51
|
+
("running", str(stats.running)),
|
|
52
|
+
("retrying", str(stats.retrying)),
|
|
53
|
+
("completed", str(stats.completed)),
|
|
54
|
+
("failed", str(stats.failed)),
|
|
55
|
+
]
|
|
56
|
+
)
|
|
57
|
+
terminal.blank()
|
|
58
|
+
if not tasks:
|
|
59
|
+
terminal.muted("no tasks found")
|
|
60
|
+
return
|
|
61
|
+
from rich.table import Table
|
|
62
|
+
|
|
63
|
+
table = Table(
|
|
64
|
+
show_header=True, header_style="dim", border_style="#262626", pad_edge=False
|
|
65
|
+
)
|
|
66
|
+
for c in ("id", "type", "status", "priority", "attempts", "created_at"):
|
|
67
|
+
table.add_column(c)
|
|
68
|
+
for t in tasks:
|
|
69
|
+
table.add_row(
|
|
70
|
+
str(t.id),
|
|
71
|
+
t.type,
|
|
72
|
+
t.status.value,
|
|
73
|
+
str(t.priority),
|
|
74
|
+
f"{t.attempts}/{t.max_attempts}",
|
|
75
|
+
t.created_at.strftime("%H:%M:%S") if t.created_at else "-",
|
|
76
|
+
)
|
|
77
|
+
terminal.console.print(table)
|
|
78
|
+
terminal.blank()
|
|
79
|
+
|
|
80
|
+
asyncio.run(run())
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@tasks_app.command("retry")
|
|
84
|
+
def retry_task(
|
|
85
|
+
task_id: int = typer.Argument(..., help="Task ID to retry"),
|
|
86
|
+
json_mode: bool = typer.Option(False, "--json", help="Machine-readable JSON"),
|
|
87
|
+
):
|
|
88
|
+
"""Manually retry a failed task."""
|
|
89
|
+
import asyncio
|
|
90
|
+
|
|
91
|
+
async def run():
|
|
92
|
+
q = await _get_queue()
|
|
93
|
+
result = await q.retry(task_id)
|
|
94
|
+
if result is None:
|
|
95
|
+
if json_mode or terminal.is_json_mode():
|
|
96
|
+
terminal.json_output(
|
|
97
|
+
{"error": f"task {task_id} not found or not failed"}
|
|
98
|
+
)
|
|
99
|
+
return
|
|
100
|
+
terminal.wordmark()
|
|
101
|
+
terminal.blank()
|
|
102
|
+
terminal.muted(f"task {task_id} not found or not failed")
|
|
103
|
+
return
|
|
104
|
+
if json_mode or terminal.is_json_mode():
|
|
105
|
+
terminal.json_output(result.describe())
|
|
106
|
+
return
|
|
107
|
+
terminal.wordmark()
|
|
108
|
+
terminal.blank()
|
|
109
|
+
terminal.status_block(
|
|
110
|
+
[("task", str(result.id)), ("status", result.status.value)]
|
|
111
|
+
)
|
|
112
|
+
terminal.blank()
|
|
113
|
+
|
|
114
|
+
asyncio.run(run())
|
|
@@ -151,7 +151,9 @@ def json_output(data: Any) -> None:
|
|
|
151
151
|
"""
|
|
152
152
|
import json
|
|
153
153
|
|
|
154
|
-
console.print(
|
|
154
|
+
console.print(
|
|
155
|
+
json.dumps(data, indent=2, default=str), highlight=False, soft_wrap=True
|
|
156
|
+
)
|
|
155
157
|
|
|
156
158
|
|
|
157
159
|
def is_json_mode() -> bool:
|
|
@@ -6,10 +6,13 @@ This package re-exports the names that previously lived in the flat
|
|
|
6
6
|
|
|
7
7
|
Storage backend boundary
|
|
8
8
|
-------------------------
|
|
9
|
-
The default backend is SQLite (via :mod:`aiosqlite`)
|
|
10
|
-
``
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
The default backend is SQLite (via :mod:`aiosqlite`) managed by the
|
|
10
|
+
``VoodooDatabase`` adapter in :mod:`voodoo.storage.database` — it owns
|
|
11
|
+
connection lifecycle, WAL pragmas and the ``schema_migrations`` ledger.
|
|
12
|
+
``init_db`` publishes the raw aiosqlite connection as
|
|
13
|
+
``_db_connection`` / ``get_db()``; everything above it (``BaseModel``,
|
|
14
|
+
``Model`` and the hooks) is backend-agnostic. A future PostgreSQL
|
|
15
|
+
adapter replaces the adapter, not this package.
|
|
13
16
|
"""
|
|
14
17
|
|
|
15
18
|
from voodoo.data.base import (
|
|
@@ -1,30 +1,54 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import os
|
|
3
2
|
from collections.abc import Callable
|
|
4
3
|
from typing import Any, get_type_hints
|
|
5
4
|
|
|
6
|
-
import
|
|
5
|
+
from voodoo.storage.database import Migration, SQLiteDatabase, VoodooDatabase
|
|
7
6
|
|
|
8
7
|
_db_connection = None
|
|
8
|
+
_database: SQLiteDatabase | None = None
|
|
9
9
|
_triggers: dict[str, dict[str, list[Callable]]] = {}
|
|
10
10
|
_rls_policies: dict[str, Callable] = {}
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
async def _ensure_user_tables(db: VoodooDatabase) -> None:
|
|
14
|
+
"""Create tables for every registered user model (idempotent DDL)."""
|
|
15
|
+
for model in _models:
|
|
16
|
+
await model._create_table()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Migration 0001: the user-model baseline. Runs once to enter the ledger,
|
|
20
|
+
# then re-runs its idempotent DDL on every ``init_db`` so models imported
|
|
21
|
+
# after the baseline still get their tables (preserves pre-migration
|
|
22
|
+
# behavior exactly).
|
|
23
|
+
USER_MODEL_BASELINE = Migration(
|
|
24
|
+
version=1,
|
|
25
|
+
name="user_model_baseline",
|
|
26
|
+
fn=_ensure_user_tables,
|
|
27
|
+
rerun=True,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
13
31
|
async def init_db(db_path: str = None):
|
|
32
|
+
"""Open (and migrate) the SQLite database backing the data layer.
|
|
33
|
+
|
|
34
|
+
The raw :class:`aiosqlite.Connection` remains the value of
|
|
35
|
+
``_db_connection`` / ``get_db()`` so all existing callers keep working;
|
|
36
|
+
schema management now flows through the ``SQLiteDatabase`` adapter and
|
|
37
|
+
its migration ledger.
|
|
38
|
+
"""
|
|
14
39
|
if db_path is None:
|
|
15
40
|
from voodoo.config import config
|
|
16
41
|
|
|
17
42
|
db_path = config.db_path
|
|
18
43
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
await model._create_table()
|
|
44
|
+
global _db_connection, _database
|
|
45
|
+
database = SQLiteDatabase(db_path, migrations=(USER_MODEL_BASELINE,))
|
|
46
|
+
await database.connect()
|
|
47
|
+
_db_connection = database.connection
|
|
48
|
+
_database = database
|
|
49
|
+
# Migrate after the connection is published so rerunnable migration
|
|
50
|
+
# functions can use ``get_db()``.
|
|
51
|
+
await database.migrate()
|
|
28
52
|
|
|
29
53
|
|
|
30
54
|
async def get_db():
|
|
@@ -40,12 +64,13 @@ async def close_db():
|
|
|
40
64
|
unclosed connection would keep the interpreter alive at shutdown.
|
|
41
65
|
No-op when no connection is open (keeps startup lazy).
|
|
42
66
|
"""
|
|
43
|
-
global _db_connection
|
|
67
|
+
global _db_connection, _database
|
|
44
68
|
if _db_connection is not None:
|
|
45
69
|
try:
|
|
46
70
|
await _db_connection.close()
|
|
47
71
|
finally:
|
|
48
72
|
_db_connection = None
|
|
73
|
+
_database = None
|
|
49
74
|
|
|
50
75
|
|
|
51
76
|
def _get_table_name(cls_or_obj: Any) -> str:
|
|
@@ -8,13 +8,11 @@ storage layer and the ``on_insert`` / ``on_update`` hook system from
|
|
|
8
8
|
Storage backend boundary
|
|
9
9
|
-------------------------
|
|
10
10
|
Everything in this module talks to the database through ``get_db()``
|
|
11
|
-
and builds portable ANSI-SQL strings.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
future migration runner can hook into ``init_db`` without changing any
|
|
17
|
-
call-site here.
|
|
11
|
+
and builds portable ANSI-SQL strings. Schema management flows through
|
|
12
|
+
the ``VoodooDatabase`` adapter in :mod:`voodoo.storage.database`
|
|
13
|
+
(SQLite default); the user-model DDL is migration 0001 in its ledger.
|
|
14
|
+
Swapping backends means providing another adapter — no call-site here
|
|
15
|
+
changes.
|
|
18
16
|
"""
|
|
19
17
|
|
|
20
18
|
from typing import Any, get_type_hints
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Compatibility alias — the durable queue lives in ``voodoo.workers.queue``.
|
|
2
|
+
|
|
3
|
+
This shim replaces itself with the real module in ``sys.modules`` so that
|
|
4
|
+
``voodoo.queue is voodoo.workers.queue`` and mutable globals (``_workers``,
|
|
5
|
+
``_worker_tasks``) are always current.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
from voodoo.workers import queue # noqa: F401
|
|
11
|
+
|
|
12
|
+
sys.modules[__name__] = queue
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Voodoo storage package.
|
|
2
|
+
|
|
3
|
+
Two concerns live here:
|
|
4
|
+
|
|
5
|
+
- ``voodoo.storage.manager`` — the legacy ``StorageManager`` facade over
|
|
6
|
+
local-filesystem / S3-compatible uploads (import-compatible re-export of
|
|
7
|
+
the former flat ``voodoo/storage.py`` module).
|
|
8
|
+
- ``voodoo.storage.database`` — the ``VoodooDatabase`` capability adapters
|
|
9
|
+
(SQLite default, PostgreSQL later) that own all durable schema through
|
|
10
|
+
the migration runner.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from voodoo.storage.manager import StorageManager, storage
|
|
14
|
+
|
|
15
|
+
__all__ = ["StorageManager", "storage"]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Voodoo database adapters (spec §11).
|
|
2
|
+
|
|
3
|
+
SQLite is the embedded default; PostgreSQL arrives behind the same
|
|
4
|
+
``VoodooDatabase`` protocol as an optional extra. Migrations form one global
|
|
5
|
+
version namespace: 1 = user-model baseline (registered by ``voodoo.data``),
|
|
6
|
+
2+ reserved by the framework, 100+ available to applications.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from voodoo.storage.database.interfaces import (
|
|
10
|
+
DatabaseCapabilities,
|
|
11
|
+
Migration,
|
|
12
|
+
VoodooDatabase,
|
|
13
|
+
)
|
|
14
|
+
from voodoo.storage.database.sqlite import (
|
|
15
|
+
FRAMEWORK_MIGRATIONS,
|
|
16
|
+
LEDGER_TABLE,
|
|
17
|
+
SQLiteDatabase,
|
|
18
|
+
register_framework_migration,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"DatabaseCapabilities",
|
|
23
|
+
"FRAMEWORK_MIGRATIONS",
|
|
24
|
+
"LEDGER_TABLE",
|
|
25
|
+
"Migration",
|
|
26
|
+
"SQLiteDatabase",
|
|
27
|
+
"VoodooDatabase",
|
|
28
|
+
"register_framework_migration",
|
|
29
|
+
]
|